Compare commits
2 Commits
fb8b003444
...
a553a71f45
Author | SHA1 | Date |
---|---|---|
NoDRM | a553a71f45 | |
NoDRM | 740b46546f |
|
@ -89,3 +89,5 @@ List of changes since the fork of Apprentice Harper's repository:
|
||||||
- PDF: Ignore invalid PDF objids unless the script is running in strict mode. Fixes some PDFs, apparently. Fixes #233.
|
- PDF: Ignore invalid PDF objids unless the script is running in strict mode. Fixes some PDFs, apparently. Fixes #233.
|
||||||
- Bugfix: EPUBs with remaining content in the encryption.xml after decryption weren't written correctly.
|
- Bugfix: EPUBs with remaining content in the encryption.xml after decryption weren't written correctly.
|
||||||
- Support for Adobe's 'aes128-cbc-uncompressed' encryption method (fixes #242).
|
- Support for Adobe's 'aes128-cbc-uncompressed' encryption method (fixes #242).
|
||||||
|
- Two bugfixes for Amazon DeDRM from Satuoni ( https://github.com/noDRM/DeDRM_tools/issues/315#issuecomment-1508305428 ) and andrewc12 ( https://github.com/andrewc12/DeDRM_tools/commit/d9233d61f00d4484235863969919059f4d0b2057 ) that might make the plugin work with newer versions.
|
||||||
|
- Fix font decryption not working with some books (fixes #347), thanks for the patch @bydioeds.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# epubfontdecrypt.py
|
# epubfontdecrypt.py
|
||||||
# Copyright © 2021 by noDRM
|
# Copyright © 2021-2023 by noDRM
|
||||||
|
|
||||||
# Released under the terms of the GNU General Public Licence, version 3
|
# Released under the terms of the GNU General Public Licence, version 3
|
||||||
# <http://www.gnu.org/licenses/>
|
# <http://www.gnu.org/licenses/>
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
# Revision history:
|
# Revision history:
|
||||||
# 1 - Initial release
|
# 1 - Initial release
|
||||||
|
# 2 - Bugfix for multiple book IDs, reported at #347
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Decrypts / deobfuscates font files in EPUB files
|
Decrypts / deobfuscates font files in EPUB files
|
||||||
|
@ -18,7 +19,7 @@ Decrypts / deobfuscates font files in EPUB files
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__version__ = "1"
|
__version__ = "2"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -193,9 +194,10 @@ def decryptFontsBook(inpath, outpath):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
identify_element = container.find(packageNS("metadata")).find(metadataDCNS("identifier"))
|
identify_elements = container.find(packageNS("metadata")).findall(metadataDCNS("identifier"))
|
||||||
if (secret_key_name is None or secret_key_name == identify_element.get("id")):
|
for element in identify_elements:
|
||||||
font_master_key = identify_element.text
|
if (secret_key_name is None or secret_key_name == element.get("id")):
|
||||||
|
font_master_key = element.text
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -835,6 +835,107 @@ def obfuscate(secret, version):
|
||||||
return obfuscated
|
return obfuscated
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# scramble() and obfuscate2() from https://github.com/andrewc12/DeDRM_tools/commit/d9233d61f00d4484235863969919059f4d0b2057
|
||||||
|
|
||||||
|
def scramble(st,magic):
|
||||||
|
ret=bytearray(len(st))
|
||||||
|
padlen=len(st)
|
||||||
|
for counter in range(len(st)):
|
||||||
|
ivar2=(padlen//2)-2*(counter%magic)+magic+counter-1
|
||||||
|
ret[ivar2%padlen]=st[counter]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def obfuscate2(secret, version):
|
||||||
|
if version == 1: # v1 does not use obfuscation
|
||||||
|
return secret
|
||||||
|
magic, word = OBFUSCATION_TABLE["V%d" % version]
|
||||||
|
# extend secret so that its length is divisible by the magic number
|
||||||
|
if len(secret) % magic != 0:
|
||||||
|
secret = secret + b'\x00' * (magic - len(secret) % magic)
|
||||||
|
obfuscated = bytearray(len(secret))
|
||||||
|
wordhash = bytearray(hashlib.sha256(word).digest()[16:])
|
||||||
|
#print(wordhash.hex())
|
||||||
|
shuffled = bytearray(scramble(secret,magic))
|
||||||
|
for i in range(0, len(secret)):
|
||||||
|
obfuscated[i] = shuffled[i] ^ wordhash[i % 16]
|
||||||
|
return obfuscated
|
||||||
|
|
||||||
|
# scramble3() and obfuscate3() from https://github.com/Satsuoni/DeDRM_tools/commit/da6b6a0c911b6d45fe1b13042b690daebc1cc22f
|
||||||
|
|
||||||
|
def scramble3(st,magic):
|
||||||
|
ret=bytearray(len(st))
|
||||||
|
padlen=len(st)
|
||||||
|
divs = padlen // magic
|
||||||
|
cntr = 0
|
||||||
|
iVar6 = 0
|
||||||
|
offset = 0
|
||||||
|
if (0 < ((magic - 1) + divs)):
|
||||||
|
while True:
|
||||||
|
if (offset & 1) == 0 :
|
||||||
|
uVar4 = divs - 1
|
||||||
|
if offset < divs:
|
||||||
|
iVar3 = 0
|
||||||
|
uVar4 = offset
|
||||||
|
else:
|
||||||
|
iVar3 = (offset - divs) + 1
|
||||||
|
if uVar4>=0:
|
||||||
|
iVar5 = uVar4 * magic
|
||||||
|
index = ((padlen - 1) - cntr)
|
||||||
|
while True:
|
||||||
|
if (magic <= iVar3): break
|
||||||
|
ret[index] = st[iVar3 + iVar5]
|
||||||
|
iVar3 = iVar3 + 1
|
||||||
|
cntr = cntr + 1
|
||||||
|
uVar4 = uVar4 - 1
|
||||||
|
iVar5 = iVar5 - magic
|
||||||
|
index -= 1
|
||||||
|
if uVar4<=-1: break
|
||||||
|
else:
|
||||||
|
if (offset < magic):
|
||||||
|
iVar3 = 0
|
||||||
|
else :
|
||||||
|
iVar3 = (offset - magic) + 1
|
||||||
|
if (iVar3 < divs):
|
||||||
|
uVar4 = offset
|
||||||
|
if (magic <= offset):
|
||||||
|
uVar4 = magic - 1
|
||||||
|
|
||||||
|
index = ((padlen - 1) - cntr)
|
||||||
|
iVar5 = iVar3 * magic
|
||||||
|
while True:
|
||||||
|
if (uVar4 < 0) : break
|
||||||
|
iVar3 += 1
|
||||||
|
ret[index] = st[uVar4 + iVar5]
|
||||||
|
uVar4 -= 1
|
||||||
|
index=index-1
|
||||||
|
iVar5 = iVar5 + magic;
|
||||||
|
cntr += 1;
|
||||||
|
if iVar3>=divs: break
|
||||||
|
offset = offset + 1
|
||||||
|
if offset >= ((magic - 1) + divs) :break
|
||||||
|
return ret
|
||||||
|
|
||||||
|
#not sure if the third variant is used anywhere, but it is in Kindle, so I tried to add it
|
||||||
|
def obfuscate3(secret, version):
|
||||||
|
if version == 1: # v1 does not use obfuscation
|
||||||
|
return secret
|
||||||
|
magic, word = OBFUSCATION_TABLE["V%d" % version]
|
||||||
|
# extend secret so that its length is divisible by the magic number
|
||||||
|
if len(secret) % magic != 0:
|
||||||
|
secret = secret + b'\x00' * (magic - len(secret) % magic)
|
||||||
|
#secret = bytearray(secret)
|
||||||
|
obfuscated = bytearray(len(secret))
|
||||||
|
wordhash = bytearray(hashlib.sha256(word).digest())
|
||||||
|
#print(wordhash.hex())
|
||||||
|
shuffled=bytearray(scramble3(secret,magic))
|
||||||
|
#print(shuffled)
|
||||||
|
# shuffle secret and xor it with the first half of the word hash
|
||||||
|
for i in range(0, len(secret)):
|
||||||
|
obfuscated[i] = shuffled[i] ^ wordhash[i % 16]
|
||||||
|
return obfuscated
|
||||||
|
|
||||||
class DrmIonVoucher(object):
|
class DrmIonVoucher(object):
|
||||||
envelope = None
|
envelope = None
|
||||||
version = None
|
version = None
|
||||||
|
@ -878,12 +979,25 @@ class DrmIonVoucher(object):
|
||||||
else:
|
else:
|
||||||
_assert(False, "Unknown lock parameter: %s" % param)
|
_assert(False, "Unknown lock parameter: %s" % param)
|
||||||
|
|
||||||
sharedsecret = obfuscate(shared, self.version)
|
|
||||||
|
|
||||||
key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
|
sharedsecrets = [obfuscate(shared, self.version),obfuscate2(shared, self.version),obfuscate3(shared, self.version)]
|
||||||
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
|
decrypted=False
|
||||||
b = aes.decrypt(self.ciphertext)
|
ex=None
|
||||||
b = pkcs7unpad(b, 16)
|
for sharedsecret in sharedsecrets:
|
||||||
|
key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
|
||||||
|
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
|
||||||
|
try:
|
||||||
|
b = aes.decrypt(self.ciphertext)
|
||||||
|
b = pkcs7unpad(b, 16)
|
||||||
|
decrypted=True
|
||||||
|
print("Decryption succeeded")
|
||||||
|
break
|
||||||
|
except Exception as ex:
|
||||||
|
print("Decryption failed, trying next fallback ")
|
||||||
|
if not decrypted:
|
||||||
|
raise ex
|
||||||
|
|
||||||
|
sharedsecret = obfuscate(shared, self.version)
|
||||||
|
|
||||||
self.drmkey = BinaryIonParser(BytesIO(b))
|
self.drmkey = BinaryIonParser(BytesIO(b))
|
||||||
addprottable(self.drmkey)
|
addprottable(self.drmkey)
|
||||||
|
|
Loading…
Reference in New Issue