From 9b7725521289b6f8745874488dc55fe1e5293980 Mon Sep 17 00:00:00 2001 From: Apprentice Harper Date: Mon, 13 Apr 2015 07:45:43 +0100 Subject: [PATCH] Starting to move ignoblekeyfetch into all tools. --- .../Contents/Resources/androidkindlekey.py | 4 +- .../Contents/Resources/ignoblekeyfetch.py | 239 ++++++++++++++++++ .../lib/DeDRM_Barnes and Noble Key_Help.htm | 30 +-- .../DeDRM_lib/lib/androidkindlekey.py | 4 +- .../DeDRM_App/DeDRM_lib/lib/config.py | 41 ++- .../DeDRM_lib/lib/ignoblekeyfetch.py | 239 ++++++++++++++++++ DeDRM_calibre_plugin/DeDRM_plugin.zip | Bin 343352 -> 343355 bytes .../DeDRM_plugin/androidkindlekey.py | 4 +- .../DeDRM_plugin/ignoblekeyfetch.py | 17 +- .../ignoblekeyfetch.pyw | 239 ++++++++++++++++++ .../Kindle_for_Android/androidkindlekey.pyw | 8 +- ReadMe_First.txt | 8 +- 12 files changed, 774 insertions(+), 59 deletions(-) create mode 100644 DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeyfetch.py create mode 100644 DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/ignoblekeyfetch.py create mode 100644 Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py index d88e1df..7a25710 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py @@ -11,7 +11,9 @@ from __future__ import with_statement # Revision history: # 1.0 - AmazonSecureStorage.xml decryption to serial number # 1.1 - map_data_storage.db decryption to serial number -# 1.2 - BugFix +# 1.2 - Changed to be callable from AppleScript by returning only serial number +# - and changed name to androidkindlekey.py +# - and added in unicode command line support # 1.3 - added in TkInter interface, output to a file and attempt to get backup from a connected android device. """ diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeyfetch.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeyfetch.py new file mode 100644 index 0000000..2ecbe96 --- /dev/null +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeyfetch.py @@ -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 +# + +# 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>(.+?) ".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/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/DeDRM_Barnes and Noble Key_Help.htm b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/DeDRM_Barnes and Noble Key_Help.htm index 47da891..aba641d 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/DeDRM_Barnes and Noble Key_Help.htm +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/DeDRM_Barnes and Noble Key_Help.htm @@ -1,4 +1,4 @@ - @@ -24,28 +24,17 @@ li {margin-top: 0.5em}

Changes at Barnes & Noble

-

In mid-2014, Barnes & Noble changed the way they generated encryption keys. Instead of deriving the key from the user's name and credit card number, they started generating a random key themselves, sending that key through to devices when they connected to the Barnes & Noble servers. This means that some users will find that no combination of their name and CC# will work in decrypting their ebooks.

+

In mid-2014, Barnes & Noble changed the way they generated encryption keys. Instead of deriving the key from the user's name and credit card number, they started generating a random key themselves, sending that key through to devices when they connected to the Barnes & Noble servers. This means that most users will now find that no combination of their name and CC# will work in decrypting their recently downloaded ebooks.

-

There is a work-around. Barnes & Noble’s desktop app NOOK Study generates a log file that contains the encryption key. You can download NOOK Study from https://yuzu.com/nsdownload.

-

Once downloaded, install the application, register with your Barnes & Noble or nook account, and download at least one DRMed ebook through NOOK Study. It will be saved somewhere in a folder called "My Barnes & Noble eBooks" in your Documents folder.

-

Now import that book into calibre. The log file and the key in the log should be automatically found by the plugin and used to decrypt the book.

-

If the automatic process doesn't work for you, you can still find extract it manually and save it as a .b64 file for import into the plugin's preferences as follows:

