switch smartpgp-cli to Python3

This commit is contained in:
Arnaud Fontaine 2021-09-27 14:08:35 +02:00
parent 461e5fd123
commit 1720a3494b
4 changed files with 78 additions and 69 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# SmartPGP : JavaCard implementation of OpenPGP card v3 specification # SmartPGP : JavaCard implementation of OpenPGP card v3 specification
# https://github.com/ANSSI-FR/SmartPGP # https://github.com/ANSSI-FR/SmartPGP
@ -83,8 +83,8 @@ def parse_args(ctx):
varname = args.pin[4:] varname = args.pin[4:]
try: try:
ctx.admin_pin=os.environ[varname] ctx.admin_pin=os.environ[varname]
except KeyError,e: except KeyError:
print "Environment variable %s not found" % varname print("Environment variable %s not found" % varname)
sys.exit(1) sys.exit(1)
else: else:
ctx.admin_pin = args.pin ctx.admin_pin = args.pin
@ -103,8 +103,8 @@ def main():
if args.command in VALID_COMMANDS: if args.command in VALID_COMMANDS:
VALID_COMMANDS[args.command](ctx) VALID_COMMANDS[args.command](ctx)
else: else:
print "Unknown command '%s'" % args.command print("Unknown command '%s'" % args.command)
print "Run '%s -h' for help" % sys.argv[0] print("Run '%s -h' for help" % sys.argv[0])
sys.exit(1) sys.exit(1)
if __name__=='__main__': if __name__=='__main__':

View File

@ -1,5 +1,3 @@
#!/usr/bin/env python
# SmartPGP : JavaCard implementation of OpenPGP card v3 specification # SmartPGP : JavaCard implementation of OpenPGP card v3 specification
# https://github.com/ANSSI-FR/SmartPGP # https://github.com/ANSSI-FR/SmartPGP
# Copyright (C) 2016 ANSSI # Copyright (C) 2016 ANSSI
@ -79,6 +77,9 @@ OID_ALGS = {
'brainpoolP512r1': [0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D], 'brainpoolP512r1': [0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D],
} }
class InvalidBitStringLength(Exception):
pass
class WrongKeyRole(Exception): class WrongKeyRole(Exception):
pass pass
@ -93,8 +94,8 @@ def assemble_with_len(prefix,data):
def asOctets(bs): def asOctets(bs):
l = len(bs) l = len(bs)
if l%8 is not 0: if l%8 != 0:
raise "BitString length is not a multiple of 8" raise InvalidBitStringLength
result = [] result = []
i = 0 i = 0
while i < l: while i < l:
@ -122,11 +123,11 @@ def kdf_itersalted_s2k(salt, value, algo, count):
elif algo == 0x0A: #SHA512 elif algo == 0x0A: #SHA512
f = hashlib.new('sha512') f = hashlib.new('sha512')
else: else:
print "invalid KDF" print("invalid KDF")
salt = [ord(c) for c in salt] salt = list(salt)#[ord(c) for c in salt]
value = [ord(c) for c in value] value = list(value)#[ord(c) for c in value]
data = salt + value data = bytes(salt + value)
data = array.array('B', data).tostring() #data = array.array('B', data).tostring()
datalen = len(data) datalen = len(data)
while datalen <= count: while datalen <= count:
f.update(data) f.update(data)
@ -137,12 +138,12 @@ def kdf_itersalted_s2k(salt, value, algo, count):
def _raw_send_apdu(connection, text, apdu): def _raw_send_apdu(connection, text, apdu):
print "%s" % text print("%s" % text)
apdu = [int(c) for c in apdu] apdu = [int(c) for c in apdu]
#print ' '.join('{:02X}'.format(c) for c in apdu) #print ' '.join('{:02X}'.format(c) for c in apdu)
(data, sw1, sw2) = connection.transmit(apdu) (data, sw1, sw2) = connection.transmit(apdu)
#print ' '.join('{:02X}'.format(c) for c in data) #print ' '.join('{:02X}'.format(c) for c in data)
print "%02X %02X" % (sw1, sw2) print("%02X %02X" % (sw1, sw2))
return (data,sw1,sw2) return (data,sw1,sw2)
def list_readers(): def list_readers():
@ -475,8 +476,6 @@ def get_kdf_do(connection):
def change_reference_data_pw1(connection, old, new): def change_reference_data_pw1(connection, old, new):
prefix = [0x00, 0x24, 0x00, 0x81] prefix = [0x00, 0x24, 0x00, 0x81]
old = ascii_encode_pin(old)
new = ascii_encode_pin(new)
data = old + new data = old + new
apdu = assemble_with_len(prefix, data) apdu = assemble_with_len(prefix, data)
(_,sw1,sw2) = _raw_send_apdu(connection,"Change PW1", apdu) (_,sw1,sw2) = _raw_send_apdu(connection,"Change PW1", apdu)
@ -484,8 +483,6 @@ def change_reference_data_pw1(connection, old, new):
def change_reference_data_pw3(connection, old, new): def change_reference_data_pw3(connection, old, new):
prefix = [0x00, 0x24, 0x00, 0x83] prefix = [0x00, 0x24, 0x00, 0x83]
old = ascii_encode_pin(old)
new = ascii_encode_pin(new)
data = old + new data = old + new
apdu = assemble_with_len(prefix, data) apdu = assemble_with_len(prefix, data)
(_,sw1,sw2) = _raw_send_apdu(connection,"Change PW3", apdu) (_,sw1,sw2) = _raw_send_apdu(connection,"Change PW3", apdu)

