From 7f4e6698efac2f57b660fcc07fc128989a0377cf Mon Sep 17 00:00:00 2001 From: Aldo Bleeker Date: Fri, 6 Nov 2020 23:49:18 +0100 Subject: [PATCH 1/3] More Python 3 fixes for Customize plugin dialog --- DeDRM_plugin/config.py | 29 ++++++++++++++++------------- DeDRM_plugin/erdr2pml.py | 8 ++++---- 2 files changed, 20 insertions(+), 17 deletions(-) mode change 100644 => 100755 DeDRM_plugin/config.py mode change 100644 => 100755 DeDRM_plugin/erdr2pml.py diff --git a/DeDRM_plugin/config.py b/DeDRM_plugin/config.py old mode 100644 new mode 100755 index c319a6e..c81b615 --- a/DeDRM_plugin/config.py +++ b/DeDRM_plugin/config.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' # Python 3, September 2020 # Standard Python modules. -import os, traceback, json +import os, traceback, json, binascii from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, QGroupBox, QPushButton, QListWidget, QListWidgetItem, @@ -378,7 +378,7 @@ class ManageKeysDialog(QDialog): with open(fpath,'rb') as keyfile: new_key_value = keyfile.read() if self.binary_file: - new_key_value = new_key_value.encode('hex') + new_key_value = binascii.b2a_hex(new_key_value) elif self.json_file: new_key_value = json.loads(new_key_value) elif self.android_file: @@ -431,17 +431,20 @@ class ManageKeysDialog(QDialog): defaultname = "{0}.{1}".format(keyname, self.keyfile_ext) filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname) if filename: - with open(filename, 'w') as fname: - if self.binary_file: - fname.write(self.plugin_keys[keyname].decode('hex')) - elif self.json_file: + if self.binary_file: + with open(filename, 'wb') as fname: + fname.write(binascii.a2b_hex(self.plugin_keys[keyname])) + elif self.json_file: + with open(filename, 'w') as fname: fname.write(json.dumps(self.plugin_keys[keyname])) - elif self.android_file: + elif self.android_file: + with open(filename, 'w') as fname: for key in self.plugin_keys[keyname]: - fname.write(key) - fname.write("\n") - else: - fname.write(self.plugin_keys[keyname]) + fname.write(key.decode('utf-8')) + fname.write('\n') + else: + with open(filename, 'w') as fname: + fname.write(self.plugin_keys[keyname].decode('utf-8')) @@ -670,7 +673,7 @@ class AddEReaderDialog(QDialog): @property def key_value(self): from calibre_plugins.dedrm.erdr2pml import getuser_key as generate_ereader_key - return generate_ereader_key(self.user_name,self.cc_number).encode('hex') + return binascii.b2a_hex(generate_ereader_key(self.user_name, self.cc_number)) @property def user_name(self): @@ -752,7 +755,7 @@ class AddAdeptDialog(QDialog): @property def key_value(self): - return self.default_key.encode('hex') + return binascii.b2a_hex(self.default_key) def accept(self): diff --git a/DeDRM_plugin/erdr2pml.py b/DeDRM_plugin/erdr2pml.py old mode 100644 new mode 100755 index 6c65ac2..6e1054e --- a/DeDRM_plugin/erdr2pml.py +++ b/DeDRM_plugin/erdr2pml.py @@ -542,10 +542,10 @@ def usage(): print(" It's enough to enter the last 8 digits of the credit card number") return -def getuser_key(name,cc): +def getuser_key(name, cc): newname = "".join(c for c in name.lower() if c >= 'a' and c <= 'z' or c >= '0' and c <= '9') cc = cc.replace(" ","") - return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff) + return struct.pack('>LL', binascii.crc32(bytes(newname.encode('utf-8'))) & 0xffffffff, binascii.crc32(bytes(cc[-8:].encode('utf-8'))) & 0xffffffff) def cli_main(): print("eRdr2Pml v{0}. Copyright © 2009–2020 The Dark Reverser et al.".format(__version__)) @@ -580,9 +580,9 @@ def cli_main(): elif len(args)==4: infile, outpath, name, cc = args - print(getuser_key(name,cc).encode('hex')) + print(bin2ascii.b2a_hex(getuser_key(name, cc))) - return decryptBook(infile, outpath, make_pmlz, getuser_key(name,cc)) + return decryptBook(infile, outpath, make_pmlz, bin2ascii.b2a_hex(getuser_key(name, cc))) if __name__ == "__main__": From a74f37c79ee8b654699a9723e2d566025fa10be9 Mon Sep 17 00:00:00 2001 From: Aldo Bleeker Date: Sat, 7 Nov 2020 13:43:58 +0100 Subject: [PATCH 2/3] Minor Python 3 fix for Customize dialog --- DeDRM_plugin/erdr2pml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DeDRM_plugin/erdr2pml.py b/DeDRM_plugin/erdr2pml.py index 6e1054e..84a4001 100755 --- a/DeDRM_plugin/erdr2pml.py +++ b/DeDRM_plugin/erdr2pml.py @@ -580,9 +580,9 @@ def cli_main(): elif len(args)==4: infile, outpath, name, cc = args - print(bin2ascii.b2a_hex(getuser_key(name, cc))) + print(binascii.b2a_hex(getuser_key(name, cc))) - return decryptBook(infile, outpath, make_pmlz, bin2ascii.b2a_hex(getuser_key(name, cc))) + return decryptBook(infile, outpath, make_pmlz, getuser_key(name, cc)) if __name__ == "__main__": From 74bcf33591a509d856b898175455315936f08d06 Mon Sep 17 00:00:00 2001 From: Aldo Bleeker Date: Sun, 22 Nov 2020 16:03:45 +0100 Subject: [PATCH 3/3] Python 3 fixes --- DeDRM_plugin/androidkindlekey.py | 37 ++++++++++++----------- DeDRM_plugin/config.py | 14 ++++----- DeDRM_plugin/erdr2pml.py | 6 ++-- DeDRM_plugin/ineptepub.py | 52 +++++++++++++++++--------------- DeDRM_plugin/prefs.py | 6 ++-- DeDRM_plugin/zipfilerugged.py | 4 +-- 6 files changed, 62 insertions(+), 57 deletions(-) mode change 100644 => 100755 DeDRM_plugin/androidkindlekey.py mode change 100644 => 100755 DeDRM_plugin/ineptepub.py mode change 100644 => 100755 DeDRM_plugin/prefs.py mode change 100644 => 100755 DeDRM_plugin/zipfilerugged.py diff --git a/DeDRM_plugin/androidkindlekey.py b/DeDRM_plugin/androidkindlekey.py old mode 100644 new mode 100755 index 0e4b648..2949535 --- a/DeDRM_plugin/androidkindlekey.py +++ b/DeDRM_plugin/androidkindlekey.py @@ -136,7 +136,7 @@ class AndroidObfuscationV2(AndroidObfuscation): ''' count = 503 - password = 'Thomsun was here!' + password = b'Thomsun was here!' def __init__(self, salt): key = self.password + salt @@ -182,7 +182,7 @@ def get_serials1(path=STORAGE1): obfuscation = AndroidObfuscation() def get_value(key): - encrypted_key = obfuscation.encrypt(key) + encrypted_key = obfuscation.encrypt(a2b_hex(key)) encrypted_value = storage.get(encrypted_key) if encrypted_value: return obfuscation.decrypt(encrypted_value) @@ -217,15 +217,14 @@ def get_serials2(path=STORAGE2): import sqlite3 connection = sqlite3.connect(path) cursor = connection.cursor() - cursor.execute('''select userdata_value from userdata where userdata_key like '%/%token.device.deviceserialname%' ''') - userdata_keys = cursor.fetchall() + cursor.execute('''select device_data_value from device_data where device_data_key like '%serial.number%' ''') + device_data_keys = cursor.fetchall() dsns = [] - for userdata_row in userdata_keys: + for device_data_row in device_data_keys: try: - if userdata_row and userdata_row[0]: - userdata_utf8 = userdata_row[0].encode('utf8') - if len(userdata_utf8) > 0: - dsns.append(userdata_utf8) + if device_data_row and device_data_row[0]: + if len(device_data_row[0]) > 0: + dsns.append(device_data_row[0]) except: print("Error getting one of the device serial name keys") traceback.print_exc() @@ -238,9 +237,12 @@ def get_serials2(path=STORAGE2): for userdata_row in userdata_keys: try: if userdata_row and userdata_row[0]: - userdata_utf8 = userdata_row[0].encode('utf8') - if len(userdata_utf8) > 0: - tokens.append(userdata_utf8) + if len(userdata_row[0]) > 0: + if ',' in userdata_row[0]: + splits = userdata_row[0].split(',') + for split in splits: + tokens.append(split) + tokens.append(userdata_row[0]) except: print("Error getting one of the account token keys") traceback.print_exc() @@ -249,11 +251,10 @@ def get_serials2(path=STORAGE2): serials = [] for x in dsns: - serials.append(x) for y in tokens: - serials.append('%s%s' % (x, y)) - for y in tokens: - serials.append(y) + serials.append(x) + serials.append(y) + serials.append(x+y) return serials def get_serials(path=STORAGE): @@ -275,7 +276,7 @@ def get_serials(path=STORAGE): try : read = open(path, 'rb') head = read.read(24) - if head[:14] == 'ANDROID BACKUP': + if head[:14] == b'ANDROID BACKUP': output = StringIO(zlib.decompress(read.read())) except Exception: pass @@ -313,7 +314,7 @@ def getkey(outfile, inpath): if len(keys) > 0: with open(outfile, 'w') as keyfileout: for key in keys: - keyfileout.write(key) + keyfileout.write(b2a_hex(key)) keyfileout.write("\n") return True return False diff --git a/DeDRM_plugin/config.py b/DeDRM_plugin/config.py index c81b615..c701c4a 100755 --- a/DeDRM_plugin/config.py +++ b/DeDRM_plugin/config.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' # Python 3, September 2020 # Standard Python modules. -import os, traceback, json, binascii +import os, traceback, json, codecs from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, QGroupBox, QPushButton, QListWidget, QListWidgetItem, @@ -378,7 +378,7 @@ class ManageKeysDialog(QDialog): with open(fpath,'rb') as keyfile: new_key_value = keyfile.read() if self.binary_file: - new_key_value = binascii.b2a_hex(new_key_value) + new_key_value = codecs.encode(new_key_value,'hex') elif self.json_file: new_key_value = json.loads(new_key_value) elif self.android_file: @@ -433,18 +433,18 @@ class ManageKeysDialog(QDialog): if filename: if self.binary_file: with open(filename, 'wb') as fname: - fname.write(binascii.a2b_hex(self.plugin_keys[keyname])) + fname.write(codecs.decode(self.plugin_keys[keyname],'hex')) elif self.json_file: with open(filename, 'w') as fname: fname.write(json.dumps(self.plugin_keys[keyname])) elif self.android_file: with open(filename, 'w') as fname: for key in self.plugin_keys[keyname]: - fname.write(key.decode('utf-8')) + fname.write(key) fname.write('\n') else: with open(filename, 'w') as fname: - fname.write(self.plugin_keys[keyname].decode('utf-8')) + fname.write(self.plugin_keys[keyname]) @@ -673,7 +673,7 @@ class AddEReaderDialog(QDialog): @property def key_value(self): from calibre_plugins.dedrm.erdr2pml import getuser_key as generate_ereader_key - return binascii.b2a_hex(generate_ereader_key(self.user_name, self.cc_number)) + return codecs.encode(generate_ereader_key(self.user_name, self.cc_number),'hex') @property def user_name(self): @@ -755,7 +755,7 @@ class AddAdeptDialog(QDialog): @property def key_value(self): - return binascii.b2a_hex(self.default_key) + return codecs.encode(self.default_key,'hex') def accept(self): diff --git a/DeDRM_plugin/erdr2pml.py b/DeDRM_plugin/erdr2pml.py index 84a4001..5cc395f 100755 --- a/DeDRM_plugin/erdr2pml.py +++ b/DeDRM_plugin/erdr2pml.py @@ -542,7 +542,7 @@ def usage(): print(" It's enough to enter the last 8 digits of the credit card number") return -def getuser_key(name, cc): +def getuser_key(name,cc): newname = "".join(c for c in name.lower() if c >= 'a' and c <= 'z' or c >= '0' and c <= '9') cc = cc.replace(" ","") return struct.pack('>LL', binascii.crc32(bytes(newname.encode('utf-8'))) & 0xffffffff, binascii.crc32(bytes(cc[-8:].encode('utf-8'))) & 0xffffffff) @@ -580,9 +580,9 @@ def cli_main(): elif len(args)==4: infile, outpath, name, cc = args - print(binascii.b2a_hex(getuser_key(name, cc))) + print(binascii.b2a_hex(getuser_key(name,cc))) - return decryptBook(infile, outpath, make_pmlz, getuser_key(name, cc)) + return decryptBook(infile, outpath, make_pmlz, getuser_key(name,cc)) if __name__ == "__main__": diff --git a/DeDRM_plugin/ineptepub.py b/DeDRM_plugin/ineptepub.py old mode 100644 new mode 100755 index 1be1a89..8d88733 --- a/DeDRM_plugin/ineptepub.py +++ b/DeDRM_plugin/ineptepub.py @@ -203,6 +203,7 @@ def _load_crypto_libcrypto(): def _load_crypto_pycrypto(): from Crypto.Cipher import AES as _AES from Crypto.PublicKey import RSA as _RSA + from Crypto.Cipher import PKCS1_v1_5 as _PKCS1_v1_5 # ASN.1 parsing code from tlslite class ASN1Error(Exception): @@ -294,14 +295,14 @@ def _load_crypto_pycrypto(): class AES(object): def __init__(self, key): - self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16) + self._aes = _AES.new(key, _AES.MODE_CBC, b'\x00'*16) def decrypt(self, data): return self._aes.decrypt(data) class RSA(object): def __init__(self, der): - key = ASN1Parser([ord(x) for x in der]) + key = ASN1Parser([x for x in der]) key = [key.getChild(x).value for x in range(1, 4)] key = [self.bytesToNumber(v) for v in key] self._rsa = _RSA.construct(key) @@ -313,7 +314,7 @@ def _load_crypto_pycrypto(): return total def decrypt(self, data): - return self._rsa.decrypt(data) + return _PKCS1_v1_5.new(self._rsa).decrypt(data, 172) return (AES, RSA) @@ -410,11 +411,14 @@ def decryptBook(userkey, inpath, outpath): return 1 bookkey = rsa.decrypt(codecs.decode(bookkey.encode('ascii'), 'base64')) # Padded as per RSAES-PKCS1-v1_5 - if bookkey[-17] != '\x00' and bookkey[-17] != 0: - print("Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath))) - return 2 + if len(bookkey) != 16: + if bookkey[-17] != '\x00' and bookkey[-17] != 0: + print("Could not decrypt {0:s}. Wrong key".format(os.path.basename(inpath))) + return 2 + else: + bookkey = bookkey[-16:] encryption = inf.read('META-INF/encryption.xml') - decryptor = Decryptor(bookkey[-16:], encryption) + decryptor = Decryptor(bookkey, encryption) kwds = dict(compression=ZIP_DEFLATED, allowZip64=False) with closing(ZipFile(open(outpath, 'wb'), 'w', **kwds)) as outf: zi = ZipInfo('mimetype') @@ -475,9 +479,9 @@ def cli_main(): def gui_main(): try: import tkinter - import tkinter_constants - import tkinter_filedialog - import tkinter_messagebox + import tkinter.constants + import tkinter.filedialog + import tkinter.messagebox import traceback except: return cli_main() @@ -486,10 +490,10 @@ def gui_main(): def __init__(self, root): tkinter.Frame.__init__(self, root, border=5) self.status = tkinter.Label(self, text="Select files for decryption") - self.status.pack(fill=tkinter_constants.X, expand=1) + self.status.pack(fill=tkinter.constants.X, expand=1) body = tkinter.Frame(self) - body.pack(fill=tkinter_constants.X, expand=1) - sticky = tkinter_constants.E + tkinter_constants.W + body.pack(fill=tkinter.constants.X, expand=1) + sticky = tkinter.constants.E + tkinter.constants.W body.grid_columnconfigure(1, weight=2) tkinter.Label(body, text="Key file").grid(row=0) self.keypath = tkinter.Entry(body, width=30) @@ -512,41 +516,41 @@ def gui_main(): buttons.pack() botton = tkinter.Button( buttons, text="Decrypt", width=10, command=self.decrypt) - botton.pack(side=tkinter_constants.LEFT) - tkinter.Frame(buttons, width=10).pack(side=tkinter_constants.LEFT) + botton.pack(side=tkinter.constants.LEFT) + tkinter.Frame(buttons, width=10).pack(side=tkinter.constants.LEFT) button = tkinter.Button( buttons, text="Quit", width=10, command=self.quit) - button.pack(side=tkinter_constants.RIGHT) + button.pack(side=tkinter.constants.RIGHT) def get_keypath(self): - keypath = tkinter_filedialog.askopenfilename( + keypath = tkinter.filedialog.askopenfilename( parent=None, title="Select Adobe Adept \'.der\' key file", defaultextension=".der", filetypes=[('Adobe Adept DER-encoded files', '.der'), ('All Files', '.*')]) if keypath: keypath = os.path.normpath(keypath) - self.keypath.delete(0, tkinter_constants.END) + self.keypath.delete(0, tkinter.constants.END) self.keypath.insert(0, keypath) return def get_inpath(self): - inpath = tkinter_filedialog.askopenfilename( + inpath = tkinter.filedialog.askopenfilename( parent=None, title="Select ADEPT-encrypted ePub file to decrypt", defaultextension=".epub", filetypes=[('ePub files', '.epub')]) if inpath: inpath = os.path.normpath(inpath) - self.inpath.delete(0, tkinter_constants.END) + self.inpath.delete(0, tkinter.constants.END) self.inpath.insert(0, inpath) return def get_outpath(self): - outpath = tkinter_filedialog.asksaveasfilename( + outpath = tkinter.filedialog.asksaveasfilename( parent=None, title="Select unencrypted ePub file to produce", defaultextension=".epub", filetypes=[('ePub files', '.epub')]) if outpath: outpath = os.path.normpath(outpath) - self.outpath.delete(0, tkinter_constants.END) + self.outpath.delete(0, tkinter.constants.END) self.outpath.insert(0, outpath) return @@ -576,13 +580,13 @@ def gui_main(): if decrypt_status == 0: self.status['text'] = "File successfully decrypted" else: - self.status['text'] = "The was an error decrypting the file." + self.status['text'] = "There was an error decrypting the file." root = tkinter.Tk() root.title("Adobe Adept ePub Decrypter v.{0}".format(__version__)) root.resizable(True, False) root.minsize(300, 0) - DecryptionDialog(root).pack(fill=tkinter_constants.X, expand=1) + DecryptionDialog(root).pack(fill=tkinter.constants.X, expand=1) root.mainloop() return 0 diff --git a/DeDRM_plugin/prefs.py b/DeDRM_plugin/prefs.py old mode 100644 new mode 100755 index ee16a30..3e8d78b --- a/DeDRM_plugin/prefs.py +++ b/DeDRM_plugin/prefs.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' # Standard Python modules. import os, sys, re, hashlib -import json +import codecs, json import traceback from calibre.utils.config import dynamic, config_dir, JSONConfig @@ -116,7 +116,7 @@ def convertprefs(always = False): name, cc = keystring.split(',') # Generate eReader user key from name and credit card number. keyname = "{0}_{1}".format(name.strip(),cc.strip()[-4:]) - keyvalue = getuser_key(name,cc).encode('hex') + keyvalue = codecs.encode(getuser_key(name,cc),'hex') userkeys.append([keyname,keyvalue]) except Exception as e: traceback.print_exc() @@ -146,7 +146,7 @@ def convertprefs(always = False): key = os.path.splitext(filename)[0] value = open(fpath, 'rb').read() if encoding is not None: - value = value.encode(encoding) + value = codecs.encode(value,encoding) userkeys.append([key,value]) except: traceback.print_exc() diff --git a/DeDRM_plugin/zipfilerugged.py b/DeDRM_plugin/zipfilerugged.py old mode 100644 new mode 100755 index 25aed8a..b2f3762 --- a/DeDRM_plugin/zipfilerugged.py +++ b/DeDRM_plugin/zipfilerugged.py @@ -286,8 +286,8 @@ class ZipInfo (object): # This is used to ensure paths in generated ZIP files always use # forward slashes as the directory separator, as required by the # ZIP format specification. - if os.sep != "/" and os.sep in filename: - filename = filename.replace(os.sep, "/") + if os.sep != "/" and os.sep.encode('utf-8') in filename: + filename = filename.replace(os.sep.encode('utf-8'), b"/") self.filename = filename # Normalized file name self.date_time = date_time # year, month, day, hour, min, sec