X9.24-3-2017-Python-Source-20180129-1
X9.24-3-2017-Python-Source-20180129-1
24-3-2017
Python Source Code—
American National Standards, Technical Reports and Guides developed through the
Accredited Standards Committee X9, Inc., are copyrighted. Copying these documents for
personal or commercial use outside X9 membership agreements is prohibited without
express written permission of the Accredited Standards Committee X9, Inc. For additional
information please contact ASC X9, Inc., 275 West Street Suite 107, Annapolis, Maryland
21401 USA.
This page intentionally left blank
Contents Page
1 Scope ......................................................................................................................................................1
2 Python Reference Implementation.......................................................................................................1
2.1 General ....................................................................................................................................................1
2.2 Test Vectors ...........................................................................................................................................1
2.3 Debugging Trace (Host Algorithm) ....................................................................................................14
2.4 Debugging Trace (Terminal Algorithm) .............................................................................................22
ASC X9 Supplement to ANSI X9.24-3-2017
1 Scope
This document is a supplement to ANSI X9.24-3-2017 and describes a set of source code that can be used as a
reference implementation of the AES DUKPT algorithm and to support validation of an implementation of the AES
DUKPT algorithm on a transaction-originating SCD or a receiving SCD. AES DUKPT is used to derive
transaction key(s) from an initial terminal DUKPT key based on the transaction number. Keys that can be derived
include symmetric encryption/decryption keys, authentication keys, and HMAC (keyed hash message
authentication code) keys. AES DUKPT supports the derivation of AES-128, AES-192, AES-256, double length
TDEA, and triple length TDEA keys from AES-128, AES-192, and AES-256 initial keys.
While the included source code contains a reference implementation of the AES DUKPT algorithm, in no way
should the included source code be considered an implementation of the entirety of the requirements of the ANSI
X9.24 Part 3 standard. Care must be taken to follow all requirements when deploying a complete implementation
of the standard.
The included source code contains no warranty or guarantees and is considered open source.
2.1 General
This Annex gives the Python source code that was used to generate the test vectors in Annex B. In the event
that it disagrees with the pseudo code in the main body of the standard, the text in the main body of the standard
is considered normative.
It was developed using Python 3.4 and PyCrypto version 2.6.1, but should work with any version of Python 3.
Information about PyCrypto can be found at https://pypi.python.org/pypi/pycrypto.
The original Python source files for this AES DUKPT reference implementation can be found at
http://x9.org/standards/x9-24-part-3-test-vectors/, and it is recommended that the Python files be used rather than
trying to copy the python source out of this document.
# Print out a debug message at depth d. Takes an arbitrary list of strings and byte
lists or bytearrays.
# Lists of bytes are pretty-printed in hex.
def D(d, *args):
global Debug
if Debug:
print(".", end="")
for i in range(0,2*d):
print(" ", end="")
for x in args:
if type(x) is str:
print(x, " ", end="")
elif type(x) is list:
value = binascii.hexlify(bytearray(x)).decode("utf-8")
ToGroups(value)
elif type(x) is bytearray:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
elif type(x) is bytes:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
else:
print(x, " ", end="")
print("")
def P(*args):
for x in args:
if type(x) is str:
print(x, " ", end="")
elif type(x) is list:
value = binascii.hexlify(bytearray(x)).decode("utf-8")
ToGroups(value)
elif type(x) is bytearray:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
elif type(x) is bytes:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
else:
print(x, " ", end="")
print("")
# B.3.1. Enumerations
class DerivationPurpose(Enum):
_InitialKey = 0
_DerivationOrWorkingKey = 1
class KeyType(Enum):
_2TDEA = 0
_3TDEA = 1
_AES128 = 2
_AES192 = 3
_AES256 = 4
class KeyUsage(Enum):
_KeyEncryptionKey = 0x0002
_PINEncryption = 0x1000
_MessageAuthenticationGeneration = 0x2000
_MessageAuthenticationVerification = 0x2001
_MessageAuthenticationBothWays = 0x2002
_DataEncryptionEncrypt = 0x3000
_DataEncryptionDecrypt = 0x3001
_DataEncryptionBothWays = 0x3002
_KeyDerivation = 0x8000
_KeyDerivationInitialKey = 9
# Count the number of 1 bits in a counter value. Readable, but not efficient.
def Count_One_Bits(x):
bits = 0
mask = 1 << (NUMREG-1)
while mask > 0:
if x & mask:
bits = bits + 1
mask = mask >> 1
return bits
result = bytearray(n*16)
for i in range(1, n+1):
derivationData[1] = i
result[(i-1)*16:i*16] = AES_Encrypt_ECB(d+2, derivationKey, derivationData)
derivedKey = result[0:(L//8)]
D(d+1, "derivedKey:", derivedKey)
return derivedKey
if (keyUsage == KeyUsage._KeyEncryptionKey):
derivationData[2:4] = [0,2]
elif (keyUsage == KeyUsage._PINEncryption):
derivationData[2:4] = [16,0]
elif (keyUsage == KeyUsage._MessageAuthenticationGeneration):
derivationData[2:4] = [32,0]
elif (keyUsage == KeyUsage._MessageAuthenticationVerification):
derivationData[2:4] = [32,1]
elif (keyUsage == KeyUsage._MessageAuthenticationBothWays):
derivationData[2:4] = [32,2]
elif (keyUsage == KeyUsage._DataEncryptionEncrypt):
derivationData[2:4] = [48,0]
elif (keyUsage == KeyUsage._DataEncryptionDecrypt):
derivationData[2:4] = [48,1]
elif (keyUsage == KeyUsage._DataEncryptionBothWays):
derivationData[2:4] = [48,2]
elif (keyUsage == KeyUsage._KeyDerivation):
derivationData[2:4] = [128,0]
elif (keyUsage == KeyUsage._KeyDerivationInitialKey):
derivationData[2:4] = [128,1]
else:
assert False
if (derivedKeyType == KeyType._2TDEA):
derivationData[4:6] = [0,0]
elif (derivedKeyType == KeyType._3TDEA):
derivationData[4:6] = [0,1]
elif (derivedKeyType == KeyType._AES128):
derivationData[4:6] = [0,2]
elif (derivedKeyType == KeyType._AES192):
derivationData[4:6] = [0,3]
elif (derivedKeyType == KeyType._AES256):
derivationData[4:6] = [0,4]
else:
assert False
if (derivedKeyType == KeyType._2TDEA):
derivationData[6:8] = [0,128]
elif (derivedKeyType == KeyType._3TDEA):
derivationData[6:8] = [0,192]
elif (derivedKeyType == KeyType._AES128):
derivationData[6:8] = [0,128]
elif (derivedKeyType == KeyType._AES192):
derivationData[6:8] = [0,192]
elif (derivedKeyType == KeyType._AES256):
derivationData[6:8] = [1,0]
else:
assert False
if (derivationPurpose == DerivationPurpose._InitialKey):
derivationData[8:16] = initialKeyID[0:8]
elif (derivationPurpose == DerivationPurpose._DerivationOrWorkingKey):
derivationData[8:12] = initialKeyID[4:8]
derivationData[12:16] = IntToBytes(counter)
else:
assert False
return derivationData
return initialKey
mask = 0x80000000
workingCounter = 0
derivationKey = initialKey
gIntermediateDerivationKeyRegister = [None]*NUMREG
gIntermediateDerivationKeyInUse = [False]*NUMREG
gIntermediateDerivationKeyRegister[0] = initialKey
gIntermediateDerivationKeyInUse[0] = True
gDeviceID = initialKeyID
gCounter = 0
gShiftRegister = 1
gCurrentKey = 0
gDeriveKeyType = deriveKeyType
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey,
KeyUsage._KeyEncryptingKey, initialKeyType, gDeviceID, gCounter)
D(d+1, "derivationData:", derivationData)
keyEncryptionKey = DeriveKey(d+2, FutureKeyRegister[CurrentKey], initialKeyType,
derivationData)
D(d, "keyEncryptionKey:", keyEncryptionKey)
n = (Key_Length(initialKeyType)+127)//128
for i in range(1,n):
newInitialKey[(i-1)*16:i*16] = AES_Decrypt_ECB(d+1, keyEncryptionKey,
encryptedInitialKey[(i-1)*16:i*16])
return True
Set_Shift_Register(d+1)
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey, workingKeyUsage,
workingKeyType, gDeviceID, gCounter)
D(d+1, "derivationData:", derivationData)
assert gIntermediateDerivationKeyInUse[gCurrentKey]
workingKey = Derive_Key(d+2, gIntermediateDerivationKeyRegister[gCurrentKey],
workingKeyType, derivationData, gDeriveKeyType)
Update_State_for_next_Transaction(d+2)
return workingKey
D(d, "Update_State_for_next_Transaction")
oneBits = Count_One_Bits(gCounter)
if oneBits <= MAX_WORK:
Update_Derivation_Keys(d+2, gCurrentKey, gDeriveKeyType)
gIntermediateDerivationKeyRegister[gCurrentKey] = 0
gIntermediateDerivationKeyInUse[gCurrentKey] = False
gCounter = gCounter + 1
else:
gIntermediateDerivationKeyRegister[gCurrentKey] = 0
gIntermediateDerivationKeyInUse[gCurrentKey] = False
gCounter = gCounter + gShiftRegister
D(d+1, "gCounter:", gCounter)
i = start
j = 1 << start
return True
gShiftRegister = 1
gCurrentKey = 0
if gCounter == 0:
D(d, "Set_Shift_Register -> gShiftRegister:", gShiftRegister, "gCurrentKey:",
gCurrentKey)
return True
# Generate the test vectors for a particular BDK, initial key ID, and transaction
key type
def GenerateTestVectors(bdk, bk, initialKeyID, k):
P("")
P("Test Vectors for generating ", k, " from ", bk, " Base Derivation Key")
initialKey = Derive_Initial_Key(0, bdk, bk, initialKeyID)
P("")
P("Initial Key:")
P(initialKey)
P("")
Load_Initial_Key(0, initialKey, bk, initialKeyID )
P("")
if i == max:
P("Derivation Key:")
P(keyKEK[0])
P("Derivation Data:")
P(keyKEK[1])
P("Key Encryption Key:")
P(keyKEK[2])
else:
P("Derivation Key:")
P(keyPIN[0])
P("PIN Encryption Derivation Data:")
P(keyPIN[1])
P("PIN Encryption Key:")
P(keyPIN[2])
P("MAC Derivation Data:")
P(keyMAC[1])
P("Message Authentication, Generation:")
P(keyMAC[2])
P("Encryption Derivation Data:")
P(keyDEE[1])
P("Data Encryption, Encrypt:")
P(keyDEE[2])
P("")
P(" All Key Usages for Transaction ",i," (AES-128 under AES-128 BDK)")
initialKey = Derive_Initial_Key(0, bdk, bk, initialKeyID)
P("")
P(" Initial Key:")
P(initialKey)
P("")
Load_Initial_Key(0, initialKey, bk, initialKeyID )
P("")
P("Derivation Key:")
P(keyKEK[0])
P("Key Encryption Key Derivation Data:")
P(keyKEK[1])
P("Key Encryption Key:")
P(keyKEK[2])
P("")
P("PIN Encryption Derivation Data:")
P(keyPIN[1])
P("PIN Encryption Key:")
P(keyPIN[2])
P("")
P("MAC Generation Derivation Data:")
P(keyMACG[1])
P("Message Auth, Generation:")
P(keyMACG[2])
P("")
P("MAC Verification Derivation Data:")
P(keyMACV[1])
P("Message Auth, Verification:")
P(keyMACV[2])
P("")
P("MAC Both Ways Derivation Data:")
P(keyMACB[1])
P("")
P("Calculation of AES PIN Block (Format 4)")
P("")
P("PAN = 4111111111111111")
P("PIN = 1234")
P("Random Number = 2F69ADDE2E9E7ACE")
P("")
P("Plaintext PIN field:")
P("441234AA AAAAAAAA 2F69ADDE 2E9E7ACE")
P("Plaintext PAN field:")
P("44111111 11111111 10000000 00000000")
P("PIN Encryption Key:")
P(keyPIN[2])
pinField = [ 0x44, 0x12, 0x34, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x2F, 0x69, 0xAD,
0xDE, 0x2E, 0x9E, 0x7A, 0xCE ]
panField = [ 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00 ]
blockA = AES_Encrypt_ECB(0, keyPIN[2], pinField)
blockB = XOR(blockA, panField)
pinBlock = AES_Encrypt_ECB(0, keyPIN[2], blockB)
P("")
P("Intermediate Block A:")
P(blockA)
P("Intermediate Block B:")
P(blockB)
P("Encrypted PIN Block:")
P(pinBlock)
Debug = False
NUMREG = 32
MAX_WORK = 16
bdk = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1 ]
bdk192 = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 ]
bdk256 = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1]
P("BDK-128:")
P(bdk)
P("")
P("BDK-256:")
P(bdk256)
P("")
P("InitialKeyID:")
P(initialKeyID)
P("")
P("Derivation Data:")
P(derivationData)
P("")
P("Initial Key:")
P(initialKey)
P("")
P("")
# Print out a debug message at depth d. Takes an arbitrary list of strings and byte
lists or bytearrays.
# Lists of bytes are pretty-printed in hex.
def D(d, *args):
for i in range(0,2*d):
print(" ", end="")
for x in args:
if type(x) is str:
print(x, " ", end="")
elif type(x) is list:
value = binascii.hexlify(bytearray(x)).decode("utf-8")
ToGroups(value)
elif type(x) is bytearray:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
elif type(x) is bytes:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
else:
print(x, " ", end="")
print("")
# B.3.1. Enumerations
class DerivationPurpose(Enum):
_InitialKey = 0
_DerivationOrWorkingKey = 1
class KeyType(Enum):
_2TDEA = 0
_3TDEA = 1
_AES128 = 2
_AES192 = 3
_AES256 = 4
class KeyUsage(Enum):
_KeyEncryptionKey = 0x0002
_PINEncryption = 0x1000
_MessageAuthenticationGeneration = 0x2000
_MessageAuthenticationVerification = 0x2001
_MessageAuthenticationBothWays = 0x2002
_DataEncryptionEncrypt = 0x3000
_DataEncryptionDecrypt = 0x3001
_DataEncryptionBothWays = 0x3002
_KeyDerivation = 0x8000
_KeyDerivationInitialKey = 9
# Count the number of 1 bits in a counter value. Readable, but not efficient.
def Count_One_Bits(x):
bits = 0
mask = 1 << (NUMREG-1)
while mask > 0:
if x & mask:
bits = bits + 1
mask = mask >> 1
return bits
result = bytearray(n*16)
for i in range(1, n+1):
derivationData[1] = i
result[(i-1)*16:i*16] = AES_Encrypt_ECB(d+2, derivationKey, derivationData)
derivedKey = result[0:(L//8)]
D(d+1, "derivedKey:", derivedKey)
return derivedKey
if (keyUsage == KeyUsage._KeyEncryptionKey):
derivationData[2:4] = [0,2]
elif (keyUsage == KeyUsage._PINEncryption):
derivationData[2:4] = [16,0]
elif (keyUsage == KeyUsage._MessageAuthenticationGeneration):
derivationData[2:4] = [32,0]
elif (keyUsage == KeyUsage._MessageAuthenticationVerification):
derivationData[2:4] = [32,1]
elif (keyUsage == KeyUsage._MessageAuthenticationBothWays):
derivationData[2:4] = [32,2]
elif (keyUsage == KeyUsage._DataEncryptionEncrypt):
derivationData[2:4] = [48,0]
elif (keyUsage == KeyUsage._DataEncryptionDecrypt):
derivationData[2:4] = [48,1]
elif (keyUsage == KeyUsage._DataEncryptionBothWays):
derivationData[2:4] = [48,2]
elif (keyUsage == KeyUsage._KeyDerivation):
derivationData[2:4] = [128,0]
elif (keyUsage == KeyUsage._KeyDerivationInitialKey):
derivationData[2:4] = [128,1]
else:
assert False
if (derivedKeyType == KeyType._2TDEA):
derivationData[4:6] = [0,0]
elif (derivedKeyType == KeyType._3TDEA):
derivationData[4:6] = [0,1]
elif (derivedKeyType == KeyType._AES128):
derivationData[4:6] = [0,2]
elif (derivedKeyType == KeyType._AES192):
derivationData[4:6] = [0,3]
elif (derivedKeyType == KeyType._AES256):
derivationData[4:6] = [0,4]
else:
assert False
if (derivedKeyType == KeyType._2TDEA):
derivationData[6:8] = [0,128]
elif (derivedKeyType == KeyType._3TDEA):
derivationData[6:8] = [0,192]
elif (derivedKeyType == KeyType._AES128):
derivationData[6:8] = [0,128]
elif (derivedKeyType == KeyType._AES192):
derivationData[6:8] = [0,192]
elif (derivedKeyType == KeyType._AES256):
derivationData[6:8] = [1,0]
else:
assert False
if (derivationPurpose == DerivationPurpose._InitialKey):
derivationData[8:16] = initialKeyID[0:8]
elif (derivationPurpose == DerivationPurpose._DerivationOrWorkingKey):
derivationData[8:12] = initialKeyID[4:8]
derivationData[12:16] = IntToBytes(counter)
else:
assert False
return derivationData
return initialKey
mask = 0x80000000
workingCounter = 0
derivationKey = initialKey
gIntermediateDerivationKeyRegister = [None]*NUMREG
gIntermediateDerivationKeyInUse = [False]*NUMREG
gIntermediateDerivationKeyRegister[0] = initialKey
gIntermediateDerivationKeyInUse[0] = True
gDeviceID = initialKeyID
gCounter = 0
gShiftRegister = 1
gCurrentKey = 0
gDeriveKeyType = deriveKeyType
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey,
KeyUsage._KeyEncryptingKey, initialKeyType, gDeviceID, gCounter)
D(d+1, "derivationData:", derivationData)
keyEncryptionKey = DeriveKey(d+2, FutureKeyRegister[CurrentKey], initialKeyType,
derivationData)
n = (Key_Length(initialKeyType)+127)//128
for i in range(1,n):
newInitialKey[(i-1)*16:i*16] = AES_Decrypt_ECB(d+1, keyEncryptionKey,
encryptedInitialKey[(i-1)*16:i*16])
return True
Set_Shift_Register(d+1)
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey, workingKeyUsage,
workingKeyType, gDeviceID, gCounter)
D(d+1, "derivationData:", derivationData)
assert gIntermediateDerivationKeyInUse[gCurrentKey]
workingKey = Derive_Key(d+2, gIntermediateDerivationKeyRegister[gCurrentKey],
workingKeyType, derivationData, gDeriveKeyType)
D(d+1, "workingKey:", workingKey)
Update_State_for_next_Transaction(d+2)
return workingKey
global MAX_WORK
global gIntermediateDerivationKeyRegister
global gIntermediateDerivationKeyInUse
global gCurrentKey
global gDeviceID
global gCounter
global gShiftRegister
global gDeriveKeyType
D(d, "Update_State_for_next_Transaction")
oneBits = Count_One_Bits(gCounter)
if oneBits <= MAX_WORK:
Update_Derivation_Keys(d+2, gCurrentKey, gDeriveKeyType)
gIntermediateDerivationKeyRegister[gCurrentKey] = 0
gIntermediateDerivationKeyInUse[gCurrentKey] = False
gCounter = gCounter + 1
else:
gIntermediateDerivationKeyRegister[gCurrentKey] = 0
gIntermediateDerivationKeyInUse[gCurrentKey] = False
gCounter = gCounter + gShiftRegister
D(d+1, "gCounter:", gCounter)
i = start
j = 1 << start
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey,
KeyUsage._KeyDerivation, deriveKeyType, gDeviceID, gCounter | j)
D(d+1, "derivationData:", derivationData)
assert gIntermediateDerivationKeyInUse[gCurrentKey]
gIntermediateDerivationKeyRegister[i] = Derive_Key(d+1, baseKey,
deriveKeyType, derivationData, deriveKeyType)
gIntermediateDerivationKeyInUse[i] = True
j = j >> 1
i = i - 1
return True
gShiftRegister = 1
gCurrentKey = 0
if gCounter == 0:
D(d, "Set_Shift_Register -> gShiftRegister:", gShiftRegister, "gCurrentKey:",
gCurrentKey)
return True
print("")
D(0, "PIN Encryption Key: ", keyPIN[2])
NUMREG = 32
MAX_WORK = 16
bdk = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1 ]
bdk192 = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 ]
bdk256 = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1]
# Print out a debug message at depth d. Takes an arbitrary list of strings and byte
lists or bytearrays.
# Lists of bytes are pretty-printed in hex.
def D(d, *args):
for i in range(0,2*d):
print(" ", end="")
for x in args:
if type(x) is str:
print(x, " ", end="")
elif type(x) is list:
value = binascii.hexlify(bytearray(x)).decode("utf-8")
ToGroups(value)
elif type(x) is bytearray:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
elif type(x) is bytes:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
else:
print(x, " ", end="")
print("")
# B.3.1. Enumerations
class DerivationPurpose(Enum):
_InitialKey = 0
_DerivationOrWorkingKey = 1
class KeyType(Enum):
_2TDEA = 0
_3TDEA = 1
_AES128 = 2
_AES192 = 3
_AES256 = 4
class KeyUsage(Enum):
_KeyEncryptionKey = 0x0002
_PINEncryption = 0x1000
_MessageAuthenticationGeneration = 0x2000
_MessageAuthenticationVerification = 0x2001
_MessageAuthenticationBothWays = 0x2002
_DataEncryptionEncrypt = 0x3000
_DataEncryptionDecrypt = 0x3001
_DataEncryptionBothWays = 0x3002
_KeyDerivation = 0x8000
_KeyDerivationInitialKey = 9
# Count the number of 1 bits in a counter value. Readable, but not efficient.
def Count_One_Bits(x):
bits = 0
mask = 1 << (NUMREG-1)
while mask > 0:
if x & mask:
bits = bits + 1
mask = mask >> 1
return bits
result = bytearray(n*16)
for i in range(1, n+1):
derivationData[1] = i
result[(i-1)*16:i*16] = AES_Encrypt_ECB(d+2, derivationKey, derivationData)
derivedKey = result[0:(L//8)]
D(d+1, "derivedKey:", derivedKey)
return derivedKey
if (keyUsage == KeyUsage._KeyEncryptionKey):
derivationData[2:4] = [0,2]
elif (keyUsage == KeyUsage._PINEncryption):
derivationData[2:4] = [16,0]
elif (keyUsage == KeyUsage._MessageAuthenticationGeneration):
derivationData[2:4] = [32,0]
elif (keyUsage == KeyUsage._MessageAuthenticationVerification):
derivationData[2:4] = [32,1]
elif (keyUsage == KeyUsage._MessageAuthenticationBothWays):
derivationData[2:4] = [32,2]
elif (keyUsage == KeyUsage._DataEncryptionEncrypt):
derivationData[2:4] = [48,0]
elif (keyUsage == KeyUsage._DataEncryptionDecrypt):
derivationData[2:4] = [48,1]
elif (keyUsage == KeyUsage._DataEncryptionBothWays):
derivationData[2:4] = [48,2]
elif (keyUsage == KeyUsage._KeyDerivation):
derivationData[2:4] = [128,0]
elif (keyUsage == KeyUsage._KeyDerivationInitialKey):
derivationData[2:4] = [128,1]
else:
assert False
if (derivedKeyType == KeyType._2TDEA):
derivationData[4:6] = [0,0]
elif (derivedKeyType == KeyType._3TDEA):
derivationData[4:6] = [0,1]
elif (derivedKeyType == KeyType._AES128):
derivationData[4:6] = [0,2]
elif (derivedKeyType == KeyType._AES192):
derivationData[4:6] = [0,3]
elif (derivedKeyType == KeyType._AES256):
derivationData[4:6] = [0,4]
else:
assert False
if (derivedKeyType == KeyType._2TDEA):
derivationData[6:8] = [0,128]
elif (derivedKeyType == KeyType._3TDEA):
derivationData[6:8] = [0,192]
elif (derivedKeyType == KeyType._AES128):
derivationData[6:8] = [0,128]
elif (derivedKeyType == KeyType._AES192):
derivationData[6:8] = [0,192]
elif (derivedKeyType == KeyType._AES256):
derivationData[6:8] = [1,0]
else:
assert False
if (derivationPurpose == DerivationPurpose._InitialKey):
derivationData[8:16] = initialKeyID[0:8]
elif (derivationPurpose == DerivationPurpose._DerivationOrWorkingKey):
derivationData[8:12] = initialKeyID[4:8]
derivationData[12:16] = IntToBytes(counter)
else:
assert False
return derivationData
return initialKey
mask = 0x80000000
workingCounter = 0
derivationKey = initialKey
gIntermediateDerivationKeyRegister = [None]*NUMREG
gIntermediateDerivationKeyInUse = [False]*NUMREG
gIntermediateDerivationKeyRegister[0] = initialKey
D(d+1, "gIntermediateDerivationKeyRegister[0] <-", initialKey)
gIntermediateDerivationKeyInUse[0] = True
gDeviceID = initialKeyID
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey,
KeyUsage._KeyEncryptingKey, initialKeyType, gDeviceID, gCounter)
D(d+1, "derivationData:", derivationData)
keyEncryptionKey = DeriveKey(d+2, FutureKeyRegister[CurrentKey], initialKeyType,
derivationData)
D(d, "keyEncryptionKey:", keyEncryptionKey)
n = (Key_Length(initialKeyType)+127)//128
for i in range(1,n):
newInitialKey[(i-1)*16:i*16] = AES_Decrypt_ECB(d+1, keyEncryptionKey,
encryptedInitialKey[(i-1)*16:i*16])
return True
Set_Shift_Register(d+1)
derivationData =
Create_Derivation_Data(DerivationPurpose._DerivationOrWorkingKey, workingKeyUsage,
workingKeyType, gDeviceID, gCounter)
D(d+1, "derivationData:", derivationData)
assert gIntermediateDerivationKeyInUse[gCurrentKey]
workingKey = Derive_Key(d+2, gIntermediateDerivationKeyRegister[gCurrentKey],
workingKeyType, derivationData, gDeriveKeyType)
D(d+1, "workingKey:", workingKey)
Update_State_for_next_Transaction(d+2)
return workingKey
D(d, "Update_State_for_next_Transaction()")
oneBits = Count_One_Bits(gCounter)
if oneBits <= MAX_WORK:
Update_Derivation_Keys(d+2, gCurrentKey, gDeriveKeyType)
gIntermediateDerivationKeyRegister[gCurrentKey] = 0
D(d+1, "gIntermediateDerivationKeyRegister[", gCurrentKey, "] <-", 0, ")")
gIntermediateDerivationKeyInUse[gCurrentKey] = False
gCounter = gCounter + 1
else:
gIntermediateDerivationKeyRegister[gCurrentKey] = 0
D(d+1, "gIntermediateDerivationKeyRegister[", gCurrentKey, "] <-", 0, ")")
gIntermediateDerivationKeyInUse[gCurrentKey] = False
gCounter = gCounter + gShiftRegister
D(d+1, "gCounter <-", gCounter)
CeaseOperation()
return False
else:
return True
i = start
j = 1 << start
return True
global gShiftRegister
gShiftRegister = 1
gCurrentKey = 0
if gCounter == 0:
D(d, "Set_Shift_Register -> gShiftRegister:", gShiftRegister, "gCurrentKey:",
gCurrentKey)
return True
print("")
D(0, "PIN Encryption Key: ", keyPIN)
NUMREG = 32
MAX_WORK = 16
bdk = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1 ]
bdk192 = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 ]
bdk256 = [ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xF1, 0xF1]