View File

@ -17,7 +17,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from commands import * from smartpgp.commands import *
import binascii import binascii
import pyasn1 import pyasn1
@ -38,11 +38,15 @@ class AdminPINFailed(Exception):
class UserPINFailed(Exception): class UserPINFailed(Exception):
pass pass
class InvalidPINType(Exception):
pass
class CardConnectionContext: class CardConnectionContext:
def __init__(self): def __init__(self):
self.reader_index = 0 self.reader_index = 0
self.admin_pin = "123456" self.resetting_code = None
self.user_pin = "123456"
self.admin_pin = "12345678" self.admin_pin = "12345678"
self.connection = None self.connection = None
self.read_pin = self._default_pin_read_function self.read_pin = self._default_pin_read_function
@ -51,7 +55,13 @@ class CardConnectionContext:
self.input = None self.input = None
def _default_pin_read_function(self, pin_type): def _default_pin_read_function(self, pin_type):
return self.admin_pin if pin_type == "Admin":
return self.admin_pin
if pin_type == "User":
return self.user_pin
if pin_type == "PUK":
return self.resetting_code
raise InvalidPINType
def set_pin_read_function(self, fun): def set_pin_read_function(self, fun):
self.read_pin = fun self.read_pin = fun
@ -154,29 +164,29 @@ class CardConnectionContext:
def cmd_generate_sm_key(self): def cmd_generate_sm_key(self):
if not self.output: if not self.output:
print "Missing output file name" print("Missing output file name")
return return
self.connect() self.connect()
self.verify_admin_pin() self.verify_admin_pin()
(data,sw1,sw2) = generate_sm_key(self.connection) (data,sw1,sw2) = generate_sm_key(self.connection)
if sw1!=0x90 or sw2!=0x00: if sw1!=0x90 or sw2!=0x00:
print "generate_sm_key failed" print("generate_sm_key failed")
return return
if len(data) < 4 or data[0]!=0x7f or data[1]!=0x49: if len(data) < 4 or data[0]!=0x7f or data[1]!=0x49:
print "Strange reply for get_sm_certificate" print("Strange reply for get_sm_certificate")
return return
blob_len = data[2] blob_len = data[2]
blob = data[3:] blob = data[3:]
assert(blob_len == len(blob)) assert(blob_len == len(blob))
if blob[0]!=0x86: if blob[0]!=0x86:
print "get_sm_certificate return something not a public key" print("get_sm_certificate return something not a public key")
return return
assert(blob[1]==len(blob[2:])) assert(blob[1]==len(blob[2:]))
pubkey = blob[2:] pubkey = blob[2:]
# get curve OID # get curve OID
curve_oid_der = get_sm_curve_oid(self.connection) curve_oid_der = get_sm_curve_oid(self.connection)
if not curve_oid_der: if not curve_oid_der:
print "Error getting SM curve OID" print("Error getting SM curve OID")
return return
(curve_oid,_) = der_decoder.decode(str(curve_oid_der)) (curve_oid,_) = der_decoder.decode(str(curve_oid_der))
# now format it to DER [RFC5480] # now format it to DER [RFC5480]
@ -189,7 +199,7 @@ class CardConnectionContext:
s2.setComponentByPosition(0,s) s2.setComponentByPosition(0,s)
s2.setComponentByPosition(1,bs) s2.setComponentByPosition(1,bs)
pubkey_der = der_encoder.encode(s2) pubkey_der = der_encoder.encode(s2)
print binascii.hexlify(pubkey_der) print(binascii.hexlify(pubkey_der))
# and write result # and write result
with open(self.output,"wb") as f: with open(self.output,"wb") as f:
f.write(pubkey_der) f.write(pubkey_der)
@ -197,28 +207,28 @@ class CardConnectionContext:
def cmd_get_sm_key(self): def cmd_get_sm_key(self):
if not self.output: if not self.output:
print "Missing output file name" print("Missing output file name")
return return
self.connect() self.connect()
(data,sw1,sw2) = get_sm_key(self.connection) (data,sw1,sw2) = get_sm_key(self.connection)
if sw1!=0x90 or sw2!=0x00: if sw1!=0x90 or sw2!=0x00:
print "get_sm_key failed" print("get_sm_key failed")
return return
if len(data) < 4 or data[0]!=0x7f or data[1]!=0x49: if len(data) < 4 or data[0]!=0x7f or data[1]!=0x49:
print "Strange reply for get_sm_key" print("Strange reply for get_sm_key")
return return
blob_len = data[2] blob_len = data[2]
blob = data[3:] blob = data[3:]
assert(blob_len == len(blob)) assert(blob_len == len(blob))
if blob[0]!=0x86: if blob[0]!=0x86:
print "get_sm_key something not a public key" print("get_sm_key something not a public key")
return return
assert(blob[1]==len(blob[2:])) assert(blob[1]==len(blob[2:]))
pubkey = blob[2:] pubkey = blob[2:]
# get curve OID # get curve OID
curve_oid_der = get_sm_curve_oid(self.connection) curve_oid_der = get_sm_curve_oid(self.connection)
if not curve_oid_der: if not curve_oid_der:
print "Error getting SM curve OID" print("Error getting SM curve OID")
return return
(curve_oid,_) = der_decoder.decode(str(curve_oid_der)) (curve_oid,_) = der_decoder.decode(str(curve_oid_der))
# now format it to DER [RFC5480] # now format it to DER [RFC5480]
@ -231,7 +241,7 @@ class CardConnectionContext:
s2.setComponentByPosition(0,s) s2.setComponentByPosition(0,s)
s2.setComponentByPosition(1,bs) s2.setComponentByPosition(1,bs)
pubkey_der = der_encoder.encode(s2) pubkey_der = der_encoder.encode(s2)
print binascii.hexlify(pubkey_der) print(binascii.hexlify(pubkey_der))
# and write result # and write result
with open(self.output,"wb") as f: with open(self.output,"wb") as f:
f.write(pubkey_der) f.write(pubkey_der)
@ -239,7 +249,7 @@ class CardConnectionContext:
def cmd_put_sm_key(self): def cmd_put_sm_key(self):
if self.input is None: if self.input is None:
print "No input key file" print("No input key file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
fstr = f.read() fstr = f.read()
@ -254,11 +264,11 @@ class CardConnectionContext:
oid = oid[2:] oid = oid[2:]
assert(oid_len == len(oid)) assert(oid_len == len(oid))
curve = None curve = None
for k,v in OID_ALGS.items(): for k,v in list(OID_ALGS.items()):
if bytearray(v) == oid: if bytearray(v) == oid:
curve = k curve = k
if curve is None: if curve is None:
print "Curve not supported (%s)" % der[2] print("Curve not supported (%s)" % der[2])
return return
self.connect() self.connect()
self.verify_admin_pin() self.verify_admin_pin()
@ -279,7 +289,7 @@ class CardConnectionContext:
def cmd_put_sign_certificate(self): def cmd_put_sign_certificate(self):
if self.input is None: if self.input is None:
print "No input certificate file" print("No input certificate file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
cert = f.read() cert = f.read()
@ -291,7 +301,7 @@ class CardConnectionContext:
def cmd_put_auth_certificate(self): def cmd_put_auth_certificate(self):
if self.input is None: if self.input is None:
print "No input certificate file" print("No input certificate file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
cert = f.read() cert = f.read()
@ -303,7 +313,7 @@ class CardConnectionContext:
def cmd_put_sm_certificate(self): def cmd_put_sm_certificate(self):
if self.input is None: if self.input is None:
print "No input certificate file" print("No input certificate file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
cert = f.read() cert = f.read()
@ -315,7 +325,7 @@ class CardConnectionContext:
def cmd_get_sm_certificate(self): def cmd_get_sm_certificate(self):
if self.output is None: if self.output is None:
print "No output file" print("No output file")
return return
self.connect() self.connect()
(cert,_,_) = get_sm_certificate(self.connection) (cert,_,_) = get_sm_certificate(self.connection)
@ -326,7 +336,7 @@ class CardConnectionContext:
def cmd_put_aes_key(self): def cmd_put_aes_key(self):
if self.input is None: if self.input is None:
print "No input AES key file" print("No input AES key file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
key = f.read() key = f.read()
@ -338,10 +348,10 @@ class CardConnectionContext:
def cmd_encrypt_aes(self): def cmd_encrypt_aes(self):
if self.input is None: if self.input is None:
print "No input data file" print("No input data file")
return return
if self.output is None: if self.output is None:
print "No output data file" print("No output data file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
data = f.read() data = f.read()
@ -357,10 +367,10 @@ class CardConnectionContext:
def cmd_decrypt_aes(self): def cmd_decrypt_aes(self):
if self.input is None: if self.input is None:
print "No input data file" print("No input data file")
return return
if self.output is None: if self.output is None:
print "No output data file" print("No output data file")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
data = f.read() data = f.read()
@ -376,7 +386,7 @@ class CardConnectionContext:
def cmd_set_kdf(self): def cmd_set_kdf(self):
if self.input is None: if self.input is None:
print "No input KDF-DO" print("No input KDF-DO")
return return
f = open(self.input, 'r') f = open(self.input, 'r')
kdf_do = f.read() kdf_do = f.read()
@ -388,7 +398,7 @@ class CardConnectionContext:
def cmd_get_kdf(self): def cmd_get_kdf(self):
if self.output is None: if self.output is None:
print "No output file" print("No output file")
return return
self.connect() self.connect()
(kdf_do,_,_) = get_kdf_do(self.connection) (kdf_do,_,_) = get_kdf_do(self.connection)
@ -402,17 +412,17 @@ class CardConnectionContext:
self.connect() self.connect()
(kdf_do,_,_) = get_kdf_do(self.connection) (kdf_do,_,_) = get_kdf_do(self.connection)
if 5 < len(kdf_do): if 5 < len(kdf_do):
print "KDF already setup" print("KDF already setup")
return return
####### step 1 ####### step 1
pw1 = self.read_pin("User") pw1 = self.read_pin("User")
resetting_code = self.read_pin("PUK") resetting_code = self.read_pin("PUK")
if len(resetting_code) == 0: if resetting_code != None and len(resetting_code) == 0:
resetting_code = None resetting_code = None
pw3 = self.read_pin("Admin") pw3 = self.read_pin("Admin")
####### step 1bis ####### step 1bis
pw1 = pw1 pw1 = pw1
if resetting_code <> None: if resetting_code != None:
resetting_code = resetting_code resetting_code = resetting_code
pw3 = pw3 pw3 = pw3
####### step 2 ####### step 2
@ -423,25 +433,27 @@ class CardConnectionContext:
ndata82 = [0x82, 0x01, algo] ndata82 = [0x82, 0x01, algo]
ndata83 = [0x83, 0x04, nbiter >> 24, (nbiter >> 16) & 0xff, (nbiter >> 8) & 0xff, nbiter & 0xff] #NB ITERATIONS ndata83 = [0x83, 0x04, nbiter >> 24, (nbiter >> 16) & 0xff, (nbiter >> 8) & 0xff, nbiter & 0xff] #NB ITERATIONS
salt_pw1 = os.urandom(salt_size) salt_pw1 = os.urandom(salt_size)
ndata84 = assemble_with_len([0x84], [ord(c) for c in salt_pw1]) #SALT PW1 ndata84 = assemble_with_len([0x84], list(salt_pw1)) #SALT PW1
salt_resetting_code = os.urandom(salt_size) salt_resetting_code = os.urandom(salt_size)
ndata85 = assemble_with_len([0x85], [ord(c) for c in salt_resetting_code]) #SALT RESETTING CODE ndata85 = assemble_with_len([0x85], list(salt_resetting_code)) #SALT RESETTING CODE
salt_pw3 = os.urandom(salt_size) salt_pw3 = os.urandom(salt_size)
ndata86 = assemble_with_len([0x86], [ord(c) for c in salt_pw3]) #SALT PW3 ndata86 = assemble_with_len([0x86], list(salt_pw3)) #SALT PW3
h87 = kdf_itersalted_s2k(salt_pw1, "123456", algo, nbiter) #HASH OF "123456" h87 = kdf_itersalted_s2k(salt_pw1, ascii_encode_pin("123456"), algo, nbiter) #HASH OF "123456"
h87 = [ord(c) for c in h87] #h87 = [ord(c) for c in h87]
h87 = list(h87)
ndata87 = assemble_with_len([0x87], h87) ndata87 = assemble_with_len([0x87], h87)
h88 = kdf_itersalted_s2k(salt_pw3, "12345678", algo, nbiter) #HASH OF "12345678" h88 = kdf_itersalted_s2k(salt_pw3, ascii_encode_pin("12345678"), algo, nbiter) #HASH OF "12345678"
h88 = [ord(c) for c in h88] #h88 = [ord(c) for c in h88]
h88 = list(h88)
ndata88 = assemble_with_len([0x88], h88) ndata88 = assemble_with_len([0x88], h88)
nkdf_do = ndata81 + ndata82 + ndata83 + ndata84 + ndata85 + ndata86 + ndata87 + ndata88 nkdf_do = ndata81 + ndata82 + ndata83 + ndata84 + ndata85 + ndata86 + ndata87 + ndata88
####### step 2bis ####### step 2bis
npw1 = kdf_itersalted_s2k(salt_pw1, pw1, algo, nbiter) npw1 = kdf_itersalted_s2k(salt_pw1, ascii_encode_pin(pw1), algo, nbiter)
if resetting_code <> None: if resetting_code != None:
nresetting_code = kdf_itersalted_s2k(salt_resetting_code, resetting_code, algo, nbiter) nresetting_code = kdf_itersalted_s2k(salt_resetting_code, ascii_encode_pin(resetting_code), algo, nbiter)
else: else:
nresetting_code = None nresetting_code = None
npw3 = kdf_itersalted_s2k(salt_pw3, pw3, algo, nbiter) npw3 = kdf_itersalted_s2k(salt_pw3, ascii_encode_pin(pw3), algo, nbiter)
####### step 3 ####### step 3
(_,sw1,sw2) = verif_admin_pin(self.connection, pw3) (_,sw1,sw2) = verif_admin_pin(self.connection, pw3)
if sw1==0x90 and sw2==0x00: if sw1==0x90 and sw2==0x00:
@ -449,20 +461,20 @@ class CardConnectionContext:
else: else:
raise AdminPINFailed raise AdminPINFailed
####### step 3bis ####### step 3bis
if nresetting_code <> None: if nresetting_code != None:
set_resetting_code(self.connection, nresetting_code) set_resetting_code(self.connection, nresetting_code)
if sw1!=0x90 or sw2!=0x00: if sw1!=0x90 or sw2!=0x00:
print "set_resetting_code failed" print("set_resetting_code failed")
return return
####### step 4 ####### step 4
change_reference_data_pw1(self.connection, pw1, npw1) change_reference_data_pw1(self.connection, ascii_encode_pin(pw1), list(npw1))
if sw1!=0x90 or sw2!=0x00: if sw1!=0x90 or sw2!=0x00:
print "change_reference_data_pw1 failed" print("change_reference_data_pw1 failed")
return return
####### step 4bis ####### step 4bis
change_reference_data_pw3(self.connection, pw3, npw3) change_reference_data_pw3(self.connection, ascii_encode_pin(pw3), list(npw3))
if sw1!=0x90 or sw2!=0x00: if sw1!=0x90 or sw2!=0x00:
print "change_reference_data_pw3 failed" print("change_reference_data_pw3 failed")
return return
####### step 5 ####### step 5
put_kdf_do(self.connection, nkdf_do) put_kdf_do(self.connection, nkdf_do)