diff --git a/bin/smartpgp-cli b/bin/smartpgp-cli index e06fe2c..dec8254 100755 --- a/bin/smartpgp-cli +++ b/bin/smartpgp-cli @@ -50,6 +50,7 @@ VALID_COMMANDS={ 'decrypt-aes': CardConnectionContext.cmd_decrypt_aes, 'get-kdf': CardConnectionContext.cmd_get_kdf, 'set-kdf': CardConnectionContext.cmd_set_kdf, + 'setup-kdf': CardConnectionContext.cmd_setup_kdf, } def read_pin_interactive(name): diff --git a/bin/smartpgp/commands.py b/bin/smartpgp/commands.py index b98c687..bd98745 100644 --- a/bin/smartpgp/commands.py +++ b/bin/smartpgp/commands.py @@ -23,6 +23,10 @@ from smartcard.System import readers from smartcard.util import toHexString import struct +import os +import array +import hashlib + SELECT = [0x00, 0xA4, 0x04, 0x00, 0x06, @@ -110,6 +114,27 @@ def encode_len(data): l = [l & 0xff] return l + +def kdf_itersalted_s2k(salt, value, algo, count): + if algo == 0x08: #SHA256 + f = hashlib.new('sha256') + elif algo == 0x0A: #SHA512 + f = hashlib.new('sha512') + else: + print "invalid KDF" + salt = [ord(c) for c in salt] + value = [ord(c) for c in value] + data = salt + value + data = array.array('B', data).tostring() + datalen = len(data) + while datalen <= count: + f.update(data) + count -= datalen + if 0 < count: + f.update(data[0:count]) + return f.digest() + + def _raw_send_apdu(connection, text, apdu): print "%s" % text apdu = [int(c) for c in apdu] @@ -396,3 +421,22 @@ def get_kdf_do(connection): apdu = [0x00, 0xCA, 0x00, 0xF9, 0x00] (data,sw1,sw2) = _raw_send_apdu(connection,"Get KDF-DO",apdu) return (data,sw1,sw2) + + +def change_reference_data_pw1(connection, old, new): + prefix = [0x00, 0x24, 0x00, 0x81] + old = ascii_encode_pin(old) + new = ascii_encode_pin(new) + data = old + new + apdu = assemble_with_len(prefix, data) + (_,sw1,sw2) = _raw_send_apdu(connection,"Change PW1", apdu) + return (sw1,sw2) + +def change_reference_data_pw3(connection, old, new): + prefix = [0x00, 0x24, 0x00, 0x83] + old = ascii_encode_pin(old) + new = ascii_encode_pin(new) + data = old + new + apdu = assemble_with_len(prefix, data) + (_,sw1,sw2) = _raw_send_apdu(connection,"Change PW3", apdu) + return (sw1,sw2) diff --git a/bin/smartpgp/highlevel.py b/bin/smartpgp/highlevel.py index 6488834..a1f9309 100644 --- a/bin/smartpgp/highlevel.py +++ b/bin/smartpgp/highlevel.py @@ -24,6 +24,11 @@ import pyasn1 from pyasn1.type import univ from pyasn1.codec.der import encoder as der_encoder,decoder as der_decoder + + +class InvalidKDF(Exception): + pass + class ConnectionFailed(Exception): pass @@ -343,3 +348,73 @@ class CardConnectionContext: with open(self.output, 'w') as f: f.write(kdf_do) f.close() + + + def cmd_setup_kdf(self): + self.connect() + (kdf_do,_,_) = get_kdf_do(self.connection) + if 5 < len(kdf_do): + print "KDF already setup" + return + ####### step 1 + pw1 = self.read_pin("User") + resetting_code = self.read_pin("PUK") + if len(resetting_code) == 0: + resetting_code = None + pw3 = self.read_pin("Admin") + ####### step 1bis + pw1 = pw1 + if resetting_code <> None: + resetting_code = resetting_code + pw3 = pw3 + ####### step 2 + salt_size = 8 + algo = 0x08 #SHA256 + nbiter = 200000 + ndata81 = [0x81, 0x01, 0x03] #KDF_ITERSALTED_S2K + ndata82 = [0x82, 0x01, algo] + ndata83 = [0x83, 0x04, nbiter >> 24, (nbiter >> 16) & 0xff, (nbiter >> 8) & 0xff, nbiter & 0xff] #NB ITERATIONS + salt_pw1 = os.urandom(salt_size) + ndata84 = assemble_with_len([0x84], [ord(c) for c in salt_pw1]) #SALT PW1 + salt_resetting_code = os.urandom(salt_size) + ndata85 = assemble_with_len([0x85], [ord(c) for c in salt_resetting_code]) #SALT RESETTING CODE + salt_pw3 = os.urandom(salt_size) + ndata86 = assemble_with_len([0x86], [ord(c) for c in salt_pw3]) #SALT PW3 + h87 = kdf_itersalted_s2k(salt_pw1, "123456", algo, nbiter) #HASH OF "123456" + h87 = [ord(c) for c in h87] + ndata87 = assemble_with_len([0x87], h87) + h88 = kdf_itersalted_s2k(salt_pw3, "12345678", algo, nbiter) #HASH OF "12345678" + h88 = [ord(c) for c in h88] + ndata88 = assemble_with_len([0x88], h88) + nkdf_do = ndata81 + ndata82 + ndata83 + ndata84 + ndata85 + ndata86 + ndata87 + ndata88 + ####### step 2bis + npw1 = kdf_itersalted_s2k(salt_pw1, pw1, algo, nbiter) + if resetting_code <> None: + nresetting_code = kdf_itersalted_s2k(salt_resetting_code, resetting_code, algo, nbiter) + else: + nresetting_code = None + npw3 = kdf_itersalted_s2k(salt_pw3, pw3, algo, nbiter) + ####### step 3 + (_,sw1,sw2) = verif_admin_pin(self.connection, pw3) + if sw1==0x90 and sw2==0x00: + self.verified = True + else: + raise AdminPINFailed + ####### step 3bis + if nresetting_code <> None: + set_resetting_code(self.connection, nresetting_code) + if sw1!=0x90 or sw2!=0x00: + print "set_resetting_code failed" + return + ####### step 4 + change_reference_data_pw1(self.connection, pw1, npw1) + if sw1!=0x90 or sw2!=0x00: + print "change_reference_data_pw1 failed" + return + ####### step 4bis + change_reference_data_pw3(self.connection, pw3, npw3) + if sw1!=0x90 or sw2!=0x00: + print "change_reference_data_pw3 failed" + return + ####### step 5 + put_kdf_do(self.connection, nkdf_do)