#! /usr/bin/python # ignoblekey.pyw, version 2 # To run this program install Python 2.6 from # Save this script file as ignoblekey.pyw and double-click on it to run it. # Revision history: # 1 - Initial release # 2 - Add some missing code """ Retrieve B&N DesktopReader EPUB user AES key. """ from __future__ import with_statement __license__ = 'GPL v3' import sys import os import binascii import glob import Tkinter import Tkconstants import tkMessageBox import traceback BN_KEY_KEY = 'uhk00000000' BN_APPDATA_DIR = r'Barnes & Noble\DesktopReader' class IgnobleError(Exception): pass def retrieve_key(inpath, outpath): # The B&N DesktopReader 'ClientAPI' file is just a sqlite3 DB. Requiring # users to install sqlite3 and bindings seems like overkill for retrieving # one value, so we go in hot and dirty. with open(inpath, 'rb') as f: data = f.read() if BN_KEY_KEY not in data: raise IgnobleError('B&N user key not found; unexpected DB format?') index = data.rindex(BN_KEY_KEY) + len(BN_KEY_KEY) + 1 data = data[index:index + 40] for i in xrange(20, len(data)): try: keyb64 = data[:i] if len(keyb64.decode('base64')) == 20: break except binascii.Error: pass else: raise IgnobleError('Problem decoding key; unexpected DB format?') with open(outpath, 'wb') as f: f.write(keyb64 + '\n') def cli_main(argv=sys.argv): progname = os.path.basename(argv[0]) args = argv[1:] if len(args) != 2: sys.stderr.write("USAGE: %s CLIENTDB KEYFILE" % (progname,)) return 1 inpath, outpath = args retrieve_key(inpath, outpath) return 0 def find_bnclientdb_path(): appdata = os.environ['APPDATA'] bndir = os.path.join(appdata, BN_APPDATA_DIR) if not os.path.isdir(bndir): raise IgnobleError('Could not locate B&N Reader installation') dbpath = glob.glob(os.path.join(bndir, 'ClientAPI_*.db')) if len(dbpath) == 0: raise IgnobleError('Problem locating B&N Reader DB') return sorted(dbpath)[-1] class ExceptionDialog(Tkinter.Frame): def __init__(self, root, text): Tkinter.Frame.__init__(self, root, border=5) label = Tkinter.Label(self, text="Unexpected error:", anchor=Tkconstants.W, justify=Tkconstants.LEFT) label.pack(fill=Tkconstants.X, expand=0) self.text = Tkinter.Text(self) self.text.pack(fill=Tkconstants.BOTH, expand=1) self.text.insert(Tkconstants.END, text) def gui_main(argv=sys.argv): root = Tkinter.Tk() root.withdraw() progname = os.path.basename(argv[0]) keypath = 'bnepubkey.b64' try: dbpath = find_bnclientdb_path() retrieve_key(dbpath, keypath) except IgnobleError, e: tkMessageBox.showerror("Ignoble Key", "Error: " + str(e)) return 1 except Exception: root.wm_state('normal') root.title('Ignoble Key') text = traceback.format_exc() ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1) root.mainloop() return 1 tkMessageBox.showinfo( "Ignoble Key", "Key successfully retrieved to %s" % (keypath)) return 0 if __name__ == '__main__': if len(sys.argv) > 1: sys.exit(cli_main()) sys.exit(gui_main())