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
This commit is contained in:
a980e066a01 2022-02-22 23:47:51 +00:00 committed by noDRM
parent a1dd63ae5f
commit c5aebcca01
7 changed files with 66 additions and 39 deletions

View file

@ -206,10 +206,8 @@ def encryption(infile):
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag) adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),) expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr)) bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172: if len(bookkey) >= 172:
encryption = "Adobe (old)" encryption = "Adobe"
if len(bookkey) == 192:
encryption = "Adobe (new)"
elif len(bookkey) == 64: elif len(bookkey) == 64:
encryption = "B&N" encryption = "B&N"
else: else:

View file

@ -32,13 +32,14 @@
# 7.0 - Add Python 3 compatibility for calibre 5.0 # 7.0 - Add Python 3 compatibility for calibre 5.0
# 7.1 - Add ignoble support, dropping the dedicated ignobleepub.py script # 7.1 - Add ignoble support, dropping the dedicated ignobleepub.py script
# 7.2 - Only support PyCryptodome; clean up the code # 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. Decrypt Adobe Digital Editions encrypted ePub books.
""" """
__license__ = 'GPL v3' __license__ = 'GPL v3'
__version__ = "7.2" __version__ = "8.0"
import sys import sys
import os import os
@ -49,6 +50,8 @@ import zipfile
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
from contextlib import closing from contextlib import closing
from lxml import etree from lxml import etree
from uuid import UUID
import hashlib
try: try:
from Cryptodome.Cipher import AES, PKCS1_v1_5 from Cryptodome.Cipher import AES, PKCS1_v1_5
@ -247,6 +250,23 @@ def adeptGetUserUUID(inpath):
except: except:
return None 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): def decryptBook(userkey, inpath, outpath):
with closing(ZipFile(open(inpath, 'rb'))) as inf: with closing(ZipFile(open(inpath, 'rb'))) as inf:
namelist = inf.namelist() namelist = inf.namelist()
@ -260,15 +280,12 @@ def decryptBook(userkey, inpath, outpath):
rights = etree.fromstring(inf.read('META-INF/rights.xml')) rights = etree.fromstring(inf.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag) adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),) expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr)) bookkeyelem = rights.find(expr)
if len(bookkey) == 192: bookkey = bookkeyelem.text
print("{0:s} seems to be an Adobe ADEPT ePub with Adobe's new DRM".format(os.path.basename(inpath))) keytype = bookkeyelem.attrib.get('keyType', '0')
print("This DRM cannot be removed yet. ") if len(bookkey) >= 172 and int(keytype, 10) > 2:
print("Try getting your distributor to give you a new ACSM file, then open that in an old version of ADE (2.0).") print("{0:s} is a secure Adobe Adept ePub with hardening.".format(os.path.basename(inpath)))
print("If your book distributor is not enforcing the new DRM yet, this will give you a copy with the old DRM.") elif len(bookkey) == 172:
raise ADEPTNewVersionError("Book uses new ADEPT encryption")
if len(bookkey) == 172:
print("{0:s} is a secure Adobe Adept ePub.".format(os.path.basename(inpath))) print("{0:s} is a secure Adobe Adept ePub.".format(os.path.basename(inpath)))
elif len(bookkey) == 64: elif len(bookkey) == 64:
print("{0:s} is a secure Adobe PassHash (B&N) ePub.".format(os.path.basename(inpath))) 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 return 1
if len(bookkey) != 64: if len(bookkey) != 64:
# Normal Adobe ADEPT # Normal or "hardened" Adobe ADEPT
rsakey = RSA.import_key(userkey) # parses the ASN1 structure rsakey = RSA.import_key(userkey) # parses the ASN1 structure
bookkey = base64.b64decode(bookkey) bookkey = base64.b64decode(bookkey)
if int(keytype, 10) > 2:
bookkey = removeHardening(rights, keytype, bookkey)
try: try:
bookkey = PKCS1_v1_5.new(rsakey).decrypt(bookkey, None) # automatically unpads bookkey = PKCS1_v1_5.new(rsakey).decrypt(bookkey, None) # automatically unpads
except ValueError: except ValueError:

View file

@ -49,13 +49,14 @@
# 9.0.0 - Add Python 3 compatibility for calibre 5 # 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.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 # 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. Decrypts Adobe ADEPT-encrypted PDF files.
""" """
__license__ = 'GPL v3' __license__ = 'GPL v3'
__version__ = "9.1.1" __version__ = "10.0.0"
import codecs import codecs
import hashlib import hashlib
@ -69,6 +70,7 @@ from decimal import Decimal
import itertools import itertools
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
import traceback import traceback
from uuid import UUID
try: try:
from Cryptodome.Cipher import AES, ARC4, PKCS1_v1_5 from Cryptodome.Cipher import AES, ARC4, PKCS1_v1_5
@ -1633,6 +1635,24 @@ class PDFDocument(object):
self.ready = True self.ready = True
return 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): def initialize_ebx_inept(self, password, docid, param):
self.is_printable = self.is_modifiable = self.is_extractable = True self.is_printable = self.is_modifiable = self.is_extractable = True
rsakey = RSA.import_key(password) # parses the ASN1 structure rsakey = RSA.import_key(password) # parses the ASN1 structure
@ -1641,16 +1661,12 @@ class PDFDocument(object):
rights = zlib.decompress(rights, -15) rights = zlib.decompress(rights, -15)
rights = etree.fromstring(rights) rights = etree.fromstring(rights)
expr = './/{http://ns.adobe.com/adept}encryptedKey' 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: if int(keytype, 10) > 2:
print("This seems to be an Adobe ADEPT PDF with Adobe's new DRM") bookkey = PDFDocument.removeHardening(rights, keytype, bookkey)
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')
try: try:
bookkey = PKCS1_v1_5.new(rsakey).decrypt(bookkey, None) # automatically unpads bookkey = PKCS1_v1_5.new(rsakey).decrypt(bookkey, None) # automatically unpads
except ValueError: except ValueError:

View file

@ -4,8 +4,8 @@ DeDRM_plugin.zip
This plugin will remove the DRM from: This plugin will remove the DRM from:
- Kindle ebooks (files from Kindle for Mac/PC and eInk Kindles). - 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 ePubs (including Kobo and Google ePubs downloaded to ADE)
- Adobe Digital Editions (v2.0.1) PDFs - 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) 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)

View file

@ -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. * 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 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. * 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 ### 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). 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? ## 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. 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? ## 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. 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.

View file

@ -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. 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 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. The Obok plugin handles Kobo DRM.
Users with calibre 5.x or later should use release 7.2.0 or later of the tools. 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 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. My special thanks to all those developers who have done the hard work of reverse engineering to provide the initial tools.

View 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: This archive includes calibre plugins to remove DRM from:
- Kindle ebooks (files from Kindle for Mac/PC and eInk Kindles). - 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 ePubs (including Kobo and Google ePubs downloaded to ADE)
- Adobe Digital Editions (v2.0.1) PDFs - Adobe Digital Editions PDFs
- Kobo kePubs from the Kobo Desktop application and attached Kobo readers. - 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. These tools do NOT work with Apple's iBooks FairPlay DRM. Use iBook Copy from TunesKit.