diff --git a/DeDRM_plugin/__init__.py b/DeDRM_plugin/__init__.py index eaf505a..913d05e 100644 --- a/DeDRM_plugin/__init__.py +++ b/DeDRM_plugin/__init__.py @@ -187,15 +187,12 @@ class DeDRM(FileTypePlugin): os.mkdir(self.alfdir) # only continue if we've never run this version of the plugin before self.verdir = os.path.join(self.maindir,PLUGIN_VERSION) - if not os.path.exists(self.verdir): - if iswindows: - names = ["alfcrypto.dll","alfcrypto64.dll"] - elif isosx: - names = ["libalfcrypto.dylib"] - else: - names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"] + if not os.path.exists(self.verdir) and not iswindows and not isosx: + + names = ["kindlekey.py","adobekey.py","ignoblekeyNookStudy.py"] + lib_dict = self.load_resources(names) - print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)) + print("{0} v{1}: Copying needed Python scripts from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)) for entry, data in lib_dict.items(): file_path = os.path.join(self.alfdir, entry) diff --git a/DeDRM_plugin/alfcrypto.dll b/DeDRM_plugin/alfcrypto.dll deleted file mode 100644 index 26d740d..0000000 Binary files a/DeDRM_plugin/alfcrypto.dll and /dev/null differ diff --git a/DeDRM_plugin/alfcrypto.py b/DeDRM_plugin/alfcrypto.py index 5a31d09..ecb7916 100644 --- a/DeDRM_plugin/alfcrypto.py +++ b/DeDRM_plugin/alfcrypto.py @@ -8,260 +8,90 @@ # pbkdf2.py Copyright © 2009 Daniel Holth # pbkdf2.py This code may be freely used and modified for any purpose. -import sys, os import hmac from struct import pack import hashlib +import aescbc -# interface to needed routines libalfcrypto -def _load_libalfcrypto(): - import ctypes - from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \ - Structure, c_ulong, create_string_buffer, addressof, string_at, cast, sizeof +class Pukall_Cipher(object): + def __init__(self): + self.key = None - pointer_size = ctypes.sizeof(ctypes.c_voidp) - name_of_lib = None - if sys.platform.startswith('darwin'): - name_of_lib = 'libalfcrypto.dylib' - elif sys.platform.startswith('win'): - if pointer_size == 4: - name_of_lib = 'alfcrypto.dll' - else: - name_of_lib = 'alfcrypto64.dll' - else: - if pointer_size == 4: - name_of_lib = 'libalfcrypto32.so' - else: - name_of_lib = 'libalfcrypto64.so' - - # hard code to local location for libalfcrypto - libalfcrypto = os.path.join(sys.path[0],name_of_lib) - if not os.path.isfile(libalfcrypto): - libalfcrypto = os.path.join(sys.path[0], 'lib', name_of_lib) - if not os.path.isfile(libalfcrypto): - libalfcrypto = os.path.join('.',name_of_lib) - if not os.path.isfile(libalfcrypto): - raise Exception('libalfcrypto not found at %s' % libalfcrypto) - - libalfcrypto = CDLL(libalfcrypto) - - c_char_pp = POINTER(c_char_p) - c_int_p = POINTER(c_int) - - - def F(restype, name, argtypes): - func = getattr(libalfcrypto, name) - func.restype = restype - func.argtypes = argtypes - return func - - # aes cbc decryption - # - # struct aes_key_st { - # unsigned long rd_key[4 *(AES_MAXNR + 1)]; - # int rounds; - # }; - # - # typedef struct aes_key_st AES_KEY; - # - # int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); - # - # - # void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, - # const unsigned long length, const AES_KEY *key, - # unsigned char *ivec, const int enc); - - AES_MAXNR = 14 - - class AES_KEY(Structure): - _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)] - - AES_KEY_p = POINTER(AES_KEY) - AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p, c_int]) - AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p]) - - - - # Pukall 1 Cipher - # unsigned char *PC1(const unsigned char *key, unsigned int klen, const unsigned char *src, - # unsigned char *dest, unsigned int len, int decryption); - - PC1 = F(c_char_p, 'PC1', [c_char_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong]) - - # Topaz Encryption - # typedef struct _TpzCtx { - # unsigned int v[2]; - # } TpzCtx; - # - # void topazCryptoInit(TpzCtx *ctx, const unsigned char *key, int klen); - # void topazCryptoDecrypt(const TpzCtx *ctx, const unsigned char *in, unsigned char *out, int len); - - class TPZ_CTX(Structure): - _fields_ = [('v', c_long * 2)] - - TPZ_CTX_p = POINTER(TPZ_CTX) - topazCryptoInit = F(None, 'topazCryptoInit', [TPZ_CTX_p, c_char_p, c_ulong]) - topazCryptoDecrypt = F(None, 'topazCryptoDecrypt', [TPZ_CTX_p, c_char_p, c_char_p, c_ulong]) - - - class AES_CBC(object): - def __init__(self): - self._blocksize = 0 - self._keyctx = None - self._iv = 0 - - def set_decrypt_key(self, userkey, iv): - self._blocksize = len(userkey) - if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : - raise Exception('AES CBC improper key used') - return - keyctx = self._keyctx = AES_KEY() - self._iv = iv - rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) - if rv < 0: - raise Exception('Failed to initialize AES CBC key') - - def decrypt(self, data): - out = create_string_buffer(len(data)) - mutable_iv = create_string_buffer(self._iv, len(self._iv)) - rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, mutable_iv, 0) - if rv == 0: - raise Exception('AES CBC decryption failed') - return out.raw - - class Pukall_Cipher(object): - def __init__(self): - self.key = None - - def PC1(self, key, src, decryption=True): - self.key = key - out = create_string_buffer(len(src)) - de = 0 + def PC1(self, key, src, decryption=True): + sum1 = 0; + sum2 = 0; + keyXorVal = 0; + if len(key)!=16: + raise Exception("PC1: Bad key length") + wkey = [] + for i in range(8): + wkey.append(key[i*2]<<8 | key[i*2+1]) + dst = bytearray(len(src)) + for i in range(len(src)): + temp1 = 0; + byteXorVal = 0; + for j in range(8): + temp1 ^= wkey[j] + sum2 = (sum2+j)*20021 + sum1 + sum1 = (temp1*346)&0xFFFF + sum2 = (sum2+sum1)&0xFFFF + temp1 = (temp1*20021+1)&0xFFFF + byteXorVal ^= temp1 ^ sum2 + curByte = src[i] + if not decryption: + keyXorVal = curByte * 257; + curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF if decryption: - de = 1 - rv = PC1(key, len(key), src, out, len(src), de) - return out.raw + keyXorVal = curByte * 257; + for j in range(8): + wkey[j] ^= keyXorVal; + dst[i] = curByte + return bytes(dst) - class Topaz_Cipher(object): - def __init__(self): - self._ctx = None +class Topaz_Cipher(object): + def __init__(self): + self._ctx = None - def ctx_init(self, key): - tpz_ctx = self._ctx = TPZ_CTX() - topazCryptoInit(tpz_ctx, key, len(key)) - return tpz_ctx + def ctx_init(self, key): + ctx1 = 0x0CAFFE19E + if isinstance(key, str): + key = key.encode('latin-1') + for keyByte in key: + ctx2 = ctx1 + ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF ) + self._ctx = [ctx1, ctx2] + return [ctx1,ctx2] - def decrypt(self, data, ctx=None): - if ctx == None: - ctx = self._ctx - out = create_string_buffer(len(data)) - topazCryptoDecrypt(ctx, data, out, len(data)) - return out.raw + def decrypt(self, data, ctx=None): + if ctx == None: + ctx = self._ctx + ctx1 = ctx[0] + ctx2 = ctx[1] + plainText = "" + if isinstance(data, str): + data = data.encode('latin-1') + for dataByte in data: + m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF + ctx2 = ctx1 + ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF) + plainText += chr(m) + return plainText - print("Using Library AlfCrypto DLL/DYLIB/SO") - return (AES_CBC, Pukall_Cipher, Topaz_Cipher) +class AES_CBC(object): + def __init__(self): + self._key = None + self._iv = None + self.aes = None + def set_decrypt_key(self, userkey, iv): + self._key = userkey + self._iv = iv + self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey)) -def _load_python_alfcrypto(): - - import aescbc - - class Pukall_Cipher(object): - def __init__(self): - self.key = None - - def PC1(self, key, src, decryption=True): - sum1 = 0; - sum2 = 0; - keyXorVal = 0; - if len(key)!=16: - raise Exception('Pukall_Cipher: Bad key length.') - wkey = [] - for i in range(8): - wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1])) - dst = "" - for i in range(len(src)): - temp1 = 0; - byteXorVal = 0; - for j in range(8): - temp1 ^= wkey[j] - sum2 = (sum2+j)*20021 + sum1 - sum1 = (temp1*346)&0xFFFF - sum2 = (sum2+sum1)&0xFFFF - temp1 = (temp1*20021+1)&0xFFFF - byteXorVal ^= temp1 ^ sum2 - curByte = ord(src[i]) - if not decryption: - keyXorVal = curByte * 257; - curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF - if decryption: - keyXorVal = curByte * 257; - for j in range(8): - wkey[j] ^= keyXorVal; - dst+=chr(curByte) - return dst - - class Topaz_Cipher(object): - def __init__(self): - self._ctx = None - - def ctx_init(self, key): - ctx1 = 0x0CAFFE19E - if isinstance(key, str): - key = key.encode('latin-1') - for keyByte in key: - ctx2 = ctx1 - ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF ) - self._ctx = [ctx1, ctx2] - return [ctx1,ctx2] - - def decrypt(self, data, ctx=None): - if ctx == None: - ctx = self._ctx - ctx1 = ctx[0] - ctx2 = ctx[1] - plainText = "" - if isinstance(data, str): - data = data.encode('latin-1') - for dataByte in data: - m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF - ctx2 = ctx1 - ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF) - plainText += chr(m) - return plainText - - class AES_CBC(object): - def __init__(self): - self._key = None - self._iv = None - self.aes = None - - def set_decrypt_key(self, userkey, iv): - self._key = userkey - self._iv = iv - self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey)) - - def decrypt(self, data): - iv = self._iv - cleartext = self.aes.decrypt(iv + data) - return cleartext - - print("Using Library AlfCrypto Python") - return (AES_CBC, Pukall_Cipher, Topaz_Cipher) - - -def _load_crypto(): - AES_CBC = Pukall_Cipher = Topaz_Cipher = None - cryptolist = (_load_libalfcrypto, _load_python_alfcrypto) - for loader in cryptolist: - try: - AES_CBC, Pukall_Cipher, Topaz_Cipher = loader() - break - except (ImportError, Exception): - pass - return AES_CBC, Pukall_Cipher, Topaz_Cipher - -AES_CBC, Pukall_Cipher, Topaz_Cipher = _load_crypto() + def decrypt(self, data): + iv = self._iv + cleartext = self.aes.decrypt(iv + data) + return cleartext class KeyIVGen(object): diff --git a/DeDRM_plugin/alfcrypto64.dll b/DeDRM_plugin/alfcrypto64.dll deleted file mode 100644 index 7bef68e..0000000 Binary files a/DeDRM_plugin/alfcrypto64.dll and /dev/null differ diff --git a/DeDRM_plugin/alfcrypto_src.zip b/DeDRM_plugin/alfcrypto_src.zip deleted file mode 100644 index 269810c..0000000 Binary files a/DeDRM_plugin/alfcrypto_src.zip and /dev/null differ diff --git a/DeDRM_plugin/libalfcrypto.dylib b/DeDRM_plugin/libalfcrypto.dylib deleted file mode 100644 index 01c348c..0000000 Binary files a/DeDRM_plugin/libalfcrypto.dylib and /dev/null differ diff --git a/DeDRM_plugin/libalfcrypto32.so b/DeDRM_plugin/libalfcrypto32.so deleted file mode 100644 index 9a5a442..0000000 Binary files a/DeDRM_plugin/libalfcrypto32.so and /dev/null differ diff --git a/DeDRM_plugin/libalfcrypto64.so b/DeDRM_plugin/libalfcrypto64.so deleted file mode 100644 index a08ac28..0000000 Binary files a/DeDRM_plugin/libalfcrypto64.so and /dev/null differ diff --git a/DeDRM_plugin/mobidedrm.py b/DeDRM_plugin/mobidedrm.py index fe761c4..22cdb35 100755 --- a/DeDRM_plugin/mobidedrm.py +++ b/DeDRM_plugin/mobidedrm.py @@ -80,10 +80,7 @@ import sys import os import struct import binascii -try: - from alfcrypto import Pukall_Cipher -except: - print("AlfCrypto not found. Using python PC1 implementation.") +from alfcrypto import Pukall_Cipher from utilities import SafeUnbuffered @@ -140,41 +137,8 @@ def PC1(key, src, decryption=True): # if we can get it from alfcrypto, use that try: return Pukall_Cipher().PC1(key,src,decryption) - except NameError: - pass - except TypeError: - pass - - # use slow python version, since Pukall_Cipher didn't load - sum1 = 0; - sum2 = 0; - keyXorVal = 0; - if len(key)!=16: - DrmException ("PC1: Bad key length") - wkey = [] - for i in range(8): - wkey.append(key[i*2]<<8 | key[i*2+1]) - dst = bytearray(len(src)) - for i in range(len(src)): - temp1 = 0; - byteXorVal = 0; - for j in range(8): - temp1 ^= wkey[j] - sum2 = (sum2+j)*20021 + sum1 - sum1 = (temp1*346)&0xFFFF - sum2 = (sum2+sum1)&0xFFFF - temp1 = (temp1*20021+1)&0xFFFF - byteXorVal ^= temp1 ^ sum2 - curByte = src[i] - if not decryption: - keyXorVal = curByte * 257; - curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF - if decryption: - keyXorVal = curByte * 257; - for j in range(8): - wkey[j] ^= keyXorVal; - dst[i] = curByte - return bytes(dst) + except: + raise # accepts unicode returns unicode def checksumPid(s): @@ -232,12 +196,7 @@ class MobiBook: pass def __init__(self, infile): - print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__)) - - try: - from alfcrypto import Pukall_Cipher - except: - print("AlfCrypto not found. Using python PC1 implementation.") + print("MobiDeDrm v{0:s}.\nCopyright © 2008-2022 The Dark Reverser, Apprentice Harper et al.".format(__version__)) # initial sanity check on file self.data_file = open(infile, 'rb').read() diff --git a/DeDRM_plugin/subasyncio.py b/DeDRM_plugin/subasyncio.py deleted file mode 100644 index de084d3..0000000 --- a/DeDRM_plugin/subasyncio.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python -# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab - -import os, sys -import signal -import threading -import subprocess -from subprocess import Popen, PIPE, STDOUT - -# **heavily** chopped up and modfied version of asyncproc.py -# to make it actually work on Windows as well as Mac/Linux -# For the original see: -# "http://www.lysator.liu.se/~bellman/download/" -# author is "Thomas Bellman " -# available under GPL version 3 or Later - -# create an asynchronous subprocess whose output can be collected in -# a non-blocking manner - -# What a mess! Have to use threads just to get non-blocking io -# in a cross-platform manner - -# luckily all thread use is hidden within this class - -class Process(object): - def __init__(self, *params, **kwparams): - if len(params) <= 3: - kwparams.setdefault('stdin', subprocess.PIPE) - if len(params) <= 4: - kwparams.setdefault('stdout', subprocess.PIPE) - if len(params) <= 5: - kwparams.setdefault('stderr', subprocess.PIPE) - self.__pending_input = [] - self.__collected_outdata = [] - self.__collected_errdata = [] - self.__exitstatus = None - self.__lock = threading.Lock() - self.__inputsem = threading.Semaphore(0) - self.__quit = False - - self.__process = subprocess.Popen(*params, **kwparams) - - if self.__process.stdin: - self.__stdin_thread = threading.Thread( - name="stdin-thread", - target=self.__feeder, args=(self.__pending_input, - self.__process.stdin)) - self.__stdin_thread.setDaemon(True) - self.__stdin_thread.start() - - if self.__process.stdout: - self.__stdout_thread = threading.Thread( - name="stdout-thread", - target=self.__reader, args=(self.__collected_outdata, - self.__process.stdout)) - self.__stdout_thread.setDaemon(True) - self.__stdout_thread.start() - - if self.__process.stderr: - self.__stderr_thread = threading.Thread( - name="stderr-thread", - target=self.__reader, args=(self.__collected_errdata, - self.__process.stderr)) - self.__stderr_thread.setDaemon(True) - self.__stderr_thread.start() - - def pid(self): - return self.__process.pid - - def kill(self, signal): - self.__process.send_signal(signal) - - # check on subprocess (pass in 'nowait') to act like poll - def wait(self, flag): - if flag.lower() == 'nowait': - rc = self.__process.poll() - else: - rc = self.__process.wait() - if rc != None: - if self.__process.stdin: - self.closeinput() - if self.__process.stdout: - self.__stdout_thread.join() - if self.__process.stderr: - self.__stderr_thread.join() - return self.__process.returncode - - def terminate(self): - if self.__process.stdin: - self.closeinput() - self.__process.terminate() - - # thread gets data from subprocess stdout - def __reader(self, collector, source): - while True: - data = os.read(source.fileno(), 65536) - self.__lock.acquire() - collector.append(data) - self.__lock.release() - if data == "": - source.close() - break - return - - # thread feeds data to subprocess stdin - def __feeder(self, pending, drain): - while True: - self.__inputsem.acquire() - self.__lock.acquire() - if not pending and self.__quit: - drain.close() - self.__lock.release() - break - data = pending.pop(0) - self.__lock.release() - drain.write(data) - - # non-blocking read of data from subprocess stdout - def read(self): - self.__lock.acquire() - outdata = "".join(self.__collected_outdata) - del self.__collected_outdata[:] - self.__lock.release() - return outdata - - # non-blocking read of data from subprocess stderr - def readerr(self): - self.__lock.acquire() - errdata = "".join(self.__collected_errdata) - del self.__collected_errdata[:] - self.__lock.release() - return errdata - - # non-blocking write to stdin of subprocess - def write(self, data): - if self.__process.stdin is None: - raise ValueError("Writing to process with stdin not a pipe") - self.__lock.acquire() - self.__pending_input.append(data) - self.__inputsem.release() - self.__lock.release() - - # close stdinput of subprocess - def closeinput(self): - self.__lock.acquire() - self.__quit = True - self.__inputsem.release() - self.__lock.release()