Compare commits

...

16 Commits

Author SHA1 Message Date
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
NoDRM ef67dbd204 Fix more Py2/Py3 stuff 2023-08-06 15:49:52 +02:00
NoDRM 10b6caf9f5 Enable autorelease into 2nd repo 2023-08-03 21:53:16 +02:00
NoDRM 53996cf49c More Python2 fixes 2023-08-03 20:45:06 +02:00
NoDRM d388ae72fd More Py2 fixes 2023-08-03 20:14:33 +02:00
NoDRM bc089ee46d More Python2 bugfixes 2023-08-03 20:01:38 +02:00
NoDRM e509b7d520 Fix python2 issues in kgenpids and kindlekey 2023-08-03 11:26:05 +02:00
NoDRM e82d2b5c9c Fix PDF decryption for 256-bit AES with V=5 2023-08-02 18:13:42 +02:00
NoDRM 7f6dd84389 Fix PDF decryption of ancient 40-bit RC4 with R=2 2023-08-02 16:55:41 +02:00
NoDRM b9bad26d4b Prepare release candidate v10.0.9 2023-08-02 07:39:35 +02:00
NoDRM 2a1413297e Add warning to the standalone code 2023-08-02 07:30:39 +02:00
NoDRM 815f880e34 Disable auto-prerelease again (#358) 2023-06-25 18:51:46 +02:00
37 changed files with 691 additions and 595 deletions

View File

@ -10,16 +10,16 @@ body:
id: calibre-version
attributes:
label: Which version of Calibre are you running?
description: "Example: 5.32"
placeholder: "5.32"
description: "Example: 6.23"
placeholder: "6.23"
validations:
required: true
- type: input
id: plugin-version
attributes:
label: Which version of the DeDRM plugin are you running?
description: "Example: v10.0.0"
placeholder: "v10.0.0"
description: "Example: v10.0.2"
placeholder: "v10.0.2"
validations:
required: true
- type: input

View File

@ -20,45 +20,33 @@ jobs:
path: |
DeDRM_tools_*.zip
DeDRM_tools.zip
- name: Delete old release
uses: cb80/delrel@latest
with:
tag: autorelease
token: ${{ github.token }}
- name: Delete old tag
uses: dev-drprasad/delete-tag-and-release@v1.0
with:
tag_name: autorelease
github_token: ${{ github.token }}
delete_release: true
- name: Prepare release
run: cp DeDRM_tools.zip DeDRM_alpha_${{ github.sha }}.zip
- uses: dev-drprasad/delete-older-releases@v0.2.1
with:
repo: noDRM/DeDRM_tools_autorelease
keep_latest: 0
delete_tags: true
env:
GITHUB_TOKEN: ${{ secrets.AUTORELEASE_KEY }}
- name: Auto-release
id: autorelease
uses: softprops/action-gh-release@v1
with:
tag_name: autorelease
token: ${{ github.token }}
tag_name: autorelease_${{ github.sha }}
repository: noDRM/DeDRM_tools_autorelease
token: ${{ secrets.AUTORELEASE_KEY }}
name: Automatic alpha release with latest changes
body: |
This release is automatically generated by Github for each commit.
This means, every time a change is made to this repo, this release will be updated to contain an untested copy of the plugin at that stage. This will contain the most up-to-date code, but it's not tested at all and may be broken.
This means, every time a change is made to the repo, a release with an untested copy of the plugin at that stage will be created. This will contain the most up-to-date code, but it's not tested at all and may be broken.
Last update based on Git commit ${{ github.sha }}.
Last update based on Git commit [${{ github.sha }}](https://github.com/noDRM/DeDRM_tools/commit/${{ github.sha }}).
prerelease: true
draft: true
files: DeDRM_alpha_${{ github.sha }}.zip
- name: Make release public
uses: irongut/EditRelease@v1.2.0
with:
token: ${{ github.token }}
id: ${{ steps.autorelease.outputs.id }}
draft: false
prerelease: true
files: DeDRM_alpha_${{ github.sha }}.zip

View File

@ -67,7 +67,11 @@ List of changes since the fork of Apprentice Harper's repository:
- Fix Nook Study key retrieval code (partially fixes #50).
- Make the plugin work on Calibre 6 (Qt 6). (fixes #54 and #98) If you're running Calibre 6 and you notice any issues, please open a bug report.
## Fixes on master (not yet released):
## Fixes in v10.0.9 (RC for v10.1.0, 2023-08-02):
Note that versions v10.0.4(s), v10.0.5(s) and v10.0.6(s) were released by other people in various forks, so I have decided to make a larger version jump so there are no conflicting version numbers / different builds with the same version number.
This is v10.0.9, a release candidate for v10.1.0. I don't expect there to be major issues / bugs, but since a lot of code has changed in the last year I wanted to get some "extended testing" before this becomes v10.1.0.
- Fix a bug introduced with #48 that breaks DeDRM'ing on Calibre 4 (fixes #101).
- Fix some more Calibre-6 bugs in the Obok plugin (should fix #114).
@ -92,3 +96,13 @@ List of changes since the fork of Apprentice Harper's repository:
- Two bugfixes for Amazon DeDRM from Satuoni ( https://github.com/noDRM/DeDRM_tools/issues/315#issuecomment-1508305428 ) and andrewc12 ( https://github.com/andrewc12/DeDRM_tools/commit/d9233d61f00d4484235863969919059f4d0b2057 ) that might make the plugin work with newer versions.
- Fix font decryption not working with some books (fixes #347), thanks for the patch @bydioeds.
- Fix a couple unicode errors for Python2 in Kindle and Nook code.
## Fixes on master (not yet released):
- Fix a bug where decrypting a 40-bit RC4 pdf with R=2 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 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).

View File

@ -17,7 +17,7 @@ p {margin-top: 0}
<body>
<h1>DeDRM Plugin <span class="version">(v10.0.3)</span></h1>
<h1>DeDRM Plugin <span class="version">(v10.0.9 / v10.1.0 RC1)</span></h1>
<p>This plugin removes DRM from ebooks when they are imported into calibre. If you already have DRMed ebooks in your calibre library, you will need to remove them and import them again.</p>
@ -26,6 +26,8 @@ p {margin-top: 0}
<h3>Installation</h3>
<p>You have obviously managed to install the plugin, as otherwise you wouldnt be reading this help file. However, you should also delete any older DRM removal plugins, as this DeDRM plugin replaces the five older plugins: Kindle and Mobipocket DeDRM (K4MobiDeDRM), Ignoble Epub DeDRM (ignobleepub), Inept Epub DeDRM (ineptepub), Inept PDF DeDRM (ineptepub) and eReader PDB 2 PML (eReaderPDB2PML).</p>
<p>This plugin (in versions v10.0.0 and above) will automatically replace the older 7.X and below versions from Apprentice Alf and Apprentice Harper.</p>
<h3>Configuration</h3>
<p>On Windows and Mac, the keys for ebooks downloaded for Kindle for Mac/PC and Adobe Digital Editions are automatically generated. If all your DRMed ebooks can be opened and read in Kindle for Mac/PC and/or Adobe Digital Editions on the same computer on which you are running calibre, you do not need to do any configuration of this plugin. On Linux, keys for Kindle for PC and Adobe Digital Editions need to be generated separately (see the Linux section below).</p>
@ -60,7 +62,7 @@ p {margin-top: 0}
<li>And probably many more.</li>
</ul>
<h3>For additional help read the <a href="https://github.com/noDRM/DeDRM_tools/blob/master/FAQs.md">FAQs</a> at <a href="https://github.com/noDRM/DeDRM_tools">NoDRM's GitHub repository</a> (or the corresponding <a href="https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md">FAQs</a> at <a href="https://github.com/apprenticeharper/DeDRM_tools/">Apprentice Harperss GitHub repository</a>). You can <a href="https://github.com/noDRM/DeDRM_tools/issues">open issue reports</a>related to this fork at NoDRM's GitHub repository.</h3>
<h4>For additional help read the <a href="https://github.com/noDRM/DeDRM_tools/blob/master/FAQs.md">FAQs</a> at <a href="https://github.com/noDRM/DeDRM_tools">NoDRM's GitHub repository</a> (or the corresponding <a href="https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md">FAQs</a> at <a href="https://github.com/apprenticeharper/DeDRM_tools/">Apprentice Harperss GitHub repository</a>). You can <a href="https://github.com/noDRM/DeDRM_tools/issues">open issue reports</a> related to this fork at NoDRM's GitHub repository.</h4>
<h2>Linux Systems Only</h2>

View File

@ -14,7 +14,8 @@ if "calibre" in sys.modules and sys.version_info[0] == 2:
if os.path.join(config_dir, "plugins", "DeDRM.zip") not in sys.path:
sys.path.insert(0, os.path.join(config_dir, "plugins", "DeDRM.zip"))
# Explicitly set the package identifier so we are allowed to import stuff ...
#__package__ = "DeDRM_plugin"
if "calibre" in sys.modules:
# Explicitly set the package identifier so we are allowed to import stuff ...
__package__ = "calibre_plugins.dedrm"
#@@CALIBRE_COMPAT_CODE_END@@

View File

@ -5,7 +5,7 @@ from __future__ import print_function
# __init__.py for DeDRM_plugin
# Copyright © 2008-2020 Apprentice Harper et al.
# Copyright © 2021 NoDRM
# Copyright © 2021-2023 NoDRM
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
@ -82,6 +82,7 @@ __docformat__ = 'restructuredtext en'
# 10.0.0 - First forked version by NoDRM. See CHANGELOG.md for details.
# 10.0.1 - Fixes a bug in the watermark code.
# 10.0.2 - Fix Kindle for Mac & update Adobe key retrieval
# For changes made in 10.0.3 and above, see the CHANGELOG.md file
"""
Decrypt DRMed ebooks.
@ -95,7 +96,10 @@ import traceback
#@@CALIBRE_COMPAT_CODE@@
try:
import __version
try:
from . import __version
except:
import __version
except:
print("#############################")
print("Failed to load the DeDRM plugin")
@ -133,8 +137,10 @@ try:
except:
config_dir = ""
import utilities
try:
from . import utilities
except:
import utilities
PLUGIN_NAME = __version.PLUGIN_NAME
@ -914,6 +920,9 @@ class DeDRM(FileTypePlugin):
# perhaps we need to get a new default Kindle for Mac/PC key
defaultkeys = []
print("{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0]))
traceback.print_exc()
print("{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
try:

View File

@ -5,10 +5,21 @@
# (CLI interface without Calibre)
# Copyright © 2021 NoDRM
"""
NOTE: This code is not functional (yet). I started working on it a while ago
to make a standalone version of the plugins that could work without Calibre,
too, but for now there's only a rough code structure and no working code yet.
Currently, to use these plugins, you will need to use Calibre. Hopwfully that'll
change in the future.
"""
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
# For revision history see __init__.py
# For revision history see CHANGELOG.md
"""
Run DeDRM plugin without Calibre.

View File

@ -4,7 +4,7 @@
#@@CALIBRE_COMPAT_CODE@@
PLUGIN_NAME = "DeDRM"
__version__ = '10.0.3'
__version__ = '10.0.9'
PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")])
PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE])

View File

@ -44,10 +44,11 @@ __version__ = '7.4'
import sys, os, struct, getopt
from base64 import b64decode
#@@CALIBRE_COMPAT_CODE@@
from utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
try:

View File

@ -8,6 +8,7 @@
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
# pbkdf2.py This code may be freely used and modified for any purpose.
import sys
import hmac
from struct import pack
import hashlib
@ -25,7 +26,10 @@ class Pukall_Cipher(object):
raise Exception("PC1: Bad key length")
wkey = []
for i in range(8):
wkey.append(key[i*2]<<8 | key[i*2+1])
if sys.version_info[0] == 2:
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
else:
wkey.append(key[i*2]<<8 | key[i*2+1])
dst = bytearray(len(src))
for i in range(len(src)):
temp1 = 0;
@ -37,7 +41,12 @@ class Pukall_Cipher(object):
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
curByte = src[i]
if sys.version_info[0] == 2:
curByte = ord(src[i])
else:
curByte = src[i]
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
@ -45,7 +54,12 @@ class Pukall_Cipher(object):
keyXorVal = curByte * 257;
for j in range(8):
wkey[j] ^= keyXorVal;
dst[i] = curByte
if sys.version_info[0] == 2:
dst[i] = chr(curByte)
else:
dst[i] = curByte
return bytes(dst)
class Topaz_Cipher(object):
@ -103,7 +117,7 @@ class KeyIVGen(object):
def xorbytes( a, b ):
if len(a) != len(b):
raise Exception("xorbytes(): lengths differ")
return bytes([x ^ y for x, y in zip(a, b)])
return bytes(bytearray([x ^ y for x, y in zip(a, b)]))
def prf( h, data ):
hm = h.copy()

View File

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

View File

@ -29,7 +29,7 @@ from calibre.constants import iswindows, isosx
from __init__ import PLUGIN_NAME, PLUGIN_VERSION
from __version import RESOURCE_NAME as help_file_name
from utilities import uStrCmp
from .utilities import uStrCmp
import prefs
import androidkindlekey

View File

@ -5,7 +5,10 @@
# For use with Topaz Scripts Version 2.6
# Python 3, September 2020
from utilities import SafeUnbuffered
#@@CALIBRE_COMPAT_CODE@@
from .utilities import SafeUnbuffered
import sys
import csv

View File

@ -49,16 +49,18 @@
__version__ = '2.0'
#@@CALIBRE_COMPAT_CODE@@
import sys, struct, os, traceback
import zlib
import zipfile
import xml.etree.ElementTree as etree
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
_FILENAME_LEN_OFFSET = 26

View File

@ -79,12 +79,13 @@ except ImportError:
#@@CALIBRE_COMPAT_CODE@@
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
from argv_utils import unicode_argv
import cgi
import logging
@ -141,14 +142,20 @@ def sanitizeFileName(name):
def fixKey(key):
def fixByte(b):
if sys.version_info[0] == 2:
b = ord(b)
return b ^ ((b ^ (b<<1) ^ (b<<2) ^ (b<<3) ^ (b<<4) ^ (b<<5) ^ (b<<6) ^ (b<<7) ^ 0x80) & 0x80)
return bytes([fixByte(a) for a in key])
return bytes(bytearray([fixByte(a) for a in key]))
def deXOR(text, sp, table):
r=''
r=b''
j = sp
for i in range(len(text)):
r += chr(ord(table[j]) ^ ord(text[i]))
if sys.version_info[0] == 2:
r += chr(ord(table[j]) ^ ord(text[i]))
else:
r += bytes(bytearray([table[j] ^ text[i]]))
j = j + 1
if j == len(table):
j = 0

View File

@ -4,7 +4,9 @@
# Python 3 for calibre 5.0
from __future__ import print_function
from utilities import SafeUnbuffered
#@@CALIBRE_COMPAT_CODE@@
from .utilities import SafeUnbuffered
import sys
import csv

View File

@ -45,14 +45,16 @@ import os
import hashlib
import base64
#@@CALIBRE_COMPAT_CODE@@
try:
from Cryptodome.Cipher import AES
except ImportError:
from Crypto.Cipher import AES
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
class IGNOBLEError(Exception):
pass

View File

@ -27,14 +27,16 @@ import hashlib
import getopt
import re
from utilities import SafeUnbuffered
#@@CALIBRE_COMPAT_CODE@@
from .utilities import SafeUnbuffered
try:
from calibre.constants import iswindows
except:
iswindows = sys.platform.startswith('win')
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
class DrmException(Exception):
pass

View File

@ -70,9 +70,10 @@ def unpad(data, padding=16):
return data[:-pad_len]
from utilities import SafeUnbuffered
#@@CALIBRE_COMPAT_CODE@@
from argv_utils import unicode_argv
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
class ADEPTError(Exception):

View File

@ -92,13 +92,14 @@ def unpad(data, padding=16):
return data[:-pad_len]
#@@CALIBRE_COMPAT_CODE@@
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
from argv_utils import unicode_argv
class ADEPTError(Exception):
pass
@ -833,7 +834,7 @@ def num_value(x):
x = resolve1(x)
if not (isinstance(x, int) or isinstance(x, Decimal)):
if STRICT:
raise PDFTypeError('Int or Float required: %r' % x)
raise PDFTypeError('Int or Decimal required: %r' % x)
return 0
return x
@ -1366,14 +1367,14 @@ class PDFDocument(object):
def process_with_aes(self, key, encrypt, data, repetitions = 1, iv = None):
if iv is None:
keylen = len(key)
iv = bytes([0x00]*keylen)
iv = bytes(bytearray(16))
aes = AES.new(key, AES.MODE_CBC, iv)
if not encrypt:
plaintext = AES.new(key,AES.MODE_CBC,iv, True).decrypt(data)
plaintext = aes.decrypt(data)
return plaintext
else:
aes = AES.new(key, AES.MODE_CBC, iv, False)
new_data = bytes(data * repetitions)
crypt = aes.encrypt(new_data)
return crypt
@ -1394,10 +1395,18 @@ class PDFDocument(object):
raise Exception("K1 < 32 ...")
#def process_with_aes(self, key: bytes, encrypt: bool, data: bytes, repetitions: int = 1, iv: bytes = None):
E = self.process_with_aes(K[:16], True, K1, 64, K[16:32])
K = (hashlib.sha256, hashlib.sha384, hashlib.sha512)[sum(E) % 3](E).digest()
E = bytearray(E)
E_mod_3 = 0
for i in range(16):
E_mod_3 += E[i]
E_mod_3 %= 3
K = (hashlib.sha256, hashlib.sha384, hashlib.sha512)[E_mod_3](E).digest()
if round_number >= 64:
ch = int.from_bytes(E[-1:], "big", signed=False)
ch = E[-1:][0] # get last byte
if ch <= round_number - 32:
done = True
@ -1478,14 +1487,23 @@ class PDFDocument(object):
EncMetadata = b'True'
if (EncMetadata == ('False' or 'false') or V < 4) and R >= 4:
hash.update(codecs.decode(b'ffffffff','hex'))
# Finish hash:
hash = hash.digest()
if R >= 3:
# 8
for _ in range(50):
hash = hashlib.md5(hash.digest()[:length//8])
key = hash.digest()[:length//8]
hash = hashlib.md5(hash[:length//8]).digest()
if R == 2:
# R=2 only uses first five bytes.
key = hash[:5]
else:
key = hash[:length//8]
if R == 2:
# Algorithm 3.4
u1 = ARC4.new(key).decrypt(password)
u1 = ARC4.new(key).decrypt(self.PASSWORD_PADDING)
elif R >= 3:
# Algorithm 3.5
hash = hashlib.md5(self.PASSWORD_PADDING) # 2
@ -1498,6 +1516,7 @@ class PDFDocument(object):
k = b''.join(bytes([c ^ i]) for c in key )
x = ARC4.new(k).decrypt(x)
u1 = x+x # 32bytes total
if R == 2:
is_authenticated = (u1 == U)
else:
@ -2023,7 +2042,7 @@ class PDFParser(PSStackParser):
except PDFNoValidXRef:
# fallback
self.seek(0)
pat = re.compile(b'^(\\d+)\\s+(\\d+)\\s+obj\\b')
pat = re.compile(br'^(\\d+)\\s+(\\d+)\\s+obj\\b')
offsets = {}
xref = PDFXRef()
while 1:

View File

@ -30,6 +30,9 @@ import struct
from io import BytesIO
#@@CALIBRE_COMPAT_CODE@@
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.py3compat import bchr
@ -57,7 +60,7 @@ except ImportError:
# Windows-friendly choice: pylzma wheels
import pylzma as lzma
from kfxtables import *
from .kfxtables import *
TID_NULL = 0
TID_BOOLEAN = 1

View File

@ -88,9 +88,9 @@ import kgenpids
import androidkindlekey
import kfxdedrm
from utilities import SafeUnbuffered
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
# cleanup unicode filenames

View File

@ -74,7 +74,7 @@ class KFXZipBook:
# Belt and braces. PIDs should be unicode strings, but just in case...
if isinstance(pid, bytes):
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:
break # split pid into DSN and account secret
else:

View File

@ -53,11 +53,17 @@ def SHA1(message):
def encode(data, map):
result = b''
for char in data:
value = char
if sys.version_info[0] == 2:
value = ord(char)
else:
value = char
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += bytes([map[Q]])
result += bytes([map[R]])
result += bytes(bytearray([map[Q]]))
result += bytes(bytearray([map[R]]))
return result
# Hash the bytes in data and then encode the digest with the characters in map
@ -84,8 +90,11 @@ def decode(data,map):
def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4
bitPosition = 6 - 2*(offset % 4)
return bitField[byteNumber] >> bitPosition & 3
if sys.version_info[0] == 2:
return ord(bitField[byteNumber]) >> bitPosition & 3
else:
return bitField[byteNumber] >> bitPosition & 3
# Returns the six bits at offset from a bit field
def getSixBitsFromBitField(bitField,offset):
offset *= 3
@ -97,7 +106,8 @@ def encodePID(hash):
global charMap3
PID = b''
for position in range (0,8):
PID += bytes([charMap3[getSixBitsFromBitField(hash,position)]])
PID += bytes(bytearray([charMap3[getSixBitsFromBitField(hash,position)]]))
return PID
# Encryption table used to generate the device PID
@ -134,7 +144,7 @@ def generateDevicePID(table,dsn,nbRoll):
index = (index+1) %8
for counter in range (0,8):
index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
pidAscii += bytes([charMap4[index]])
pidAscii += bytes(bytearray([charMap4[index]]))
return pidAscii
def crc32(s):
@ -150,7 +160,7 @@ def checksumPid(s):
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += bytes([charMap4[pos%l]])
res += bytes(bytearray([charMap4[pos%l]]))
crc >>= 8
return res
@ -161,14 +171,17 @@ def pidFromSerial(s, l):
crc = crc32(s)
arr1 = [0]*l
for i in range(len(s)):
arr1[i%l] ^= s[i]
if sys.version_info[0] == 2:
arr1[i%l] ^= ord(s[i])
else:
arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
for i in range(l):
arr1[i] ^= crc_bytes[i&3]
pid = b""
for i in range(l):
b = arr1[i] & 0xff
pid += bytes([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]])
pid += bytes(bytearray([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]]))
return pid
@ -177,6 +190,10 @@ def getKindlePids(rec209, token, serialnum):
if isinstance(serialnum,str):
serialnum = serialnum.encode('utf-8')
if sys.version_info[0] == 2:
if isinstance(serialnum,unicode):
serialnum = serialnum.encode('utf-8')
if rec209 is None:
return [serialnum]

View File

@ -62,7 +62,11 @@ except NameError:
# Routines common to Mac and PC
from utilities import SafeUnbuffered
#@@CALIBRE_COMPAT_CODE@@
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
try:
from calibre.constants import iswindows, isosx
@ -70,7 +74,7 @@ except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
from argv_utils import unicode_argv
class DrmException(Exception):
pass
@ -115,11 +119,17 @@ def primes(n):
def encode(data, map):
result = b''
for char in data:
value = char
if sys.version_info[0] == 2:
value = ord(char)
else:
value = char
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += bytes([map[Q]])
result += bytes([map[R]])
result += bytes(bytearray([map[Q]]))
result += bytes(bytearray([map[R]]))
return result
# Hash the bytes in data and then encode the digest with the characters in map
@ -232,9 +242,14 @@ if iswindows:
# replace any non-ASCII values with 0xfffd
for i in range(0,len(buffer)):
if buffer[i]>"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = "\ufffd"
if sys.version_info[0] == 2:
if buffer[i]>u"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = u"\ufffd"
else:
if buffer[i]>"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = "\ufffd"
# return utf-8 encoding of modified username
#print "modified username:"+buffer.value
return buffer.value.encode('utf-8')

View File

@ -16,24 +16,25 @@
import sys
import binascii
from utilities import SafeUnbuffered
#@@CALIBRE_COMPAT_CODE@@
from argv_utils import unicode_argv
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
letters = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
def crc32(s):
return (~binascii.crc32(s,-1))&0xFFFFFFFF
def checksumPid(s):
crc = crc32(s.encode('ascii'))
crc = crc32(s)
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l]
res += bytes(bytearray([letters[pos%l]]))
crc >>= 8
return res
@ -43,16 +44,19 @@ def pidFromSerial(s, l):
arr1 = [0]*l
for i in range(len(s)):
arr1[i%l] ^= s[i]
if sys.version_info[0] == 2:
arr1[i%l] ^= ord(s[i])
else:
arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
for i in range(l):
arr1[i] ^= crc_bytes[i&3]
pid = ''
pid = b""
for i in range(l):
b = arr1[i] & 0xff
pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
pid+=bytes(bytearray([letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]]))
return pid

View File

@ -80,11 +80,14 @@ import sys
import os
import struct
import binascii
from alfcrypto import Pukall_Cipher
from utilities import SafeUnbuffered
from argv_utils import unicode_argv
#@@CALIBRE_COMPAT_CODE@@
from .alfcrypto import Pukall_Cipher
from .utilities import SafeUnbuffered
from .argv_utils import unicode_argv
class DrmException(Exception):
@ -103,19 +106,26 @@ def PC1(key, src, decryption=True):
except:
raise
# accepts unicode returns unicode
letters = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
def crc32(s):
return (~binascii.crc32(s,-1))&0xFFFFFFFF
def checksumPid(s):
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
crc = (~binascii.crc32(s.encode('utf-8'),-1))&0xFFFFFFFF
s = s.encode()
crc = crc32(s)
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l]
res += bytes(bytearray([letters[pos%l]]))
crc >>= 8
return res
return res.decode()
# expects bytearray
def getSizeOfTrailingDataEntries(ptr, size, flags):
@ -124,7 +134,11 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
if size <= 0:
return result
while True:
v = ptr[size-1]
if sys.version_info[0] == 2:
v = ord(ptr[size-1])
else:
v = ptr[size-1]
result |= (v & 0x7F) << bitpos
bitpos += 7
size -= 1
@ -140,7 +154,10 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
# if multibyte data is included in the encryped data, we'll
# have already cleared this flag.
if flags & 1:
num += (ptr[size - num - 1] & 0x3) + 1
if sys.version_info[0] == 2:
num += (ord(ptr[size - num - 1]) & 0x3) + 1
else:
num += (ptr[size - num - 1] & 0x3) + 1
return num
@ -299,7 +316,10 @@ class MobiBook:
for pid in pidlist:
bigpid = pid.encode('utf-8').ljust(16,b'\0')
temp_key = PC1(keyvec1, bigpid, False)
temp_key_sum = sum(temp_key) & 0xff
if sys.version_info[0] == 2:
temp_key_sum = sum(map(ord,temp_key)) & 0xff
else:
temp_key_sum = sum(temp_key) & 0xff
found_key = None
for i in range(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
@ -315,7 +335,11 @@ class MobiBook:
# Then try the default encoding that doesn't require a PID
pid = '00000000'
temp_key = keyvec1
temp_key_sum = sum(temp_key) & 0xff
if sys.version_info[0] == 2:
temp_key_sum = sum(map(ord,temp_key)) & 0xff
else:
temp_key_sum = sum(temp_key) & 0xff
for i in range(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
if cksum == temp_key_sum:

View File

@ -7,6 +7,18 @@ from __future__ import absolute_import, print_function
# Copyright © 2021 NoDRM
"""
NOTE: This code is not functional (yet). I started working on it a while ago
to make a standalone version of the plugins that could work without Calibre,
too, but for now there's only a rough code structure and no working code yet.
Currently, to use these plugins, you will need to use Calibre. Hopwfully that'll
change in the future.
"""
OPT_SHORT_TO_LONG = [
["c", "config"],
["e", "extract"],

View File

@ -8,6 +8,17 @@ from __future__ import absolute_import, print_function
# Taken from Calibre code - Copyright © 2008, Kovid Goyal kovid@kovidgoyal.net, GPLv3
"""
NOTE: This code is not functional (yet). I started working on it a while ago
to make a standalone version of the plugins that could work without Calibre,
too, but for now there's only a rough code structure and no working code yet.
Currently, to use these plugins, you will need to use Calibre. Hopwfully that'll
change in the future.
"""
#@@CALIBRE_COMPAT_CODE@@
import sys, os, codecs, json

View File

@ -8,6 +8,17 @@ from __future__ import absolute_import, print_function
# Copyright © 2021 NoDRM
"""
NOTE: This code is not functional (yet). I started working on it a while ago
to make a standalone version of the plugins that could work without Calibre,
too, but for now there's only a rough code structure and no working code yet.
Currently, to use these plugins, you will need to use Calibre. Hopwfully that'll
change in the future.
"""
#@@CALIBRE_COMPAT_CODE@@
import os, sys

View File

@ -8,6 +8,17 @@ from __future__ import absolute_import, print_function
# Copyright © 2021 NoDRM
"""
NOTE: This code is not functional (yet). I started working on it a while ago
to make a standalone version of the plugins that could work without Calibre,
too, but for now there's only a rough code structure and no working code yet.
Currently, to use these plugins, you will need to use Calibre. Hopwfully that'll
change in the future.
"""
#@@CALIBRE_COMPAT_CODE@@
import os, sys

View File

@ -24,10 +24,10 @@ import traceback
from struct import pack
from struct import unpack
from alfcrypto import Topaz_Cipher
from utilities import SafeUnbuffered
from .alfcrypto import Topaz_Cipher
from .utilities import SafeUnbuffered
from argv_utils import unicode_argv
from .argv_utils import unicode_argv
#global switch

View File

@ -3,7 +3,7 @@ from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__version__ = '10.0.3'
__version__ = '10.0.9'
__docformat__ = 'restructuredtext en'
#####################################################################
@ -20,7 +20,7 @@ except NameError:
PLUGIN_NAME = 'Obok DeDRM'
PLUGIN_SAFE_NAME = PLUGIN_NAME.strip().lower().replace(' ', '_')
PLUGIN_DESCRIPTION = _('Removes DRM from Kobo kepubs and adds them to the library.')
PLUGIN_VERSION_TUPLE = (10, 0, 3)
PLUGIN_VERSION_TUPLE = (10, 0, 9)
PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
HELPFILE_NAME = PLUGIN_SAFE_NAME + '_Help.htm'
PLUGIN_AUTHORS = 'Anon'

View File

@ -168,8 +168,8 @@
"""Manage all Kobo books, either encrypted or DRM-free."""
from __future__ import print_function
__version__ = '10.0.1'
__about__ = "Obok v{0}\nCopyright © 2012-2022 Physisticated et al.".format(__version__)
__version__ = '10.0.9'
__about__ = "Obok v{0}\nCopyright © 2012-2023 Physisticated et al.".format(__version__)
import sys
import os

View File

@ -8,7 +8,7 @@
<body>
<h1>Obok DeDRM Plugin</h1>
<h3>(version 10.0.2)</h3>
<h3>(version 10.0.9 / 10.1.0 RC1)</h3>
<h3>Installation:</h3>

View File

@ -3,11 +3,13 @@ DeDRM tools for ebooks
This is a fork of Apprentice Harper's version of the DeDRM tools. Apprentice Harper said that the original version of the plugin [is no longer maintained](https://github.com/apprenticeharper/DeDRM_tools#no-longer-maintained), so I've taken over, merged a bunch of open PRs, and added a ton more features and bugfixes.
The latest stable (released) version is v10.0.3 which [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3). The latest `master` build (will be automatically updated with every code change, may be unstable) [can be found here](https://github.com/noDRM/DeDRM_tools/releases/tag/autorelease).
The latest stable (released) version is v10.0.3 which [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3). The latest beta is v10.0.9, as a release candidate for v10.1.0. It [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.9).
Take a look at [the CHANGELOG](https://github.com/noDRM/DeDRM_tools/blob/master/CHANGELOG.md) to see a list of changes since the last version by Apprentice Harper (v7.2.1). This plugin will start with version v10.0.0.
The latest alpha version is available [at this link](https://github.com/noDRM/DeDRM_tools_autorelease/releases). This version is completely untested and will contain the latest code changes in this repository. With each commit in this repository, a new automatic alpha version will be uploaded there. If you want the most up-to-date code to test things and are okay with the plugin occasionally breaking, you can download this version.
The v10.0.0 versions of this plugin should both work with Calibre 5.x (Python 3) as well as Calibre 4.x and lower (Python 2). If you encounter issues with this plugin in Calibre 4.x or lower, please open a bug report.
Take a look at [the CHANGELOG](https://github.com/noDRM/DeDRM_tools/blob/master/CHANGELOG.md) to see a list of changes since the last version by Apprentice Harper (v7.2.1).
My version of the plugin should both work with Calibre 5.x/6.x (Python 3) as well as Calibre 4.x and lower (Python 2). If you encounter issues with this plugin in Calibre 4.x or lower, please open a bug report.
# Original README from Apprentice Harper