mirror of
https://github.com/LedgerHQ/openpgp-card-app
synced 2024-11-09 07:10:30 +00:00
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
This commit is contained in:
parent
80ee7ef8d1
commit
14cfe899ff
8
Makefile
8
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)
|
||||
|
||||
|
@ -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))
|
||||
if sw&sw_mask == sw_expected:
|
||||
return resp,sw
|
||||
raise GPGCardExcpetion(binascii.hexlify(resp), "%.04x"%sw)
|
||||
|
||||
def _disconnect_ledger(self):
|
||||
return self.token.close()
|
||||
@ -189,10 +213,12 @@ class GPGCard() :
|
||||
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)
|
||||
return self.exchange(binascii.unhexlify(b"00DA%.04x"%tag), value)
|
||||
|
||||
def verify(self,id,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)
|
||||
|
||||
@ -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
|
||||
|
123
pytools/gpgcard/gpgcli.py
Normal file
123
pytools/gpgcard/gpgcli.py
Normal file
@ -0,0 +1,123 @@
|
||||
# Copyright 2018 Cedric Mesnil <cslashm@gmail.com>, 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 <cslashm@gmail.com>, 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))
|
@ -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 );
|
||||
|
178
src/gpg_data.c
178
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;
|
||||
}
|
||||
|
@ -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,9 +331,17 @@ int gpg_dispatch() {
|
||||
|
||||
case INS_GET_DATA:
|
||||
gpg_check_access_read_DO();
|
||||
|
||||
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:
|
||||
gpg_check_access_read_DO();
|
||||
@ -335,8 +352,17 @@ int gpg_dispatch() {
|
||||
case INS_PUT_DATA_ODD:
|
||||
case INS_PUT_DATA:
|
||||
gpg_check_access_write_DO();
|
||||
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 -- */
|
||||
case INS_VERIFY:
|
||||
|
@ -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,7 +38,7 @@ 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,
|
||||
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];
|
||||
@ -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);
|
||||
|
@ -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);
|
||||
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_AlgoAttrRSA, 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));
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.value, (void*)C_default_AlgoAttrRSA, 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));
|
||||
#else
|
||||
l = sizeof(C_default_AlgoAttrECC_sig);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.value, (void*)C_default_AlgoAttrECC_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.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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,9 @@ typedef struct gpg_key_s {
|
||||
/* C1 C2 C3 */
|
||||
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
|
||||
/* key value */
|
||||
/* WARN: changing the cx_<key>_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;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user