From 14cfe899fff0c903eadfd7b4bd7daaeba072114f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Tue, 9 Oct 2018 23:34:00 +0200 Subject: [PATCH] Add full backup/restore with python tool app: - support DO 'B6'/'B8'/'A4' in get/put data for full backup. private key are backuped encrypted with AES and a key based on the master seed - fix missing DO 'CB' access - fix TERMINATE_DF command: the command did not return - fix stack corruption in ECC key generation, when key size is greater than 256bits tools: - add full backup/restore cli tool misc: - add 'make run' rules --- Makefile | 8 +- pytools/gpgcard/gpgcard.py | 110 ++++++++++++++++++----- pytools/gpgcard/gpgcli.py | 123 +++++++++++++++++++++++++ src/gpg_api.h | 5 ++ src/gpg_data.c | 178 +++++++++++++++++++++++++++++++++++++ src/gpg_dispatch.c | 44 +++++++-- src/gpg_gen.c | 10 +-- src/gpg_init.c | 73 +++++++-------- src/gpg_types.h | 6 +- 9 files changed, 472 insertions(+), 85 deletions(-) create mode 100644 pytools/gpgcard/gpgcli.py diff --git a/Makefile b/Makefile index 9378403..9bf1e80 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ # limitations under the License. #******************************************************************************* +-include Makefile.env ifeq ($(BOLOS_SDK),) $(error Environment variable BOLOS_SDK is not set) endif @@ -27,8 +28,8 @@ APPNAME = OpenPGP SPECVERSION="3.3.1" APPVERSION_M=1 -APPVERSION_N=2 -APPVERSION_P=1 +APPVERSION_N=3 +APPVERSION_P=0 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) ifeq ($(TARGET_NAME),TARGET_BLUE) @@ -96,6 +97,9 @@ SDK_SOURCE_PATH += lib_stusb load: all python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) +run: + python -m ledgerblue.runApp --appName $(APPNAME) + delete: python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) diff --git a/pytools/gpgcard/gpgcard.py b/pytools/gpgcard/gpgcard.py index 5d248bc..d4101ef 100644 --- a/pytools/gpgcard/gpgcard.py +++ b/pytools/gpgcard/gpgcard.py @@ -56,6 +56,9 @@ def decode_tlv(tlv) : tlv = tlv[o+l:] return tags +class GPGCardExcpetion(Exception): + pass + class GPGCard() : def __init__(self): self.reset() @@ -103,9 +106,14 @@ class GPGCard() : self.private_02 = b'' self.private_03 = b'' + #keys + self.sig_key = b'' + self.dec_key = b'' + self.aut_key = b'' def connect(self, device): + self.token = None if device.startswith("ledger:"): self.token = getDongle(True) self.exchange = self._exchange_ledger @@ -132,7 +140,7 @@ class GPGCard() : ### APDU interface ### - def _exchange_ledger(self,cmd,sw=0x9000): + def _exchange_ledger(self,cmd,data=None, sw=0x9000): resp = b'' cond = True while cond: @@ -150,10 +158,25 @@ class GPGCard() : cond = False return resp,sw - def _exchange_pcsc(self,apdu,sw=0x9000): - #print("xch S cmd : %s"%(binascii.hexlify(apdu))) + def _exchange_pcsc(self,apdu, data=None, sw_expected=0x9000, sw_mask=0xFFFF): + if data: + data = [x for x in data] apdu = [x for x in apdu] + + #send + if data: + while len(data) > 0xFE: + apdux = apdu[0:5]+[0xfe]+data[0:0xFE] + apdux[0] |= 0x10 + resp, sw1, sw2 = self.connection.transmit(apdux) + sw = (sw1<<8)|sw2 + if sw != 0x9000: + return resp,sw + data = data[0xFE:] + apdu = apdu+[len(data)]+data resp, sw1, sw2 = self.connection.transmit(apdu) + + #receive while sw1==0x61: apdu = binascii.unhexlify(b"00c00000%.02x"%sw2) apdu = [x for x in apdu] @@ -161,8 +184,9 @@ class GPGCard() : resp = resp + resp2 resp = bytes(resp) sw = (sw1<<8)|sw2 - #print("xch S resp: %s %.04x"%(binascii.hexlify(resp),sw)) - return resp,sw + if sw&sw_mask == sw_expected: + return resp,sw + raise GPGCardExcpetion(binascii.hexlify(resp), "%.04x"%sw) def _disconnect_ledger(self): return self.token.close() @@ -188,12 +212,14 @@ class GPGCard() : apdu = binascii.unhexlify(b"00CA%.04x00"%tag) return self.exchange(apdu) - def put_data(self,tag,value): - apdu = binascii.unhexlify(b"00DA%.04x%.02x"%(tag,len(value)))+value - return self.exchange(apdu) + def put_data(self,tag,value): + return self.exchange(binascii.unhexlify(b"00DA%.04x"%tag), value) - def verify(self,id,value): - apdu = binascii.unhexlify(b"002000%.02x%.02x"%(id,len(value)))+value + def verify(self,id,value, pinpad=False): + if pinpad: + apdu = binascii.unhexlify(b"EF2000%.02x00"%id) + else: + apdu = binascii.unhexlify(b"002000%.02x%.02x"%(id,len(value)))+value return self.exchange(apdu) def change_reference_data(self,id,value,new_value): @@ -215,7 +241,7 @@ class GPGCard() : return self.exchange(apdu) ### API interfaces ### - def get_all(self): + def get_all(self, with_key=False): self.reset() self.AID,sw = self.get_data(0x4f) @@ -283,7 +309,15 @@ class GPGCard() : self.private_03,sw = self.get_data(0x0103) self.private_04,sw = self.get_data(0x0104) - def set_all(self): + if with_key: + self.sig_key,sw = self.get_data(0x00B6) + self.dec_key,sw = self.get_data(0x00B8) + self.aut_key,sw = self.get_data(0x00A4) + + return True + + def set_all(self, with_key= False): + self.put_data(0x4f, self.AID[10:14]) self.put_data(0x0101, self.private_01) self.put_data(0x0102, self.private_02) @@ -317,9 +351,15 @@ class GPGCard() : self.put_data(0xd7, self.UIF_DEC) self.put_data(0xd8, self.UIF_AUT) - def backup(self, file_name): + if with_key: + self.put_data(0x00B6, self.sig_key) + self.put_data(0x00B8, self.dec_key) + self.put_data(0x00A4, self.aut_key) + return True + + def backup(self, file_name, with_key=False): f = open(file_name,mode='w+b') - self.get_all(); + self.get_all(with_key) pickle.dump( (self.AID, self.private_01, self.private_02, self.private_03, self.private_04, @@ -330,8 +370,10 @@ class GPGCard() : self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints, self.sig_date, self.dec_date, self.aut_date, self.cardholder_cert, - self.UIF_SIG, self.UIF_DEC, self.UIF_AUT), + self.UIF_SIG, self.UIF_DEC, self.UIF_AUT, + self.sig_key, self.dec_key, self.aut_key), f, 2) + return True def restore(self, file_name, seed_key=False): @@ -340,13 +382,15 @@ class GPGCard() : self.private_01, self.private_02, self.private_03, self.private_04, self.name, self.login, self.sex, self.url, self.sig_attribute, self.dec_attribute, self.aut_attribute, - self.status, + self.PW_status, self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints, self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints, self.sig_date, self.dec_date, self.aut_date, self.cardholder_cert, - self.UIF_SIG, self.UIF_DEC, self.UIF_AUT) = pickle.load(f) - self.set_all() + self.UIF_SIG, self.UIF_DEC, self.UIF_AUT, + self.sig_key, self.dec_key, self.aut_key) = pickle.load(f) + with_key = len(self.sig_key)>0 and len(self.dec_key)>0 and len(self.aut_key)>0 + self.set_all(with_key) if seed_key : apdu = binascii.unhexlify(b"0047800102B600") self.exchange(apdu) @@ -354,7 +398,7 @@ class GPGCard() : self.exchange(apdu) apdu = binascii.unhexlify(b"0047800102A400") self.exchange(apdu) - + return True @@ -399,9 +443,9 @@ class GPGCard() : d['SM'] = ('Secure Messaging', "no") if b1&0x40 : - d['CHAL'] = ('GET CHALLENGE', "yes") + d['CHAL'] = ('Get Challenge', "yes") else: - d['CHAL'] = ('GET CHALLENGE', "no") + d['CHAL'] = ('Get Challenge', "no") if b1&0x20 : d['KEY'] = ('Key import', "yes") @@ -479,6 +523,12 @@ class GPGCard() : return d #USER Info + def set_serial(self, ser): + ser=binascii.unhexlify(ser) + + self.AID = self.AID[0:10]+ser + self.put_data(0x4f, self.AID[10:14]) + # internals are always store as byres, get/set automatically convert from/to def set_name(self,name): """ Args: @@ -532,13 +582,13 @@ class GPGCard() : #PINs - def verify_pin(self,id,value): + def verify_pin(self,id,value, pinpad=False): """ Args: id (int) : 0x81, 0x82, ox83 value (str) : ascii string """ value = value.encode('ascii') - resp,sw = self.verify(id,value) + resp,sw = self.verify(id,value, pinpad) return sw == 0x9000 def change_pin(self, id, value,new_value): @@ -606,6 +656,20 @@ class GPGCard() : fprint = '-' return fprints.decode('ascii') + def set_key_fingerprints(self, key, fprints): + + fprints = binascii.unhexlify(fprints) + if key=='sig': + self.sig_fingerprints = fprints + self.put_data(0xc7, fprints) + if key=='aut': + self.aut_fingerprints = fprints + self.put_data(0xc9, fprints) + if key=='dec': + self.dec_fingerprints = fprints + self.put_data(0xc8, fprints) + + def get_key_CA_fingerprints(self, key): """ Returns: (str) CA fingerprints hex string diff --git a/pytools/gpgcard/gpgcli.py b/pytools/gpgcard/gpgcli.py new file mode 100644 index 0000000..6eb13c2 --- /dev/null +++ b/pytools/gpgcard/gpgcli.py @@ -0,0 +1,123 @@ +# Copyright 2018 Cedric Mesnil , Ledger SAS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import argparse + +from .gpgcard import GPGCard + +def get_argparser(): + parser = argparse.ArgumentParser(epilog=""" +Note 1: reset, backup, restore are always executed in THIS order + """ +) + parser.add_argument('--adm-pin', metavar='PIN', help='Administrative PIN, if pinpad not used') + parser.add_argument('--backup', help='Perfom a full backup except the key', action='store_true') + parser.add_argument('--backup-keys', help='Perfom keys encrypted backup', action='store_true') + parser.add_argument('--file', help='basckup/restore file', type=str, default='backup_card.pickle') + parser.add_argument('--pinpad', help='PIN validation will be deledated to pinpad', action='store_true') + parser.add_argument('--reader', help='PCSC reader', type=str, default='pcsc:Ledger') + parser.add_argument('--reset', help='Reset the application. All data are erased', action='store_true') + parser.add_argument('--restore', help='Perfom a full restore except the key', action='store_true') + parser.add_argument('--set-serial', metavar='SERIAL', help='set the four serial bytes') + parser.add_argument('--set-fp', metavar='SIG:DEC:AUT', help='sig:dec:aut fingerprints, 20 bytes each in hexa') + parser.add_argument('--seed-key', help='Regenerate all keys, based on seed mode', action='store_true') + parser.add_argument('--user-pin', metavar='PIN', help='User PIN, if pinpad not used') + return parser + +def banner(): + print( +""" +GPG Ledger Admin Tool v0.1. +Copyright 2018 Cedric Mesnil , Ledger SAS + +""" + ) + + +def error(msg) : + print("Error: ") + print(" "+msg) + sys.exit() + +banner() + +args = get_argparser().parse_args() + +if args.backup and args.restore: + error('Only one backup or restore must be specified') + + +if not args.pinpad: + if not args.adm_pin is None or not args.user_pin: + error('If pinpad is not use, userpin and admpin must be provided') + + +try: + + print("Connect to card...", end='', flush=True) + gpgcard = GPGCard() + gpgcard.connect(args.reader) + print("OK") + + print("Verfify PINs...", end='', flush=True) + if args.pinpad: + if not gpgcard.verify_pin(0x82, "", True) or not gpgcard.verify_pin(0x83, "", True): + error("PIN not verified") + else: + if not gpgcard.verify_pin(0x82, args.user_pin) or not gpgcard.verify_pin(0x83, args.adm_pin): + error("PIN not verified") + print("OK") + + if args.reset: + print("Reset application...", end='', flush=True) + if not gpgcard.terminate() or not gpgcard.activate(): + error ("NOK") + print("OK") + + print("Get card info...", end='', flush=True) + gpgcard.get_all() + print("OK", flush=True) + + if args.backup: + print("Backup application...", end='', flush=True) + if not gpgcard.backup(args.file, args.backup_keys): + error("NOK") + print("OK") + + if args.restore: + print("Restore application...", end='', flush=True) + if not gpgcard.restore(args.file, args.seed_key): + error("NOK") + print("OK", flush=True) + + if args.set_fp: + print("Set fingerprints...", end='', flush=True) + sig,dec,aut = args.setfp.split(":") + if (not gpgcard.set_key_fingerprints("sig", sig) or + not gpgcard.set_key_fingerprints("dec", dec) or + not gpgcard.set_key_fingerprints("aut", aut)): + error("NOK") + print("OK", flush=True) + + if args.set_serial: + print("Set serial...", end='', flush=True) + serial = binascii.unhexilify(args.set_serial) + if len(serial>8) : + error('Serial must be a 4 bytes hexa string value (8 characters)') + gpgcard.set_serial(args.set_serial) + print("OK", flush=True) +except Exception as e: + error(str(e)) diff --git a/src/gpg_api.h b/src/gpg_api.h index 378fd01..f6fe7d8 100644 --- a/src/gpg_api.h +++ b/src/gpg_api.h @@ -31,7 +31,12 @@ int gpg_apdu_select_data(unsigned int ref, int reccord) ; int gpg_apdu_get_data(unsigned int ref) ; int gpg_apdu_get_next_data(unsigned int ref) ; int gpg_apdu_put_data(unsigned int ref) ; +int gpg_apdu_get_key_data(unsigned int ref); +int gpg_apdu_put_key_data(unsigned int ref); +void gpg_pso_derive_slot_seed(int slot, unsigned char *seed); +void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, unsigned int idx, + unsigned char *Ski, unsigned int Ski_len); int gpg_apdu_pso(void); int gpg_apdu_internal_authenticate(void); int gpg_apdu_gen(void ); diff --git a/src/gpg_data.c b/src/gpg_data.c index 8273bf9..a879bbf 100644 --- a/src/gpg_data.c +++ b/src/gpg_data.c @@ -700,3 +700,181 @@ int gpg_apdu_put_data(unsigned int ref) { } + +static void gpg_init_keyenc(cx_aes_key_t *keyenc) { + unsigned char seed[32]; + + gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed); + gpg_pso_derive_key_seed(seed, (unsigned char*)PIC("key "), 1, seed, 16); + cx_aes_init_key(seed, 16, keyenc); +} + +//cmd +//resp TID API COMPAT len_pub len_priv priv +int gpg_apdu_get_key_data(unsigned int ref) { + cx_aes_key_t keyenc; + gpg_key_t *keygpg; + unsigned int len = 0; + gpg_init_keyenc(&keyenc); + + switch(ref) { + case 0x00B6: + keygpg = &G_gpg_vstate.kslot->sig; + break; + case 0x00B8: + keygpg = &G_gpg_vstate.kslot->dec; + break; + case 0x00A4: + keygpg = &G_gpg_vstate.kslot->aut; + break; + default: + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + + gpg_io_discard(1); + //clear part + gpg_io_insert_u32(TARGET_ID); + gpg_io_insert_u32(CX_APILEVEL); + gpg_io_insert_u32(CX_COMPAT_APILEVEL); + + //encrypted part + switch(keygpg->attributes.value[0]) { + case 0x01: //RSA + //insert pubkey; + gpg_io_insert_u32(4); + gpg_io_insert(keygpg->pub_key.rsa, 4); + + //insert privkey + gpg_io_mark(); + len = cx_aes(&keyenc, + CX_ENCRYPT|CX_CHAIN_CBC|CX_PAD_ISO9797M2|CX_LAST, + (unsigned char*)&keygpg->priv_key.rsa4096, sizeof(cx_rsa_4096_private_key_t), + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, GPG_IO_BUFFER_LENGTH-G_gpg_vstate.io_offset); + gpg_io_inserted(len); + gpg_io_set_offset(IO_OFFSET_MARK); + gpg_io_insert_u32(len); + gpg_io_set_offset(IO_OFFSET_END); + break; + + case 18: //ECC + case 19: + case 22: + //insert pubkey; + gpg_io_insert_u32(sizeof(cx_ecfp_640_public_key_t)); + gpg_io_insert((unsigned char*)&keygpg->pub_key.ecfp640, sizeof(cx_ecfp_640_public_key_t)); + + //insert privkey + gpg_io_mark(); + len = cx_aes(&keyenc, + CX_ENCRYPT|CX_CHAIN_CBC|CX_PAD_ISO9797M2|CX_LAST, + (unsigned char*)&keygpg->priv_key.ecfp640, sizeof(cx_ecfp_640_private_key_t), + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, GPG_IO_BUFFER_LENGTH-G_gpg_vstate.io_offset); + gpg_io_inserted(len); + gpg_io_set_offset(IO_OFFSET_MARK); + gpg_io_insert_u32(len); + gpg_io_set_offset(IO_OFFSET_END); + break; + + default: + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return SW_REFERENCED_DATA_NOT_FOUND; + } + return SW_OK; +} + +//cmd TID API COMPAT len_pub len_priv priv +//resp - +int gpg_apdu_put_key_data(unsigned int ref) { + cx_aes_key_t keyenc; + gpg_key_t *keygpg; + unsigned int len; + unsigned int offset; + + gpg_init_keyenc(&keyenc); + + switch(ref) { + case 0xB6: + keygpg = &G_gpg_vstate.kslot->sig; + break; + case 0xB8: + keygpg = &G_gpg_vstate.kslot->dec; + break; + case 0xA4: + keygpg = &G_gpg_vstate.kslot->aut; + break; + default: + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + + /* unsigned int target_id = */ + gpg_io_fetch_u32(); + /* unsigned int cx_apilevel = */ + gpg_io_fetch_u32(); + /* unsigned int cx_compat_apilevel = */ + gpg_io_fetch_u32(); + + switch(keygpg->attributes.value[0]) { + case 0x01: //RSA + //insert pubkey; + len = gpg_io_fetch_u32(); + if (len != 4) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + gpg_io_fetch_nv(keygpg->pub_key.rsa, len); + + //insert privkey + len = gpg_io_fetch_u32(); + if (len > (G_gpg_vstate.io_length-G_gpg_vstate.io_offset)) { + THROW(SW_WRONG_DATA); + } + offset = G_gpg_vstate.io_offset; + gpg_io_discard(0); + len = cx_aes(&keyenc, + CX_DECRYPT|CX_CHAIN_CBC|CX_PAD_ISO9797M2|CX_LAST, + G_gpg_vstate.work.io_buffer+offset, len, + G_gpg_vstate.work.io_buffer, GPG_IO_BUFFER_LENGTH); + if (len != sizeof(cx_rsa_4096_private_key_t)) { + THROW(SW_WRONG_DATA); + } + gpg_nvm_write((unsigned char*)&keygpg->priv_key.rsa4096, G_gpg_vstate.work.io_buffer, len); + break; + + case 18: //ECC + case 19: + case 22: + //insert pubkey; + len = gpg_io_fetch_u32(); + if (len != sizeof(cx_ecfp_640_public_key_t)) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + gpg_io_fetch_nv((unsigned char*)&keygpg->pub_key.ecfp640, len); + + //insert privkey + len = gpg_io_fetch_u32(); + if (len > (G_gpg_vstate.io_length-G_gpg_vstate.io_offset)) { + THROW(SW_WRONG_DATA); + } + offset = G_gpg_vstate.io_offset; + gpg_io_discard(0); + len = cx_aes(&keyenc, + CX_DECRYPT|CX_CHAIN_CBC|CX_PAD_ISO9797M2|CX_LAST, + G_gpg_vstate.work.io_buffer+offset, len, + G_gpg_vstate.work.io_buffer, GPG_IO_BUFFER_LENGTH); + if (len != sizeof(cx_ecfp_640_private_key_t)) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + gpg_nvm_write((unsigned char*)&keygpg->priv_key.ecfp640, G_gpg_vstate.work.io_buffer, len); + break; + + default: + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return SW_REFERENCED_DATA_NOT_FOUND; + } + gpg_io_discard(1); + return SW_OK; +} diff --git a/src/gpg_dispatch.c b/src/gpg_dispatch.c index 9640a36..2962d37 100644 --- a/src/gpg_dispatch.c +++ b/src/gpg_dispatch.c @@ -91,6 +91,10 @@ void gpg_check_access_ins() { case INS_ACTIVATE_FILE: return; + + default: + THROW(SW_INS_NOT_SUPPORTED); + break; } THROW(SW_CONDITIONS_NOT_SATISFIED); } @@ -144,7 +148,7 @@ void gpg_check_access_read_DO() { case 0x00D8: return; - //PW1 + //PW2 case 0x0103: if (gpg_pin_is_verified(PIN_ID_PW2)) { return; @@ -152,6 +156,9 @@ void gpg_check_access_read_DO() { break; //PW3 + case 0x00B6: + case 0x00A4: + case 0x00B8: case 0x0104: if (gpg_pin_is_verified(PIN_ID_PW3)) { return; @@ -173,7 +180,7 @@ void gpg_check_access_write_DO() { ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; switch(ref) { - //PW1 + //PW2 case 0x0101: case 0x0103: case 0x01F2: @@ -196,6 +203,9 @@ void gpg_check_access_write_DO() { case 0x5F50: case 0x5F48: case 0x7F21: + case 0x00B6: + case 0x00A4: + case 0x00B8: case 0x00C1: case 0x00C2: case 0x00C3: @@ -206,8 +216,9 @@ void gpg_check_access_write_DO() { case 0x00C9: case 0x00C6: case 0x00CA: - case 0x00CD: + case 0x00CB: case 0x00CC: + case 0x00CD: case 0x00CE: case 0x00CF: case 0x00D0: @@ -223,9 +234,7 @@ void gpg_check_access_write_DO() { return; } break; - } - THROW(SW_CONDITIONS_NOT_SATISFIED); } @@ -252,7 +261,6 @@ int gpg_dispatch() { return sw; break; - /* --- ACTIVATE/TERMINATE FILE --- */ case INS_ACTIVATE_FILE: gpg_io_discard(0); @@ -270,6 +278,7 @@ int gpg_dispatch() { break; } THROW(SW_CONDITIONS_NOT_SATISFIED); + break; } @@ -322,8 +331,16 @@ int gpg_dispatch() { case INS_GET_DATA: gpg_check_access_read_DO(); - - sw = gpg_apdu_get_data(tag); + switch(tag) { + case 0x00B6: + case 0x00A4: + case 0x00B8: + sw = gpg_apdu_get_key_data(tag); + break; + default: + sw = gpg_apdu_get_data(tag); + break; + } break; case INS_GET_NEXT_DATA: @@ -335,7 +352,16 @@ int gpg_dispatch() { case INS_PUT_DATA_ODD: case INS_PUT_DATA: gpg_check_access_write_DO(); - sw = gpg_apdu_put_data(tag); + switch(tag) { + case 0x00B6: + case 0x00A4: + case 0x00B8: + sw = gpg_apdu_put_key_data(tag); + break; + default: + sw = gpg_apdu_put_data(tag); + break; + } break; /* --- PIN -- */ diff --git a/src/gpg_gen.c b/src/gpg_gen.c index 22564f4..e10437c 100644 --- a/src/gpg_gen.c +++ b/src/gpg_gen.c @@ -22,7 +22,7 @@ /* @in slot slot num [0 ; GPG_KEYS_SLOTS[ * @out seed 32 bytes master seed for given slot */ -static void gpg_pso_derive_slot_seed(int slot, unsigned char *seed) { +void gpg_pso_derive_slot_seed(int slot, unsigned char *seed) { unsigned int path[2]; unsigned char chain[32]; @@ -38,8 +38,8 @@ static void gpg_pso_derive_slot_seed(int slot, unsigned char *seed) { * @out Ski generated sub_seed * @in Ski_len sub-seed length */ -static void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, unsigned int idx, - unsigned char *Ski, unsigned int Ski_len) { +void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, unsigned int idx, + unsigned char *Ski, unsigned int Ski_len) { unsigned char h[32]; h[0] = idx >>8; @@ -59,7 +59,7 @@ static void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, int gpg_apdu_gen() { unsigned int t,l,ksz,reset_cnt; gpg_key_t *keygpg; - unsigned char seed[32]; + unsigned char seed[66]; unsigned char* name; switch ((G_gpg_vstate.io_p1<<8)|G_gpg_vstate.io_p2) { @@ -168,7 +168,7 @@ int gpg_apdu_gen() { THROW(SW_REFERENCED_DATA_NOT_FOUND); return SW_REFERENCED_DATA_NOT_FOUND; } - if ((G_gpg_vstate.io_p2 == 0x01) | (G_gpg_vstate.seed_mode)) { + if ((G_gpg_vstate.io_p2 == 0x01) || (G_gpg_vstate.seed_mode)) { ksz = gpg_curve2domainlen(curve); gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed); gpg_pso_derive_key_seed(seed, name, 1, seed, ksz); diff --git a/src/gpg_init.c b/src/gpg_init.c index 07e4d85..ff77e2b 100644 --- a/src/gpg_init.c +++ b/src/gpg_init.c @@ -252,7 +252,10 @@ const unsigned char C_default_Histo[] = { 0x90, 0x00 }; -const unsigned char C_default_AlgoAttrRSA[] = { + +// Default template: RSA2048 +#if 1 +const unsigned char C_default_AlgoAttr_sig[] = { // RSA 0x01, // Modulus default length 2048 @@ -262,41 +265,44 @@ const unsigned char C_default_AlgoAttrRSA[] = { // std: e,p,q with modulus (n) 0x01 }; +const unsigned char C_default_AlgoAttr_dec[] = { + // RSA + 0x01, + // Modulus default length 2048 + 0x08, 0x00, + // PubExp length 32 + 0x00, 0x20, + // std: e,p,q with modulus (n) + 0x01 +}; +#endif +// Default template: NIST P256 #if 0 -const unsigned char C_default_AlgoAttrECC_sig[] = { +const unsigned char C_default_AlgoAttr_sig[] = { // ecdsa 0x13, // NIST-P256 0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07 }; -const unsigned char C_default_AlgoAttrECC_dec[] = { +const unsigned char C_default_AlgoAttr_dec[] = { // ecdh 0x12, // NIST-P256 0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07 }; -#elif 0 -const unsigned char C_default_AlgoAttrECC_sig[] = { - // ecdsa - 0x13, - // NIST-P384 - 0x2B, 0x81, 0x04, 0x00 , 0x22 -}; -const unsigned char C_default_AlgoAttrECC_dec[] = { - // ecdh - 0x12, - // NIST-P384 - 0x2B, 0x81, 0x04, 0x00 , 0x22 -}; -#elif 1 -const unsigned char C_default_AlgoAttrECC_sig[] = { +#endif + + +// Default template: Ed/Cv-25519 +#if 0 +const unsigned char C_default_AlgoAttr_sig[] = { // eddsa 0x16, // ed25519 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01, }; -const unsigned char C_default_AlgoAttrECC_dec[] = { +const unsigned char C_default_AlgoAttr_dec[] = { // ecdh 0x12, //cv25519 @@ -410,15 +416,9 @@ int gpg_install(unsigned char app_state) { nvm_write(&N_gpg_pstate->default_RSA_exponent, G_gpg_vstate.work.io_buffer, 4); //config pin - #if 1 G_gpg_vstate.work.io_buffer[0] = PIN_MODE_CONFIRM; gpg_nvm_write(&N_gpg_pstate->config_pin, G_gpg_vstate.work.io_buffer, 1); USBD_CCID_activate_pinpad(3); - #else - G_gpg_vstate.work.io_buffer[0] = PIN_MODE_HOST; - gpg_nvm_write(&N_gpg_pstate->config_pin, G_gpg_vstate.work.io_buffer, 1); - USBD_CCID_activate_pinpad(0); - #endif //default key template: RSA 2048) @@ -426,32 +426,19 @@ int gpg_install(unsigned char app_state) { unsigned char uif[2]; uif[0] = 0x00; uif[1] = 0x20; -#if 1 - l = sizeof(C_default_AlgoAttrRSA); - gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.value, (void*)C_default_AlgoAttrRSA, l); - gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.length, &l, sizeof(unsigned int)); - gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.value, (void*)C_default_AlgoAttrRSA, l); - gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.length, &l, sizeof(unsigned int)); - gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.value, (void*)C_default_AlgoAttrRSA, l); - gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.length, &l, sizeof(unsigned int)); - #else - l = sizeof(C_default_AlgoAttrECC_sig); - gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.value, (void*)C_default_AlgoAttrECC_sig, l); + l = sizeof(C_default_AlgoAttr_sig); + gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.value, (void*)C_default_AlgoAttr_sig, l); gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.length, &l, sizeof(unsigned int)); - gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.value, (void*)C_default_AlgoAttrECC_sig, l); + gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.value, (void*)C_default_AlgoAttr_sig, l); gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.length, &l, sizeof(unsigned int)); - l = sizeof(C_default_AlgoAttrECC_dec); - gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.value, (void*)C_default_AlgoAttrECC_dec, l); + l = sizeof(C_default_AlgoAttr_dec); + gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.value, (void*)C_default_AlgoAttr_dec, l); gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.length, &l, sizeof(unsigned int)); -#endif gpg_nvm_write(&N_gpg_pstate->keys[s].sig.UIF, &uif, 2); gpg_nvm_write(&N_gpg_pstate->keys[s].dec.UIF, &uif, 2); gpg_nvm_write(&N_gpg_pstate->keys[s].aut.UIF, &uif, 2); - - } } - return 0; } diff --git a/src/gpg_types.h b/src/gpg_types.h index c98b766..b090f90 100644 --- a/src/gpg_types.h +++ b/src/gpg_types.h @@ -65,6 +65,9 @@ typedef struct gpg_key_s { /* C1 C2 C3 */ LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH); /* key value */ + /* WARN: changing the cx__t structures breaks backup/restore. Adapt backup/restore code + * to ensure backward compatibility. + */ union { cx_rsa_private_key_t rsa; cx_rsa_1024_private_key_t rsa1024; @@ -96,9 +99,6 @@ typedef struct gpg_key_s { unsigned char date[4]; /* D6/D7/D8- */ unsigned char UIF[2]; - - - } gpg_key_t;