Enhancement + bugfix

Application

- full independant serial per slot
- EXIT instruction ('02') controlled by PW2
- remove code belonging  curves other than Ed25519/NISTP256
- fix io bug when APDU is 4 bytes lenght

Makefile:

- dual version XL/1slot. XL contains 3 key slots, normal only one.
  use "MULTISLOT=1 make" to compile XL version
- add exit rule

doc:

- update dev/user

tool:

- froce backup filename suffix: _slot<x>.pickle
- add --set_template option
- add --slot option
- rename --set-fp to  --set-fingerprints
pull/40/head 1.3.0
Cédric 6 years ago
parent 4b2dcacf41
commit 8f841e7188

@ -23,14 +23,21 @@ include $(BOLOS_SDK)/Makefile.defines
APP_LOAD_PARAMS=--appFlags 0x40 --path "2152157255'" --curve secp256k1 $(COMMON_LOAD_PARAMS)
APPNAME = OpenPGP
ifeq ($(MULTISLOT),)
GPG_MULTISLOT:=0
APPNAME:=OpenPGP
else
GPG_MULTISLOT:=1
APPNAME+=OpenPGP.XL
endif
SPECVERSION="3.3.1"
SPECVERSION:="3.3.1"
APPVERSION_M=1
APPVERSION_N=3
APPVERSION_P=0
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
APPVERSION_M:=1
APPVERSION_N:=3
APPVERSION_P:=0
APPVERSION:=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
ifeq ($(TARGET_NAME),TARGET_BLUE)
ICONNAME=images/icon_pgp_blue.gif
@ -38,7 +45,7 @@ else
ICONNAME=images/icon_pgp.gif
endif
DEFINES += $(GPG_CONFIG) GPG_VERSION=$(APPVERSION) GPG_NAME=$(APPNAME) SPEC_VERSION=$(SPECVERSION)
DEFINES += GPG_MULTISLOT=$(GPG_MULTISLOT) $(GPG_CONFIG) GPG_VERSION=$(APPVERSION) GPG_NAME=$(APPNAME) SPEC_VERSION=$(SPECVERSION)
################
# Default rule #
@ -100,6 +107,8 @@ load: all
run:
python -m ledgerblue.runApp --appName $(APPNAME)
exit:
echo -e "0020008206313233343536\n0002000000" |scriptor -r "Ledger Nano S [Nano S] (0001) 01 00"
delete:
python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS)

@ -66,6 +66,12 @@ This add-on specification propose new type of random generation:
- seeded prime number generation
Key Backup
~~~~~~~~~~
A full keybackup mecanism is provided.
GPG-ledger
==========
@ -178,6 +184,43 @@ For a given length *L* and seed *S*:
- return rp
Key Backup & Restore
~~~~~~~~~~~~~~~~~~~~
In order to backup/restore private key the commands `put_data` and
`get_data` accept the tag `B6` (signature key), `B8`(encryption key),
`A4` (authentication).
put_data command accept the exact output of get_data. The get_data command
return both the public and private key.
For security and confidentiality private key is returned encryped in AES.
The key used is derived according to previously described AES key derivation
with name 'key '.
The data payload is formatted as follow:
+-------+--------------------------------------------------+
| size | Description |
+=======+==================================================+
| 4 | OS Target ID |
+-------+--------------------------------------------------+
| 4 | API Level |
+-------+--------------------------------------------------+
| 4 | compliance Level |
+-------+--------------------------------------------------+
| 4 | public key size |
+-------+--------------------------------------------------+
| var | public key |
+-------+--------------------------------------------------+
| 4 | private key size |
+-------+--------------------------------------------------+
| var | encrypted private key |
+-------+--------------------------------------------------+
APDU Modification
-----------------
@ -243,7 +286,7 @@ Byte 3 is endoced as follow:
+-------+------------+-------------+
| 01F1 | Always | Verify PW3 |
+-------+------------+-------------+
| 01F2 | Always | Verify PW1 |
| 01F2 | Always | Verify PW2 |
+-------+------------+-------------+

