Starting on Version 7.0 using the work done by others. Completely untested. I will be testing things, but I thought I'd get this base version up for others to give pull requests.

THIS IS ON THE MASTER BRANCH. The Master branch will be Python 3.0 from now on. While Python 2.7 support will not be deliberately broken, all efforts should now focus on Python 3.0 compatibility.

I can see a lot of work has been done. There's more to do. I've bumped the version number of everything I came across to the next major number for Python 3.0 compatibility indication.

Thanks everyone. I hope to update here at least once a week until we have a stable 7.0 release for calibre 5.0
This commit is contained in:
Apprentice Harper
2020-09-26 21:22:47 +01:00
parent 4868a7460e
commit afa4ac5716
40 changed files with 757 additions and 729 deletions

View File

@@ -5,15 +5,17 @@ from __future__ import with_statement
from __future__ import print_function
# kgenpids.py
# Copyright © 2008-2017 Apprentice Harper et al.
# Copyright © 2008-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
__version__ = '2.1'
__version__ = '3.0'
# Revision history:
# 2.0 - Fix for non-ascii Windows user names
# 2.1 - Actual fix for non-ascii WIndows user names.
# x.x - Return information needed for KFX decryption
# 2.2 - Return information needed for KFX decryption
# 3.0 - Added Python 3 compatibility for calibre 5.0
import sys
import os, csv
@@ -31,9 +33,9 @@ global charMap3
global charMap4
charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
charMap3 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
charMap4 = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
charMap1 = b'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
charMap3 = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
charMap4 = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
# crypto digestroutines
import hashlib
@@ -84,7 +86,7 @@ def decode(data,map):
def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4
bitPosition = 6 - 2*(offset % 4)
return ord(bitField[byteNumber]) >> bitPosition & 3
return bitField[byteNumber] >> bitPosition & 3
# Returns the six bits at offset from a bit field
def getSixBitsFromBitField(bitField,offset):
@@ -95,9 +97,9 @@ def getSixBitsFromBitField(bitField,offset):
# 8 bits to six bits encoding from hash to generate PID string
def encodePID(hash):
global charMap3
PID = ''
PID = b''
for position in range (0,8):
PID += charMap3[getSixBitsFromBitField(hash,position)]
PID += bytes([charMap3[getSixBitsFromBitField(hash,position)]])
return PID
# Encryption table used to generate the device PID
@@ -126,7 +128,7 @@ def generatePidSeed(table,dsn) :
def generateDevicePID(table,dsn,nbRoll):
global charMap4
seed = generatePidSeed(table,dsn)
pidAscii = ''
pidAscii = b''
pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
index = 0
for counter in range (0,nbRoll):
@@ -134,7 +136,7 @@ def generateDevicePID(table,dsn,nbRoll):
index = (index+1) %8
for counter in range (0,8):
index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
pidAscii += charMap4[index]
pidAscii += bytes([charMap4[index]])
return pidAscii
def crc32(s):
@@ -150,7 +152,7 @@ def checksumPid(s):
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += charMap4[pos%l]
res += bytes([charMap4[pos%l]])
crc >>= 8
return res
@@ -160,15 +162,15 @@ def pidFromSerial(s, l):
global charMap4
crc = crc32(s)
arr1 = [0]*l
for i in xrange(len(s)):
arr1[i%l] ^= ord(s[i])
for i in range(len(s)):
arr1[i%l] ^= s[i]
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
for i in xrange(l):
for i in range(l):
arr1[i] ^= crc_bytes[i&3]
pid = ""
for i in xrange(l):
pid = b""
for i in range(l):
b = arr1[i] & 0xff
pid+=charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
pid += bytes([charMap4[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]])
return pid
@@ -179,7 +181,7 @@ def getKindlePids(rec209, token, serialnum):
pids=[]
if isinstance(serialnum,unicode):
if isinstance(serialnum,str):
serialnum = serialnum.encode('utf-8')
# Compute book PID
@@ -189,7 +191,7 @@ def getKindlePids(rec209, token, serialnum):
pids.append(bookPID)
# compute fixed pid for old pre 2.5 firmware update pid as well
kindlePID = pidFromSerial(serialnum, 7) + "*"
kindlePID = pidFromSerial(serialnum, 7) + b"*"
kindlePID = checksumPid(kindlePID)
pids.append(kindlePID)
@@ -206,7 +208,7 @@ def getK4Pids(rec209, token, kindleDatabase):
try:
# Get the kindle account token, if present
kindleAccountToken = (kindleDatabase[1])['kindle.account.tokens'].decode('hex')
kindleAccountToken = bytearray.fromhex((kindleDatabase[1])['kindle.account.tokens']).decode()
except KeyError:
kindleAccountToken=""
@@ -214,30 +216,30 @@ def getK4Pids(rec209, token, kindleDatabase):
try:
# Get the DSN token, if present
DSN = (kindleDatabase[1])['DSN'].decode('hex')
DSN = bytearray.fromhex((kindleDatabase[1])['DSN']).decode()
print(u"Got DSN key from database {0}".format(kindleDatabase[0]))
except KeyError:
# See if we have the info to generate the DSN
try:
# Get the Mazama Random number
MazamaRandomNumber = (kindleDatabase[1])['MazamaRandomNumber'].decode('hex')
MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode()
#print u"Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
try:
# Get the SerialNumber token, if present
IDString = (kindleDatabase[1])['SerialNumber'].decode('hex')
IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode()
print(u"Got SerialNumber from database {0}".format(kindleDatabase[0]))
except KeyError:
# Get the IDString we added
IDString = (kindleDatabase[1])['IDString'].decode('hex')
IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode()
try:
# Get the UsernameHash token, if present
encodedUsername = (kindleDatabase[1])['UsernameHash'].decode('hex')
encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode()
print(u"Got UsernameHash from database {0}".format(kindleDatabase[0]))
except KeyError:
# Get the UserName we added
UserName = (kindleDatabase[1])['UserName'].decode('hex')
UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode()
# encode it
encodedUsername = encodeHash(UserName,charMap1)
#print u"encodedUsername",encodedUsername.encode('hex')
@@ -267,19 +269,19 @@ def getK4Pids(rec209, token, kindleDatabase):
# Compute book PIDs
# book pid
pidHash = SHA1(DSN+kindleAccountToken+rec209+token)
pidHash = SHA1(DSN.encode()+kindleAccountToken.encode()+rec209+token)
bookPID = encodePID(pidHash)
bookPID = checksumPid(bookPID)
pids.append(bookPID)
# variant 1
pidHash = SHA1(kindleAccountToken+rec209+token)
pidHash = SHA1(kindleAccountToken.encode()+rec209+token)
bookPID = encodePID(pidHash)
bookPID = checksumPid(bookPID)
pids.append(bookPID)
# variant 2
pidHash = SHA1(DSN+rec209+token)
pidHash = SHA1(DSN.encode()+rec209+token)
bookPID = encodePID(pidHash)
bookPID = checksumPid(bookPID)
pids.append(bookPID)
@@ -297,14 +299,14 @@ def getPidList(md1, md2, serials=[], kDatabases=[]):
for kDatabase in kDatabases:
try:
pidlst.extend(getK4Pids(md1, md2, kDatabase))
except Exception, e:
except Exception as e:
print(u"Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
traceback.print_exc()
for serialnum in serials:
try:
pidlst.extend(getKindlePids(md1, md2, serialnum))
except Exception, e:
except Exception as e:
print(u"Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
traceback.print_exc()