Compare commits

...

18 Commits

Author SHA1 Message Date
NoDRM
7379b45319 Remove future import from ion.py 2024-11-10 20:15:33 +01:00
NoDRM
bde82fd7ab Fix python2 support for ion.py 2024-11-10 16:10:29 +01:00
NoDRM
de3d91f5e5 Don't repack EPUB if nothing has changed 2024-11-10 15:21:09 +01:00
NoDRM
c5ee327a60 Add note about key import/export for K4PC in the help file (fixes #663) 2024-11-10 14:44:57 +01:00
NoDRM
501a1e6d31 Update obok readme to include wmic requirement (fixes #670) 2024-11-10 14:41:38 +01:00
NoDRM
815d86efe0 Update changelog 2024-11-10 14:36:27 +01:00
NoDRM
65646f4493 Fix CI 2024-11-10 14:25:35 +01:00
Josh Cotton
808dc7d29a Fix Obok import in Calibre flatpak by using /sys/class/net/IFACE/address instead of ip (#586)
Fix #585.
Use /sys/class/net/IFACE/address for the MAC address instead of the ip
command.
2024-11-10 13:14:59 +00:00
precondition
2cd2792306 Obok.py/action.py: invoke _() only once 2024-11-10 13:11:28 +00:00
precondition
2e53d70e88 Catch FileNotFoundError due to undownloaded ebooks 2024-11-10 13:11:28 +00:00
Ben Combee
05fff5217b Fix crash using bare sha1 symbol
Use sha1 from hashlib, as it isn't imported globally, fixed crash trying to decrypt a eReader PDB file
2024-11-10 13:10:11 +00:00
Martin Rys
34c4c067e8 DeDRM ion: Correctly throw last exception if decrypt fails 2024-11-10 13:09:45 +00:00
Martin Rys
195ea69537 DeDRM ion: Clean out errorneous whitespace and UTF8 definition from python 2 times 2024-11-10 13:09:45 +00:00
NoDRM
3373d93874 Add binascii import, fixes FileOpen #514 2024-03-15 13:13:45 +01:00
NoDRM
bf2471e65b Update kfxdedrm as suggested in #440 2023-12-21 12:35:11 +01:00
NoDRM
5492dcdbf4 More FileOpen fixes 2023-12-21 11:57:39 +01:00
NoDRM
737d5e7f1e Bunch of updates for the FileOpen script 2023-12-03 10:45:09 +01:00
NoDRM
e4e5808894 Fix file lock issue in androidkindlekey.py 2023-12-03 10:42:41 +01:00
13 changed files with 449 additions and 508 deletions

View File

@@ -14,7 +14,7 @@ jobs:
run: python3 make_release.py run: python3 make_release.py
- name: Upload - name: Upload
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: plugin name: plugin
path: | path: |

View File

@@ -103,3 +103,11 @@ This is v10.0.9, a release candidate for v10.1.0. I don't expect there to be maj
- Fix a bug where decrypting a 256-bit AES pdf with V=5 didn't work. - Fix a bug where decrypting a 256-bit AES pdf with V=5 didn't work.
- Fix bugs in kgenpids.py, alfcrypto.py, mobidedrm.py and kindlekey.py that caused it to fail on Python 2 (#380). - Fix bugs in kgenpids.py, alfcrypto.py, mobidedrm.py and kindlekey.py that caused it to fail on Python 2 (#380).
- Fix some bugs (Python 2 and Python 3) in erdr2pml.py (untested). - Fix some bugs (Python 2 and Python 3) in erdr2pml.py (untested).
- Fix file lock bug in androidkindlekey.py on Windows with Calibre >= 7 (untested).
- A bunch of updates to the external FileOpen ineptpdf script, might fix #442 (untested).
- Fix exception handling on decrypt in ion.py (#662, thanks @C0rn3j).
- Fix SHA1 hash function for erdr2pml.py script (#608, thanks @unwiredben).
- Make Kobo DRM removal not fail when there are undownloaded ebooks (#384, thanks @precondition).
- Fix Obok import failing in Calibre flatpak due to missing ip command (#586 and #585, thanks @jcotton42).
- Don't re-pack EPUB if there's no DRM to remove and no postprocessing done (fixes #555).

View File

@@ -22,6 +22,8 @@ li {margin-top: 0.5em}
<p>If you have upgraded from an earlier version of the plugin, any existing Kindle for Mac/PC keys will have been automatically imported, so you might not need to do any more configuration. In addition, on Windows and Mac, the default Kindle for Mac/PC key is added the first time the plugin is run. Continue reading for key generation and management instructions.</p> <p>If you have upgraded from an earlier version of the plugin, any existing Kindle for Mac/PC keys will have been automatically imported, so you might not need to do any more configuration. In addition, on Windows and Mac, the default Kindle for Mac/PC key is added the first time the plugin is run. Continue reading for key generation and management instructions.</p>
<p>Note that for best results, you should run Calibre / this plugin on the same machine where Kindle 4 PC / Kindle 4 Mac is running. It is possible to export/import the keys to another machine, but this may not always work, particularly with the newer DRM versions.</p>
<h3>Creating New Keys:</h3> <h3>Creating New Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog prompting you to enter a key name for the default Kindle for Mac/PC key. </p> <p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog prompting you to enter a key name for the default Kindle for Mac/PC key. </p>

View File

@@ -216,12 +216,16 @@ class DeDRM(FileTypePlugin):
traceback.print_exc() traceback.print_exc()
raise raise
def postProcessEPUB(self, path_to_ebook): def postProcessEPUB(self, path_to_ebook, path_to_original_ebook = None):
# This is called after the DRM is removed (or if no DRM was present) # This is called after the DRM is removed (or if no DRM was present)
# It does stuff like de-obfuscating fonts (by calling checkFonts) # It does stuff like de-obfuscating fonts (by calling checkFonts)
# or removing watermarks. # or removing watermarks.
postProcessStart = time.time() postProcessStart = time.time()
postProcessingNeeded = False
# Save a backup of the EPUB path after DRM removal but before any postprocessing is done.
pre_postprocessing_EPUB_path = path_to_ebook
try: try:
import prefs import prefs
@@ -248,6 +252,15 @@ class DeDRM(FileTypePlugin):
postProcessEnd = time.time() postProcessEnd = time.time()
print("{0} v{1}: Post-processing took {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, postProcessEnd-postProcessStart)) print("{0} v{1}: Post-processing took {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, postProcessEnd-postProcessStart))
# If the EPUB is DRM-free (path_to_original_ebook will only be set in this case),
# and the post-processing hasn't changed anything in the EPUB,
# return the raw original file from path_to_original_ebook from before the
# zipfix code was executed.
if ((path_to_ebook == pre_postprocessing_EPUB_path) and path_to_original_ebook is not None):
print("{0} v{1}: Post-processing didn't do anything on DRM-free EPUB, returning original file".format(PLUGIN_NAME, PLUGIN_VERSION))
return path_to_original_ebook
return path_to_ebook return path_to_ebook
except: except:
@@ -299,9 +312,9 @@ class DeDRM(FileTypePlugin):
# import the LCP handler # import the LCP handler
import lcpdedrm import lcpdedrm
if (lcpdedrm.isLCPbook(path_to_ebook)): if (lcpdedrm.isLCPbook(inf.name)):
try: try:
retval = lcpdedrm.decryptLCPbook(path_to_ebook, dedrmprefs['lcp_passphrases'], self) retval = lcpdedrm.decryptLCPbook(inf.name, dedrmprefs['lcp_passphrases'], self)
except: except:
print("Looks like that didn't work:") print("Looks like that didn't work:")
raise raise
@@ -628,7 +641,7 @@ class DeDRM(FileTypePlugin):
# Not a Barnes & Noble nor an Adobe Adept # Not a Barnes & Noble nor an Adobe Adept
# Probably a DRM-free EPUB, but we should still check for fonts. # Probably a DRM-free EPUB, but we should still check for fonts.
return self.postProcessEPUB(inf.name) return self.postProcessEPUB(inf.name, path_to_ebook)
def PDFIneptDecrypt(self, path_to_ebook): def PDFIneptDecrypt(self, path_to_ebook):

View File

@@ -201,6 +201,9 @@ def get_serials2(path=STORAGE2):
for y in tokens: for y in tokens:
serials.append(y) serials.append(y)
serials.append(x+y) serials.append(x+y)
connection.close()
return serials return serials
def get_serials(path=STORAGE): def get_serials(path=STORAGE):

View File

@@ -255,7 +255,7 @@ class EreaderProcessor(object):
encrypted_key = r[172:172+8] encrypted_key = r[172:172+8]
encrypted_key_sha = r[56:56+20] encrypted_key_sha = r[56:56+20]
self.content_key = des.decrypt(encrypted_key) self.content_key = des.decrypt(encrypted_key)
if sha1(self.content_key).digest() != encrypted_key_sha: if hashlib.sha1(self.content_key).digest() != encrypted_key_sha:
raise ValueError('Incorrect Name and/or Credit Card') raise ValueError('Incorrect Name and/or Credit Card')
def getNumImages(self): def getNumImages(self):

View File

@@ -834,7 +834,7 @@ def num_value(x):
x = resolve1(x) x = resolve1(x)
if not (isinstance(x, int) or isinstance(x, Decimal)): if not (isinstance(x, int) or isinstance(x, Decimal)):
if STRICT: if STRICT:
raise PDFTypeError('Int or Float required: %r' % x) raise PDFTypeError('Int or Decimal required: %r' % x)
return 0 return 0
return x return x
@@ -2042,7 +2042,7 @@ class PDFParser(PSStackParser):
except PDFNoValidXRef: except PDFNoValidXRef:
# fallback # fallback
self.seek(0) self.seek(0)
pat = re.compile(b'^(\\d+)\\s+(\\d+)\\s+obj\\b') pat = re.compile(br'^(\\d+)\\s+(\\d+)\\s+obj\\b')
offsets = {} offsets = {}
xref = PDFXRef() xref = PDFXRef()
while 1: while 1:

View File

@@ -1,24 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ion.py """ion.py: Decrypt Kindle KFX files.
# Copyright © 2013-2020 Apprentice Harper et al.
__license__ = 'GPL v3' Revision history:
__version__ = '3.0' Pascal implementation by lulzkabulz.
BinaryIon.pas + DrmIon.pas + IonSymbols.pas
1.0 - Python translation by apprenticenaomi.
1.1 - DeDRM integration by anon.
1.2 - Added pylzma import fallback
1.3 - Fixed lzma support for calibre 4.6+
2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
3.0 - Added Python 3 compatibility for calibre 5.0
# Revision history: Copyright © 2013-2020 Apprentice Harper et al.
# Pascal implementation by lulzkabulz.
# BinaryIon.pas + DrmIon.pas + IonSymbols.pas
# 1.0 - Python translation by apprenticenaomi.
# 1.1 - DeDRM integration by anon.
# 1.2 - Added pylzma import fallback
# 1.3 - Fixed lzma support for calibre 4.6+
# 2.0 - VoucherEnvelope v2/v3 support by apprenticesakuya.
# 3.0 - Added Python 3 compatibility for calibre 5.0
"""
Decrypt Kindle KFX files.
""" """
import collections import collections
@@ -30,6 +25,9 @@ import struct
from io import BytesIO from io import BytesIO
__license__ = 'GPL v3'
__version__ = '3.0'
#@@CALIBRE_COMPAT_CODE@@ #@@CALIBRE_COMPAT_CODE@@
@@ -1349,7 +1347,7 @@ class DrmIonVoucher(object):
process_V4648(shared), process_V5683(shared)] process_V4648(shared), process_V5683(shared)]
decrypted=False decrypted=False
ex=None lastexception = None # type: Exception | None
for sharedsecret in sharedsecrets: for sharedsecret in sharedsecrets:
key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest() key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16]) aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
@@ -1366,9 +1364,10 @@ class DrmIonVoucher(object):
print("Decryption succeeded") print("Decryption succeeded")
break break
except Exception as ex: except Exception as ex:
lastexception = ex
print("Decryption failed, trying next fallback ") print("Decryption failed, trying next fallback ")
if not decrypted: if not decrypted:
raise ex raise lastexception
self.drmkey.stepin() self.drmkey.stepin()
while self.drmkey.hasnext(): while self.drmkey.hasnext():

View File

@@ -74,7 +74,7 @@ class KFXZipBook:
# Belt and braces. PIDs should be unicode strings, but just in case... # Belt and braces. PIDs should be unicode strings, but just in case...
if isinstance(pid, bytes): if isinstance(pid, bytes):
pid = pid.decode('ascii') pid = pid.decode('ascii')
for dsn_len,secret_len in [(0,0), (16,0), (16,40), (32,40), (40,0), (40,40)]: for dsn_len,secret_len in [(0,0), (16,0), (16,40), (32,0), (32,40), (40,0), (40,40)]:
if len(pid) == dsn_len + secret_len: if len(pid) == dsn_len + secret_len:
break # split pid into DSN and account secret break # split pid into DSN and account secret
else: else:

View File

@@ -374,7 +374,11 @@ class InterfacePluginAction(InterfaceAction):
result['success'] = False result['success'] = False
result['fileobj'] = None result['fileobj'] = None
zin = zipfile.ZipFile(book.filename, 'r') try:
zin = zipfile.ZipFile(book.filename, 'r')
except FileNotFoundError:
print (_('{0} - File "{1}" not found. Make sure the eBook has been properly downloaded in the Kobo app.').format(PLUGIN_NAME, book.filename))
return result
#print ('Kobo library filename: {0}'.format(book.filename)) #print ('Kobo library filename: {0}'.format(book.filename))
for userkey in self.userkeys: for userkey in self.userkeys:
print (_('Trying key: '), codecs.encode(userkey, 'hex')) print (_('Trying key: '), codecs.encode(userkey, 'hex'))

View File

@@ -449,9 +449,15 @@ class KoboLibrary(object):
for m in matches: for m in matches:
# print "m:{0}".format(m[0]) # print "m:{0}".format(m[0])
macaddrs.append(m[0].upper()) macaddrs.append(m[0].upper())
elif sys.platform.startswith('linux'):
for interface in os.listdir('/sys/class/net'):
with open('/sys/class/net/' + interface + '/address', 'r') as f:
mac = f.read().strip().upper()
# some interfaces, like Tailscale's VPN interface, do not have a MAC address
if mac != '':
macaddrs.append(mac)
else: else:
# probably linux # final fallback
# let's try ip # let's try ip
c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE) c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
for line in os.popen('ip -br link'): for line in os.popen('ip -br link'):

View File

@@ -8,6 +8,8 @@ Installation
------------ ------------
Open calibre's Preferences dialog. Click on the "Plugins" button. Next, click on the button, "Load plugin from file". Navigate to the unzipped DeDRM_tools folder, find the file "obok_plugin.zip". Click to select the file and select "Open". Click "Yes" in the "Are you sure?" dialog box. Click the "OK" button in the "Success" dialog box. Open calibre's Preferences dialog. Click on the "Plugins" button. Next, click on the button, "Load plugin from file". Navigate to the unzipped DeDRM_tools folder, find the file "obok_plugin.zip". Click to select the file and select "Open". Click "Yes" in the "Are you sure?" dialog box. Click the "OK" button in the "Success" dialog box.
Note: This plugin requires the "wmic" component on Windows. On Windows 10 and below this will be available by default, on Windows 11 it needs to be explicitly enabled. Make sure that on your Windows 11 machine, under Settings -> System -> Optional features -> Add an optional feature -> View features, "WMIC" is enabled / activated, otherwise this plugin may not work correctly.
Customization Customization
------------- -------------
@@ -16,7 +18,6 @@ No customization is required, except choosing which menus will show the plugin.
Using the plugin Using the plugin
---------------- ----------------
Select the plugin's menu or icon from whichever part of the calibre interface you have chosen to have it. Follow the instructions in the dialog that appears. Select the plugin's menu or icon from whichever part of the calibre interface you have chosen to have it. Follow the instructions in the dialog that appears.
@@ -29,5 +30,5 @@ If you find that the DeDRM plugin is not working for you (imported ebooks still
- Once calibre has re-started, import the problem ebook. - Once calibre has re-started, import the problem ebook.
- Now close calibre. - Now close calibre.
A log will appear that you can copy and paste into a comment at Apprentice Alf's blog, http://apprenticealf.wordpress.com/ or an issue at Apprentice Harper's repository, https://github.com/apprenticeharper/DeDRM_tools/issues . You should also give details of your computer, and how you obtained the ebook file. A log will appear that you can copy and paste into a GitHub issue at noDRM's repository, https://github.com/noDRM/DeDRM_tools/issues . You should also give details of your computer, and how you obtained the ebook file.