@ -46,7 +46,7 @@ Introduction
GnuPG application for Ledger Blue and Nano S
This application implements "The OpenPGP card" specification revision 3.0. This specification is available in doc directory and at https://g10code.com/p-card.html .
This application implements "The OpenPGP card" specification revision 3.3. This specification is available in doc directory and at https://g10code.com/p-card.html .
The application supports:
@ -169,7 +169,6 @@ The full menu layout is :
| RSA 3072
| RSA 4096
| NIST P256
| Brainpool 256R1
| ED25519
| Set Template
| Seed mode
@ -195,6 +194,7 @@ The full menu layout is :
| A "**+**" after the entry label means current value.
Device Info
-------------
@ -213,6 +213,8 @@ encode the current slot value.
Select Slot
-------------
This menu is only available on ``XL`` version
A Slot is a set of
three key pairs *Signature, Decryption, Authentication* as defined by gnupg
specification.
@ -264,7 +266,6 @@ Supported curve name are:
- secp256k1 with tag 19
- nistp256 with tag 19
- brainpoolP256r1 with tag 19
- cv25519 (only for key 2)
- ed25519 with tag 22 (only for key 1 and 3)
@ -860,8 +861,8 @@ generate the two other under a new identity and will erase existing keys
on the current slot on the device.
Nevertheless, if you want to use a different identity for ssh login, you can use
another slot on the device. See `Nano S OpenPGP Card application explained`_
and `Generate new key pair`_.
another slot on the device. See `Nano S OpenPGP Card application explained`
and `Generate new key pair`.
Add sub-key
@ -1078,6 +1079,166 @@ Now, if everything is correctly setup and running, an ``ssh-add -l`` should sho
And you should be able to ssh to your remote server with your gpg key!
Backup and Restore
------------------
Introduction
~~~~~~~~~~~~
"The OpenPGP card" specification does not provide any mechanism for backuping you key.
Thus if you generate your keys on device and loose it, you definitively loose you private key.
In order to avoid such extreme panic situation, a backup/restore mechanism is provided.
At any time you can backup a snapshot of your device data, including your private keys.
All public data are retrieve in clear form. The private key are stored
encrypted with a key derived from your seed, i.e. from your 24 BIP words.
The backup/restore tool is located in ``pytools`` directory:
| ``usage: gpgcli.py [-h] [--adm-pin PIN] [--backup] [--backup-keys] [--file FILE]``
| `` [--pinpad] [--reader READER] [--reset] [--restore]``
| `` [--set-serial SERIAL] [--set-fp SIG:DEC:AUT] [--seed-key]``
| `` [--user-pin PIN]``
|
| ``optional arguments:``
| `` -h, --help show this help message and exit``
| `` --adm-pin PIN Administrative PIN, if pinpad not used``
| `` --backup Perfom a full backup except the key``
| `` --backup-keys Perfom keys encrypted backup``
| `` --file FILE basckup/restore file``
| `` --pinpad PIN validation will be deledated to pinpad``
| `` --reader READER PCSC reader``
| `` --reset Reset the application. All data are erased``
| `` --restore Perfom a full restore except the key``
| `` --set-serial SERIAL set the four serial bytes``
| `` --set-fp SIG:DEC:AUT sig:dec:aut fingerprints, 20 bytes each in hexa``
| `` --seed-key Regenerate all keys, based on seed mode``
| `` --slot SLOT slot to backup``
| `` --user-pin PIN User PIN, if pinpad not used``
First you must either provide your pin codes or use the pinpad (onscreen pin). This is
done by giving either ``--adm-pin`` AND ``--user-pin`` or ``--pinpad``. Note that
using ``--xx-pin`` may compromise your pin codes.
Then you must precise if you want a backup or a restore with ``--backup`` or ``--restore``
By default backup is performed without saving keys, assuming you use the seed mode.
If you also want to backup keys you have to pass the ``--backup-keys`` option.
Note that backup and restore works on current slot, so you have to perform a backup per slot
even if some data are shared. You can precise the slot/backup to restore with ``--slot``
Backup and Restore example
~~~~~~~~~~~~~~~~~~~~~~~~~~
full backup command:
| ``python3 -m gpgcard.gpgcli --backup --pinpad --backup-keys``
backup command without private keys:
| ``python3 -m gpgcard.gpgcli --backup --pinpad ``
full restore command:
| ``python3 -m gpgcard.gpgcli --backup --pinpad``
full restore command with seed key generation:
| ``python3 -m gpgcard.gpgcli --backup --pinpad --seed``
Restore without backup
~~~~~~~~~~~~~~~~~~~~~~
If you have seeded key but do not have done a backup and still have your keyring, there is a
solution to restore at least the key and their related information: serial and fingerprints.
All other information such as name, url, ... shall be set manually with ``gpg --card-edit``.
**Step 1: retrieve information**
Run the command ``gpg --edit-key John``, replace John by your own key id.
| ``$ gpg --edit-key John``
| ``gpg: WARNING: unsafe permissions on homedir './test/ring'``
| ``gpg (GnuPG) 2.2.4; Copyright (C) 2017 Free Software Foundation, Inc.``
| ``This is free software: you are free to change and redistribute it.``
| ``There is NO WARRANTY, to the extent permitted by law.``
|
| ``Secret key is available.``
|
| ``sec ed25519/8451AAF7D43D1095``
| `` created: 2018-10-10 expires: never usage: SC ``
| `` card-no: 2C97 FD6C11BE``
| `` trust: ultimate validity: ultimate``
| ``ssb ed25519/C5A8FB078520ABBB``
| `` created: 2018-10-10 expires: never usage: A ``
| `` card-no: 2C97 FD6C11BE``
| ``ssb cv25519/0953D871FC4B9EA4``
| `` created: 2018-10-10 expires: never usage: E ``
| `` card-no: 2C97 FD6C11BE``
| ``[ultimate] (1). John Doe``
|
| ``gpg> ``
|
The ``usage`` field tells you each key purpose: ``SC`` or ``S`` for signature, ``A`` for authentication, ``E`` for encryption.
The ``card-no``field provides you with the serial number of the card on which the key are stored.
You should have three or less keys with the same serial. These are the keys we want to restore.
For each key you also have the key template (rsa2048, rsa3072, rsa4096, ed2559, cv25519) followed by the
short fingerprint, e.g. ``ed25519/8451AAF7D43D1095``
Note the serial and the three key template names: ``FD6C11BE`` , ``ed25519:cv25519:ed25519``.
Take care of the order: ``SC:E:A``.
Now type the ``quit`` command.
To get the full fingerprint of each key, run (yes twice ``--fingerprint``):
``gpg --fingerprint --fingerprint John``,
| ``$ gpg --fingerprint --fingerprint John``
| ``gpg: WARNING: unsafe permissions on homedir './test/ring'``
| ``pub ed25519 2018-10-10 [SC]``
| `` 2C68 8345 BDDA 0EDF B24D B4FB 8451 AAF7 D43D 1095``
| ``uid [ultimate] John Doe``
| ``sub ed25519 2018-10-10 [A]``
| `` CEC5 9AE6 A766 14BC 3C6D 37D9 C5A8 FB07 8520 ABBB``
| ``sub cv25519 2018-10-10 [E]``
| `` DF15 7BD4 AC3B D1EE 9910 99C8 0953 D871 FC4B 9EA4``
Assemble the three full fingerprint, corresponding to the one identified previously,
in the the following order ``SC:E:A`` :
``2C688345BDDA0EDFB24DB4FB8451AAF7D43D1095:DF157BD4AC3BD1EE991099C80953D871FC4B9EA4:
CEC59AE6A76614BC3C6D37D9C5A8FB078520ABBB``.
**Step 1: restore**
Plug you Nano S and run the OpenPGP application.
Finally run the following command :
| ``python3 -m gpgcard.gpgcli --pinpad --set-template ed255519:cv25519:ed255519 --set-fingerprints ``
| `` '2C688345BDDA0EDFB24DB4FB8451AAF7D43D1095:DF157BD4AC3BD1EE991099C80953D871FC4B9EA4:CEC59AE6A76614BC3C6D37D9C5A8FB078520ABBB'``
| `` --set-serial 'FD6C11BE' --seed ``
Trouble/FAQ
-----------
@ -1110,6 +1271,7 @@ Add the follwing option to ~/.gnupg/scdaemon.conf
| ``debug-all``
Make a nice issue report under github providing log and and command line you run.
**!*WARNING*!** : this may reveal confidential information such as key values. Do your log with a test key.

