From 60b6b091922f507e7d409bfe2a1e9510efc0da15 Mon Sep 17 00:00:00 2001 From: Arnaud Fontaine Date: Tue, 11 Jul 2017 16:57:11 +0200 Subject: [PATCH] Add support for AES commands in smartpgp-cli --- bin/smartpgp-cli | 3 ++ bin/smartpgp/commands.py | 70 +++++++++++++++++++++++++++++++++++---- bin/smartpgp/highlevel.py | 67 ++++++++++++++++++++++++++++++++++++- 3 files changed, 133 insertions(+), 7 deletions(-) diff --git a/bin/smartpgp-cli b/bin/smartpgp-cli index c995a56..07af3a5 100755 --- a/bin/smartpgp-cli +++ b/bin/smartpgp-cli @@ -43,6 +43,9 @@ VALID_COMMANDS={ 'put-sm-key': CardConnectionContext.cmd_put_sm_key, 'put-sm-certificate': CardConnectionContext.cmd_put_sm_certificate, 'get-sm-certificate': CardConnectionContext.cmd_get_sm_certificate, + 'put-aes-key': CardConnectionContext.cmd_put_aes_key, + 'encrypt-aes': CardConnectionContext.cmd_encrypt_aes, + 'decrypt-aes': CardConnectionContext.cmd_decrypt_aes, } def read_pin_interactive(name): diff --git a/bin/smartpgp/commands.py b/bin/smartpgp/commands.py index e0c699e..995c04a 100644 --- a/bin/smartpgp/commands.py +++ b/bin/smartpgp/commands.py @@ -29,7 +29,8 @@ SELECT = [0x00, 0xA4, 0x04, 0x00, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01, 0x00] -VERIFY = [0x00, 0x20, 0x00, 0x83] +VERIFY_ADMIN = [0x00, 0x20, 0x00, 0x83] +VERIFY_USER_82 = [0x00, 0x20, 0x00, 0x82] TERMINATE = [0x00, 0xe6, 0x00, 0x00] ACTIVATE = [0x00, 0x44, 0x00, 0x00] ACTIVATE_FULL = [0x00, 0x44, 0x00, 0x01] @@ -111,9 +112,10 @@ def encode_len(data): def _raw_send_apdu(connection, text, apdu): print "%s" % text - data, sw1, sw2 = connection.transmit(apdu) - print data - print "%s: %02X %02X" % (text, sw1, sw2) + #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) return (data,sw1,sw2) def list_readers(): @@ -136,9 +138,13 @@ def select_applet(connection): return _raw_send_apdu(connection,"Select OpenPGP Applet",SELECT) def verif_admin_pin(connection, admin_pin): - verif_apdu = assemble_with_len(VERIFY,ascii_encode_pin(admin_pin)) + verif_apdu = assemble_with_len(VERIFY_ADMIN,ascii_encode_pin(admin_pin)) return _raw_send_apdu(connection,"Verify Admin PIN",verif_apdu) +def verif_user_pin(connection, user_pin): + verif_apdu = assemble_with_len(VERIFY_USER_82,ascii_encode_pin(user_pin)) + return _raw_send_apdu(connection,"Verify User PIN",verif_apdu) + def full_reset_card(connection): _raw_send_apdu(connection,"Terminate",TERMINATE) _raw_send_apdu(connection,"Activate",ACTIVATE_FULL) @@ -259,7 +265,7 @@ def get_sm_certificate(connection): apdu = [0x00, 0xCA, 0x7F, 0x21, 0x00] (data,sw1,sw2) = _raw_send_apdu(connection,"Receiving SM certificate chunk",apdu) while sw1 == 0x61: - apdu = [0x00, 0xC0, 0x00, 0x00, 0x00] + apdu = [0x00, 0xC0, 0x00, 0x00, sw2] (ndata,sw1,sw2) = _raw_send_apdu(connection,"Receiving SM certificate chunk",apdu) data = data + ndata return (data,sw1,sw2) @@ -282,3 +288,55 @@ def get_sm_curve_oid(connection): #print ' '.join('{:02X}'.format(c) for c in curve) # Add DER OID header manually ... return '\x06' + struct.pack('B',len(curve)) + curve + +def put_aes_key(connection, key): + prefix = [0x00, 0xDA, 0x00, 0xD5] + data = key + apdu = assemble_with_len(prefix, data) + _raw_send_apdu(connection,"Put AES key",apdu) + +def encrypt_aes(connection, msg): + ins_p1_p2 = [0x2A, 0x86, 0x80] + i = 0 + cl = 255 + l = len(msg) + while i < l: + if (l - i) <= cl: + cla = 0x00 + data = msg[i:] + i = l + else: + cla = 0x10 + data = msg[i:i+cl] + i = i + cl + apdu = assemble_with_len([cla] + ins_p1_p2, data) + (res,sw1,sw2) = _raw_send_apdu(connection,"Encrypt AES chunk",apdu) + while sw1 == 0x61: + apdu = [0x00, 0xC0, 0x00, 0x00, sw2] + (nres,sw1,sw2) = _raw_send_apdu(connection,"Receiving encrypted chunk",apdu) + res = res + nres + return (res[1:],sw1,sw2) + + +def decrypt_aes(connection, msg): + ins_p1_p2 = [0x2A, 0x80, 0x86] + i = 0 + cl = 255 + msg = [0x02] + msg + l = len(msg) + while i < l: + if (l - i) <= cl: + cla = 0x00 + data = msg[i:] + i = l + else: + cla = 0x10 + data = msg[i:i+cl] + i = i + cl + apdu = assemble_with_len([cla] + ins_p1_p2, data) + (res,sw1,sw2) = _raw_send_apdu(connection,"Decrypt AES chunk",apdu) + while sw1 == 0x61: + apdu = [0x00, 0xC0, 0x00, 0x00, sw2] + (nres,sw1,sw2) = _raw_send_apdu(connection,"Receiving decrypted chunk",apdu) + res = res + nres + return (res,sw1,sw2) diff --git a/bin/smartpgp/highlevel.py b/bin/smartpgp/highlevel.py index b9268be..5e51d36 100644 --- a/bin/smartpgp/highlevel.py +++ b/bin/smartpgp/highlevel.py @@ -30,10 +30,14 @@ class ConnectionFailed(Exception): class AdminPINFailed(Exception): pass +class UserPINFailed(Exception): + pass + class CardConnectionContext: def __init__(self): self.reader_index = 0 + self.admin_pin = "123456" self.admin_pin = "12345678" self.connection = None self.read_pin = self._default_pin_read_function @@ -57,6 +61,16 @@ class CardConnectionContext: else: raise AdminPINFailed + def verify_user_pin(self): + if self.verified: + return + user_pin = self.read_pin("User") + (_,sw1,sw2)=verif_user_pin(self.connection, user_pin) + if sw1==0x90 and sw2==0x00: + self.verified = True + else: + raise UserPINFailed + def connect(self): if self.connected: return @@ -220,7 +234,7 @@ class CardConnectionContext: f.close() self.connect() self.verify_admin_pin() - put_sm_certificate(self.connection,cert) + put_sm_certificate(self.connection, cert) def cmd_get_sm_certificate(self): if self.output is None: @@ -232,3 +246,54 @@ class CardConnectionContext: with open(self.output, 'w') as f: f.write(cert) f.close() + + def cmd_put_aes_key(self): + if self.input is None: + print "No input AES key file" + return + f = open(self.input, 'r') + key = f.read() + key = [ord(c) for c in key] + f.close() + self.connect() + self.verify_admin_pin() + put_aes_key(self.connection, key) + + def cmd_encrypt_aes(self): + if self.input is None: + print "No input data file" + return + if self.output is None: + print "No output data file" + return + f = open(self.input, 'r') + data = f.read() + data = [ord(c) for c in data] + f.close() + self.connect() + self.verify_user_pin() + (data,_,_) = encrypt_aes(self.connection, data) + data = "".join([chr(c) for c in data]) + with open(self.output, 'w') as f: + f.write(data) + f.close() + + def cmd_decrypt_aes(self): + if self.input is None: + print "No input data file" + return + if self.output is None: + print "No output data file" + return + f = open(self.input, 'r') + data = f.read() + data = [ord(c) for c in data] + f.close() + self.connect() + self.verify_user_pin() + (data,_,_) = decrypt_aes(self.connection, data) + data = "".join([chr(c) for c in data]) + with open(self.output, 'w') as f: + f.write(data) + f.close() +