Compare commits

...

8 Commits

Author SHA1 Message Date
Giovanni Carvelli
57ad7bf4d0 Merge 7673503228 into 65646f4493 2024-11-10 08:30:55 -05:00
NoDRM
65646f4493 Fix CI 2024-11-10 14:25:35 +01:00
Josh Cotton
808dc7d29a Fix Obok import in Calibre flatpak by using /sys/class/net/IFACE/address instead of ip (#586)
Fix #585.
Use /sys/class/net/IFACE/address for the MAC address instead of the ip
command.
2024-11-10 13:14:59 +00:00
precondition
2cd2792306 Obok.py/action.py: invoke _() only once 2024-11-10 13:11:28 +00:00
precondition
2e53d70e88 Catch FileNotFoundError due to undownloaded ebooks 2024-11-10 13:11:28 +00:00
Ben Combee
05fff5217b Fix crash using bare sha1 symbol
Use sha1 from hashlib, as it isn't imported globally, fixed crash trying to decrypt a eReader PDB file
2024-11-10 13:10:11 +00:00
Martin Rys
34c4c067e8 DeDRM ion: Correctly throw last exception if decrypt fails 2024-11-10 13:09:45 +00:00
Martin Rys
195ea69537 DeDRM ion: Clean out errorneous whitespace and UTF8 definition from python 2 times 2024-11-10 13:09:45 +00:00
5 changed files with 45 additions and 37 deletions

View File

@@ -14,7 +14,7 @@ jobs:
run: python3 make_release.py run: python3 make_release.py
- name: Upload - name: Upload
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: plugin name: plugin
path: | path: |

View File

@@ -255,7 +255,7 @@ class EreaderProcessor(object):
encrypted_key = r[172:172+8] encrypted_key = r[172:172+8]
encrypted_key_sha = r[56:56+20] encrypted_key_sha = r[56:56+20]
self.content_key = des.decrypt(encrypted_key) self.content_key = des.decrypt(encrypted_key)
if sha1(self.content_key).digest() != encrypted_key_sha: if hashlib.sha1(self.content_key).digest() != encrypted_key_sha:
raise ValueError('Incorrect Name and/or Credit Card') raise ValueError('Incorrect Name and/or Credit Card')
def getNumImages(self): def getNumImages(self):

View File

@@ -1,25 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- """ion.py: Decrypt Kindle KFX files.
# ion.py Revision history:
# Copyright © 2013-2020 Apprentice Harper et al. Pascal implementation by lulzkabulz.
BinaryIon.pas + DrmIon.pas + IonSymbols.pas
__license__ = 'GPL v3' 1.0 - Python translation by apprenticenaomi.
__version__ = '3.0' 1.1 - DeDRM integration by anon.
1.2 - Added pylzma import fallback
# Revision history: 1.3 - Fixed lzma support for calibre 4.6+
# Pascal implementation by lulzkabulz. 2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
# BinaryIon.pas + DrmIon.pas + IonSymbols.pas 3.0 - Added Python 3 compatibility for calibre 5.0
# 1.0 - Python translation by apprenticenaomi.
# 1.1 - DeDRM integration by anon.
# 1.2 - Added pylzma import fallback
# 1.3 - Fixed lzma support for calibre 4.6+
# 2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
# 3.0 - Added Python 3 compatibility for calibre 5.0
Copyright © 2013-2020 Apprentice Harper et al.
""" """
Decrypt Kindle KFX files. from __future__ import annotations
"""
import collections import collections
import hashlib import hashlib
@@ -30,6 +24,9 @@ import struct
from io import BytesIO from io import BytesIO
__license__ = 'GPL v3'
__version__ = '3.0'
#@@CALIBRE_COMPAT_CODE@@ #@@CALIBRE_COMPAT_CODE@@
@@ -1096,11 +1093,11 @@ def process_V9888(st):
ws.sbox(d0x6a0bf4d0,d0x6a0dab50,[1,2]) ws.sbox(d0x6a0bf4d0,d0x6a0dab50,[1,2])
ws.sbox(d0x6a0ba4d0,d0x6a0dab50,[1,2]) ws.sbox(d0x6a0ba4d0,d0x6a0dab50,[1,2])
ws.shuffle(repl) ws.shuffle(repl)
ws.shuffle(repl) ws.shuffle(repl)
ws.shuffle(repl) ws.shuffle(repl)
ws.sbox(d0x6a0bf4d0,d0x6a0dab50,[1,2]) ws.sbox(d0x6a0bf4d0,d0x6a0dab50,[1,2])
ws.sbox(d0x6a0ba4d0,d0x6a0dab50,[1,2]) ws.sbox(d0x6a0ba4d0,d0x6a0dab50,[1,2])
ws.exlookup(d0x6a0be4d0) ws.exlookup(d0x6a0be4d0)
dat=ws.mask(st[sto:sto+16]) dat=ws.mask(st[sto:sto+16])
out+=dat out+=dat
sto+=16 sto+=16
@@ -1124,7 +1121,7 @@ def process_V4648(st):
ws.sbox(d0x6a0c51a8,d0x6a0dab50,[1,3]) ws.sbox(d0x6a0c51a8,d0x6a0dab50,[1,3])
ws.shuffle(repl) ws.shuffle(repl)
ws.shuffle(repl) ws.shuffle(repl)
ws.exlookup(d0x6a0c91a8) ws.exlookup(d0x6a0c91a8)
dat=ws.mask(st[sto:sto+16]) dat=ws.mask(st[sto:sto+16])
out+=dat out+=dat
sto+=16 sto+=16
@@ -1148,7 +1145,7 @@ def process_V5683(st):
ws.shuffle(repl) ws.shuffle(repl)
ws.sbox(d0x6a0cfe80,d0x6a0dab50,[]) ws.sbox(d0x6a0cfe80,d0x6a0dab50,[])
ws.shuffle(repl) ws.shuffle(repl)
ws.exlookup(d0x6a0d3e80) ws.exlookup(d0x6a0d3e80)
dat=ws.mask(st[sto:sto+16]) dat=ws.mask(st[sto:sto+16])
out+=dat out+=dat
sto+=16 sto+=16
@@ -1163,12 +1160,12 @@ def process_V5683(st):
# if a<0: a=256+a # if a<0: a=256+a
# ax.append(ha[(a>>4)]+ha[a%16]) # ax.append(ha[(a>>4)]+ha[a%16])
# return "".join(ax) # return "".join(ax)
# #
# def memhex(adr,sz): # def memhex(adr,sz):
# emu=EmulatorHelper(currentProgram) # emu=EmulatorHelper(currentProgram)
# arr=emu.readMemory(getAddress(adr),sz) # arr=emu.readMemory(getAddress(adr),sz)
# return a2hex(arr) # return a2hex(arr)
# #
@@ -1254,7 +1251,7 @@ def scramble3(st,magic):
iVar5 = iVar5 - magic iVar5 = iVar5 - magic
index -= 1 index -= 1
if uVar4<=-1: break if uVar4<=-1: break
else: else:
if (offset < magic): if (offset < magic):
iVar3 = 0 iVar3 = 0
else : else :
@@ -1274,9 +1271,9 @@ def scramble3(st,magic):
index=index-1 index=index-1
iVar5 = iVar5 + magic; iVar5 = iVar5 + magic;
cntr += 1; cntr += 1;
if iVar3>=divs: break if iVar3>=divs: break
offset = offset + 1 offset = offset + 1
if offset >= ((magic - 1) + divs) :break if offset >= ((magic - 1) + divs) :break
return ret return ret
#not sure if the third variant is used anywhere, but it is in Kindle, so I tried to add it #not sure if the third variant is used anywhere, but it is in Kindle, so I tried to add it
@@ -1342,14 +1339,14 @@ class DrmIonVoucher(object):
_assert(False, "Unknown lock parameter: %s" % param) _assert(False, "Unknown lock parameter: %s" % param)
# i know that version maps to scramble pretty much 1 to 1, but there was precendent where they changed it, so... # i know that version maps to scramble pretty much 1 to 1, but there was precendent where they changed it, so...
sharedsecrets = [obfuscate(shared, self.version),obfuscate2(shared, self.version),obfuscate3(shared, self.version), sharedsecrets = [obfuscate(shared, self.version),obfuscate2(shared, self.version),obfuscate3(shared, self.version),
process_V9708(shared), process_V1031(shared), process_V2069(shared), process_V9041(shared), process_V9708(shared), process_V1031(shared), process_V2069(shared), process_V9041(shared),
process_V3646(shared), process_V6052(shared), process_V9479(shared), process_V9888(shared), process_V3646(shared), process_V6052(shared), process_V9479(shared), process_V9888(shared),
process_V4648(shared), process_V5683(shared)] process_V4648(shared), process_V5683(shared)]
decrypted=False decrypted=False
ex=None lastexception: Exception | None = None
for sharedsecret in sharedsecrets: for sharedsecret in sharedsecrets:
key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest() key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16]) aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
@@ -1361,14 +1358,15 @@ class DrmIonVoucher(object):
_assert(self.drmkey.hasnext() and self.drmkey.next() == TID_LIST and self.drmkey.gettypename() == "com.amazon.drm.KeySet@1.0", _assert(self.drmkey.hasnext() and self.drmkey.next() == TID_LIST and self.drmkey.gettypename() == "com.amazon.drm.KeySet@1.0",
"Expected KeySet, got %s" % self.drmkey.gettypename()) "Expected KeySet, got %s" % self.drmkey.gettypename())
decrypted=True decrypted=True
print("Decryption succeeded") print("Decryption succeeded")
break break
except Exception as ex: except Exception as ex:
lastexception = ex
print("Decryption failed, trying next fallback ") print("Decryption failed, trying next fallback ")
if not decrypted: if not decrypted:
raise ex raise lastexception
self.drmkey.stepin() self.drmkey.stepin()
while self.drmkey.hasnext(): while self.drmkey.hasnext():