@ -2,4 +2,4 @@
rm -f blue-app-monero.pdf blue-app-monero.latex
pandoc -s --template=blue-app-openpgp-card.template -f rst+raw_tex+line_blocks+citations -t latex --toc -N -o blue-app-openpgp-card.pdf blue-app-openpgp-card.rst
pandoc -s --template=blue-app-openpgp-card.template -f rst+raw_tex+line_blocks+citations -V geometry:a4paper -V geometry:margin=1in -V fontsize=10pt -t latex --toc -N -o blue-app-openpgp-card.pdf blue-app-openpgp-card.rst

@ -212,7 +212,7 @@ class GPGCard() :
apdu = binascii.unhexlify(b"00CA%.04x00"%tag)
return self.exchange(apdu)
def put_data(self,tag,value):
def put_data(self,tag,value):
return self.exchange(binascii.unhexlify(b"00DA%.04x"%tag), value)
def verify(self,id,value, pinpad=False):
@ -244,6 +244,7 @@ class GPGCard() :
def get_all(self, with_key=False):
self.reset()
self.slot,sw = self.get_data(0x01F2)
self.AID,sw = self.get_data(0x4f)
self.login ,sw = self.get_data(0x5e)
self.url,sw = self.get_data(0x5f50)
@ -316,7 +317,7 @@ class GPGCard() :
return True
def set_all(self, with_key= False):
def set_all(self):
self.put_data(0x4f, self.AID[10:14])
self.put_data(0x0101, self.private_01)
@ -351,15 +352,22 @@ class GPGCard() :
self.put_data(0xd7, self.UIF_DEC)
self.put_data(0xd8, self.UIF_AUT)
if with_key:
if len(self.sig_key):
self.put_data(0x00B6, self.sig_key)
if len(self.dec_key):
self.put_data(0x00B8, self.dec_key)
if len(self.aut_key):
self.put_data(0x00A4, self.aut_key)
return True
def _backup_file_name(self,file_name):
return file_name+"_slot%d"%(self.slot[0]+1)+".pickle"
def backup(self, file_name, with_key=False):
f = open(file_name,mode='w+b')
self.get_all(with_key)
file_name = self._backup_file_name(file_name)
f = open(file_name,mode='w+b')
pickle.dump(
(self.AID,
self.private_01, self.private_02, self.private_03, self.private_04,
@ -376,7 +384,8 @@ class GPGCard() :
return True
def restore(self, file_name, seed_key=False):
def restore(self, file_name):
file_name = self._backup_file_name(file_name)
f = open(file_name,mode='r+b')
(self.AID,
self.private_01, self.private_02, self.private_03, self.private_04,
@ -388,19 +397,18 @@ class GPGCard() :
self.sig_date, self.dec_date, self.aut_date,
self.cardholder_cert,
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)
apdu = binascii.unhexlify(b"0047800102B800")
self.exchange(apdu)
apdu = binascii.unhexlify(b"0047800102A400")
self.exchange(apdu)
self.sig_key, self.dec_key, self.aut_key) = pickle.load(f)
self.set_all()
return True
def seed_key(self):
apdu = binascii.unhexlify(b"0047800102B600")
self.exchange(apdu)
apdu = binascii.unhexlify(b"0047800102B800")
self.exchange(apdu)
apdu = binascii.unhexlify(b"0047800102A400")
self.exchange(apdu)
def decode_AID(self):
return {
@ -522,6 +530,13 @@ class GPGCard() :
return d
#slot
def select_slot(self, slot):
""" Args:
slot (int) : slot id (1 to MAX) to select
"""
self.put_data( 0x01F2, (slot-1).to_bytes(1,'big'))
#USER Info
def set_serial(self, ser):
ser=binascii.unhexlify(ser)
@ -751,10 +766,16 @@ class GPGCard() :
print ("NONE: %s"%binascii.hexlify(attributes))
return None
def set_template(self, template):
def set_template(self, sig, dec, aut):
"""
See get_template
"""
if (sig):
self.put_data(0x00C1, binascii.unhexlify(sig))
if dec:
self.put_data(0x00C2, binascii.unhexlify(dec))
if aut:
self.put_data(0x00C3, binascii.unhexlify(aut))
pass

@ -15,26 +15,31 @@
import sys
import argparse
import binascii
from .gpgcard import GPGCard
def get_argparser():
parser = argparse.ArgumentParser(epilog="""
Note 1: reset, backup, restore are always executed in THIS order
reset, backup, restore are always executed in THIS order.
Template identifiers are ed2559, cv25519, rsa2048, rsa3072, rsa4096.
"""
)
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')
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='gpg_backup')
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-templates', metavar='SIG:DEC:AUT', help='sig:dec:aut templates identifier')
parser.add_argument('--set-fingerprints', 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('--slot', metavar='SLOT', help='slot to backup', type=int, default=1)
parser.add_argument('--user-pin', metavar='PIN', help='User PIN, if pinpad not used')
return parser
def banner():
@ -67,7 +72,7 @@ if not args.pinpad:
try:
print("Connect to card...", end='', flush=True)
print("Connect to card %s..."%args.reader, end='', flush=True)
gpgcard = GPGCard()
gpgcard.connect(args.reader)
print("OK")
@ -81,10 +86,14 @@ try:
error("PIN not verified")
print("OK")
print("Select slot %d..."%args.slot, end='', flush=True)
gpgcard.select_slot(args.slot)
print("OK")
if args.reset:
print("Reset application...", end='', flush=True)
if not gpgcard.terminate() or not gpgcard.activate():
error ("NOK")
gpgcard.terminate()
gpgcard.activate()
print("OK")
print("Get card info...", end='', flush=True)
@ -99,24 +108,42 @@ try:
if args.restore:
print("Restore application...", end='', flush=True)
if not gpgcard.restore(args.file, args.seed_key):
if not gpgcard.restore(args.file):
error("NOK")
print("OK", flush=True)
if args.set_fp:
if args.set_templates:
print("Set template...", end='', flush=True)
templates= {
'rsa2048' : "010800002001",
'rsa3072' : "010C00002001",
'rsa4096' : "011000002001",
'nistp256' : "132A8648CE3D030107",
'ed255519' : "162B06010401DA470F01",
'cv25519' : "122B060104019755010501"
}
sig,dec,aut = args.set_templates.split(":")
gpgcard.set_template(templates[sig],templates[dec],templates[aut])
print("OK", flush=True)
if (args.seed_key):
print("Seed Key...", end='', flush=True)
gpgcard.seed_key();
print("OK", flush=True)
if args.set_fingerprints:
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")
sig,dec,aut = args.set_fingerprints.split(":")
gpgcard.set_key_fingerprints("sig", sig)
gpgcard.set_key_fingerprints("dec", dec)
gpgcard.set_key_fingerprints("aut", aut)
print("OK", flush=True)
if args.set_serial:
print("Set serial...", end='', flush=True)
serial = binascii.unhexilify(args.set_serial)
if len(serial>8) :
if len(args.set_serial) != 8 :
error('Serial must be a 4 bytes hexa string value (8 characters)')
serial = binascii.unhexlify(args.set_serial)
gpgcard.set_serial(args.set_serial)
print("OK", flush=True)
except Exception as e:

@ -73,8 +73,9 @@ int gpg_apdu_get_data(unsigned int ref) {
/* ----------------- Application ----------------- */
/* Full Application identifier */
case 0x004F:
gpg_io_insert(N_gpg_pstate->AID, 16);
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset-3] |= G_gpg_vstate.slot+1;
gpg_io_insert(N_gpg_pstate->AID, 10);
gpg_io_insert(G_gpg_vstate.kslot->serial, 4);
gpg_io_insert_u16(0x0000);
break;
/* Historical bytes, */
case 0x5F52:
@ -104,6 +105,7 @@ int gpg_apdu_get_data(unsigned int ref) {
/* ----------------- aid, histo, ext_length, ... ----------------- */
case 0x6E:
gpg_io_insert_tlv(0x4F, 16, N_gpg_pstate->AID);
os_memmove(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset-6, G_gpg_vstate.kslot->serial, 4);
gpg_io_insert_tlv(0x5F52, 15, N_gpg_pstate->histo);
gpg_io_insert_tlv(0x7F66, sizeof(C_ext_length), C_ext_length);
@ -282,8 +284,7 @@ int gpg_apdu_put_data(unsigned int ref) {
if (G_gpg_vstate.io_length != 4) {
THROW(SW_WRONG_LENGTH);
}
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+3] &= ~0x07;
nvm_write(&N_gpg_pstate->AID[10], &G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset], 4);
nvm_write(G_gpg_vstate.kslot->serial, &G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset], 4);
break;
/* ----------------- Extended Header list -----------------*/

@ -27,6 +27,12 @@ void gpg_check_access_ins() {
ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ;
switch (G_gpg_vstate.io_ins) {
case INS_EXIT:
if (gpg_pin_is_verified(PIN_ID_PW2)) {
return;
}
break;
case INS_SELECT:
return;
case INS_GET_DATA:
@ -43,6 +49,7 @@ void gpg_check_access_ins() {
if (gpg_pin_is_verified(PIN_ID_PW3) || gpg_pin_is_verified(PIN_ID_RC)) {
return;
}
break;
case INS_PUT_DATA:
case INS_PUT_DATA_ODD:
@ -298,6 +305,12 @@ int gpg_dispatch() {
sw = debug_apdu();
break;
#endif
case INS_EXIT:
os_sched_exit(0);
sw = SW_OK;
break;
/* --- CHALLENGE --- */
case INS_GET_CHALLENGE:
sw = gpg_apdu_get_challenge();

@ -32,17 +32,12 @@ const unsigned char C_MAGIC[8] = {
/* --ECC OID -- */
/* ----------------------*/
/*
//brainpool 256t1: 1.3.36.3.3.2.8.1.1.8
const unsigned char C_OID_BRAINPOOL256T1[9] = {
0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07
};
*/
//secp256r1 / NIST P256 /ansi-x9.62 : 1.2.840.10045.3.1.7
const unsigned char C_OID_SECP256R1[8] = {
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
};
/*
//secp384r1 / NIST P384 /ansi-x9.62 :1.3.132.0.34
const unsigned char C_OID_SECP384R1[5] = {
0x2B, 0x81, 0x04, 0x00 , 0x22
@ -52,13 +47,18 @@ const unsigned char C_OID_SECP521R1[5] = {
0x2B, 0x81, 0x04, 0x00, 0x23
};
//secp256k1: 1.3.132.0.10
const unsigned char C_OID_SECP256K1[5] = {
0x2B, 0x81, 0x04, 0x00, 0x0A
};
*/
/*
//brainpool 256t1: 1.3.36.3.3.2.8.1.1.8
const unsigned char C_OID_BRAINPOOL256T1[9] = {
0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07
};
//brainpool 256r1: 1.3.36.3.3.2.8.1.1.7
const unsigned char C_OID_BRAINPOOL256R1[9] = {
0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x08
@ -71,7 +71,7 @@ const unsigned char C_OID_BRAINPOOL384R1[9] = {
const unsigned char C_OID_BRAINPOOL512R1[9] = {
0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D
};
*/
//Ed25519/curve25519: 1.3.6.1.4.1.11591.15.1
const unsigned char C_OID_Ed25519[9] = {
@ -84,20 +84,24 @@ const unsigned char C_OID_cv25519[10] = {
};
unsigned int gpg_oid2curve(unsigned char* oid, unsigned int len) {
if ( (len == sizeof(C_OID_SECP256K1)) && (os_memcmp(oid, C_OID_SECP256K1, len)==0) ) {
return CX_CURVE_SECP256K1;
}
if ( (len == sizeof(C_OID_SECP256R1)) && (os_memcmp(oid, C_OID_SECP256R1, len)==0) ) {
return CX_CURVE_SECP256R1;
}
/*
if ( (len == sizeof(C_OID_SECP256K1)) && (os_memcmp(oid, C_OID_SECP256K1, len)==0) ) {
return CX_CURVE_SECP256K1;
}
if ( (len == sizeof(C_OID_SECP384R1)) && (os_memcmp(oid, C_OID_SECP384R1, len)==0) ) {
return CX_CURVE_SECP384R1;
}
if ( (len == sizeof(C_OID_SECP521R1)) && (os_memcmp(oid, C_OID_SECP521R1, len)==0) ) {
return CX_CURVE_SECP521R1;
}
*/
/*
if ( (len == sizeof(C_OID_BRAINPOOL256R1)) && (os_memcmp(oid, C_OID_BRAINPOOL256R1, len)==0) ) {
return CX_CURVE_BrainPoolP256R1;
}
@ -107,7 +111,7 @@ unsigned int gpg_oid2curve(unsigned char* oid, unsigned int len) {
if ( (len == sizeof(C_OID_BRAINPOOL512R1)) && (os_memcmp(oid, C_OID_BRAINPOOL512R1, len)==0) ) {
return CX_CURVE_BrainPoolP512R1;
}
*/
if ( (len == sizeof(C_OID_Ed25519)) && (os_memcmp(oid, C_OID_Ed25519, len)==0) ) {
return CX_CURVE_Ed25519;
}
@ -130,15 +134,15 @@ unsigned int gpg_oid2curve(unsigned char* oid, unsigned int len) {
unsigned char* gpg_curve2oid(unsigned int cv, unsigned int *len) {
switch (cv) {
case CX_CURVE_SECP256R1:
*len = sizeof(C_OID_SECP256R1);
return (unsigned char*)PIC(C_OID_SECP256R1);
/*
case CX_CURVE_SECP256K1:
*len = sizeof(C_OID_SECP256K1);
return (unsigned char*)PIC(C_OID_SECP256K1);
case CX_CURVE_SECP256R1:
*len = sizeof(C_OID_SECP256R1);
return (unsigned char*)PIC(C_OID_SECP256R1);
case CX_CURVE_SECP384R1:
*len = sizeof(C_OID_SECP384R1);
return (unsigned char*)PIC(C_OID_SECP384R1);
@ -146,7 +150,9 @@ unsigned char* gpg_curve2oid(unsigned int cv, unsigned int *len) {
case CX_CURVE_SECP521R1:
*len = sizeof(C_OID_SECP521R1);
return (unsigned char*)PIC(C_OID_SECP521R1);
*/
/*
case CX_CURVE_BrainPoolP256R1:
*len = sizeof(C_OID_SECP256R1);
return (unsigned char*)PIC(C_OID_SECP256R1);
@ -158,7 +164,7 @@ unsigned char* gpg_curve2oid(unsigned int cv, unsigned int *len) {
case CX_CURVE_BrainPoolP512R1:
*len = sizeof(C_OID_SECP521R1);
return (unsigned char*)PIC(C_OID_SECP521R1);
*/
case CX_CURVE_Ed25519:
*len = sizeof(C_OID_Ed25519);
return (unsigned char*)PIC(C_OID_Ed25519);
@ -174,25 +180,11 @@ unsigned char* gpg_curve2oid(unsigned int cv, unsigned int *len) {
unsigned int gpg_curve2domainlen(unsigned int cv) {
switch (cv) {
case CX_CURVE_SECP256K1:
case CX_CURVE_SECP256R1:
case CX_CURVE_BrainPoolP256R1:
case CX_CURVE_Ed25519:
case CX_CURVE_Curve25519:
return 32;
case CX_CURVE_SECP384R1:
case CX_CURVE_BrainPoolP384R1:
return 48;
case CX_CURVE_BrainPoolP512R1:
return 64;
case CX_CURVE_SECP521R1:
return 66;
}
return 0;
}
@ -235,7 +227,7 @@ const unsigned char C_default_AID[] = {
//manufacturer
0x2C, 0x97,
//serial
0x42, 0x42, 0x42, 0x42,
0x00, 0x00, 0x00, 0x00,
//RFU
0x00,0x00
};
@ -253,7 +245,7 @@ const unsigned char C_default_Histo[] = {
};
// Default template: RSA2048
// Default template: RSA2048 010800002001 / 010800002001
#if 1
const unsigned char C_default_AlgoAttr_sig[] = {
// RSA
@ -277,7 +269,7 @@ const unsigned char C_default_AlgoAttr_dec[] = {
};
#endif
// Default template: NIST P256
// Default template: NIST P256 132A8648CE3D030107 / 122A8648CE3D030107
#if 0
const unsigned char C_default_AlgoAttr_sig[] = {
// ecdsa
@ -294,7 +286,7 @@ const unsigned char C_default_AlgoAttr_dec[] = {
#endif
// Default template: Ed/Cv-25519
// Default template: Ed/Cv-25519 162B06010401DA470F01 / 122B060104019755010501
#if 0
const unsigned char C_default_AlgoAttr_sig[] = {
// eddsa
@ -371,10 +363,14 @@ int gpg_install(unsigned char app_state) {
//AID
os_memmove(G_gpg_vstate.work.io_buffer, C_default_AID, sizeof(C_default_AID));
cx_rng(G_gpg_vstate.work.io_buffer+10, 4);
G_gpg_vstate.work.io_buffer[13] &= ~0x07;
gpg_nvm_write(N_gpg_pstate->AID, &G_gpg_vstate.work.io_buffer, sizeof(C_default_AID));
//Serial
cx_rng(G_gpg_vstate.work.io_buffer, 4*GPG_KEYS_SLOTS);
for (int s=0; s<GPG_KEYS_SLOTS; s++) {
gpg_nvm_write(N_gpg_pstate->keys[s].serial, G_gpg_vstate.work.io_buffer+4*s, 4);
}
if (app_state == STATE_ACTIVATE) {
//default sex: none

@ -242,17 +242,17 @@ int gpg_io_fetch(unsigned char* buffer, int len) {
#define MAX_OUT GPG_APDU_LENGTH
int gpg_io_do(unsigned int io_flags) {
unsigned int rx = 0;
//if pending input chaining
if (G_gpg_vstate.io_cla & 0x10) {
goto in_chaining;
}
if (io_flags & IO_ASYNCH_REPLY) {
// if IO_ASYNCH_REPLY has been set,
// gpg_io_exchange will return when IO_RETURN_AFTER_TX will set in ui
gpg_io_exchange(CHANNEL_APDU | IO_ASYNCH_REPLY, 0);
rx = gpg_io_exchange(CHANNEL_APDU | IO_ASYNCH_REPLY, 0);
} else {
// --- full out chaining ---
G_gpg_vstate.io_offset = 0;
@ -270,7 +270,7 @@ int gpg_io_do(unsigned int io_flags) {
xx = G_gpg_vstate.io_length-2;
}
G_io_apdu_buffer[tx+1] = xx;
gpg_io_exchange(CHANNEL_APDU, tx+2);
rx = gpg_io_exchange(CHANNEL_APDU, tx+2);
//check get response
if ((G_io_apdu_buffer[0] != 0x00) ||
(G_io_apdu_buffer[1] != 0xc0) ||
@ -283,14 +283,22 @@ int gpg_io_do(unsigned int io_flags) {
os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_length);
if (io_flags & IO_RETURN_AFTER_TX) {
gpg_io_exchange(CHANNEL_APDU |IO_RETURN_AFTER_TX, G_gpg_vstate.io_length);
rx = gpg_io_exchange(CHANNEL_APDU |IO_RETURN_AFTER_TX, G_gpg_vstate.io_length);
return 0;
} else {
gpg_io_exchange(CHANNEL_APDU, G_gpg_vstate.io_length);
rx = gpg_io_exchange(CHANNEL_APDU, G_gpg_vstate.io_length);
}
}
//--- full in chaining ---
if (rx < 4) {
THROW(SW_COMMAND_NOT_ALLOWED);
return SW_COMMAND_NOT_ALLOWED;
}
if (rx == 4) {
G_io_apdu_buffer[4]=0;
rx = 4;
}
G_gpg_vstate.io_offset = 0;
G_gpg_vstate.io_length = 0;
G_gpg_vstate.io_cla = G_io_apdu_buffer[0];
@ -332,15 +340,20 @@ int gpg_io_do(unsigned int io_flags) {
G_io_apdu_buffer[0] = 0x90;
G_io_apdu_buffer[1] = 0x00;
gpg_io_exchange(CHANNEL_APDU, 2);
rx = gpg_io_exchange(CHANNEL_APDU, 2);
in_chaining:
if (((G_io_apdu_buffer[0] & 0xEF) != (G_gpg_vstate.io_cla& 0xEF)) ||
if ((rx < 4) ||
((G_io_apdu_buffer[0] & 0xEF) != (G_gpg_vstate.io_cla& 0xEF)) ||
(G_io_apdu_buffer[1] != G_gpg_vstate.io_ins) ||
(G_io_apdu_buffer[2] != G_gpg_vstate.io_p1) ||
(G_io_apdu_buffer[3] != G_gpg_vstate.io_p2) ) {
THROW(SW_COMMAND_NOT_ALLOWED);
return SW_COMMAND_NOT_ALLOWED;
}
if (rx == 4) {
G_io_apdu_buffer[4]=0;
rx = 4;
}
G_gpg_vstate.io_cla = G_io_apdu_buffer[0];
G_gpg_vstate.io_lc = G_io_apdu_buffer[4];
if ((G_gpg_vstate.io_length + G_gpg_vstate.io_lc) > GPG_IO_BUFFER_LENGTH) {

@ -32,7 +32,11 @@
/* accpet long PW, but less than one sha256 block */
#define GPG_MAX_PW_LENGTH 12
#if GPG_MULTISLOT
#define GPG_KEYS_SLOTS 3
#else
#define GPG_KEYS_SLOTS 3
#endif
#define GPG_KEY_ATTRIBUTES_LENGTH 12
@ -103,6 +107,7 @@ typedef struct gpg_key_s {
typedef struct gpg_key_slot_s{
unsigned char serial[4];
/* */
gpg_key_t sig;
gpg_key_t aut;
@ -303,6 +308,8 @@ typedef struct gpg_v_state_s gpg_v_state_t;
/* --- INS --- */
#define INS_EXIT 0x02
#define INS_SELECT 0xa4
#define INS_TERMINATE_DF 0xe6
#define INS_ACTIVATE_FILE 0x44

@ -50,10 +50,12 @@ void ui_menu_seed_action(unsigned int value) ;
const ux_menu_entry_t ui_menu_reset[] ;
void ui_menu_reset_action(unsigned int value);
#if GPG_MULTISLOT
const ux_menu_entry_t ui_menu_slot[];
void ui_menu_slot_display(unsigned int value);
const bagl_element_t* ui_menu_slot_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
void ui_menu_slot_action(unsigned int value);
#endif
const ux_menu_entry_t ui_menu_settings[] ;
@ -571,11 +573,10 @@ const bagl_element_t* ui_menu_template_preprocessor(const ux_menu_entry_t* entry
case CX_CURVE_SECP521R1:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_NISTP521);
break;
*/
case CX_CURVE_SECP256K1:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_SECP256K1);
break;
*/
/*
case CX_CURVE_BrainPoolP256R1:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_BPOOL256R1);
@ -626,11 +627,11 @@ void ui_menu_tmpl_set_action(unsigned int value) {
attributes.length = 6;
break;
case CX_CURVE_SECP256K1:
case CX_CURVE_SECP256R1:
//case CX_CURVE_SECP256K1:
//case CX_CURVE_SECP384R1:
//case CX_CURVE_SECP521R1:
case CX_CURVE_BrainPoolP256R1:
//case CX_CURVE_BrainPoolP256R1:
//case CX_CURVE_BrainPoolP384R1:
//case CX_CURVE_BrainPoolP512R1:
if (G_gpg_vstate.ux_key == 2) {
@ -708,7 +709,7 @@ const ux_menu_entry_t ui_menu_tmpl_type[] = {
{NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0},
// {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP384R1, NULL, LABEL_NISTP384, NULL, 0, 0},
// {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP521R1, NULL, LABEL_NISTP521, NULL, 0, 0},
{NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256K1, NULL, LABEL_SECP256K1, NULL, 0, 0},
// {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256K1, NULL, LABEL_SECP256K1, NULL, 0, 0},
// {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOL256R1, NULL, 0, 0},
// {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP384R1, NULL, LABEL_BPOOL384R1, NULL, 0, 0},
// {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP512R1, NULL, LABEL_BPOOL512R1, NULL, 0, 0},
@ -953,6 +954,7 @@ const ux_menu_entry_t ui_menu_settings[] = {
/* --------------------------------- SLOT UX --------------------------------- */
#if GPG_MULTISLOT
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
@ -1009,6 +1011,8 @@ void ui_menu_slot_action(unsigned int value) {
// redisplay first entry of the idle menu
ui_menu_slot_display(value);
}
#endif
/* --------------------------------- INFO UX --------------------------------- */
#if GPG_KEYS_SLOTS != 3
@ -1033,8 +1037,10 @@ const ux_menu_entry_t ui_menu_info[] = {
/* --------------------------------- MAIN UX --------------------------------- */
const ux_menu_entry_t ui_menu_main[] = {
{NULL, NULL, 0, NULL, "", NULL, 0, 0},
{NULL, NULL, 0, NULL, "", "", 0, 0},
#if GPG_MULTISLOT
{NULL, ui_menu_slot_display, 0, NULL, "Select slot", NULL, 0, 0},
#endif
{ui_menu_settings, NULL, 0, NULL, "Settings", NULL, 0, 0},
{ui_menu_info, NULL, 0, NULL, "About", NULL, 0, 0},
{NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app" , NULL, 50, 29},
@ -1043,26 +1049,30 @@ const ux_menu_entry_t ui_menu_main[] = {
extern const uint8_t N_USBD_CfgDesc[];
const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if (entry == &ui_menu_main[0]) {
if(element->component.userid==0x20) {
if(element->component.userid==0x21) {
os_memset(G_gpg_vstate.menu, 0, sizeof(G_gpg_vstate.menu));
os_memmove(G_gpg_vstate.menu, N_gpg_pstate->name.value, 12);
if (G_gpg_vstate.menu[0] == 0) {
os_memmove(G_gpg_vstate.menu, "<No Name>", 9);
}
}
if(element->component.userid==0x22) {
unsigned int serial;
char name[20];
serial = MIN(N_gpg_pstate->name.length,19);
os_memset(name, 0, 20);
os_memmove(name, N_gpg_pstate->name.value, serial);
serial = (N_gpg_pstate->AID[10] << 24) |
(N_gpg_pstate->AID[11] << 16) |
(N_gpg_pstate->AID[12] << 8) |
(N_gpg_pstate->AID[13] |(G_gpg_vstate.slot+1));
serial = (G_gpg_vstate.kslot->serial[0] << 24) |
(G_gpg_vstate.kslot->serial[1] << 16) |
(G_gpg_vstate.kslot->serial[2] << 8) |
(G_gpg_vstate.kslot->serial[3]);
os_memset(G_gpg_vstate.menu, 0, sizeof(G_gpg_vstate.menu));
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "< User: %s / SLOT: %d / Serial: %x >",
name, G_gpg_vstate.slot+1, serial);
element->component.stroke = 10; // 1 second stop in each way
element->component.icon_id = 26; // roundtrip speed in pixel/s
element->text = G_gpg_vstate.menu;
UX_CALLBACK_SET_INTERVAL(bagl_label_roundtrip_duration_ms(element, 7));
#if GPG_MULTISLOT
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "ID: %x / %d",
serial, G_gpg_vstate.slot+1);
#else
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "ID: %x",
serial);
#endif
}
element->text = G_gpg_vstate.menu;
}
return element;
}

Loading…
Cancel
Save