]> xmof Git - DeDRM.git/commitdiff
Add support for "hardened" Adobe DRM
authora980e066a01 <100724039+a980e066a01@users.noreply.github.com>
Tue, 22 Feb 2022 23:47:51 +0000 (23:47 +0000)
committernoDRM <no_drm123@protonmail.com>
Fri, 18 Mar 2022 15:45:39 +0000 (15:45 +0000)
What took the most time was not reverse-engineering
the scheme, but actually finding books using it...

Closes #20, #25, #45

DeDRM_plugin/epubtest.py
DeDRM_plugin/ineptepub.py
DeDRM_plugin/ineptpdf.py
DeDRM_plugin_ReadMe.txt
FAQs.md
README.md
ReadMe_Overview.txt

index 11f8b4b4eb3fb9fecb31835cb571f8e116fca764..79657a581c8157123221ebc8dd2644d6daa3e109 100644 (file)
@@ -175,7 +175,7 @@ def getfiledata(file, zi):
     return data
 
 def encryption(infile):
-    # Supports Adobe (old & new), B&N, Kobo, Apple, Readium LCP.    
+    # Supports Adobe (old & new), B&N, Kobo, Apple, Readium LCP.
     encryption = "Error"
     try:
         with open(infile,'rb') as infileobject:
@@ -206,10 +206,8 @@ def encryption(infile):
                         adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
                         expr = './/%s' % (adept('encryptedKey'),)
                         bookkey = ''.join(rights.findtext(expr))
-                        if len(bookkey) == 172:
-                            encryption = "Adobe (old)"
-                        if len(bookkey) == 192:
-                            encryption = "Adobe (new)"
+                        if len(bookkey) >= 172:
+                            encryption = "Adobe"
                         elif len(bookkey) == 64:
                             encryption = "B&N"
                         else:
index 5fc6d4b0223e6cde4bf8d5962037d302a751205f..aa7b57142a92f8aa926975ed37197893569047fa 100644 (file)
 #   7.0 - Add Python 3 compatibility for calibre 5.0
 #   7.1 - Add ignoble support, dropping the dedicated ignobleepub.py script
 #   7.2 - Only support PyCryptodome; clean up the code
+#   8.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10)
 
 """
 Decrypt Adobe Digital Editions encrypted ePub books.
 """
 
 __license__ = 'GPL v3'
-__version__ = "7.2"
+__version__ = "8.0"
 
 import sys
 import os
@@ -49,6 +50,8 @@ import zipfile
 from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
 from contextlib import closing
 from lxml import etree
+from uuid import UUID
+import hashlib
 
 try:
     from Cryptodome.Cipher import AES, PKCS1_v1_5
@@ -247,6 +250,23 @@ def adeptGetUserUUID(inpath):
         except:
             return None
 
+def removeHardening(rights, keytype, keydata):
+    adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
+    textGetter = lambda name: ''.join(rights.findtext('.//%s' % (adept(name),)))
+
+    # Gather what we need, and generate the IV
+    resourceuuid = UUID(textGetter("resource"))
+    deviceuuid = UUID(textGetter("device"))
+    fullfillmentuuid = UUID(textGetter("fulfillment")[:36])
+    kekiv = UUID(int=resourceuuid.int ^ deviceuuid.int ^ fullfillmentuuid.int).bytes
+
+    # Derive kek from just "keytype"
+    rem = int(keytype, 10) % 16
+    H = hashlib.sha256(keytype.encode("ascii")).digest()
+    kek = H[2*rem : 16 + rem] + H[rem : 2*rem]
+
+    return unpad(AES.new(kek, AES.MODE_CBC, kekiv).decrypt(keydata), 16) # PKCS#7
+
 def decryptBook(userkey, inpath, outpath):
     with closing(ZipFile(open(inpath, 'rb'))) as inf:
         namelist = inf.namelist()
@@ -260,15 +280,12 @@ def decryptBook(userkey, inpath, outpath):
             rights = etree.fromstring(inf.read('META-INF/rights.xml'))
             adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
             expr = './/%s' % (adept('encryptedKey'),)