View File

@@ -374,7 +374,11 @@ class InterfacePluginAction(InterfaceAction):
result['success'] = False result['success'] = False
result['fileobj'] = None result['fileobj'] = None
zin = zipfile.ZipFile(book.filename, 'r') try:
zin = zipfile.ZipFile(book.filename, 'r')
except FileNotFoundError:
print (_('{0} - File "{1}" not found. Make sure the eBook has been properly downloaded in the Kobo app.').format(PLUGIN_NAME, book.filename))
return result
#print ('Kobo library filename: {0}'.format(book.filename)) #print ('Kobo library filename: {0}'.format(book.filename))
for userkey in self.userkeys: for userkey in self.userkeys:
print (_('Trying key: '), codecs.encode(userkey, 'hex')) print (_('Trying key: '), codecs.encode(userkey, 'hex'))

View File

@@ -449,9 +449,15 @@ class KoboLibrary(object):
for m in matches: for m in matches:
# print "m:{0}".format(m[0]) # print "m:{0}".format(m[0])
macaddrs.append(m[0].upper()) macaddrs.append(m[0].upper())
elif sys.platform.startswith('linux'):
for interface in os.listdir('/sys/class/net'):
with open('/sys/class/net/' + interface + '/address', 'r') as f:
mac = f.read().strip().upper()
# some interfaces, like Tailscale's VPN interface, do not have a MAC address
if mac != '':
macaddrs.append(mac)
else: else:
# probably linux # final fallback
# let's try ip # let's try ip
c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE) c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
for line in os.popen('ip -br link'): for line in os.popen('ip -br link'):