|
|
@ -2,7 +2,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
Comprehensive Mazama Book DRM with Topaz Cryptography V1.0
|
|
|
|
Comprehensive Mazama Book DRM with Topaz Cryptography V1.1
|
|
|
|
|
|
|
|
|
|
|
|
-----BEGIN PUBLIC KEY-----
|
|
|
|
-----BEGIN PUBLIC KEY-----
|
|
|
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdBHJ4CNc6DNFCw4MRCw4SWAK6
|
|
|
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdBHJ4CNc6DNFCw4MRCw4SWAK6
|
|
|
@ -142,7 +142,7 @@ def MD5(message):
|
|
|
|
return ctx.digest()
|
|
|
|
return ctx.digest()
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Returns the SHA1 digest of "message"
|
|
|
|
# Returns the MD5 digest of "message"
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
def SHA1(message):
|
|
|
|
def SHA1(message):
|
|
|
@ -229,7 +229,7 @@ def findNameForHash(hash):
|
|
|
|
if hash == encodeHash(name, charMap2):
|
|
|
|
if hash == encodeHash(name, charMap2):
|
|
|
|
result = name
|
|
|
|
result = name
|
|
|
|
break
|
|
|
|
break
|
|
|
|
return result
|
|
|
|
return name
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Print all the records from the kindle.info file (option -i)
|
|
|
|
# Print all the records from the kindle.info file (option -i)
|
|
|
@ -297,7 +297,7 @@ def bookReadString():
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
def bookReadHeaderRecordData():
|
|
|
|
def bookReadHeaderRecordData():
|
|
|
|
nbValues = ord(bookFile.read(1))
|
|
|
|
nbValues = bookReadEncodedNumber()
|
|
|
|
values = []
|
|
|
|
values = []
|
|
|
|
for i in range (0,nbValues):
|
|
|
|
for i in range (0,nbValues):
|
|
|
|
values.append([bookReadEncodedNumber(),bookReadEncodedNumber(),bookReadEncodedNumber()])
|
|
|
|
values.append([bookReadEncodedNumber(),bookReadEncodedNumber(),bookReadEncodedNumber()])
|
|
|
@ -327,7 +327,7 @@ def parseTopazHeader():
|
|
|
|
if magic != 'TPZ0':
|
|
|
|
if magic != 'TPZ0':
|
|
|
|
raise CMBDTCFatal("Parse Error : Invalid Header, not a Topaz file")
|
|
|
|
raise CMBDTCFatal("Parse Error : Invalid Header, not a Topaz file")
|
|
|
|
|
|
|
|
|
|
|
|
nbRecords = ord(bookFile.read(1))
|
|
|
|
nbRecords = bookReadEncodedNumber()
|
|
|
|
bookHeaderRecords = {}
|
|
|
|
bookHeaderRecords = {}
|
|
|
|
|
|
|
|
|
|
|
|
for i in range (0,nbRecords):
|
|
|
|
for i in range (0,nbRecords):
|
|
|
@ -591,10 +591,11 @@ def generateDevicePID(table,dsn,nbRoll):
|
|
|
|
def usage():
|
|
|
|
def usage():
|
|
|
|
print("\nUsage:")
|
|
|
|
print("\nUsage:")
|
|
|
|
print("\nCMBDTC.py [options] bookFileName\n")
|
|
|
|
print("\nCMBDTC.py [options] bookFileName\n")
|
|
|
|
print("-r prints a record indicated in the form name:index (e.g \"img:0\")")
|
|
|
|
print("-p Adds a PID to the list of PIDs that are tried to decrypt the book key (can be used several times)")
|
|
|
|
|
|
|
|
print("-r Prints or writes to disk a record indicated in the form name:index (e.g \"img:0\")")
|
|
|
|
print("-o Output file name to write records")
|
|
|
|
print("-o Output file name to write records")
|
|
|
|
print("-v verbose (can be used several times)")
|
|
|
|
print("-v Verbose (can be used several times)")
|
|
|
|
print("-i print kindle.info database")
|
|
|
|
print("-i Print kindle.info database")
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Main
|
|
|
|
# Main
|
|
|
@ -612,9 +613,12 @@ def main(argv=sys.argv):
|
|
|
|
recordName = ""
|
|
|
|
recordName = ""
|
|
|
|
recordIndex = 0
|
|
|
|
recordIndex = 0
|
|
|
|
outputFile = ""
|
|
|
|
outputFile = ""
|
|
|
|
|
|
|
|
PIDs = []
|
|
|
|
|
|
|
|
kindleDatabase = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
opts, args = getopt.getopt(sys.argv[1:], "vir:o:")
|
|
|
|
opts, args = getopt.getopt(sys.argv[1:], "vir:o:p:")
|
|
|
|
except getopt.GetoptError, err:
|
|
|
|
except getopt.GetoptError, err:
|
|
|
|
# print help information and exit:
|
|
|
|
# print help information and exit:
|
|
|
|
print str(err) # will print something like "option -a not recognized"
|
|
|
|
print str(err) # will print something like "option -a not recognized"
|
|
|
@ -634,13 +638,20 @@ def main(argv=sys.argv):
|
|
|
|
outputFile = a
|
|
|
|
outputFile = a
|
|
|
|
if o =="-r":
|
|
|
|
if o =="-r":
|
|
|
|
recordName,recordIndex = a.split(':')
|
|
|
|
recordName,recordIndex = a.split(':')
|
|
|
|
|
|
|
|
if o =="-p":
|
|
|
|
|
|
|
|
PIDs.append(a)
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Read the encrypted database
|
|
|
|
# Read the encrypted database
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
kindleDatabase = parseKindleInfo()
|
|
|
|
kindleDatabase = parseKindleInfo()
|
|
|
|
|
|
|
|
except Exception as message:
|
|
|
|
|
|
|
|
if verbose>0:
|
|
|
|
|
|
|
|
print(message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if kindleDatabase != None :
|
|
|
|
if printInfo:
|
|
|
|
if printInfo:
|
|
|
|
printKindleInfo()
|
|
|
|
printKindleInfo()
|
|
|
|
|
|
|
|
|
|
|
@ -659,6 +670,7 @@ def main(argv=sys.argv):
|
|
|
|
|
|
|
|
|
|
|
|
# concat, hash and encode
|
|
|
|
# concat, hash and encode
|
|
|
|
DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
|
|
|
|
DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
|
|
|
|
|
|
|
|
|
|
|
|
if verbose >1:
|
|
|
|
if verbose >1:
|
|
|
|
print("DSN: " + DSN)
|
|
|
|
print("DSN: " + DSN)
|
|
|
|
|
|
|
|
|
|
|
@ -668,6 +680,7 @@ def main(argv=sys.argv):
|
|
|
|
|
|
|
|
|
|
|
|
table = generatePidEncryptionTable()
|
|
|
|
table = generatePidEncryptionTable()
|
|
|
|
devicePID = generateDevicePID(table,DSN,4)
|
|
|
|
devicePID = generateDevicePID(table,DSN,4)
|
|
|
|
|
|
|
|
PIDs.append(devicePID)
|
|
|
|
|
|
|
|
|
|
|
|
if verbose > 0:
|
|
|
|
if verbose > 0:
|
|
|
|
print("Device PID: " + devicePID)
|
|
|
|
print("Device PID: " + devicePID)
|
|
|
@ -687,6 +700,8 @@ def main(argv=sys.argv):
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
# Get the account token
|
|
|
|
# Get the account token
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if kindleDatabase != None:
|
|
|
|
kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
|
|
|
|
kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
|
|
|
|
|
|
|
|
|
|
|
|
if verbose >1:
|
|
|
|
if verbose >1:
|
|
|
@ -697,10 +712,11 @@ def main(argv=sys.argv):
|
|
|
|
|
|
|
|
|
|
|
|
pidHash = SHA1(DSN+kindleAccountToken+keysRecord+keysRecordRecord)
|
|
|
|
pidHash = SHA1(DSN+kindleAccountToken+keysRecord+keysRecordRecord)
|
|
|
|
|
|
|
|
|
|
|
|
PID = encodePID(pidHash)
|
|
|
|
bookPID = encodePID(pidHash)
|
|
|
|
|
|
|
|
PIDs.append(bookPID)
|
|
|
|
|
|
|
|
|
|
|
|
if verbose > 0:
|
|
|
|
if verbose > 0:
|
|
|
|
print ("Book PID: " + PID )
|
|
|
|
print ("Book PID: " + bookPID )
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Decrypt book key
|
|
|
|
# Decrypt book key
|
|
|
@ -708,8 +724,15 @@ def main(argv=sys.argv):
|
|
|
|
|
|
|
|
|
|
|
|
dkey = getBookPayloadRecord('dkey', 0)
|
|
|
|
dkey = getBookPayloadRecord('dkey', 0)
|
|
|
|
|
|
|
|
|
|
|
|
bookKey = decryptDkeyRecords(dkey,PID)[0]
|
|
|
|
bookKeys = []
|
|
|
|
|
|
|
|
for PID in PIDs :
|
|
|
|
|
|
|
|
bookKeys+=decryptDkeyRecords(dkey,PID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(bookKeys) == 0 :
|
|
|
|
|
|
|
|
if verbose > 0 :
|
|
|
|
|
|
|
|
print ("Book key could not be found. Maybe this book is not registered with this device.")
|
|
|
|
|
|
|
|
else :
|
|
|
|
|
|
|
|
bookKey = bookKeys[0]
|
|
|
|
if verbose > 0:
|
|
|
|
if verbose > 0:
|
|
|
|
print("Book key: " + bookKey.encode('hex'))
|
|
|
|
print("Book key: " + bookKey.encode('hex'))
|
|
|
|
|
|
|
|
|
|
|
|