-            bookkey = ''.join(rights.findtext(expr))
-            if len(bookkey) == 192:
-                print("{0:s} seems to be an Adobe ADEPT ePub with Adobe's new DRM".format(os.path.basename(inpath)))
-                print("This DRM cannot be removed yet. ")
-                print("Try getting your distributor to give you a new ACSM file, then open that in an old version of ADE (2.0).")
-                print("If your book distributor is not enforcing the new DRM yet, this will give you a copy with the old DRM.")
-                raise ADEPTNewVersionError("Book uses new ADEPT encryption")
-
-            if len(bookkey) == 172:
+            bookkeyelem = rights.find(expr)
+            bookkey = bookkeyelem.text
+            keytype = bookkeyelem.attrib.get('keyType', '0')
+            if len(bookkey) >= 172 and int(keytype, 10) > 2:
+                print("{0:s} is a secure Adobe Adept ePub with hardening.".format(os.path.basename(inpath)))
+            elif len(bookkey) == 172:
                 print("{0:s} is a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
             elif len(bookkey) == 64:
                 print("{0:s} is a secure Adobe PassHash (B&N) ePub.".format(os.path.basename(inpath)))
@@ -277,9 +294,11 @@ def decryptBook(userkey, inpath, outpath):
                 return 1
 
             if len(bookkey) != 64:
-                # Normal Adobe ADEPT
+                # Normal or "hardened" Adobe ADEPT
                 rsakey = RSA.import_key(userkey) # parses the ASN1 structure
                 bookkey = base64.b64decode(bookkey)
+                if int(keytype, 10) > 2:
+                    bookkey = removeHardening(rights, keytype, bookkey)
                 try:
                     bookkey = PKCS1_v1_5.new(rsakey).decrypt(bookkey, None) # automatically unpads
                 except ValueError:
index 2364c12ae5b18ecd3885dac7196d26c637a4f9db..22cd1489ed9c7bfb0ce9077d31ed060bcdc37e8e 100755 (executable)
 #   9.0.0 - Add Python 3 compatibility for calibre 5
 #   9.1.0 - Support for decrypting with owner password, support for V=5, R=5 and R=6 PDF files, support for AES256-encrypted PDFs.
 #   9.1.1 - Only support PyCryptodome; clean up the code
+#   10.0.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10)
 
 """
 Decrypts Adobe ADEPT-encrypted PDF files.
 """
 
 __license__ = 'GPL v3'
-__version__ = "9.1.1"
+__version__ = "10.0.0"
 
 import codecs
 import hashlib
@@ -69,6 +70,7 @@ from decimal import Decimal
 import itertools
 import xml.etree.ElementTree as etree
 import traceback
+from uuid import UUID
 
 try:
     from Cryptodome.Cipher import AES, ARC4, PKCS1_v1_5
@@ -1633,6 +1635,24 @@ class PDFDocument(object):
         self.ready = True
         return
 
+    @staticmethod
+    def removeHardening(rights, keytype, keydata):
+        adept = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag)
+        textGetter = lambda name: ''.join(rights.findtext('.//%s' % (adept(name),)))
+
+        # Gather what we need, and generate the IV
+        resourceuuid = UUID(textGetter("resource"))
+        deviceuuid = UUID(textGetter("device"))
+        fullfillmentuuid = UUID(textGetter("fulfillment")[:36])
+        kekiv = UUID(int=resourceuuid.int ^ deviceuuid.int ^ fullfillmentuuid.int).bytes
+
+        # Derive kek from just "keytype"
+        rem = int(keytype, 10) % 16
+        H = SHA256(keytype.encode("ascii"))
+        kek = H[2*rem : 16 + rem] + H[rem : 2*rem]
+
+        return unpad(AES.new(kek, AES.MODE_CBC, kekiv).decrypt(keydata), 16)
+
     def initialize_ebx_inept(self, password, docid, param):
         self.is_printable = self.is_modifiable = self.is_extractable = True
         rsakey = RSA.import_key(password) # parses the ASN1 structure
@@ -1641,16 +1661,12 @@ class PDFDocument(object):
         rights = zlib.decompress(rights, -15)
         rights = etree.fromstring(rights)
         expr = './/{http://ns.adobe.com/adept}encryptedKey'
