diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bc4b00d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + pull_request: + branches: [ master ] + +jobs: + test: + name: Unit tests (Python ${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # 3.9/3.11 use the stdlib `cgi` module; 3.13 has it removed, so this + # also exercises the `legacy-cgi` backport that the manifest pulls in. + python-version: ['3.9', '3.11', '3.13'] + + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + run: pipx install poetry + + - name: Install dependencies + run: | + poetry env use python + poetry install --with dev + + - name: Run test suite + run: poetry run pytest diff --git a/.github/workflows/main.yml b/.github/workflows/release.yml similarity index 72% rename from .github/workflows/main.yml rename to .github/workflows/release.yml index 9ddd8a1..7b324a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/release.yml @@ -5,29 +5,30 @@ on: jobs: package: + name: Package and auto-release runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Package run: python3 make_release.py - name: Upload - uses: actions/upload-artifact@v4 - with: + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: name: plugin path: | DeDRM_tools_*.zip DeDRM_tools.zip - + - name: Prepare release run: cp DeDRM_tools.zip DeDRM_alpha_${{ github.sha }}.zip - - - uses: dev-drprasad/delete-older-releases@v0.2.1 + - name: Delete older auto-releases + uses: dev-drprasad/delete-older-releases@dfbe6be2a006e9475dfcbe5b8d201f1824c2a9fe # v0.3.4 with: - repo: noDRM/DeDRM_tools_autorelease + repo: noDRM/DeDRM_tools_autorelease keep_latest: 0 delete_tags: true env: @@ -35,14 +36,14 @@ jobs: - name: Auto-release id: autorelease - uses: softprops/action-gh-release@v1 - with: + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 + with: tag_name: autorelease_${{ github.sha }} repository: noDRM/DeDRM_tools_autorelease token: ${{ secrets.AUTORELEASE_KEY }} name: Automatic alpha release with latest changes body: | - This release is automatically generated by Github for each commit. + This release is automatically generated by Github for each commit. This means, every time a change is made to the repo, a release with an untested copy of the plugin at that stage will be created. This will contain the most up-to-date code, but it's not tested at all and may be broken. diff --git a/.gitignore b/.gitignore index f3f3d47..fa744b3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,10 @@ # Cache /DeDRM_plugin/__pycache__ -/DeDRM_plugin/standalone/__pycache__ \ No newline at end of file +/DeDRM_plugin/standalone/__pycache__ + +# Python / pytest +__pycache__/ +*.py[cod] +.pytest_cache/ +.venv/ \ No newline at end of file diff --git a/DeDRM_plugin/__calibre_compat_code.py b/DeDRM_plugin/__calibre_compat_code.py index b44b8f3..8257c3e 100644 --- a/DeDRM_plugin/__calibre_compat_code.py +++ b/DeDRM_plugin/__calibre_compat_code.py @@ -8,12 +8,6 @@ if os.path.dirname(os.path.dirname(os.path.abspath(__file__))) not in sys.path: if os.path.dirname(os.path.abspath(__file__)) not in sys.path: sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -# Bugfix for Calibre < 5: -if "calibre" in sys.modules and sys.version_info[0] == 2: - from calibre.utils.config import config_dir - if os.path.join(config_dir, "plugins", "DeDRM.zip") not in sys.path: - sys.path.insert(0, os.path.join(config_dir, "plugins", "DeDRM.zip")) - if "calibre" in sys.modules: # Explicitly set the package identifier so we are allowed to import stuff ... __package__ = "calibre_plugins.dedrm" diff --git a/DeDRM_plugin/__init__.py b/DeDRM_plugin/__init__.py index 8200122..29e585a 100644 --- a/DeDRM_plugin/__init__.py +++ b/DeDRM_plugin/__init__.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from __future__ import print_function - # __init__.py for DeDRM_plugin # Copyright © 2008-2020 Apprentice Harper et al. # Copyright © 2021-2023 NoDRM diff --git a/DeDRM_plugin/adobekey.py b/DeDRM_plugin/adobekey.py index 5ad771f..7465d37 100644 --- a/DeDRM_plugin/adobekey.py +++ b/DeDRM_plugin/adobekey.py @@ -47,7 +47,7 @@ from base64 import b64decode #@@CALIBRE_COMPAT_CODE@@ -from .utilities import SafeUnbuffered +from .utilities import SafeUnbuffered, unpad from .argv_utils import unicode_argv @@ -68,24 +68,13 @@ if iswindows: c_long, c_ulong from ctypes.wintypes import LPVOID, DWORD, BOOL - try: - import winreg - except ImportError: - import _winreg as winreg + import winreg try: from Cryptodome.Cipher import AES except ImportError: from Crypto.Cipher import AES - def unpad(data, padding=16): - if sys.version_info[0] == 2: - pad_len = ord(data[-1]) - else: - pad_len = data[-1] - - return data[:-pad_len] - DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device' PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation' @@ -135,19 +124,9 @@ if iswindows: GetUserName = GetUserName() def GetUserName2(): - try: - from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER - except ImportError: - # We're on Python 2 - try: - # The default _winreg on Python2 isn't unicode-safe. - # Check if we have winreg_unicode, a unicode-safe alternative. - # Without winreg_unicode, this will fail with Unicode chars in the username. - from adobekey_winreg_unicode import OpenKey, QueryValueEx, HKEY_CURRENT_USER - except: - from _winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER + from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER - try: + try: DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device' regkey = OpenKey(HKEY_CURRENT_USER, DEVICE_KEY_PATH) userREG = QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2] diff --git a/DeDRM_plugin/adobekey_get_passhash.py b/DeDRM_plugin/adobekey_get_passhash.py index 1e9b8e2..d894c6b 100644 --- a/DeDRM_plugin/adobekey_get_passhash.py +++ b/DeDRM_plugin/adobekey_get_passhash.py @@ -28,10 +28,7 @@ except ImportError: def unpad(data, padding=16): - if sys.version_info[0] == 2: - pad_len = ord(data[-1]) - else: - pad_len = data[-1] + pad_len = data[-1] return data[:-pad_len] @@ -60,10 +57,7 @@ def decrypt_passhash(passhash, fp): if iswindows: - try: - import winreg - except ImportError: - import _winreg as winreg + import winreg PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation' diff --git a/DeDRM_plugin/adobekey_winreg_unicode.py b/DeDRM_plugin/adobekey_winreg_unicode.py deleted file mode 100644 index 6c719c4..0000000 --- a/DeDRM_plugin/adobekey_winreg_unicode.py +++ /dev/null @@ -1,271 +0,0 @@ -# This is based on https://github.com/DanielStutzbach/winreg_unicode -# The original _winreg in Python2 doesn't support unicode. -# This causes issues if there's unicode chars in the username needed to decrypt the key. - -''' -Copyright 2010 Stutzbach Enterprises, LLC (daniel@stutzbachenterprises.com) - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - 3. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -''' - -import ctypes, ctypes.wintypes - -ERROR_SUCCESS = 0 -ERROR_MORE_DATA = 234 - -KEY_READ = 0x20019 - -REG_NONE = 0 -REG_SZ = 1 -REG_EXPAND_SZ = 2 -REG_BINARY = 3 -REG_DWORD = 4 -REG_DWORD_BIG_ENDIAN = 5 -REG_DWORD_LITTLE_ENDIAN = 4 -REG_LINK = 6 -REG_MULTI_SZ = 7 -REG_RESOURCE_LIST = 8 -REG_FULL_RESOURCE_DESCRIPTOR = 9 -REG_RESOURCE_REQUIREMENTS_LIST = 10 - -c_HKEY = ctypes.c_void_p -DWORD = ctypes.wintypes.DWORD -BYTE = ctypes.wintypes.BYTE -LPDWORD = ctypes.POINTER(DWORD) -LPBYTE = ctypes.POINTER(BYTE) - -advapi32 = ctypes.windll.advapi32 - -class FILETIME(ctypes.Structure): - _fields_ = [("dwLowDateTime", DWORD), - ("dwHighDateTime", DWORD)] - -RegCloseKey = advapi32.RegCloseKey -RegCloseKey.restype = ctypes.c_long -RegCloseKey.argtypes = [c_HKEY] - -RegOpenKeyEx = advapi32.RegOpenKeyExW -RegOpenKeyEx.restype = ctypes.c_long -RegOpenKeyEx.argtypes = [c_HKEY, ctypes.c_wchar_p, ctypes.c_ulong, - ctypes.c_ulong, ctypes.POINTER(c_HKEY)] - -RegQueryInfoKey = advapi32.RegQueryInfoKeyW -RegQueryInfoKey.restype = ctypes.c_long -RegQueryInfoKey.argtypes = [c_HKEY, ctypes.c_wchar_p, LPDWORD, LPDWORD, - LPDWORD, LPDWORD, LPDWORD, LPDWORD, - LPDWORD, LPDWORD, LPDWORD, - ctypes.POINTER(FILETIME)] - -RegEnumValue = advapi32.RegEnumValueW -RegEnumValue.restype = ctypes.c_long -RegEnumValue.argtypes = [c_HKEY, DWORD, ctypes.c_wchar_p, LPDWORD, - LPDWORD, LPDWORD, LPBYTE, LPDWORD] - -RegEnumKeyEx = advapi32.RegEnumKeyExW -RegEnumKeyEx.restype = ctypes.c_long -RegEnumKeyEx.argtypes = [c_HKEY, DWORD, ctypes.c_wchar_p, LPDWORD, - LPDWORD, ctypes.c_wchar_p, LPDWORD, - ctypes.POINTER(FILETIME)] - -RegQueryValueEx = advapi32.RegQueryValueExW -RegQueryValueEx.restype = ctypes.c_long -RegQueryValueEx.argtypes = [c_HKEY, ctypes.c_wchar_p, LPDWORD, LPDWORD, - LPBYTE, LPDWORD] - -def check_code(code): - if code == ERROR_SUCCESS: - return - raise ctypes.WinError(2) - -class HKEY(object): - def __init__(self): - self.hkey = c_HKEY() - - def __enter__(self): - return self - - def __exit__(self, exc_type=None, exc_val=None, exc_tb=None): - self.Close() - return False - - def Detach(self): - rv = self.cast(self.hkey, self.c_ulong).value - self.hkey = c_HKEY() - return rv - - def __nonzero__(self): - return bool(self.hkey) - - def Close(self): - if not self.hkey: - return - if RegCloseKey is None or check_code is None or c_HKEY is None: - return # globals become None during exit - rc = RegCloseKey(self.hkey) - self.hkey = c_HKEY() - check_code(rc) - - def __del__(self): - self.Close() - -class RootHKEY(ctypes.Structure): - def __init__(self, value): - self.hkey = c_HKEY(value) - - def Close(self): - pass - -HKEY_CLASSES_ROOT = RootHKEY(0x80000000) -HKEY_CURRENT_USER = RootHKEY(0x80000001) -HKEY_LOCAL_MACHINE = RootHKEY(0x80000002) -HKEY_USERS = RootHKEY(0x80000003) -HKEY_PERFORMANCE_DATA = RootHKEY(0x80000004) -HKEY_CURRENT_CONFIG = RootHKEY(0x80000005) -HKEY_DYN_DATA = RootHKEY(0x80000006) - -def OpenKey(key, sub_key): - new_key = HKEY() - rc = RegOpenKeyEx(key.hkey, sub_key, 0, KEY_READ, - ctypes.cast(ctypes.byref(new_key.hkey), - ctypes.POINTER(c_HKEY))) - check_code(rc) - return new_key - -def QueryInfoKey(key): - null = LPDWORD() - num_sub_keys = DWORD() - num_values = DWORD() - ft = FILETIME() - rc = RegQueryInfoKey(key.hkey, ctypes.c_wchar_p(), null, null, - ctypes.byref(num_sub_keys), null, null, - ctypes.byref(num_values), null, null, null, - ctypes.byref(ft)) - check_code(rc) - return (num_sub_keys.value, num_values.value, - ft.dwLowDateTime | (ft.dwHighDateTime << 32)) - -def EnumValue(key, index): - null = LPDWORD() - value_size = DWORD() - data_size = DWORD() - rc = RegQueryInfoKey(key.hkey, ctypes.c_wchar_p(), null, null, null, - null, null, null, - ctypes.byref(value_size), ctypes.byref(data_size), - null, ctypes.POINTER(FILETIME)()) - check_code(rc) - value_size.value += 1 - data_size.value += 1 - - value = ctypes.create_unicode_buffer(value_size.value) - - while True: - data = ctypes.create_string_buffer(data_size.value) - - tmp_value_size = DWORD(value_size.value) - tmp_data_size = DWORD(data_size.value) - typ = DWORD() - rc = RegEnumValue(key.hkey, index, - ctypes.cast(value, ctypes.c_wchar_p), - ctypes.byref(tmp_value_size), null, - ctypes.byref(typ), - ctypes.cast(data, LPBYTE), - ctypes.byref(tmp_data_size)) - - if rc != ERROR_MORE_DATA: - break - - data_size.value *= 2 - - check_code(rc) - return (value.value, Reg2Py(data, tmp_data_size.value, typ.value), - typ.value) - -def split_multi_sz(data, size): - if size == 0: - return [] - Q = size - P = 0 - rv = [] - while P < Q and data[P].value != u'\0': - rv.append[P] - while P < Q and data[P].value != u'\0': - P += 1 - P += 1 - rv.append(size) - return [ctypes.wstring_at(ctypes.pointer(data[rv[i]]), - rv[i+1] - rv[i]).rstrip(u'\x00') - for i in range(len(rv)-1)] - -def Reg2Py(data, size, typ): - if typ == REG_DWORD: - if size == 0: - return 0 - return ctypes.cast(data, ctypes.POINTER(ctypes.c_int)).contents.value - elif typ == REG_SZ or typ == REG_EXPAND_SZ: - return ctypes.wstring_at(data, size // 2).rstrip(u'\x00') - elif typ == REG_MULTI_SZ: - return split_multi_sz(ctypes.cast(data, ctypes.c_wchar_p), size // 2) - else: - if size == 0: - return None - return ctypes.string_at(data, size) - -def EnumKey(key, index): - tmpbuf = ctypes.create_unicode_buffer(257) - length = DWORD(257) - rc = RegEnumKeyEx(key.hkey, index, - ctypes.cast(tmpbuf, ctypes.c_wchar_p), - ctypes.byref(length), - LPDWORD(), ctypes.c_wchar_p(), LPDWORD(), - ctypes.POINTER(FILETIME)()) - check_code(rc) - return ctypes.wstring_at(tmpbuf, length.value).rstrip(u'\x00') - -def QueryValueEx(key, value_name): - size = 256 - typ = DWORD() - while True: - tmp_size = DWORD(size) - buf = ctypes.create_string_buffer(size) - rc = RegQueryValueEx(key.hkey, value_name, LPDWORD(), - ctypes.byref(typ), - ctypes.cast(buf, LPBYTE), ctypes.byref(tmp_size)) - if rc != ERROR_MORE_DATA: - break - - size *= 2 - check_code(rc) - return (Reg2Py(buf, tmp_size.value, typ.value), typ.value) - -__all__ = ['OpenKey', 'QueryInfoKey', 'EnumValue', 'EnumKey', 'QueryValueEx', - 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', - 'HKEY_USERS', 'HKEY_PERFORMANCE_DATA', 'HKEY_CURRENT_CONFIG', - 'HKEY_DYN_DATA', 'REG_NONE', 'REG_SZ', 'REG_EXPAND_SZ', - 'REG_BINARY', 'REG_DWORD', 'REG_DWORD_BIG_ENDIAN', - 'REG_DWORD_LITTLE_ENDIAN', 'REG_LINK', 'REG_MULTI_SZ', - 'REG_RESOURCE_LIST', 'REG_FULL_RESOURCE_DESCRIPTOR', - 'REG_RESOURCE_REQUIREMENTS_LIST'] \ No newline at end of file diff --git a/DeDRM_plugin/aescbc.py b/DeDRM_plugin/aescbc.py deleted file mode 100644 index ae08d28..0000000 --- a/DeDRM_plugin/aescbc.py +++ /dev/null @@ -1,571 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" - Routines for doing AES CBC in one file - - Modified by some_updates to extract - and combine only those parts needed for AES CBC - into one simple to add python file - - Original Version - Copyright (c) 2002 by Paul A. Lambert - Under: - CryptoPy Artisitic License Version 1.0 - See the wonderful pure python package cryptopy-1.2.5 - and read its LICENSE.txt for complete license details. - - Adjusted for Python 3, September 2020 -""" - -class CryptoError(Exception): - """ Base class for crypto exceptions """ - def __init__(self,errorMessage='Error!'): - self.message = errorMessage - def __str__(self): - return self.message - -class InitCryptoError(CryptoError): - """ Crypto errors during algorithm initialization """ -class BadKeySizeError(InitCryptoError): - """ Bad key size error """ -class EncryptError(CryptoError): - """ Error in encryption processing """ -class DecryptError(CryptoError): - """ Error in decryption processing """ -class DecryptNotBlockAlignedError(DecryptError): - """ Error in decryption processing """ - -def xorS(a,b): - """ XOR two strings """ - assert len(a)==len(b) - x = [] - for i in range(len(a)): - x.append( chr(ord(a[i])^ord(b[i]))) - return ''.join(x) - -def xor(a,b): - """ XOR two strings """ - x = [] - for i in range(min(len(a),len(b))): - x.append( chr(ord(a[i])^ord(b[i]))) - return ''.join(x) - -""" - Base 'BlockCipher' and Pad classes for cipher instances. - BlockCipher supports automatic padding and type conversion. The BlockCipher - class was written to make the actual algorithm code more readable and - not for performance. -""" - -class BlockCipher: - """ Block ciphers """ - def __init__(self): - self.reset() - - def reset(self): - self.resetEncrypt() - self.resetDecrypt() - def resetEncrypt(self): - self.encryptBlockCount = 0 - self.bytesToEncrypt = '' - def resetDecrypt(self): - self.decryptBlockCount = 0 - self.bytesToDecrypt = '' - - def encrypt(self, plainText, more = None): - """ Encrypt a string and return a binary string """ - self.bytesToEncrypt += plainText # append plainText to any bytes from prior encrypt - numBlocks, numExtraBytes = divmod(len(self.bytesToEncrypt), self.blockSize) - cipherText = '' - for i in range(numBlocks): - bStart = i*self.blockSize - ctBlock = self.encryptBlock(self.bytesToEncrypt[bStart:bStart+self.blockSize]) - self.encryptBlockCount += 1 - cipherText += ctBlock - if numExtraBytes > 0: # save any bytes that are not block aligned - self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:] - else: - self.bytesToEncrypt = '' - - if more == None: # no more data expected from caller - finalBytes = self.padding.addPad(self.bytesToEncrypt,self.blockSize) - if len(finalBytes) > 0: - ctBlock = self.encryptBlock(finalBytes) - self.encryptBlockCount += 1 - cipherText += ctBlock - self.resetEncrypt() - return cipherText - - def decrypt(self, cipherText, more = None): - """ Decrypt a string and return a string """ - self.bytesToDecrypt += cipherText # append to any bytes from prior decrypt - - numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize) - if more == None: # no more calls to decrypt, should have all the data - if numExtraBytes != 0: - raise DecryptNotBlockAlignedError('Data not block aligned on decrypt') - - # hold back some bytes in case last decrypt has zero len - if (more != None) and (numExtraBytes == 0) and (numBlocks >0) : - numBlocks -= 1 - numExtraBytes = self.blockSize - - plainText = '' - for i in range(numBlocks): - bStart = i*self.blockSize - ptBlock = self.decryptBlock(self.bytesToDecrypt[bStart : bStart+self.blockSize]) - self.decryptBlockCount += 1 - plainText += ptBlock - - if numExtraBytes > 0: # save any bytes that are not block aligned - self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:] - else: - self.bytesToEncrypt = '' - - if more == None: # last decrypt remove padding - plainText = self.padding.removePad(plainText, self.blockSize) - self.resetDecrypt() - return plainText - - -class Pad: - def __init__(self): - pass # eventually could put in calculation of min and max size extension - -class padWithPadLen(Pad): - """ Pad a binary string with the length of the padding """ - - def addPad(self, extraBytes, blockSize): - """ Add padding to a binary string to make it an even multiple - of the block size """ - blocks, numExtraBytes = divmod(len(extraBytes), blockSize) - padLength = blockSize - numExtraBytes - return extraBytes + padLength*chr(padLength) - - def removePad(self, paddedBinaryString, blockSize): - """ Remove padding from a binary string """ - if not(0 6 and i%Nk == 4 : - temp = [ Sbox[byte] for byte in temp ] # SubWord(temp) - w.append( [ w[i-Nk][byte]^temp[byte] for byte in range(4) ] ) - return w - -Rcon = (0,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36, # note extra '0' !!! - 0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6, - 0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91) - -#------------------------------------- -def AddRoundKey(algInstance, keyBlock): - """ XOR the algorithm state with a block of key material """ - for column in range(algInstance.Nb): - for row in range(4): - algInstance.state[column][row] ^= keyBlock[column][row] -#------------------------------------- - -def SubBytes(algInstance): - for column in range(algInstance.Nb): - for row in range(4): - algInstance.state[column][row] = Sbox[algInstance.state[column][row]] - -def InvSubBytes(algInstance): - for column in range(algInstance.Nb): - for row in range(4): - algInstance.state[column][row] = InvSbox[algInstance.state[column][row]] - -Sbox = (0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5, - 0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, - 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0, - 0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, - 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc, - 0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, - 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a, - 0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, - 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0, - 0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, - 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b, - 0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, - 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85, - 0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, - 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5, - 0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, - 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17, - 0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, - 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88, - 0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, - 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c, - 0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, - 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9, - 0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, - 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6, - 0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, - 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e, - 0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, - 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94, - 0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, - 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68, - 0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16) - -InvSbox = (0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, - 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, - 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, - 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, - 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, - 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, - 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, - 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, - 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, - 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, - 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, - 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, - 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, - 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, - 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, - 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, - 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, - 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, - 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, - 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, - 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, - 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, - 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, - 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, - 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, - 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, - 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, - 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, - 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, - 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, - 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, - 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d) - -#------------------------------------- -""" For each block size (Nb), the ShiftRow operation shifts row i - by the amount Ci. Note that row 0 is not shifted. - Nb C1 C2 C3 - ------------------- """ -shiftOffset = { 4 : ( 0, 1, 2, 3), - 5 : ( 0, 1, 2, 3), - 6 : ( 0, 1, 2, 3), - 7 : ( 0, 1, 2, 4), - 8 : ( 0, 1, 3, 4) } -def ShiftRows(algInstance): - tmp = [0]*algInstance.Nb # list of size Nb - for r in range(1,4): # row 0 reamains unchanged and can be skipped - for c in range(algInstance.Nb): - tmp[c] = algInstance.state[(c+shiftOffset[algInstance.Nb][r]) % algInstance.Nb][r] - for c in range(algInstance.Nb): - algInstance.state[c][r] = tmp[c] -def InvShiftRows(algInstance): - tmp = [0]*algInstance.Nb # list of size Nb - for r in range(1,4): # row 0 reamains unchanged and can be skipped - for c in range(algInstance.Nb): - tmp[c] = algInstance.state[(c+algInstance.Nb-shiftOffset[algInstance.Nb][r]) % algInstance.Nb][r] - for c in range(algInstance.Nb): - algInstance.state[c][r] = tmp[c] -#------------------------------------- -def MixColumns(a): - Sprime = [0,0,0,0] - for j in range(a.Nb): # for each column - Sprime[0] = mul(2,a.state[j][0])^mul(3,a.state[j][1])^mul(1,a.state[j][2])^mul(1,a.state[j][3]) - Sprime[1] = mul(1,a.state[j][0])^mul(2,a.state[j][1])^mul(3,a.state[j][2])^mul(1,a.state[j][3]) - Sprime[2] = mul(1,a.state[j][0])^mul(1,a.state[j][1])^mul(2,a.state[j][2])^mul(3,a.state[j][3]) - Sprime[3] = mul(3,a.state[j][0])^mul(1,a.state[j][1])^mul(1,a.state[j][2])^mul(2,a.state[j][3]) - for i in range(4): - a.state[j][i] = Sprime[i] - -def InvMixColumns(a): - """ Mix the four bytes of every column in a linear way - This is the opposite operation of Mixcolumn """ - Sprime = [0,0,0,0] - for j in range(a.Nb): # for each column - Sprime[0] = mul(0x0E,a.state[j][0])^mul(0x0B,a.state[j][1])^mul(0x0D,a.state[j][2])^mul(0x09,a.state[j][3]) - Sprime[1] = mul(0x09,a.state[j][0])^mul(0x0E,a.state[j][1])^mul(0x0B,a.state[j][2])^mul(0x0D,a.state[j][3]) - Sprime[2] = mul(0x0D,a.state[j][0])^mul(0x09,a.state[j][1])^mul(0x0E,a.state[j][2])^mul(0x0B,a.state[j][3]) - Sprime[3] = mul(0x0B,a.state[j][0])^mul(0x0D,a.state[j][1])^mul(0x09,a.state[j][2])^mul(0x0E,a.state[j][3]) - for i in range(4): - a.state[j][i] = Sprime[i] - -#------------------------------------- -def mul(a, b): - """ Multiply two elements of GF(2^m) - needed for MixColumn and InvMixColumn """ - if (a !=0 and b!=0): - return Alogtable[(Logtable[a] + Logtable[b])%255] - else: - return 0 - -Logtable = ( 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, - 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193, - 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120, - 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142, - 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, - 102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, - 126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186, - 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87, - 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, - 44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, - 127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, - 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, - 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, - 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, - 68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, - 103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7) - -Alogtable= ( 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, - 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, - 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49, - 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, - 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, - 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, - 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, - 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, - 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, - 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, - 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, - 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, - 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, - 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, - 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, - 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1) - - - - -""" - AES Encryption Algorithm - The AES algorithm is just Rijndael algorithm restricted to the default - blockSize of 128 bits. -""" - -class AES(Rijndael): - """ The AES algorithm is the Rijndael block cipher restricted to block - sizes of 128 bits and key sizes of 128, 192 or 256 bits - """ - def __init__(self, key = None, padding = padWithPadLen(), keySize=16): - """ Initialize AES, keySize is in bytes """ - if not (keySize == 16 or keySize == 24 or keySize == 32) : - raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes') - - Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 ) - - self.name = 'AES' - - -""" - CBC mode of encryption for block ciphers. - This algorithm mode wraps any BlockCipher to make a - Cipher Block Chaining mode. -""" -from random import Random # should change to crypto.random!!! - - -class CBC(BlockCipher): - """ The CBC class wraps block ciphers to make cipher block chaining (CBC) mode - algorithms. The initialization (IV) is automatic if set to None. Padding - is also automatic based on the Pad class used to initialize the algorithm - """ - def __init__(self, blockCipherInstance, padding = padWithPadLen()): - """ CBC algorithms are created by initializing with a BlockCipher instance """ - self.baseCipher = blockCipherInstance - self.name = self.baseCipher.name + '_CBC' - self.blockSize = self.baseCipher.blockSize - self.keySize = self.baseCipher.keySize - self.padding = padding - self.baseCipher.padding = noPadding() # baseCipher should NOT pad!! - self.r = Random() # for IV generation, currently uses - # mediocre standard distro version <---------------- - import time - newSeed = time.ctime()+str(self.r) # seed with instance location - self.r.seed(newSeed) # to make unique - self.reset() - - def setKey(self, key): - self.baseCipher.setKey(key) - - # Overload to reset both CBC state and the wrapped baseCipher - def resetEncrypt(self): - BlockCipher.resetEncrypt(self) # reset CBC encrypt state (super class) - self.baseCipher.resetEncrypt() # reset base cipher encrypt state - - def resetDecrypt(self): - BlockCipher.resetDecrypt(self) # reset CBC state (super class) - self.baseCipher.resetDecrypt() # reset base cipher decrypt state - - def encrypt(self, plainText, iv=None, more=None): - """ CBC encryption - overloads baseCipher to allow optional explicit IV - when iv=None, iv is auto generated! - """ - if self.encryptBlockCount == 0: - self.iv = iv - else: - assert(iv==None), 'IV used only on first call to encrypt' - - return BlockCipher.encrypt(self,plainText, more=more) - - def decrypt(self, cipherText, iv=None, more=None): - """ CBC decryption - overloads baseCipher to allow optional explicit IV - when iv=None, iv is auto generated! - """ - if self.decryptBlockCount == 0: - self.iv = iv - else: - assert(iv==None), 'IV used only on first call to decrypt' - - return BlockCipher.decrypt(self, cipherText, more=more) - - def encryptBlock(self, plainTextBlock): - """ CBC block encryption, IV is set with 'encrypt' """ - auto_IV = '' - if self.encryptBlockCount == 0: - if self.iv == None: - # generate IV and use - self.iv = ''.join([chr(self.r.randrange(256)) for i in range(self.blockSize)]) - self.prior_encr_CT_block = self.iv - auto_IV = self.prior_encr_CT_block # prepend IV if it's automatic - else: # application provided IV - assert(len(self.iv) == self.blockSize ),'IV must be same length as block' - self.prior_encr_CT_block = self.iv - """ encrypt the prior CT XORed with the PT """ - ct = self.baseCipher.encryptBlock( xor(self.prior_encr_CT_block, plainTextBlock) ) - self.prior_encr_CT_block = ct - return auto_IV+ct - - def decryptBlock(self, encryptedBlock): - """ Decrypt a single block """ - - if self.decryptBlockCount == 0: # first call, process IV - if self.iv == None: # auto decrypt IV? - self.prior_CT_block = encryptedBlock - return '' - else: - assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption" - self.prior_CT_block = self.iv - - dct = self.baseCipher.decryptBlock(encryptedBlock) - """ XOR the prior decrypted CT with the prior CT """ - dct_XOR_priorCT = xor( self.prior_CT_block, dct ) - - self.prior_CT_block = encryptedBlock - - return dct_XOR_priorCT - - -""" - AES_CBC Encryption Algorithm -""" - -class AES_CBC(CBC): - """ AES encryption in CBC feedback mode """ - def __init__(self, key=None, padding=padWithPadLen(), keySize=16): - CBC.__init__( self, AES(key, noPadding(), keySize), padding) - self.name = 'AES_CBC' diff --git a/DeDRM_plugin/alfcrypto.py b/DeDRM_plugin/alfcrypto.py index 3b9f319..879b03d 100644 --- a/DeDRM_plugin/alfcrypto.py +++ b/DeDRM_plugin/alfcrypto.py @@ -8,11 +8,9 @@ # pbkdf2.py Copyright © 2009 Daniel Holth # pbkdf2.py This code may be freely used and modified for any purpose. -import sys import hmac from struct import pack import hashlib -import aescbc class Pukall_Cipher(object): def __init__(self): @@ -26,10 +24,7 @@ class Pukall_Cipher(object): raise Exception("PC1: Bad key length") wkey = [] for i in range(8): - if sys.version_info[0] == 2: - wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1])) - else: - wkey.append(key[i*2]<<8 | key[i*2+1]) + wkey.append(key[i*2]<<8 | key[i*2+1]) dst = bytearray(len(src)) for i in range(len(src)): temp1 = 0; @@ -42,10 +37,7 @@ class Pukall_Cipher(object): temp1 = (temp1*20021+1)&0xFFFF byteXorVal ^= temp1 ^ sum2 - if sys.version_info[0] == 2: - curByte = ord(src[i]) - else: - curByte = src[i] + curByte = src[i] if not decryption: keyXorVal = curByte * 257; @@ -55,11 +47,9 @@ class Pukall_Cipher(object): for j in range(8): wkey[j] ^= keyXorVal; - if sys.version_info[0] == 2: - dst[i] = chr(curByte) - else: - dst[i] = curByte - + dst[i] = curByte + + return bytes(dst) class Topaz_Cipher(object): @@ -91,23 +81,6 @@ class Topaz_Cipher(object): 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 - - class KeyIVGen(object): # this only exists in openssl so we will use pure python implementation instead # PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1', diff --git a/DeDRM_plugin/androidkindlekey.py b/DeDRM_plugin/androidkindlekey.py index 27124bd..8ae8372 100755 --- a/DeDRM_plugin/androidkindlekey.py +++ b/DeDRM_plugin/androidkindlekey.py @@ -51,10 +51,7 @@ STORAGE2 = "map_data_storage.db" def unpad(data, padding=16): - if sys.version_info[0] == 2: - pad_len = ord(data[-1]) - else: - pad_len = data[-1] + pad_len = data[-1] return data[:-pad_len] diff --git a/DeDRM_plugin/argv_utils.py b/DeDRM_plugin/argv_utils.py index 2ca4776..ad7bf19 100644 --- a/DeDRM_plugin/argv_utils.py +++ b/DeDRM_plugin/argv_utils.py @@ -3,46 +3,13 @@ import sys -# get sys.argv arguments and encode them into utf-8 +# get sys.argv arguments as a list of Unicode strings def unicode_argv(default_name): - - try: - from calibre.constants import iswindows - except: - iswindows = sys.platform.startswith('win') - - if iswindows: - # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode - # strings. - - # Versions 2.x of Python don't support Unicode in sys.argv on - # Windows, with the underlying Windows API instead replacing multi-byte - # characters with '?'. - - - from ctypes import POINTER, byref, cdll, c_int, windll - from ctypes.wintypes import LPCWSTR, LPWSTR - - GetCommandLineW = cdll.kernel32.GetCommandLineW - GetCommandLineW.argtypes = [] - GetCommandLineW.restype = LPCWSTR - - CommandLineToArgvW = windll.shell32.CommandLineToArgvW - CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)] - CommandLineToArgvW.restype = POINTER(LPWSTR) - - cmd = GetCommandLineW() - argc = c_int(0) - argv = CommandLineToArgvW(cmd, byref(argc)) - if argc.value > 0: - # Remove Python executable and commands if present - start = argc.value - len(sys.argv) - return [argv[i] for i in - range(start, argc.value)] - # if we don't have any arguments at all, just pass back script name - # this should never happen - return [ default_name ] - else: - argvencoding = sys.stdin.encoding or "utf-8" - return [arg if (isinstance(arg, str) or isinstance(arg,unicode)) else str(arg, argvencoding) for arg in sys.argv] + # On Python 3 sys.argv is already a list of Unicode strings on every + # platform, so no special handling is needed. + if len(sys.argv) > 0: + return list(sys.argv) + # if we don't have any arguments at all, just pass back the script name + # this should never happen + return [default_name] diff --git a/DeDRM_plugin/config.py b/DeDRM_plugin/config.py index 19b40fd..bd2b492 100755 --- a/DeDRM_plugin/config.py +++ b/DeDRM_plugin/config.py @@ -1143,13 +1143,7 @@ class AddAdeptDialog(): scriptpath = os.path.join(parent.parent.alfdir,"adobekey.py") defaultkeys, defaultnames = WineGetKeys(scriptpath, ".der",parent.getwineprefix()) - if sys.version_info[0] < 3: - # Python2 - import itertools - zip_function = itertools.izip - else: - # Python3 - zip_function = zip + zip_function = zip for key, name in zip_function(defaultkeys, defaultnames): key = codecs.encode(key,'hex').decode("latin-1") diff --git a/DeDRM_plugin/convert2xml.py b/DeDRM_plugin/convert2xml.py index 1a70844..92f465b 100644 --- a/DeDRM_plugin/convert2xml.py +++ b/DeDRM_plugin/convert2xml.py @@ -124,10 +124,10 @@ class Dictionary(object): self.pos = 0 def escapestr(self, str): - str = str.replace('&','&') - str = str.replace('<','<') - str = str.replace('>','>') - str = str.replace('=','=') + str = str.replace(b'&',b'&') + str = str.replace(b'<',b'<') + str = str.replace(b'>',b'>') + str = str.replace(b'=',b'=') return str def lookup(self,val): diff --git a/DeDRM_plugin/epubfontdecrypt.py b/DeDRM_plugin/epubfontdecrypt.py index c9a982f..cde3e2b 100644 --- a/DeDRM_plugin/epubfontdecrypt.py +++ b/DeDRM_plugin/epubfontdecrypt.py @@ -16,8 +16,6 @@ Decrypts / deobfuscates font files in EPUB files """ -from __future__ import print_function - __license__ = 'GPL v3' __version__ = "2" @@ -136,12 +134,7 @@ class Decryptor(object): return data def deobfuscate_single_data(self, key, data): - try: - msg = bytes([c^k for c,k in zip(data, itertools.cycle(key))]) - except TypeError: - # Python 2 - msg = ''.join(chr(ord(c)^ord(k)) for c,k in itertools.izip(data, itertools.cycle(key))) - return msg + return bytes([c^k for c,k in zip(data, itertools.cycle(key))]) diff --git a/DeDRM_plugin/erdr2pml.py b/DeDRM_plugin/erdr2pml.py index 2efe9bb..26babd3 100755 --- a/DeDRM_plugin/erdr2pml.py +++ b/DeDRM_plugin/erdr2pml.py @@ -142,9 +142,6 @@ def sanitizeFileName(name): def fixKey(key): def fixByte(b): - if sys.version_info[0] == 2: - b = ord(b) - return b ^ ((b ^ (b<<1) ^ (b<<2) ^ (b<<3) ^ (b<<4) ^ (b<<5) ^ (b<<6) ^ (b<<7) ^ 0x80) & 0x80) return bytes(bytearray([fixByte(a) for a in key])) @@ -152,10 +149,7 @@ def deXOR(text, sp, table): r=b'' j = sp for i in range(len(text)): - if sys.version_info[0] == 2: - r += chr(ord(table[j]) ^ ord(text[i])) - else: - r += bytes(bytearray([table[j] ^ text[i]])) + r += bytes(bytearray([table[j] ^ text[i]])) j = j + 1 if j == len(table): j = 0 @@ -320,7 +314,7 @@ class EreaderProcessor(object): # now handle footnotes pages if self.num_footnote_pages > 0: - r += '\n' + r += b'\n' # the record 0 of the footnote section must pass through the Xor Table to make it useful sect = self.section_reader(self.first_footnote_page) fnote_ids = deXOR(sect, 0, self.xortable) @@ -328,11 +322,11 @@ class EreaderProcessor(object): des = DES.new(fixKey(self.content_key), DES.MODE_ECB) for i in range(1,self.num_footnote_pages): logging.debug('get footnotepage %d', i) - id_len = ord(fnote_ids[2]) + id_len = fnote_ids[2] id = fnote_ids[3:3+id_len] - fmarker = '\n' % id + fmarker = b'\n' % id fmarker += zlib.decompress(des.decrypt(self.section_reader(self.first_footnote_page + i))) - fmarker += '\n\n' + fmarker += b'\n\n' r += fmarker fnote_ids = fnote_ids[id_len+4:] @@ -344,18 +338,18 @@ class EreaderProcessor(object): # now handle sidebar pages if self.num_sidebar_pages > 0: - r += '\n' + r += b'\n' # the record 0 of the sidebar section must pass through the Xor Table to make it useful sect = self.section_reader(self.first_sidebar_page) sbar_ids = deXOR(sect, 0, self.xortable) # the remaining records of the sidebar sections need to be decoded with the content_key and zlib inflated des = DES.new(fixKey(self.content_key), DES.MODE_ECB) for i in range(1,self.num_sidebar_pages): - id_len = ord(sbar_ids[2]) + id_len = sbar_ids[2] id = sbar_ids[3:3+id_len] - smarker = '\n' % id + smarker = b'\n' % id smarker += zlib.decompress(des.decrypt(self.section_reader(self.first_sidebar_page + i))) - smarker += '\n\n' + smarker += b'\n\n' r += smarker sbar_ids = sbar_ids[id_len+4:] diff --git a/DeDRM_plugin/flatxml2svg.py b/DeDRM_plugin/flatxml2svg.py index 3768358..05e7da5 100644 --- a/DeDRM_plugin/flatxml2svg.py +++ b/DeDRM_plugin/flatxml2svg.py @@ -199,7 +199,7 @@ def convert2SVG(gdict, flat_xml, pageid, previd, nextid, svgDir, raw, meta_array mlst.append('\n') mlst.append('Page %d - %s by %s\n' % (pageid, meta_array['Title'],meta_array['Authors'])) mlst.append('