]> xmof Git - DeDRM.git/commitdiff
Try to add support for new K4PC
authorNoDRM <no_drm123@protonmail.com>
Fri, 23 Jun 2023 17:30:06 +0000 (19:30 +0200)
committerNoDRM <no_drm123@protonmail.com>
Fri, 23 Jun 2023 17:30:06 +0000 (19:30 +0200)
Co-authored-by: Andrew Innes <andrew.c12@gmail.com>
Co-authored-by: Satsuoni <satsuoni@hotmail.com>
CHANGELOG.md
DeDRM_plugin/ion.py

index 0e4301bf470070cee40e3c5f53989f29c3802e06..98524fbc091657ca3ca514861b66487e507f8bb6 100644 (file)
@@ -89,3 +89,4 @@ 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. 
 - 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).
+- 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.
index 45e96109acc0f93c9116cae524a3d796b624b0aa..7e3c9de937931c81163bcadeafbf2eb29326f568 100644 (file)
@@ -835,6 +835,107 @@ def obfuscate(secret, version):
     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):
     envelope = None
     version = None
@@ -878,12 +979,25 @@ class DrmIonVoucher(object):
             else:
                 _assert(False, "Unknown lock parameter: %s" % param)
 
-        sharedsecret = obfuscate(shared, self.version)
 
-        key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
-        aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
-        b = aes.decrypt(self.ciphertext)
-        b = pkcs7unpad(b, 16)
+        sharedsecrets = [obfuscate(shared, self.version),obfuscate2(shared, self.version),obfuscate3(shared, self.version)]
+        decrypted=False
+        ex=None
+        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))
         addprottable(self.drmkey)