2021-12-23 11:29:58 +01:00
|
|
|
# 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
|
2022-03-18 17:45:07 +01:00
|
|
|
import traceback
|
2021-12-23 11:29:58 +01:00
|
|
|
try:
|
|
|
|
from Cryptodome.Cipher import AES
|
|
|
|
except:
|
|
|
|
from Crypto.Cipher import AES
|
|
|
|
import hashlib
|
|
|
|
from lxml import etree
|
|
|
|
|
2022-03-19 10:14:45 +01:00
|
|
|
def unpad(data, padding=16):
|
|
|
|
if sys.version_info[0] == 2:
|
|
|
|
pad_len = ord(data[-1])
|
|
|
|
else:
|
|
|
|
pad_len = data[-1]
|
|
|
|
|
|
|
|
return data[:-pad_len]
|
|
|
|
|
2021-12-23 11:29:58 +01:00
|
|
|
|
|
|
|
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"):
|
2022-03-18 17:45:07 +01:00
|
|
|
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()
|
2021-12-23 11:29:58 +01:00
|
|
|
|
|
|
|
return decrypted_hashes
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
dump_keys(True)
|