-        bookkey = ''.join(rights.findtext(expr))
-
-        if len(bookkey) == 192:
-            print("This seems to be an Adobe ADEPT PDF with Adobe's new DRM")
-            print("This DRM cannot be removed yet. ")
-            print("Try getting your distributor to give you a new ACSM file, then open that in an old version of ADE (2.0).")
-            print("If your book distributor is not enforcing the new DRM yet, this will give you a copy with the old DRM.")
-            raise ADEPTNewVersionError("Book uses new ADEPT encryption")
+        bookkeyelem = rights.find(expr)
+        bookkey = codecs.decode(bookkeyelem.text.encode('utf-8'),'base64')
+        keytype = bookkeyelem.attrib.get('keyType', '0')
 
-        bookkey = codecs.decode(bookkey.encode('utf-8'),'base64')
+        if int(keytype, 10) > 2:
+            bookkey = PDFDocument.removeHardening(rights, keytype, bookkey)
         try:
             bookkey = PKCS1_v1_5.new(rsakey).decrypt(bookkey, None) # automatically unpads
         except ValueError:
index 36e3bd71b99efe7185fd16d6bf4bbb3eb5bc1257..91e1d28f1a398b784ff350d7d21337fd7f3e3c43 100644 (file)
@@ -4,8 +4,8 @@ DeDRM_plugin.zip
 This plugin will remove the DRM from:
 
  - Kindle ebooks (files from Kindle for Mac/PC and eInk Kindles).
- - Adobe Digital Editions (v2.0.1***) ePubs (including Kobo and Google ePubs downloaded to ADE)
- - Adobe Digital Editions (v2.0.1) PDFs
+ - Adobe Digital Editions ePubs (including Kobo and Google ePubs downloaded to ADE)
+ - Adobe Digital Editions PDFs
 
 For limitations and work-arounds, see the FAQ at https://github.com/noDRM/DeDRM_tools/blob/master/FAQs.md (or the FAQ in Apprentice Harper's original repository at https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md)
 
