summaryrefslogtreecommitdiffstats
path: root/Other_Tools
diff options
context:
space:
mode:
authorApprentice Harper <[email protected]>2015-06-30 17:27:33 +0100
committerApprentice Alf <[email protected]>2015-07-11 13:50:36 +0100
commit9a8d5f74a68536cd105ab08f640631f085490ec4 (patch)
treea2bf30affda2d01b730a0baee1b5e3dd57029a54 /Other_Tools
parente729ae890406c5bd13e3c8f9e36a5edc6c289640 (diff)
Improvements to nook Study key retrieval, and addition of retrieval of nook keys via the internet.
Diffstat (limited to 'Other_Tools')
-rw-r--r--Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw11
-rw-r--r--Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw239
-rw-r--r--Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw1
-rw-r--r--Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw275
4 files changed, 521 insertions, 5 deletions
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw
index 4e9eead..dbadc5d 100644
--- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw
+++ b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw
@@ -4,7 +4,7 @@
from __future__ import with_statement
# ignoblekey.py
-# Copyright © 2015 Apprentice Alf
+# Copyright © 2015 Apprentice Alf and Apprentice Harper
# Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf
@@ -13,13 +13,14 @@ from __future__ import with_statement
# Revision history:
# 1.0 - Initial release
+# 1.1 - remove duplicates and return last key as single key
"""
Get Barnes & Noble EPUB user key from nook Studio log file
"""
__license__ = 'GPL v3'
-__version__ = "1.0"
+__version__ = "1.1"
import sys
import os
@@ -143,7 +144,7 @@ def getNookLogFiles():
paths.add(path)
except WindowsError:
pass
-
+
for path in paths:
# look for nookStudy log file
logpath = path +'\\Barnes & Noble\\NOOKstudy\\logs\\BNClientLog.txt'
@@ -199,7 +200,7 @@ def nookkeys(files = []):
if fileKeys:
print u"Found {0} keys in the Nook Study log files".format(len(fileKeys))
keys.extend(fileKeys)
- return keys
+ return list(set(keys))
# interface for Python DeDRM
# returns single key or multiple keys, depending on path or file passed in
@@ -209,7 +210,7 @@ def getkey(outpath, files=[]):
if not os.path.isdir(outpath):
outfile = outpath
with file(outfile, 'w') as keyfileout:
- keyfileout.write(keys[0])
+ keyfileout.write(keys[-1])
print u"Saved a key to {0}".format(outfile)
else:
keycount = 0
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw
new file mode 100644
index 0000000..2ecbe96
--- /dev/null
+++ b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+
+# ignoblekeyfetch.pyw, version 1.0
+# Copyright © 2015 Apprentice Harper
+
+# Released under the terms of the GNU General Public Licence, version 3
+# <http://www.gnu.org/licenses/>
+
+# Based on discoveries by "Nobody You Know"
+
+# Windows users: Before running this program, you must first install Python.
+# We recommend ActiveState Python 2.7.X for Windows from
+# http://www.activestate.com/activepython/downloads.
+# Then save this script file as ignoblekeyfetch.pyw and double-click on it to run it.
+#
+# Mac OS X users: Save this script file as ignoblekeyfetch.pyw. You can run this
+# program from the command line (python ignoblekeygen.pyw) or by double-clicking
+# it when it has been associated with PythonLauncher.
+
+# Revision history:
+# 1.0 - Initial release
+
+"""
+Fetch Barnes & Noble EPUB user key from B&N servers using email and password
+"""
+
+__license__ = 'GPL v3'
+__version__ = "1.0"
+
+import sys
+import os
+
+# Wrap a stream so that output gets flushed immediately
+# and also make sure that any unicode strings get
+# encoded using "replace" before writing them.
+class SafeUnbuffered:
+ def __init__(self, stream):
+ self.stream = stream
+ self.encoding = stream.encoding
+ if self.encoding == None:
+ self.encoding = "utf-8"
+ def write(self, data):
+ if isinstance(data,unicode):
+ data = data.encode(self.encoding,"replace")
+ self.stream.write(data)
+ self.stream.flush()
+ def __getattr__(self, attr):
+ return getattr(self.stream, attr)
+
+try:
+ from calibre.constants import iswindows, isosx
+except:
+ iswindows = sys.platform.startswith('win')
+ isosx = sys.platform.startswith('darwin')
+
+def unicode_argv():
+ if iswindows:
+ # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
+ # strings.
+
+ # Versions 2.x of Python don't support Unicode in sys.argv on
+ # Windows, with the underlying Windows API instead replacing multi-byte
+ # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
+ # as a list of Unicode strings and encode them as utf-8
+
+ from ctypes import POINTER, byref, cdll, c_int, windll
+ from ctypes.wintypes import LPCWSTR, LPWSTR
+
+ GetCommandLineW = cdll.kernel32.GetCommandLineW
+ GetCommandLineW.argtypes = []
+ GetCommandLineW.restype = LPCWSTR
+
+ CommandLineToArgvW = windll.shell32.CommandLineToArgvW
+ CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
+ CommandLineToArgvW.restype = POINTER(LPWSTR)
+
+ cmd = GetCommandLineW()
+ argc = c_int(0)
+ argv = CommandLineToArgvW(cmd, byref(argc))
+ if argc.value > 0:
+ # Remove Python executable and commands if present
+ start = argc.value - len(sys.argv)
+ return [argv[i] for i in
+ xrange(start, argc.value)]
+ # if we don't have any arguments at all, just pass back script name
+ # this should never happen
+ return [u"ignoblekeygen.py"]
+ else:
+ argvencoding = sys.stdin.encoding
+ if argvencoding == None:
+ argvencoding = "utf-8"
+ return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+
+
+class IGNOBLEError(Exception):
+ pass
+
+def fetch_key(email, password):
+ # remove spaces and case from name and CC numbers.
+ if type(email)==unicode:
+ email = email.encode('utf-8')
+ if type(password)==unicode:
+ password = password.encode('utf-8')
+
+ import random
+ random = "%030x" % random.randrange(16**30)
+
+ import urllib, urllib2
+ fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
+ fetch_url += urllib.quote(password,'')+"&devID=PC_BN_2.5.6.9575_"+random+"&emailAddress="
+ fetch_url += urllib.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
+ #print fetch_url
+
+ found = ''
+ try:
+ req = urllib2.Request(fetch_url)
+ response = urllib2.urlopen(req)
+ the_page = response.read()
+ #print the_page
+
+ import re
+
+ found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
+ except:
+ found = ''
+ return found
+
+
+
+
+def cli_main():
+ sys.stdout=SafeUnbuffered(sys.stdout)
+ sys.stderr=SafeUnbuffered(sys.stderr)
+ argv=unicode_argv()
+ progname = os.path.basename(argv[0])
+ if len(argv) != 4:
+ print u"usage: {0} <email> <password> <keyfileout.b64>".format(progname)
+ return 1
+ email, password, keypath = argv[1:]
+ userkey = fetch_key(email, password)
+ if len(userkey) == 28:
+ open(keypath,'wb').write(userkey)
+ return 0
+ print u"Failed to fetch key."
+ return 1
+
+
+def gui_main():
+ try:
+ import Tkinter
+ import Tkconstants
+ import tkMessageBox
+ import traceback
+ except:
+ return cli_main()
+
+ class DecryptionDialog(Tkinter.Frame):
+ def __init__(self, root):
+ Tkinter.Frame.__init__(self, root, border=5)
+ self.status = Tkinter.Label(self, text=u"Enter parameters")
+ self.status.pack(fill=Tkconstants.X, expand=1)
+ body = Tkinter.Frame(self)
+ body.pack(fill=Tkconstants.X, expand=1)
+ sticky = Tkconstants.E + Tkconstants.W
+ body.grid_columnconfigure(1, weight=2)
+ Tkinter.Label(body, text=u"Account email address").grid(row=0)
+ self.name = Tkinter.Entry(body, width=40)
+ self.name.grid(row=0, column=1, sticky=sticky)
+ Tkinter.Label(body, text=u"Account password").grid(row=1)
+ self.ccn = Tkinter.Entry(body, width=40)
+ self.ccn.grid(row=1, column=1, sticky=sticky)
+ Tkinter.Label(body, text=u"Output file").grid(row=2)
+ self.keypath = Tkinter.Entry(body, width=40)
+ self.keypath.grid(row=2, column=1, sticky=sticky)
+ self.keypath.insert(2, u"bnepubkey.b64")
+ button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
+ button.grid(row=2, column=2)
+ buttons = Tkinter.Frame(self)
+ buttons.pack()
+ botton = Tkinter.Button(
+ buttons, text=u"Fetch", width=10, command=self.generate)
+ botton.pack(side=Tkconstants.LEFT)
+ Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+ button = Tkinter.Button(
+ buttons, text=u"Quit", width=10, command=self.quit)
+ button.pack(side=Tkconstants.RIGHT)
+
+ def get_keypath(self):
+ keypath = tkFileDialog.asksaveasfilename(
+ parent=None, title=u"Select B&N ePub key file to produce",
+ defaultextension=u".b64",
+ filetypes=[('base64-encoded files', '.b64'),
+ ('All Files', '.*')])
+ if keypath:
+ keypath = os.path.normpath(keypath)
+ self.keypath.delete(0, Tkconstants.END)
+ self.keypath.insert(0, keypath)
+ return
+
+ def generate(self):
+ email = self.name.get()
+ password = self.ccn.get()
+ keypath = self.keypath.get()
+ if not email:
+ self.status['text'] = u"Email address not given"
+ return
+ if not password:
+ self.status['text'] = u"Account password not given"
+ return
+ if not keypath:
+ self.status['text'] = u"Output keyfile path not set"
+ return
+ self.status['text'] = u"Fetching..."
+ try:
+ userkey = fetch_key(email, password)
+ except Exception, e:
+ self.status['text'] = u"Error: {0}".format(e.args[0])
+ return
+ if len(userkey) == 28:
+ open(keypath,'wb').write(userkey)
+ self.status['text'] = u"Keyfile fetched successfully"
+ else:
+ self.status['text'] = u"Keyfile fetch failed."
+
+ root = Tkinter.Tk()
+ root.title(u"Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
+ root.resizable(True, False)
+ root.minsize(300, 0)
+ DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
+ root.mainloop()
+ return 0
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1:
+ sys.exit(cli_main())
+ sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw
index 5118c87..83bb2ce 100644
--- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw
+++ b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw
@@ -240,6 +240,7 @@ def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
+ import tkFileDialog
import traceback
except:
return cli_main()
diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw
new file mode 100644
index 0000000..0c0b11b
--- /dev/null
+++ b/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+
+# kindleforios4key.py
+# Copyright © 2013 by Apprentice Alf
+# Portions Copyright © 2007, 2009 Igor Skochinsky <[email protected]>
+
+# Revision history:
+# 1.0 - Generates fixed PID for Kindle for iOS 3.1.1 running on iOS 4.x
+
+
+"""
+Generate fixed PID for Kindle for iOS 3.1.1
+"""
+
+__license__ = 'GPL v3'
+__version__ = '1.0'
+
+import sys, os
+import getopt
+import binascii
+
+# Wrap a stream so that output gets flushed immediately
+# and also make sure that any unicode strings get
+# encoded using "replace" before writing them.
+class SafeUnbuffered:
+ def __init__(self, stream):
+ self.stream = stream
+ self.encoding = stream.encoding
+ if self.encoding == None:
+ self.encoding = "utf-8"
+ def write(self, data):
+ if isinstance(data,unicode):
+ data = data.encode(self.encoding,"replace")
+ self.stream.write(data)
+ self.stream.flush()
+ def __getattr__(self, attr):
+ return getattr(self.stream, attr)
+
+iswindows = sys.platform.startswith('win')
+isosx = sys.platform.startswith('darwin')
+
+def unicode_argv():
+ if iswindows:
+ # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
+ # strings.
+
+ # Versions 2.x of Python don't support Unicode in sys.argv on
+ # Windows, with the underlying Windows API instead replacing multi-byte
+ # characters with '?'.
+
+
+ from ctypes import POINTER, byref, cdll, c_int, windll
+ from ctypes.wintypes import LPCWSTR, LPWSTR
+
+ GetCommandLineW = cdll.kernel32.GetCommandLineW
+ GetCommandLineW.argtypes = []
+ GetCommandLineW.restype = LPCWSTR
+
+ CommandLineToArgvW = windll.shell32.CommandLineToArgvW
+ CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
+ CommandLineToArgvW.restype = POINTER(LPWSTR)
+
+ cmd = GetCommandLineW()
+ argc = c_int(0)
+ argv = CommandLineToArgvW(cmd, byref(argc))
+ if argc.value > 0:
+ # Remove Python executable and commands if present
+ start = argc.value - len(sys.argv)
+ return [argv[i] for i in
+ xrange(start, argc.value)]
+ # if we don't have any arguments at all, just pass back script name
+ # this should never happen
+ return [u"mobidedrm.py"]
+ else:
+ argvencoding = sys.stdin.encoding
+ if argvencoding == None:
+ argvencoding = "utf-8"
+ return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+
+import hashlib
+
+def SHA256(message):
+ ctx = hashlib.sha256()
+ ctx.update(message)
+ return ctx.digest()
+
+def crc32(s):
+ return (~binascii.crc32(s,-1))&0xFFFFFFFF
+
+letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
+
+def checksumPid(s):
+ 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]
+ crc >>= 8
+
+ return res
+
+def pidFromSerial(s, l):
+ crc = crc32(s)
+
+ arr1 = [0]*l
+ for i in xrange(len(s)):
+ arr1[i%l] ^= ord(s[i])
+
+ crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
+ for i in xrange(l):
+ arr1[i] ^= crc_bytes[i&3]
+
+ pid = ''
+ for i in xrange(l):
+ b = arr1[i] & 0xff
+ pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
+
+ return pid
+
+def generatekeys(email, mac):
+ keys = []
+ email = email.encode('utf-8').lower()
+ mac = mac.encode('utf-8').lower()
+ cleanmac = "".join(c if (c in "0123456789abcdef") else "" for c in mac)
+ lowermac = cleanmac.lower()
+ #print lowermac
+ keyseed = lowermac + email.encode('utf-8')
+ #print keyseed
+ keysha256 = SHA256(keyseed)
+ keybase64 = keysha256.encode('base64')
+ #print keybase64
+ cleankeybase64 = "".join(c if (c in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") else "0" for c in keybase64)
+ #print cleankeybase64
+ pseudoudid = cleankeybase64[:40]
+ #print pseudoudid
+ keys.append(pidFromSerial(pseudoudid.encode("utf-8"),8))
+ return keys
+
+# interface for Python DeDRM
+# returns single key or multiple keys, depending on path or file passed in
+def getkey(email, mac, outpath):
+ keys = generatekeys(email,mac)
+ if len(keys) > 0:
+ if not os.path.isdir(outpath):
+ outfile = outpath
+ with file(outfile, 'w') as keyfileout:
+ keyfileout.write(keys[0])
+ print u"Saved a key to {0}".format(outfile)
+ else:
+ keycount = 0
+ for key in keys:
+ while True:
+ keycount += 1
+ outfile = os.path.join(outpath,u"kindleios{0:d}.pid".format(keycount))
+ if not os.path.exists(outfile):
+ break
+ with file(outfile, 'w') as keyfileout:
+ keyfileout.write(key)
+ print u"Saved a key to {0}".format(outfile)
+ return True
+ return False
+
+def usage(progname):
+ print u"Generates the key for Kindle for iOS 3.1.1"
+ print u"Requires email address of Amazon acccount"
+ print u"And MAC address for iOS device’s wifi"
+ print u"Outputs to a file or to stdout"
+ print u"Usage:"
+ print u" {0:s} [-h] <email address> <MAC address> [<outfile>]".format(progname)
+
+
+def cli_main():
+ sys.stdout=SafeUnbuffered(sys.stdout)
+ sys.stderr=SafeUnbuffered(sys.stderr)
+ argv=unicode_argv()
+ progname = os.path.basename(argv[0])
+ print u"{0} v{1}\nCopyright © 2013 Apprentice Alf".format(progname,__version__)
+
+ try:
+ opts, args = getopt.getopt(argv[1:], "h")
+ except getopt.GetoptError, err:
+ print u"Error in options or arguments: {0}".format(err.args[0])
+ usage(progname)
+ sys.exit(2)
+
+ for o, a in opts:
+ if o == "-h":
+ usage(progname)
+ sys.exit(0)
+
+
+ if len(args) < 2 or len(args) > 3:
+ usage(progname)
+ sys.exit(2)
+
+ if len(args) == 3:
+ # save to the specified file or folder
+ getkey(args[0],args[1],args[2])
+ else:
+ keys = generatekeys(args[0],args[1])
+ for key in keys:
+ print key
+
+ return 0
+
+
+def gui_main():
+ try:
+ import Tkinter
+ import Tkconstants
+ import tkMessageBox
+ except:
+ print "Tkinter not installed"
+ return cli_main()
+
+ class DecryptionDialog(Tkinter.Frame):
+ def __init__(self, root):
+ Tkinter.Frame.__init__(self, root, border=5)
+ self.status = Tkinter.Label(self, text=u"Enter parameters")
+ self.status.pack(fill=Tkconstants.X, expand=1)
+ body = Tkinter.Frame(self)
+ body.pack(fill=Tkconstants.X, expand=1)
+ sticky = Tkconstants.E + Tkconstants.W
+ body.grid_columnconfigure(1, weight=2)
+ Tkinter.Label(body, text=u"Amazon email address").grid(row=0)
+ self.email = Tkinter.Entry(body, width=40)
+ self.email.grid(row=0, column=1, sticky=sticky)
+ Tkinter.Label(body, text=u"iOS MAC address").grid(row=1)
+ self.mac = Tkinter.Entry(body, width=40)
+ self.mac.grid(row=1, column=1, sticky=sticky)
+ buttons = Tkinter.Frame(self)
+ buttons.pack()
+ button = Tkinter.Button(
+ buttons, text=u"Generate", width=10, command=self.generate)
+ button.pack(side=Tkconstants.LEFT)
+ Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+ button = Tkinter.Button(
+ buttons, text=u"Quit", width=10, command=self.quit)
+ button.pack(side=Tkconstants.RIGHT)
+
+ def generate(self):
+ email = self.email.get()
+ mac = self.mac.get()
+ if not email:
+ self.status['text'] = u"Email not specified"
+ return
+ if not mac:
+ self.status['text'] = u"MAC not specified"
+ return
+ self.status['text'] = u"Generating..."
+ try:
+ keys = generatekeys(email, mac)
+ except Exception, e:
+ self.status['text'] = u"Error: (0}".format(e.args[0])
+ return
+ self.status['text'] = ", ".join(key for key in keys)
+
+ root = Tkinter.Tk()
+ root.title(u"Kindle for iOS PID Generator v.{0}".format(__version__))
+ root.resizable(True, False)
+ root.minsize(300, 0)
+ DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
+ root.mainloop()
+ return 0
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1:
+ sys.exit(cli_main())
+ sys.exit(gui_main())