diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw index 4837627a..f3c46020 100644 --- a/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw +++ b/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw @@ -39,6 +39,12 @@ from struct import pack, unpack, unpack_from import json import getopt + + +# import sys +# from IPython.core import ultratb +# sys.excepthook = ultratb.VerboseTB(color_scheme='NoColor') + # Routines common to Mac and PC # Wrap a stream so that output gets flushed immediately @@ -92,7 +98,7 @@ def unicode_argv(): # Remove Python executable and commands if present start = argc.value - len(sys.argv) return [argv[i] for i in - xrange(start, argc.value)] + range(start, argc.value)] # if we don't have any arguments at all, just pass back script name # this should never happen return [u"kindlekey.py"] @@ -128,14 +134,14 @@ def SHA256(message): def primes(n): if n==2: return [2] elif n<2: return [] - s=range(3,n+1,2) + s=list(range(3,n+1,2)) mroot = n ** 0.5 - half=(n+1)/2-1 + half=(n+1)//2-1 i=0 m=3 while m <= mroot: if s[i]: - j=(m*m-3)/2 + j=(m*m-3)//2 s[j]=0 while j 0: # save any bytes that are not block aligned self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:] else: - self.bytesToEncrypt = '' + self.bytesToEncrypt = b'' if more == None: # no more data expected from caller finalBytes = self.padding.addPad(self.bytesToEncrypt,self.blockSize) @@ -289,14 +294,14 @@ if iswindows: numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize) if more == None: # no more calls to decrypt, should have all the data if numExtraBytes != 0: - raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt' + raise DecryptNotBlockAlignedError('Data not block aligned on decrypt') # hold back some bytes in case last decrypt has zero len if (more != None) and (numExtraBytes == 0) and (numBlocks >0) : numBlocks -= 1 numExtraBytes = self.blockSize - plainText = '' + plainText = b'' for i in range(numBlocks): bStart = i*self.blockSize ptBlock = self.decryptBlock(self.bytesToDecrypt[bStart : bStart+self.blockSize]) @@ -306,7 +311,7 @@ if iswindows: if numExtraBytes > 0: # save any bytes that are not block aligned self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:] else: - self.bytesToEncrypt = '' + self.bytesToEncrypt = b'' if more == None: # last decrypt remove padding plainText = self.padding.removePad(plainText, self.blockSize) @@ -331,7 +336,7 @@ if iswindows: def removePad(self, paddedBinaryString, blockSize): """ Remove padding from a binary string """ if not(0 6 and i%Nk == 4 : temp = [ Sbox[byte] for byte in temp ] # SubWord(temp) w.append( [ w[i-Nk][byte]^temp[byte] for byte in range(4) ] ) @@ -639,7 +644,7 @@ if iswindows: def __init__(self, key = None, padding = padWithPadLen(), keySize=16): """ Initialize AES, keySize is in bytes """ if not (keySize == 16 or keySize == 24 or keySize == 32) : - raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes' + raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes') Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 ) @@ -714,7 +719,7 @@ if iswindows: if self.encryptBlockCount == 0: if self.iv == None: # generate IV and use - self.iv = ''.join([chr(self.r.randrange(256)) for i in range(self.blockSize)]) + self.iv = bytes(self.r.randrange(256) for i in range(self.blockSize)) self.prior_encr_CT_block = self.iv auto_IV = self.prior_encr_CT_block # prepend IV if it's automatic else: # application provided IV @@ -731,7 +736,7 @@ if iswindows: if self.decryptBlockCount == 0: # first call, process IV if self.iv == None: # auto decrypt IV? self.prior_CT_block = encryptedBlock - return '' + return b'' else: assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption" self.prior_CT_block = self.iv @@ -782,7 +787,7 @@ if iswindows: def xorstr( a, b ): if len(a) != len(b): raise Exception("xorstr(): lengths differ") - return ''.join((chr(ord(x)^ord(y)) for x, y in zip(a, b))) + return bytes((x^y for x, y in zip(a, b))) def prf( h, data ): hm = h.copy() @@ -800,18 +805,18 @@ if iswindows: sha = hashlib.sha1 digest_size = sha().digest_size # l - number of output blocks to produce - l = keylen / digest_size + l = keylen // digest_size if keylen % digest_size != 0: l += 1 h = hmac.new( passwd, None, sha ) - T = "" + T = b"" for i in range(1, l+1): T += pbkdf2_F( h, salt, iter, i ) return T[0: keylen] def UnprotectHeaderData(encryptedData): - passwdData = 'header_key_data' - salt = 'HEADER.2011' + passwdData = b'header_key_data' + salt = b'HEADER.2011' iter = 0x80 keylen = 0x100 key_iv = KeyIVGen().pbkdf2(passwdData, salt, iter, keylen) @@ -824,12 +829,12 @@ if iswindows: # Various character maps used to decrypt kindle info values. # Probably supposed to act as obfuscation - charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_" - charMap5 = "AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE" + charMap2 = b"AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_" + charMap5 = b"AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE" # New maps in K4PC 1.9.0 - testMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" - testMap6 = "9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG" - testMap8 = "YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD" + testMap1 = b"n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" + testMap6 = b"9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG" + testMap8 = b"YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD" # interface with Windows OS Routines class DataBlob(Structure): @@ -891,9 +896,9 @@ if iswindows: # double the buffer size buffer = create_unicode_buffer(len(buffer) * 2) size.value = len(buffer) - + # replace any non-ASCII values with 0xfffd - for i in xrange(0,len(buffer)): + for i in range(0,len(buffer)): if buffer[i]>u"\u007f": #print u"swapping char "+str(i)+" ("+buffer[i]+")" buffer[i] = u"\ufffd" @@ -969,13 +974,13 @@ if iswindows: print ('Could not find the folder in which to look for kinfoFiles.') else: # Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8 - print(u'searching for kinfoFiles in ' + path.encode('ascii', 'ignore')) + print('searching for kinfoFiles in ' + path) # look for (K4PC 1.9.0 and later) .kinf2011 file kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011' if os.path.isfile(kinfopath): found = True - print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath.encode('ascii','ignore')) + print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath) kInfoFiles.append(kinfopath) # look for (K4PC 1.6.0 and later) rainier.2.1.1.kinf file @@ -1038,7 +1043,7 @@ if iswindows: # the .kinf file uses "/" to separate it into records # so remove the trailing "/" to make it easy to use split data = data[:-1] - items = data.split('/') + items = data.split(b'/') # starts with an encoded and encrypted header blob headerblob = items.pop(0) @@ -1047,7 +1052,8 @@ if iswindows: #print "header cleartext:",cleartext # now extract the pieces that form the added entropy pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE) - for m in re.finditer(pattern, cleartext): + + for m in re.finditer(pattern, cleartext.decode('ascii')): added_entropy = m.group(2) + m.group(4) @@ -1063,7 +1069,7 @@ if iswindows: # the sha1 of raw keyhash string is used to create entropy along # with the added entropy provided above from the headerblob - entropy = SHA1(keyhash) + added_entropy + entropy = SHA1(keyhash) + added_entropy.encode('ascii') # the remainder of the first record when decoded with charMap5 # has the ':' split char followed by the string representation @@ -1075,14 +1081,14 @@ if iswindows: # read and store in rcnt records of data # that make up the contents value edlst = [] - for i in xrange(rcnt): + for i in range(rcnt): item = items.pop(0) edlst.append(item) # key names now use the new testMap8 encoding keyname = "unknown" for name in names: - if encodeHash(name,testMap8) == keyhash: + if encodeHash(name.encode('ascii'),testMap8) == keyhash: keyname = name #print "keyname found from hash:",keyname break @@ -1103,10 +1109,10 @@ if iswindows: # move first offsets chars to end to align for decode by testMap8 # by moving noffset chars from the start of the # string to the end of the string - encdata = "".join(edlst) + encdata = b"".join(edlst) #print "encrypted data:",encdata contlen = len(encdata) - noffset = contlen - primes(int(contlen/3))[-1] + noffset = contlen - primes(int(contlen//3))[-1] pfx = encdata[0:noffset] encdata = encdata[noffset:] encdata = encdata + pfx @@ -1126,9 +1132,9 @@ if iswindows: # store values used in decryption DB['IDString'] = GetIDString() DB['UserName'] = GetUserName() - print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')) + print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().hex())) else: - print u"Couldn't decrypt file." + print(u"Couldn't decrypt file.") DB = {} return DB elif isosx: @@ -1263,7 +1269,7 @@ elif isosx: #print out1 reslst = out1.split('\n') cnt = len(reslst) - for j in xrange(cnt): + for j in range(cnt): resline = reslst[j] pp = resline.find('\"Serial Number\" = \"') if pp >= 0: @@ -1279,7 +1285,7 @@ elif isosx: out1, out2 = p.communicate() reslst = out1.split('\n') cnt = len(reslst) - for j in xrange(cnt): + for j in range(cnt): resline = reslst[j] if resline.startswith('/dev'): (devpart, mpath) = resline.split(' on ')[:2] @@ -1300,7 +1306,7 @@ elif isosx: #print out1 reslst = out1.split('\n') cnt = len(reslst) - for j in xrange(cnt): + for j in range(cnt): resline = reslst[j] pp = resline.find('\"UUID\" = \"') if pp >= 0: @@ -1320,7 +1326,7 @@ elif isosx: out1, out2 = p.communicate() reslst = out1.split('\n') cnt = len(reslst) - for j in xrange(cnt): + for j in range(cnt): resline = reslst[j] pp = resline.find('Ethernet Address: ') if pp >= 0: @@ -1370,8 +1376,8 @@ elif isosx: # unprotect the new header blob in .kinf2011 # used in Kindle for Mac Version >= 1.9.0 def UnprotectHeaderData(encryptedData): - passwdData = 'header_key_data' - salt = 'HEADER.2011' + passwdData = b'header_key_data' + salt = b'HEADER.2011' iter = 0x80 keylen = 0x100 crp = LibCrypto() @@ -1482,7 +1488,7 @@ elif isosx: try: DB = {} items = data.split('/') - + # the headerblob is the encrypted information needed to build the entropy string headerblob = items.pop(0) encryptedValue = decode(headerblob, charMap1) @@ -1521,7 +1527,7 @@ elif isosx: # read and store in rcnt records of data # that make up the contents value edlst = [] - for i in xrange(rcnt): + for i in range(rcnt): item = items.pop(0) edlst.append(item) @@ -1544,7 +1550,7 @@ elif isosx: # (in other words split 'about' 2/3rds of the way through) # move first offsets chars to end to align for decode by testMap8 - encdata = ''.join(edlst) + encdata = b''.join(edlst) contlen = len(encdata) # now properly split and recombine @@ -1569,11 +1575,11 @@ elif isosx: pass if len(DB)>6: # store values used in decryption - print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName()) + print(u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName())) DB['IDString'] = IDString DB['UserName'] = GetUserName() else: - print u"Couldn't decrypt file." + print(u"Couldn't decrypt file.") DB = {} return DB else: @@ -1590,7 +1596,12 @@ def kindlekeys(files = []): if key: # convert all values to hex, just in case. for keyname in key: - key[keyname]=key[keyname].encode('hex') + v = key[keyname] + if isinstance(v, str): + v = v.encode('ascii').hex() + else: + v = v.hex() + key[keyname]=v keys.append(key) return keys @@ -1601,9 +1612,9 @@ def getkey(outpath, files=[]): if len(keys) > 0: if not os.path.isdir(outpath): outfile = outpath - with file(outfile, 'w') as keyfileout: + with open(outfile, 'w') as keyfileout: keyfileout.write(json.dumps(keys[0])) - print u"Saved a key to {0}".format(outfile) + print(u"Saved a key to {0}".format(outfile)) else: keycount = 0 for key in keys: @@ -1612,31 +1623,31 @@ def getkey(outpath, files=[]): outfile = os.path.join(outpath,u"kindlekey{0:d}.k4i".format(keycount)) if not os.path.exists(outfile): break - with file(outfile, 'w') as keyfileout: + with open(outfile, 'w') as keyfileout: keyfileout.write(json.dumps(key)) - print u"Saved a key to {0}".format(outfile) + print(u"Saved a key to {0}".format(outfile)) return True return False def usage(progname): - print u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys." - print u"Keys are saved to the current directory, or a specified output directory." - print u"If a file name is passed instead of a directory, only the first key is saved, in that file." - print u"Usage:" - print u" {0:s} [-h] [-k ] []".format(progname) + print(u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys.") + print(u"Keys are saved to the current directory, or a specified output directory.") + print(u"If a file name is passed instead of a directory, only the first key is saved, in that file.") + print(u"Usage:") + print(u" {0:s} [-h] [-k ] []".format(progname)) def cli_main(): - sys.stdout=SafeUnbuffered(sys.stdout) - sys.stderr=SafeUnbuffered(sys.stderr) + # 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 © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__) + print(u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__)) try: opts, args = getopt.getopt(argv[1:], "hk:") - except getopt.GetoptError, err: - print u"Error in options or arguments: {0}".format(err.args[0]) + except getopt.GetoptError as err: + print(u"Error in options or arguments: {0}".format(err.args[0])) usage(progname) sys.exit(2) @@ -1665,7 +1676,7 @@ def cli_main(): outpath = os.path.realpath(os.path.normpath(outpath)) if not getkey(outpath, files): - print u"Could not retrieve Kindle for Mac/PC key." + print(u"Could not retrieve Kindle for Mac/PC key.") return 0 @@ -1709,7 +1720,7 @@ def gui_main(): keyfileout.write(json.dumps(key)) success = True tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) - except DrmException, e: + except DrmException as e: tkMessageBox.showerror(progname, u"Error: {0}".format(str(e))) except Exception: root.wm_state('normal') @@ -1722,6 +1733,7 @@ def gui_main(): return 0 if __name__ == '__main__': + print(1) if len(sys.argv) > 1: sys.exit(cli_main()) sys.exit(gui_main())