From c5aebcca016f96520dc941651a49992472a4f0e4 Mon Sep 17 00:00:00 2001 From: a980e066a01 <100724039+a980e066a01@users.noreply.github.com> Date: Tue, 22 Feb 2022 23:47:51 +0000 Subject: [PATCH] Add support for "hardened" Adobe DRM 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 | 8 +++----- DeDRM_plugin/ineptepub.py | 41 ++++++++++++++++++++++++++++----------- DeDRM_plugin/ineptpdf.py | 36 ++++++++++++++++++++++++---------- DeDRM_plugin_ReadMe.txt | 6 +++--- FAQs.md | 6 ------ README.md | 4 ++-- ReadMe_Overview.txt | 4 ++-- 7 files changed, 66 insertions(+), 39 deletions(-) diff --git a/DeDRM_plugin/epubtest.py b/DeDRM_plugin/epubtest.py index 11f8b4b..79657a5 100644 --- a/DeDRM_plugin/epubtest.py +++ b/DeDRM_plugin/epubtest.py @@ -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: diff --git a/DeDRM_plugin/ineptepub.py b/DeDRM_plugin/ineptepub.py index 5fc6d4b..aa7b571 100644 --- a/DeDRM_plugin/ineptepub.py +++ b/DeDRM_plugin/ineptepub.py @@ -32,13 +32,14 @@ # 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: diff --git a/DeDRM_plugin/ineptpdf.py b/DeDRM_plugin/ineptpdf.py index 2364c12..22cd148 100755 --- a/DeDRM_plugin/ineptpdf.py +++ b/DeDRM_plugin/ineptpdf.py @@ -49,13 +49,14 @@ # 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)) + bookkeyelem = rights.find(expr) + bookkey = codecs.decode(bookkeyelem.text.encode('utf-8'),'base64') + keytype = bookkeyelem.attrib.get('keyType', '0') - 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") - - 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: diff --git a/DeDRM_plugin_ReadMe.txt b/DeDRM_plugin_ReadMe.txt index 36e3bd7..91e1d28 100644 --- a/DeDRM_plugin_ReadMe.txt +++ b/DeDRM_plugin_ReadMe.txt @@ -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 468addb..d9d7ba3 100644 --- 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. diff --git a/README.md b/README.md index d93487e..51efe66 100644 --- 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. diff --git a/ReadMe_Overview.txt b/ReadMe_Overview.txt index 3b1e4cf..75faf22 100644 --- a/ReadMe_Overview.txt +++ b/ReadMe_Overview.txt @@ -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.