-
  1. In NOOK Study, select Settings/About (Windows) or NOOK Study/About NOOK Study (Mac) and in the dialog that appears click the link at the bottom to copy the log into the clipboard.
  2. -
  3. Paste the copied log into a text editor
  4. -
  5. Search for the text CCHashResponseV1
  6. -
  7. On the line below which starts with ccHash, copy the text between the " marks after ccHash, but don't include the " marks.
  8. -
  9. Save that text in a new plain text file, with file name extension .b64 (for example, key.b64)
  10. -
  11. Import that file into the preferences through this dialog, using the "Import Existing Key Files" button.
  12. -
+

Someone commenting at Apprentice Alf's blog detailed a way to retrieve a new account key using the account's email address and password. This method has now been incorporated into the plugin. - -

Old instructions: Creating New Keys:

+

Creating New Keys:

On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering the necessary data to generate a new key.

    -
  • Unique Key Name: this is a unique name you choose to help you identify the key. This name will show in the list of configured keys. Choose something that will help you remember the data (name, cc#) it was created with.
  • -
  • Your Name: This is the name used by Barnes and Noble to generate your encryption key. Seemingly at random, Barnes and Noble choose one of three places from which to take this name. Most commonly, it’s your name as set in your Barnes & Noble account, My Account page, directly under PERSONAL INFORMATION. Sometimes it is the the name used in the default shipping address, and sometimes it’s the name listed for the active credit card. If these names are different in your Barnes and Noble account preferences, I suggest creating one key for each version of your name. This name will not be stored anywhere on your computer or in calibre. It will only be used in the creation of the one-way hash/key that’s stored in the preferences.
  • -
  • Credit Card#: this is the default credit card number that was on file with Barnes and Noble at the time of download of the ebook to be de-DRMed. Just enter the 16 (15 for American Express) digits. As with the name, this number will not be stored anywhere on your computer or in calibre. It will only be used in the creation of the one-way hash/key that’s stored in the preferences.
  • +
  • Unique Key Name: this is a unique name you choose to help you identify the key. This name will show in the list of configured keys. Choose something that will help you remember the data (account email address) it was created with.
  • +
  • B&N/nook account email address: This is the default email address for your Barnes and Noble/nook account. This email will not be stored anywhere on your computer or in calibre. It will only be used to fetch the account key that from the B&N server, and it is that key that will be stored in the preferences.
  • +
  • B&N/nook account password: this is the password for your Barnes and Noble/nook account. As with the email address, this will not be stored anywhere on your computer or in calibre. It will only be used to fetch the key from the B&N server.

Click the OK button to create and store the generated key. Or Cancel if you don’t want to create a key.

@@ -69,6 +58,11 @@ li {margin-top: 0.5em}

Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.

+

NOOK Study

+

Books downloaded through NOOK Study may or may not use the key fetched using the above method. If a books is not decrypted successfully with any of the keys, the plugin will attempt to recover a key from the NOOK Study log file and use that.

+ + + diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py index d88e1df..7a25710 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py @@ -11,7 +11,9 @@ from __future__ import with_statement # Revision history: # 1.0 - AmazonSecureStorage.xml decryption to serial number # 1.1 - map_data_storage.db decryption to serial number -# 1.2 - BugFix +# 1.2 - Changed to be callable from AppleScript by returning only serial number +# - and changed name to androidkindlekey.py +# - and added in unicode command line support # 1.3 - added in TkInter interface, output to a file and attempt to get backup from a connected android device. """ diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/config.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/config.py index 1357b49..432a0ee 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/config.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/config.py @@ -109,14 +109,15 @@ class ConfigWidget(QWidget): self.ereader_button.setToolTip(_(u"Click to manage keys for eReader ebooks")) self.ereader_button.setText(u"eReader ebooks") self.ereader_button.clicked.connect(self.ereader_keys) + + button_layout.addWidget(self.adept_button) + button_layout.addWidget(self.kindle_key_button) button_layout.addWidget(self.kindle_serial_button) button_layout.addWidget(self.kindle_android_button) button_layout.addWidget(self.bandn_button) button_layout.addWidget(self.mobi_button) button_layout.addWidget(self.ereader_button) - button_layout.addWidget(self.adept_button) - button_layout.addWidget(self.kindle_key_button) - + self.resize(self.sizeHint()) def kindle_serials(self): @@ -124,7 +125,7 @@ class ConfigWidget(QWidget): d.exec_() def kindle_android_serials(self): - d = ManageKeysDialog(self,u"Kindle for Andoid Serial Number",self.tempdedrmprefs['androidserials'], AddAndroidSerialDialog, 'ab') + d = ManageKeysDialog(self,u"Kindle for Andoid",self.tempdedrmprefs['androidserials'], AddAndroidSerialDialog, 'ab') d.exec_() def kindle_keys(self): @@ -545,14 +546,14 @@ class AddBandNKeyDialog(QDialog): name_group = QHBoxLayout() data_group_box_layout.addLayout(name_group) - name_group.addWidget(QLabel(u"Your Name:", self)) + name_group.addWidget(QLabel(u"B&N/nook account email address:", self)) self.name_ledit = QLineEdit(u"", self) - self.name_ledit.setToolTip(_(u"

Enter your name as it appears in your B&N " + - u"account or on your credit card.

" + + self.name_ledit.setToolTip(_(u"

Enter your email address as it appears in your B&N " + + u"account.

" + u"

It will only be used to generate this " + - u"one-time key and won\'t be stored anywhere " + + u"key and won\'t be stored anywhere " + u"in calibre or on your computer.

" + - u"

(ex: Jonathan Smith)")) + u"

eg: apprenticeharper@gmail.com

")) name_group.addWidget(self.name_ledit) name_disclaimer_label = QLabel(_(u"(Will not be saved in configuration data)"), self) name_disclaimer_label.setAlignment(Qt.AlignHCenter) @@ -560,13 +561,12 @@ class AddBandNKeyDialog(QDialog): ccn_group = QHBoxLayout() data_group_box_layout.addLayout(ccn_group) - ccn_group.addWidget(QLabel(u"Credit Card#:", self)) + ccn_group.addWidget(QLabel(u"B&N/nook account password:", self)) self.cc_ledit = QLineEdit(u"", self) - self.cc_ledit.setToolTip(_(u"

Enter the full credit card number on record " + - u"in your B&N account.

" + - u"

No spaces or dashes... just the numbers. " + - u"This number will only be used to generate this " + - u"one-time key and won\'t be stored anywhere in " + + self.cc_ledit.setToolTip(_(u"

Enter the password " + + u"for your B&N account.

" + + u"

The password will only be used to generate this " + + u"key and won\'t be stored anywhere in " + u"calibre or on your computer.")) ccn_group.addWidget(self.cc_ledit) ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self) @@ -587,8 +587,8 @@ class AddBandNKeyDialog(QDialog): @property def key_value(self): - from calibre_plugins.dedrm.ignoblekeygen import generate_key as generate_bandn_key - return generate_bandn_key(self.user_name,self.cc_number) + from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key + return fetch_bandn_key(self.user_name,self.cc_number) @property def user_name(self): @@ -596,16 +596,13 @@ class AddBandNKeyDialog(QDialog): @property def cc_number(self): - return unicode(self.cc_ledit.text()).strip().replace(' ', '').replace('-','') + return unicode(self.cc_ledit.text()).strip() def accept(self): if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace(): errmsg = u"All fields are required!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) - if not self.cc_number.isdigit(): - errmsg = u"Numbers only in the credit card number field!" - return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) if len(self.key_name) < 4: errmsg = u"Key name must be at least 4 characters long!" return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False) @@ -904,7 +901,7 @@ class AddAndroidSerialDialog(QDialog): data_group_box_layout.addLayout(key_group) key_group.addWidget(QLabel(u"Kindle for Android Serial Number:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(u"Enter a Kindle for ANdroid serial number. These can be found using the androidkindlekey.py script.") + self.key_ledit.setToolTip(u"Enter a Kindle for Android serial number. These can be found using the androidkindlekey.py script.") key_group.addWidget(self.key_ledit) key_label = QLabel(_(''), self) key_label.setAlignment(Qt.AlignHCenter) diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/ignoblekeyfetch.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/ignoblekeyfetch.py new file mode 100644 index 0000000..2ecbe96 --- /dev/null +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/ignoblekeyfetch.py @@ -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 +# + +# 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>(.+?) ".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/DeDRM_calibre_plugin/DeDRM_plugin.zip b/DeDRM_calibre_plugin/DeDRM_plugin.zip index d9e1ee2d9d995ed02b1b3b094b6aed722d6aac04..5da731831cb8dcd4a84f735710d8a707bb3a6f8e 100644 GIT binary patch delta 8182 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 delta 8193 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 + +# 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>(.+?) ".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/Kindle_for_Android/androidkindlekey.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw index 6e6aef7..7a25710 100644 --- a/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw +++ b/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw @@ -4,13 +4,13 @@ from __future__ import with_statement # androidkindlekey.py -# Copyright © 2013-15 by Thom -# Some portions Copyright © 2010-15 by some_updates, Apprentice Alf and Apprentice Harper +# Copyright © 2013-15 by Thom and Apprentice Harper +# Some portions Copyright © 2010-15 by some_updates and Apprentice Alf # # Revision history: -# 1.0 - Android serial number extracted from AmazonSecureStorage.xml -# 1.1 - Fixes and enhancements of some kind +# 1.0 - AmazonSecureStorage.xml decryption to serial number +# 1.1 - map_data_storage.db decryption to serial number # 1.2 - Changed to be callable from AppleScript by returning only serial number # - and changed name to androidkindlekey.py # - and added in unicode command line support diff --git a/ReadMe_First.txt b/ReadMe_First.txt index ba0943f..9d3bb88 100644 --- a/ReadMe_First.txt +++ b/ReadMe_First.txt @@ -6,7 +6,7 @@ This ReadMe_First.txt is meant to give users a quick overview of what is availab The is archive includes tools to remove DRM from: - Kindle ebooks (Mobi, Topaz, Print Replica and KF8). - - Barnes and Noble ePubs downloaded through NOOK Study + - Barnes and Noble ePubs - Adobe Digital Editions ePubs (including Kobo and Google ePubs downloaded to ADE) - Kobo kePubs from the Kobo Desktop application - Adobe Digital Editions PDFs @@ -19,7 +19,7 @@ These tools do NOT work with Apple's iBooks FairPlay DRM (see end of this file.) About the tools --------------- -These tools are updated and maintained by Apprentice Alf and Apprentice Harper. You can find the latest updates and get support at Apprentice Alf's blog: http://www.apprenticealf.wordpress.com/ +These tools are updated and maintained by Apprentice Alf and Apprentice Harper. You can find links to the latest updates and get support at Apprentice Alf's blog: http://www.apprenticealf.wordpress.com/ If you re-post these tools, a link to the blog would be appreciated. @@ -60,13 +60,13 @@ For instructions, see the obok_plugin_ReadMe.txt file in the Obok_calibre_plugin Other_Tools ----------- -This is folder other tools that may be useful for DRMed ebooks from certain sources or for Linux users. Most users won't need any of these tools. +This is a folder other tools that may be useful for DRMed ebooks from certain sources or for Linux users. Most users won't need any of these tools. B&N_Download_Helper A Javascript to enable a download button at the B&N website for ebooks that normally won't download to your PC. Another one only for the adventurous. DRM_Key_Scripts -This folder contains python scripts that create or extract encryption keyfiles for Barnes and Noble ePubs, Adobe Digital Editions ePubs and Kindle for Mac/PC ebooks. +This folder contains python scripts that create or extract or fetch encryption keyfiles for Barnes and Noble ePubs, Adobe Digital Editions ePubs, Kindle for Mac/PC and Kindle for Android ebooks. Kindle_for_Android_Patches Definitely only for the adventurous, this folder contains information on how to modify the Kindel for Android app to b able to get a PID for use with the other Kindle tools (DeDRM apps and calibre plugin).