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
# https://github.com/ANSSI-FR/SmartPGP
@ -83,8 +83,8 @@ def parse_args(ctx):
varname = args.pin[4:]
try:
ctx.admin_pin=os.environ[varname]
except KeyError,e:
print "Environment variable %s not found" % varname
except KeyError:
print("Environment variable %s not found" % varname)
sys.exit(1)
else:
ctx.admin_pin = args.pin
@ -103,8 +103,8 @@ def main():
if args.command in VALID_COMMANDS:
VALID_COMMANDS[args.command](ctx)
else:
print "Unknown command '%s'" % args.command
print "Run '%s -h' for help" % sys.argv[0]
print("Unknown command '%s'" % args.command)
print("Run '%s -h' for help" % sys.argv[0])
sys.exit(1)
if __name__=='__main__':

View File

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

View File

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