The plugins target calibre 5/6 (Python 3) and the Poetry environment
pins Python >=3.8, so the Python 2 fallbacks are dead code. This removes
them throughout both plugins (behaviour on Python 3 is unchanged):
- Drop all `from __future__` imports.
- Collapse `if sys.version_info[0] == 2: ... else: ...` blocks to their
Python 3 branch (ineptpdf, mobidedrm, kindlekey, kgenpids, alfcrypto,
erdr2pml, ineptepub, obok, and the various unpad() helpers, etc.).
- Replace `_winreg` import fallbacks with plain `import winreg`, and
delete the py2-only adobekey_winreg_unicode module (now unreferenced).
- Drop py2 name shims: `unicode`/`unichr`, `.iteritems()`,
`from StringIO import StringIO`, `htmlentitydefs` fallback, and the
Windows CommandLineToArgvW dance in unicode_argv (py3 sys.argv is
already Unicode on every platform).
- Remove the "Calibre < 5" (py2) bugfix block from the compat header.
Verified: every .py file in both plugins still byte-compiles.
Scope: the maintained DeDRM_plugin and Obok_plugin only. The archival
standalone scripts under Other_Tools/ are left as historical snapshots.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
2.5 KiB
Python
76 lines
2.5 KiB
Python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
|
|
|
'''
|
|
Obtain the user's ccHash from the Barnes & Noble Nook Windows Store app.
|
|
https://www.microsoft.com/en-us/p/nook-books-magazines-newspapers-comics/9wzdncrfj33h
|
|
(Requires a recent Windows version in a supported region (US).)
|
|
This procedure has been tested with Nook app version 1.11.0.4 under Windows 11.
|
|
|
|
Based on experimental standalone python script created by fesiwi at
|
|
https://github.com/noDRM/DeDRM_tools/discussions/9
|
|
'''
|
|
|
|
import sys, os
|
|
import apsw
|
|
import base64
|
|
import traceback
|
|
try:
|
|
from Cryptodome.Cipher import AES
|
|
except:
|
|
from Crypto.Cipher import AES
|
|
import hashlib
|
|
from lxml import etree
|
|
|
|
def unpad(data, padding=16):
|
|
pad_len = data[-1]
|
|
|
|
return data[:-pad_len]
|
|
|
|
|
|
NOOK_DATA_FOLDER = "%LOCALAPPDATA%\\Packages\\BarnesNoble.Nook_ahnzqzva31enc\\LocalState"
|
|
PASS_HASH_SECRET = "9ca588496a1bc4394553d9e018d70b9e"
|
|
|
|
|
|
def dump_keys(print_result=False):
|
|
db_filename = os.path.expandvars(NOOK_DATA_FOLDER + "\\NookDB.db3")
|
|
|
|
|
|
if not os.path.isfile(db_filename):
|
|
print("Database file not found. Is the Nook Windows Store app installed?")
|
|
return []
|
|
|
|
|
|
# Python2 has no fetchone() so we have to use fetchall() and discard everything but the first result.
|
|
# There should only be one result anyways.
|
|
serial_number = apsw.Connection(db_filename).cursor().execute(
|
|
"SELECT value FROM bn_internal_key_value_table WHERE key = 'serialNumber';").fetchall()[0][0]
|
|
|
|
|
|
hash_key = hashlib.sha1(bytearray.fromhex(serial_number + PASS_HASH_SECRET)).digest()[:16]
|
|
|
|
activation_file_name = os.path.expandvars(NOOK_DATA_FOLDER + "\\settings\\activation.xml")
|
|
|
|
if not os.path.isfile(activation_file_name):
|
|
print("Activation file not found. Are you logged in to your Nook account?")
|
|
return []
|
|
|
|
|
|
activation_xml = etree.parse(activation_file_name)
|
|
|
|
decrypted_hashes = []
|
|
|
|
for pass_hash in activation_xml.findall(".//{http://ns.adobe.com/adept}passHash"):
|
|
try:
|
|
encrypted_cc_hash = base64.b64decode(pass_hash.text)
|
|
cc_hash = unpad(AES.new(hash_key, AES.MODE_CBC, encrypted_cc_hash[:16]).decrypt(encrypted_cc_hash[16:]), 16)
|
|
decrypted_hashes.append((base64.b64encode(cc_hash).decode("ascii")))
|
|
if print_result:
|
|
print("Nook ccHash is %s" % (base64.b64encode(cc_hash).decode("ascii")))
|
|
except:
|
|
traceback.print_exc()
|
|
|
|
return decrypted_hashes
|
|
|
|
if __name__ == "__main__":
|
|
dump_keys(True)
|