@@ -33,4 +33,4 @@ If you find that the DeDRM plugin is not working for you (imported ebooks still
 
 A log will appear that you can copy and paste into a GitHub issue report at https://github.com/noDRM/DeDRM_tools/issues. Please also include information about the eBook file.
 
-If you're using Apprentice Harper's original version, you can also comment at Apprentice Alf's blog, http://apprenticealf.wordpress.com/ or open an issue at Apprentice Harper's repository, https://github.com/apprenticeharper/DeDRM_tools/issues.
\ No newline at end of file
+If you're using Apprentice Harper's original version, you can also comment at Apprentice Alf's blog, http://apprenticealf.wordpress.com/ or open an issue at Apprentice Harper's repository, https://github.com/apprenticeharper/DeDRM_tools/issues.
diff --git a/FAQs.md b/FAQs.md
index 468addb02c3264d48be4eaf02255201401b752e8..d9d7ba311d54dc14a639702c8bf67c085419e07b 100644 (file)
--- a/FAQs.md
+++ b/FAQs.md
@@ -14,9 +14,6 @@ Just download and use these tools, that's all! Uh, almost. There are a few, uh,
 * The tools don't work on all ebooks. For example, they don't work on any ebooks from Apple's iBooks store.
 * You must own the ebook - the tools won't work on library ebooks or rented ebooks or books from a friend.
 * You must not use these tools to give your ebooks to a hundred of your closest friends. Or to a million strangers. Authors need to sell books to be able to write more books. Don't be mean to the authors.
-* Do NOT use Adobe Digital Editions 3.0 or later to download your ePubs. ADE 3.0 and later might use a new encryption scheme that the tools can't handle. While major ebook stores aren't using the new scheme yet, using ADE 2.0.1 will ensure that your ebooks are downloaded using the old scheme. Once a book has been downloaded with the new scheme, it's IMPOSSIBLE to re-download using the old scheme (without buying it again).
-
-But otherwise, if your ebook is from Amazon, Kobo, Barnes & Noble or any of the ebook stores selling ebooks compatible with Adobe Digital Editions 2.0.1, you should be able to remove the DRM that's been applied to your ebooks.
 
 ### Recent Changes to Kindle for PC/Kindle for Mac
 Starting with version 1.19, Kindle for PC/Mac uses Amazon's new KFX format which isn't quite as good a source for conversion to ePub as the older KF8 (& MOBI) formats. There are two options to get the older formats. Either stick with version 1.17 or earlier, or modify the executable by changing a file name (PC) or disabling a component of the application (Mac). 
@@ -144,9 +141,6 @@ You have found a Print Replica Kindle ebook. This is a PDF in a Kindle wrapper.
 ## Do the tools work on books from Kobo?
 If you use the Kobo desktop application for Mac or PC, install the Obok plugin. This will import and remove the DRM from your Kobo books, and is the easiest method for Kobo ebooks.
 
-## I registered Adobe Digital Editions 3.0 or later with an Adobe ID before downloading, but my epub or PDF still has DRM.
-Adobe introduced a new DRM scheme with ADE 3.0 and later. Install ADE 2.0.1 and register with the same Adobe ID. If you can't open your book in ADE 2.01, then you have a book with the new DRM scheme. These tools can't help. You can avoid the new DRM scheme by always downloading your ebooks with ADE 2.0.1. Some retailers will require ADE 3.0 or later, in which case you won't be able to download with ADE 2.0.1.
-
 ## I cannot solve my problem with the DeDRM plugin, and now I need to ‘post a log’. How do I do that?
 Remove the DRMed book from calibre. Click the Preferences drop-down menu and choose 'Restart in debug mode'. Once calibre has re-started, import the problem ebook. Now close calibre. A log will appear that you can copy and paste into a comment at Apprentice Alf's blog, or into a new issue at Apprentice Harper's github repository.
 
index d93487eaac6eb99e1219f12e8d05904fc1506a1d..51efe66ca5f2413f5df58c667fae8554b3271517 100644 (file)
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ The v10.0.0 versions of this plugin should both work with Calibre 5.x (Python 3)
 This is a repository that tracks all the scripts and other tools for removing DRM from ebooks that I could find, committed in date order as best as I could manage. (Except for the Requiem tools for Apple's iBooks, and Convert LIT for Microsoft's .lit ebooks.) This includes the tools from a time before Apprentice Alf had a blog, and continues through to when Apprentice Harper (with help) took over maintenance of the tools.
 
 The individual scripts are now released as two plugins for calibre: DeDRM and Obok. 
-The DeDRM plugin handles books that use Amazon DRM, Adobe Digital Editions DRM (version 1), Barnes & Noble DRM, and some historical formats.
+The DeDRM plugin handles books that use Amazon DRM, Adobe Digital Editions DRM, Barnes & Noble DRM, and some historical formats.
 The Obok plugin handles Kobo DRM.
 
 Users with calibre 5.x or later should use release 7.2.0 or later of the tools.
@@ -24,7 +24,7 @@ Note that Amazon changes the DRM for KFX files frequently. What works for KFX to
 
 I welcome contributions from others to improve these tools, from expanding the range of books handled, improving key retrieval,  to just general bug fixes, speed improvements and UI enhancements.
 
-I urge people to read the FAQs. But to cover the most common: Use ADE 2.0.1 to be sure not to get the new DRM scheme that these tools can't handle. Do remember to unzip the downloaded archive to get the plugin (beta versions may be just the plugin  don't unzip that). You can't load the whole tools archive into calibre.
+I urge people to read the FAQs. But to cover the most common: Do remember to unzip the downloaded archive to get the plugin (beta versions may be just the plugin  don't unzip that). You can't load the whole tools archive into calibre.
 
 My special thanks to all those developers who have done the hard work of reverse engineering to provide the initial tools.
 
index 3b1e4cf21bf67e88ef502972eb97af1984d298b9..75faf229be347ec28dbda6a3e03f4d03507fee40 100644 (file)
@@ -6,8 +6,8 @@ This file is to give users a quick overview of what is available and how to get
 This archive includes calibre plugins to remove DRM from:
 
  - Kindle ebooks (files from Kindle for Mac/PC and eInk Kindles).
- - Adobe Digital Editions (v2.0.1***) ePubs (including Kobo and Google ePubs downloaded to ADE)
- - Adobe Digital Editions (v2.0.1) PDFs
+ - Adobe Digital Editions ePubs (including Kobo and Google ePubs downloaded to ADE)
+ - Adobe Digital Editions PDFs
  - Kobo kePubs from the Kobo Desktop application and attached Kobo readers.
 
 These tools do NOT work with Apple's iBooks FairPlay DRM. Use iBook Copy from TunesKit.