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:
parent
a1dd63ae5f
commit
c5aebcca01
|
@ -175,7 +175,7 @@ def getfiledata(file, zi):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def encryption(infile):
|
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"
|
encryption = "Error"
|
||||||
try:
|
try:
|
||||||
with open(infile,'rb') as infileobject:
|
with open(infile,'rb') as infileobject:
|
||||||
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
@ -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.
|
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.
|
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.
|
||||||
|
|
6
FAQs.md
6
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.
|
* 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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue