Add-on Spec:
 Add intermediate SHA256 in seeded key derivation
 Beautify spec

Pytool:
  Remove some log

 App:
  Fix GET_CHALLENGE commande to support addon mode
  Add '01F2' '01F1' access control
  Replace ugly temporary cx_sha_t address definition by clean union
  Rewrite dynamic menu management in ui
  Add 'About' menu

 Build:
  Rename application from GNUPG3 to OpenPGP and version to 1.0
  Lock path & curve
  Compile in optimization mode
This commit is contained in:
Cédric Mesnil 2017-03-27 15:14:51 +02:00
parent f188805766
commit 278d85a821
13 changed files with 419 additions and 256 deletions

View File

@ -14,15 +14,15 @@
#
#extract TARGET_ID from the SDK to allow for makefile choices
APPNAME = "GNUPG3"
APPVERSION = "0.9"
APPNAME = "OpenPGP"
APPVERSION = "1.0RC1"
TARGET_ID = 0x31100002
$(info TARGET_ID=$(TARGET_ID))
APP_LOAD_PARAMS=--appFlags 0
APP_LOAD_PARAMS=--appFlags 0 --path "2152157255" --curve secp256k1
LOADFLAGS = --params --appVersion $(APPVERSION)
ICONNAME=icon.gif
ICONNAME=icon_pgp.gif
################
@ -60,10 +60,8 @@ DEFINES += OS_IO_SEPROXYHAL IO_SEPROXYHAL_BUFFER_SIZE_B=128
DEFINES += HAVE_BAGL HAVE_PRINTF HAVE_SPRINTF
DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64 HAVE_USB_APDU
DEFINES += HAVE_USB_CLASS_CCID
#DEFINES += PRINTF=screen_printf
DEFINES += PRINTF\(...\)=
DEFINES += $(GPG_CONFIG)
DEFINES += $(GPG_CONFIG) GPG_VERSION=$(APPVERSION) GPG_NAME=$(APPNAME)
##############
# Compiler #
@ -75,8 +73,8 @@ CC := $(CLANGPATH)/clang
CFLAGS :=
CFLAGS += -gdwarf-2 -gstrict-dwarf
#CFLAGS += -O0
CFLAGS += -O0 -g3
#CFLAGS += -O3 -Os
#CFLAGS += -O0 -g3
CFLAGS += -O3 -Os
CFLAGS += -mcpu=cortex-m0 -mthumb
CFLAGS += -fno-common -mtune=cortex-m0 -mlittle-endian
CFLAGS += -std=gnu99 -Werror=int-to-pointer-cast -Wall -Wextra -Wno-unused-variable #-save-temps

View File

@ -2,8 +2,7 @@
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:
ttps://g10code.com/p-card.html
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 .
The application supports:
- RSA with key up to 4096 bits
@ -46,6 +45,8 @@ When installed the default slot is "1". You can change it in settings.
A seeded mode is implemented in order to restore private keys on a new token.
In this mode key material is generated from the global token seeded.
Please consider SEED mode as experimental.
More details to come...
### On screen reset

View File

@ -94,7 +94,7 @@ Sn = BIP32_derive (/0x80475047/n)
Then specific seeds are derived with the SHA3-XOF function for each of the four key :
Sk[i] = SHA3-XOF(Sn \| <key_name> \| int16(i), length)
Sk[i] = SHA3-XOF(SHA256(Sn \| <key_name> \| int16(i)), length)
Sn is the dedicated slot seed from step 1.
key_name is one of 'sig ','dec ', 'aut ', 'sym0', each four characters.
@ -142,7 +142,7 @@ Deterministic random number
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The deterministic random number generation relies on the BIP32 scheme.
The master install path of GPG-ledger is set to /0x80/'G'/'P'/'G', aka /0x80/0x47/0x50/0x47
The master install path of GPG-ledger is set to /0x80'GPG', aka /80475047
**Random prime number generation** :
@ -156,8 +156,8 @@ The master install path of GPG-ledger is set to /0x80/'G'/'P'/'G', aka /0x80/0x4
For a given length *L* and seed *S*:
- generate Sr = BIP32_derive (/0x80/'G'/'P'/'G'/0)
- generate r = SHA3-XOF(Sr \| 'rnd' \| S, L)
- generate Sr = BIP32_derive(/0x80475047/0x0F0F0F0F)
- generate r = SHA3-XOF(SHA256(Sr \| 'rnd' \| S), L)
- return r
**Seeded prime number generation** :
@ -178,10 +178,10 @@ Key Slot management
Key slots are managed by data object 01F1 and 01F2 witch are
manageable by PUT/GET DATA command as for others DO and organized as follow.
On application reset the *01F2* content is set to *Default Slot* value
On application reset, the *01F2* content is set to *Default Slot* value
of *01F1*.
*01F1*
*01F1:*
+------+--------------------------------------------------+--------+
|bytes | description | R/W |
@ -190,15 +190,23 @@ of *01F1*.
+------+--------------------------------------------------+--------+
| 2 | Default slot | R/W |
+------+--------------------------------------------------+--------+
| 3 | Allowed slot selection method: | R/W |
| | 0: selection not allowed (locked to default) | |
| | 1: selection by APDU | |
| | 2: selection by screen | |
| | 3: selection by APDU and screen | |
| 3 | Allowed slot selection method | R/W |
+------+--------------------------------------------------+--------+
Byte 3 is endoced as follow:
*01F2*
+----+----+----+----+----+----+----+----+-------------------------+
| b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | Meaning |
+----+----+----+----+----+----+----+----+-------------------------+
| \- | \- | \- | \- | \- | \- | \- | x | selection by APDU |
+----+----+----+----+----+----+----+----+-------------------------+
| \- | \- | \- | \- | \- | \- | x | \- | selection by screen |
+----+----+----+----+----+----+----+----+-------------------------+
*01F2:*
+------+--------------------------------------------------+--------+
|bytes | Description | R/W |
@ -206,7 +214,7 @@ of *01F1*.
| 1 | Current slot | R/W |
+------+--------------------------------------------------+--------+
*01F0*
*01F0:*
+------+--------------------------------------------------+--------+
|bytes | Description | R/W |
@ -217,17 +225,17 @@ of *01F1*.
+------+--------------------------------------------------+--------+
*Access Conditions*
*Access Conditions:*
+------+--------------+-------------+
+-------+------------+-------------+
| DO | Read | Write |
+======+=============+=============+
+=======+============+=============+
| 01F0 | Always | Never |
+------+-------------+-------------+
+-------+------------+-------------+
| 01F1 | Always | Verify PW3 |
+------+-------------+-------------+
+-------+------------+-------------+
| 01F2 | Always | Verify PW1 |
+------+-------------+-------------+
+-------+------------+-------------+
@ -242,13 +250,19 @@ P2 parameter of GENERATE ASYMMETRIC KEY PAIR is set to (hex value):
Deterministic random number
~~~~~~~~~~~~~~~~~~~~~~~~~~~
P1 parameter of GET CHALLENGE is set to (hex value):
- 00 for true random
- 81 for prime true random
- 82 for seeded random
- 83 for prime seeded random
P1 parameter of GET CHALLENGE is a bits field encoded as follow:
When P1 is set to 82 or 83, Data field contains the seed
+----+-----+----+----+----+----+----+----+-------------------------+
| b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | Meaning |
+----+-----+----+----+----+----+----+----+-------------------------+
| \- | \- | \- | \- | \- | \- | \- | x | prime random |
+----+-----+----+----+----+----+----+----+-------------------------+
| \- | \- | \- | \- | \- | \- | x | \- | seeded random |
+----+-----+----+----+----+----+----+----+-------------------------+
When bit b2 is set, data field contains the seed and P2 contains
the length of random bytes to generate.
Other minor add-on

View File

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 90 B

View File

@ -113,7 +113,7 @@ class GPGCard() :
allreaders = readers()
for r in allreaders:
rname = str(r)
print('try: %s : %s'%(rname,device[5:]))
#print('try: %s : %s'%(rname,device[5:]))
if rname.startswith(device[5:]):
r.createConnection()
self.token = r
@ -121,7 +121,8 @@ class GPGCard() :
self.connection.connect()
self.exchange = self._exchange_pcsc
else:
print("No")
#print("No")
pass
if not self.token:
print("No token")
@ -146,7 +147,7 @@ class GPGCard() :
return resp,sw
def _exchange_pcsc(self,apdu,sw=0x9000):
print("xch S cmd : %s"%(binascii.hexlify(apdu)))
#print("xch S cmd : %s"%(binascii.hexlify(apdu)))
apdu = [x for x in apdu]
resp, sw1, sw2 = self.connection.transmit(apdu)
while sw1==0x61:
@ -156,7 +157,7 @@ class GPGCard() :
resp = resp + resp2
resp = bytes(resp)
sw = (sw1<<8)|sw2
print("xch S resp: %s %.04x"%(binascii.hexlify(resp),sw))
#print("xch S resp: %s %.04x"%(binascii.hexlify(resp),sw))
return resp,sw
def select(self):
@ -730,5 +731,3 @@ class GPGCard() :
'd': tags[0x98],
}
def dump():
pass

View File

@ -20,20 +20,47 @@
#include "gpg_vars.h"
int gpg_apdu_get_challenge() {
unsigned int olen,hlen;
if (G_gpg_vstate.io_le > GPG_EXT_CHALLENGE_LENTH) {
if ((G_gpg_vstate.io_p1&0x80) == 0x80) {
olen = G_gpg_vstate.io_p2;
} else {
olen = G_gpg_vstate.io_le;
}
if (olen > GPG_EXT_CHALLENGE_LENTH) {
THROW(SW_WRONG_LENGTH);
}
gpg_io_discard(1);
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
if (G_gpg_vstate.io_p1 & 0x80) {
cx_math_next_prime(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,G_gpg_vstate.io_le);
if ((G_gpg_vstate.io_p1&0x82) == 0x82) {
unsigned int path[2];
unsigned char chain[32];
unsigned char Sr[32];
os_memset(chain, 0, 32);
path[0] = 0x80475047;
path[1] = 0x0F0F0F0F;
os_perso_derive_node_bip32(CX_CURVE_SECP256K1, path, 2 , Sr, chain);
chain[0] = 'r'; chain[1]='n'; chain[2] = 'd';
cx_sha256_init(&G_gpg_vstate.work.md.sha256);
cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, 0, Sr, 32, NULL);
cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, 0, chain, 3, NULL);
hlen=cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256,
CX_LAST, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length,
G_gpg_vstate.work.io_buffer);
cx_sha3_xof_init(&G_gpg_vstate.work.md.sha3, 256, olen);
cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha3,
CX_LAST, G_gpg_vstate.work.io_buffer, hlen,
G_gpg_vstate.work.io_buffer);
} else {
cx_rng(G_gpg_vstate.work.io_buffer, olen);
}
gpg_io_inserted(G_gpg_vstate.io_le);
if ((G_gpg_vstate.io_p1&0x81) == 0x81) {
cx_math_next_prime(G_gpg_vstate.work.io_buffer,olen);
}
gpg_io_discard(0);
gpg_io_inserted(olen);
return SW_OK;
}

View File

@ -244,10 +244,11 @@ int gpg_apdu_put_data(unsigned int ref) {
break;
case 0x01F2:
if (G_gpg_vstate.io_length != 1) {
THROW(SW_WRONG_LENGTH);
if ((N_gpg_pstate->config_slot[2] & 2) == 0) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
}
if (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset] >= GPG_KEYS_SLOTS) {
if ((G_gpg_vstate.io_length != 1) ||
(G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset] >= GPG_KEYS_SLOTS)) {
THROW(SW_WRONG_DATA);
}
G_gpg_vstate.slot = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset];

View File

@ -157,6 +157,7 @@ void gpg_check_access_write_DO() {
//PW1
case 0x0101:
case 0x0103:
case 0x01F2:
if (gpg_is_verified(ID_PW1)) {
return;
}
@ -167,6 +168,7 @@ void gpg_check_access_write_DO() {
case 0x4f:
case 0x0102:
case 0x0104:
case 0x01F1:
case 0x005E:
case 0x005B:
case 0x5F2D:
@ -265,7 +267,6 @@ int gpg_dispatch() {
#endif
/* --- CHALLENGE --- */
case INS_GET_CHALLENGE:
sw = gpg_apdu_get_challenge();
break;

View File

@ -39,17 +39,19 @@ static void gpg_pso_derive_slot_seed(int slot, unsigned char *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,
cx_sha3_t *xof) {
unsigned char *Ski, unsigned int Ski_len) {
unsigned char idx16[2];
idx16[0] = idx >>8;
idx16[1] = idx;
unsigned char h[32];
h[0] = idx >>8;
h[1] = idx;
cx_sha3_xof_init(xof, 256, Ski_len);
cx_hash((cx_hash_t *)xof, 0, Sn, 32, NULL);
cx_hash((cx_hash_t *)xof, 0, (unsigned char *)key_name, 4, NULL);
cx_hash((cx_hash_t *)xof, CX_LAST, idx16 , 2, Ski);
cx_sha256_init(&G_gpg_vstate.work.md.sha256);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, Sn, 32, NULL);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, (unsigned char *)key_name, 4, NULL);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, CX_LAST, h , 2, h);
cx_sha3_xof_init(&G_gpg_vstate.work.md.sha3, 256, Ski_len);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha3, CX_LAST, h, 32, Ski);
}
@ -140,8 +142,8 @@ int gpg_apdu_gen() {
unsigned int size;
size = ksz>>1;
gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed);
gpg_pso_derive_key_seed(seed, name, 1, pq, size, (cx_sha3_t*)((unsigned int)&G_gpg_vstate.work.io_buffer[1024] & (~3)));
gpg_pso_derive_key_seed(seed, name, 2, pq+size, size, (cx_sha3_t*)((unsigned int)&G_gpg_vstate.work.io_buffer[1024] & (~3)));
gpg_pso_derive_key_seed(seed, name, 1, pq, size);
gpg_pso_derive_key_seed(seed, name, 2, pq+size, size);
*pq |= 0x80;
*(pq+size) |= 0x80;
cx_math_next_prime(pq,size);
@ -172,7 +174,7 @@ int gpg_apdu_gen() {
curve = gpg_oid2curve(keygpg->attributes.value+1, keygpg->attributes.length-1);
if ((G_gpg_vstate.io_p2 == 0x01) | (G_gpg_vstate.seed_mode)) {
gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed);
gpg_pso_derive_key_seed(seed, name, 1, seed, 32, (cx_sha3_t*)&G_gpg_vstate.work.io_buffer[1024]);
gpg_pso_derive_key_seed(seed, name, 1, seed, 32);
cx_ecfp_init_private_key(curve,seed, 32, &G_gpg_vstate.work.ecfp256.private);
keepprivate = 1;
}

View File

@ -219,12 +219,8 @@ void gpg_init() {
}
void gpg_init_ux() {
snprintf(G_gpg_vstate.menu_cur_slot, 16, ">>SLOT: %d<<", G_gpg_vstate.slot+1);
snprintf(G_gpg_vstate.menu_seed_mode, 16, ">>%s<<", G_gpg_vstate.seed_mode?"ON":"OFF");
snprintf(G_gpg_vstate.menu_template_key, 16, "choose key...");
snprintf(G_gpg_vstate.menu_template_type, 16, "choose type...");
G_gpg_vstate.ux_type = 0;
G_gpg_vstate.ux_key = 0;
G_gpg_vstate.ux_type = -1;
G_gpg_vstate.ux_key = -1;
}
/* ----------------------------------------------------------------------- */

View File

@ -283,11 +283,15 @@ int gpg_io_do() {
switch (G_gpg_vstate.io_ins) {
case INS_GET_DATA:
case INS_GET_RESPONSE:
case INS_GET_CHALLENGE:
case INS_TERMINATE_DF:
case INS_ACTIVATE_FILE:
G_gpg_vstate.io_le = G_io_apdu_buffer[4];
break;
case INS_GET_CHALLENGE:
if (G_gpg_vstate.io_p1 == 0) {
break;
}
default:
G_gpg_vstate.io_lc = G_io_apdu_buffer[4];
os_memmove(G_gpg_vstate.work.io_buffer, G_io_apdu_buffer+5, G_gpg_vstate.io_lc);

View File

@ -29,7 +29,7 @@
/* ----------------------------------------------------------------------- */
/* --- UI layout --- */
/* --- Blue UI layout --- */
/* ----------------------------------------------------------------------- */
/* screeen size:
blue; 320x480
@ -153,50 +153,53 @@ unsigned int ui_idle_blue_button(unsigned int button_mask,
}
#endif
/* ----------------------------------------------------------------------- */
/* --- NanoS UI layout --- */
/* ----------------------------------------------------------------------- */
const ux_menu_entry_t ui_idle_sub_template[];
void ui_idle_sub_template_display(unsigned int value);
const bagl_element_t* ui_idle_sub_template_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
void ui_idle_sub_tmpl_set_action(unsigned int value) ;
const ux_menu_entry_t ui_idle_sub_tmpl_key[];
void ui_idle_sub_tmpl_key_action(unsigned int value);
const ux_menu_entry_t ui_idle_sub_tmpl_type[];
void ui_idle_sub_tmpl_type_action(unsigned int value);
const ux_menu_entry_t ui_idle_sub_seed[];
void ui_idle_sub_seed_display(unsigned int value);
const bagl_element_t* ui_idle_sub_seed_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) ;
void ui_idle_sub_seed_action(unsigned int value) ;
const ux_menu_entry_t ui_idle_sub_reset[] ;
void ui_idle_sub_reset_action(unsigned int value);
const ux_menu_entry_t ui_idle_sub_slot[];
void ui_idle_sub_slot_display(unsigned int value);
const bagl_element_t* ui_idle_sub_slot_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
void ui_idle_sub_slot_action(unsigned int value);
const ux_menu_entry_t ui_idle_settings[] ;
const ux_menu_entry_t ui_idle_mainmenu[];
void ui_idle_main_display(unsigned int value) ;
const bagl_element_t* ui_idle_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
/* ----- Helpers UX ----- */
void ui_info(const ux_menu_entry_t *back, const char* msg1, const char* msg2) {
ux_menu_entry_t ui_invalid[2] = {
{back, NULL, 0, NULL, msg1, msg2, 0, 0},
/* ------------------------------- Helpers UX ------------------------------- */
void ui_info(const char* msg1, const char* msg2, const void *menu_display, unsigned int entry) {
ux_menu_entry_t ui_dogsays[2] = {
{NULL, menu_display, 0, NULL, msg1, msg2, 0, 0},
UX_MENU_END
};
UX_MENU_DISPLAY(0, ui_invalid, NULL);
UX_MENU_DISPLAY(entry, ui_dogsays, NULL);
};
/* ----- SLOT UX ----- */
void ui_idle_sub_slot_action(unsigned int value) {
unsigned char s;
if (value == 0) {
s = G_gpg_vstate.slot;
gpg_nvm_write(&N_gpg_pstate->config_slot[1], &s,1);
}
else {
s = (unsigned char)(value-1);
G_gpg_vstate.slot = s;
G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot];
/* ------------------------------- template UX ------------------------------- */
}
// redisplay first entry of the idle menu
gpg_init_ux();
UX_MENU_DISPLAY(0, ui_idle_mainmenu, NULL);
}
const ux_menu_entry_t ui_idle_sub_slot[] = {
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
{NULL, ui_idle_sub_slot_action, 1, NULL, "Select slot 1", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 2, NULL, "Select slot 2", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 3, NULL, "Select slot 3", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 0, NULL, "Set as default", NULL, 0, 0},
{ui_idle_mainmenu, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
/* ----- template UX ----- */
#define LABEL_SIG "Signature"
#define LABEL_AUT "Authentication"
#define LABEL_DEC "Decryption"
@ -208,84 +211,66 @@ const ux_menu_entry_t ui_idle_sub_slot[] = {
#define LABEL_BPOOLR1 "Brainpool R1"
#define LABEL_Ed25519 "Ed25519"
const ux_menu_entry_t ui_idle_sub_template[];
const ux_menu_entry_t ui_idle_sub_tmpl_key[];
const ux_menu_entry_t ui_idle_sub_tmpl_type[];
const ux_menu_entry_t ui_idle_sub_template[] = {
{ui_idle_sub_tmpl_key, NULL, -1, NULL, "Choose key...", NULL, 0, 0},
{ui_idle_sub_tmpl_type, NULL, -1, NULL, "Choose type...", NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_set_action, -1, NULL, "Set template", NULL, 0, 0},
{ui_idle_settings, NULL, 0, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
/*
const bagl_element_t* ui_idle_sub_tmpl_key_prepocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if (entry == &ui_idle_sub_tmpl_key[2]) {
void ui_idle_sub_template_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_idle_sub_template, ui_idle_sub_template_preprocessor);
}
const bagl_element_t* ui_idle_sub_template_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if(element->component.userid==0x20) {
element->component.stroke = 10; // 1 second stop in each way
element->component.icon_id = 26; // roundtrip speed in pixel/s
UX_CALLBACK_SET_INTERVAL(MAX(3000, 2*(1000+bagl_label_roundtrip_duration_ms(element, 7))));
if (entry == &ui_idle_sub_template[0]) {
switch(G_gpg_vstate.ux_key) {
case 1:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s", LABEL_SIG);
break;
case 2:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s", LABEL_DEC);
break;
case 3:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s", LABEL_AUT);
break;
default:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Choose key...");
break;
}
element->text = G_gpg_vstate.menu;
}
if (entry == &ui_idle_sub_template[1]) {
switch(G_gpg_vstate.ux_type) {
case 2048:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_RSA2048);
break;
case 3072:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_RSA3072);
break;
case 4096:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_RSA4096);
break;
case CX_CURVE_SECP256R1:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_NISTP256);
break;
case CX_CURVE_BrainPoolP256R1:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_BPOOLR1);
break;
case CX_CURVE_Ed25519:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_Ed25519);
break;
default:
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Choose type...");
break;
}
element->text = G_gpg_vstate.menu;
}
}
return element;
}
*/
void ui_idle_sub_tmpl_key_action(unsigned int value) {
switch(value) {
case 1:
snprintf(G_gpg_vstate.menu_template_key, 16, "%s", LABEL_SIG);
G_gpg_vstate.ux_key = 1;
break;
case 2:
snprintf(G_gpg_vstate.menu_template_key, 16, "%s", LABEL_AUT);
G_gpg_vstate.ux_key = 2;
break;
case 3:
snprintf(G_gpg_vstate.menu_template_key, 16," %s", LABEL_DEC);
G_gpg_vstate.ux_key = 3;
break;
}
UX_MENU_DISPLAY(0, ui_idle_sub_template, NULL);
}
const ux_menu_entry_t ui_idle_sub_tmpl_key[] = {
{NULL, ui_idle_sub_tmpl_key_action, 1, NULL, LABEL_SIG, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_key_action, 2, NULL, LABEL_DEC, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_key_action, 3, NULL, LABEL_AUT, NULL, 0, 0},
{ui_idle_sub_template, NULL, 0, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_idle_sub_tmpl_type_action(unsigned int value) {
switch(value) {
case 2048:
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_RSA2048);
break;
case 3072:
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_RSA3072);
break;
case 4096:
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_RSA4096);
break;
case CX_CURVE_SECP256R1:
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_NISTP256);
break;
case CX_CURVE_BrainPoolP256R1:
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_BPOOLR1);
break;
case CX_CURVE_Ed25519:
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_Ed25519);
break;
}
G_gpg_vstate.ux_type = value;
UX_MENU_DISPLAY(1, ui_idle_sub_template, NULL);
}
const ux_menu_entry_t ui_idle_sub_tmpl_type[] = {
{NULL, ui_idle_sub_tmpl_type_action, 2048, NULL, LABEL_RSA2048, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, 3072, NULL, LABEL_RSA3072, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, 4096, NULL, LABEL_RSA4096, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOLR1, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_Ed25519, NULL, LABEL_Ed25519, NULL, 0, 0},
{ui_idle_sub_template, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_idle_sub_tmpl_set_action(unsigned int value) {
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
@ -293,14 +278,6 @@ void ui_idle_sub_tmpl_set_action(unsigned int value) {
char* err;
err = NULL;
if (!G_gpg_vstate.ux_key) {
err = "key";
goto ERROR;
}
if (!G_gpg_vstate.ux_type){
err = "type ";
goto ERROR;
}
os_memset(&attributes, 0, sizeof(attributes));
switch (G_gpg_vstate.ux_type) {
@ -349,7 +326,7 @@ void ui_idle_sub_tmpl_set_action(unsigned int value) {
break;
default:
err = "not supported";
err = "type";
goto ERROR;
}
@ -365,97 +342,236 @@ void ui_idle_sub_tmpl_set_action(unsigned int value) {
dest = &G_gpg_vstate.kslot->aut;
break;
default:
err = "key not found";
err = "key";
goto ERROR;
}
gpg_nvm_write(dest, NULL, sizeof(gpg_key_t));
gpg_nvm_write(&dest->attributes, &attributes, sizeof(attributes));
ui_info(ui_idle_sub_template, "Template set!", NULL);
ui_info("Template set!", NULL, ui_idle_sub_template_display, 0);
return;
ERROR:
ui_info(ui_idle_sub_template, "Invalid selection:", err);
ui_info("Invalid selection:", err, ui_idle_sub_template_display, 0);
}
/*
void ui_idle_sub_tmpl_key_display(unsigned int value) {
UX_MENU_DISPLAY(0,ui_idle_sub_tmpl_key,ui_idle_sub_tmpl_key_prepocessor);
}
*/
const ux_menu_entry_t ui_idle_sub_template[] = {
{ui_idle_sub_tmpl_key, NULL, 0, NULL, G_gpg_vstate.menu_template_key, NULL, 0, 0},
{ui_idle_sub_tmpl_type, NULL, 0, NULL, G_gpg_vstate.menu_template_type, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_set_action, 0, NULL, "Set template", NULL, 0, 0},
{ui_idle_mainmenu, NULL, 2, &C_badge_back, "Back", NULL, 61, 40},
const ux_menu_entry_t ui_idle_sub_tmpl_key[] = {
{NULL, ui_idle_sub_tmpl_key_action, 1, NULL, LABEL_SIG, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_key_action, 2, NULL, LABEL_DEC, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_key_action, 3, NULL, LABEL_AUT, NULL, 0, 0},
{ui_idle_sub_template, NULL, 0, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
/* ----- SEED UX ----- */
const ux_menu_entry_t ui_idle_sub_seed[];
void ui_idle_sub_seed_action(unsigned int value) {
G_gpg_vstate.seed_mode = value;
gpg_init_ux();
UX_MENU_DISPLAY(0, ui_idle_sub_seed, NULL);
void ui_idle_sub_tmpl_key_action(unsigned int value) {
G_gpg_vstate.ux_key = value;
ui_idle_sub_template_display(0);
}
const ux_menu_entry_t ui_idle_sub_tmpl_type[] = {
{NULL, ui_idle_sub_tmpl_type_action, 2048, NULL, LABEL_RSA2048, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, 3072, NULL, LABEL_RSA3072, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, 4096, NULL, LABEL_RSA4096, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOLR1, NULL, 0, 0},
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_Ed25519, NULL, LABEL_Ed25519, NULL, 0, 0},
{ui_idle_sub_template, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_idle_sub_tmpl_type_action(unsigned int value) {
G_gpg_vstate.ux_type = value;
ui_idle_sub_template_display(1);
}
/* --------------------------------- SEED UX --------------------------------- */
const ux_menu_entry_t ui_idle_sub_seed[] = {
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
{NULL, NULL, 0, NULL, G_gpg_vstate.menu_seed_mode, NULL, 0, 0},
{NULL, NULL, 0, NULL, "", NULL, 0, 0},
{NULL, ui_idle_sub_seed_action, 1, NULL, "Set on", NULL, 0, 0},
{NULL, ui_idle_sub_seed_action, 0, NULL, "Set off", NULL, 0, 0},
{ui_idle_mainmenu, NULL, 2, &C_badge_back, "Back", NULL, 61, 40},
{ui_idle_settings, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
/* ----- RESET UX ----- */
void ui_idle_sub_reset_action(unsigned int value) {
unsigned char magic[4];
magic[0] = 0; magic[1] = 0; magic[2] = 0; magic[3] = 0;
gpg_nvm_write(N_gpg_pstate->magic, magic, 4);
gpg_init();
UX_MENU_DISPLAY(0, ui_idle_mainmenu, NULL);
void ui_idle_sub_seed_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_idle_sub_seed, ui_idle_sub_seed_preprocessor);
}
const bagl_element_t* ui_idle_sub_seed_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if(element->component.userid==0x20) {
if (entry == &ui_idle_sub_seed[0]) {
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "< %s >", G_gpg_vstate.seed_mode?"ON":"OFF");
element->text = G_gpg_vstate.menu;
}
}
return element;
}
void ui_idle_sub_seed_action(unsigned int value) {
G_gpg_vstate.seed_mode = value;
ui_idle_sub_seed_display(0);
}
/* -------------------------------- RESET UX --------------------------------- */
const ux_menu_entry_t ui_idle_sub_reset[] = {
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
{NULL, NULL, 0, NULL, "Really Reset ?", NULL, 0, 0},
{NULL, ui_idle_main_display, 0, &C_badge_back, "Oh No!", NULL, 61, 40},
{NULL, ui_idle_sub_reset_action, 0, NULL, "Yes!", NULL, 0, 0},
{ui_idle_mainmenu, NULL, 0, &C_badge_back, "Oh No!", NULL, 61, 40},
UX_MENU_END
};
/* ----- SETTINGS UX ----- */
void ui_idle_sub_reset_action(unsigned int value) {
unsigned char magic[4];
magic[0] = 0; magic[1] = 0; magic[2] = 0; magic[3] = 0;
gpg_nvm_write(N_gpg_pstate->magic, magic, 4);
gpg_init();
ui_idle_main_display(0);
}
/* ------------------------------- SETTINGS UX ------------------------------- */
const ux_menu_entry_t ui_idle_settings[] = {
{NULL, NULL, 0, NULL, G_gpg_vstate.menu_cur_slot, NULL, 0, 0},
{ui_idle_sub_template, NULL, 0, NULL, "Key template", NULL, 0, 0},
{ui_idle_sub_seed, NULL, 0, NULL, "Seed mode", NULL, 0, 0},
{NULL, ui_idle_sub_template_display, 0, NULL, "Key template", NULL, 0, 0},
{NULL, ui_idle_sub_seed_display, 0, NULL, "Seed mode", NULL, 0, 0},
{ui_idle_sub_reset, NULL, 0, NULL, "Reset", NULL, 0, 0},
{ui_idle_mainmenu, NULL, 2, &C_badge_back, "Back", NULL, 61, 40},
{NULL, ui_idle_main_display, 2, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
/* --------------------------------- SLOT UX --------------------------------- */
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
const ux_menu_entry_t ui_idle_sub_slot[] = {
{NULL, NULL, -1, NULL, "Choose:", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 1, NULL, "", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 2, NULL, "", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 3, NULL, "", NULL, 0, 0},
{NULL, ui_idle_sub_slot_action, 128, NULL, "Set Default", NULL, 0, 0},
{NULL, ui_idle_main_display, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_idle_sub_slot_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_idle_sub_slot, ui_idle_sub_slot_preprocessor);
}
const bagl_element_t* ui_idle_sub_slot_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
unsigned int slot;
if(element->component.userid==0x20) {
for (slot = 1; slot <= 3; slot ++) {
if (entry == &ui_idle_sub_slot[slot]) {
break;
}
}
if (slot != 4) {
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Slot %d %s %s",
slot,
slot == N_gpg_pstate->config_slot[1]+1?"#":" ", /* default */
slot == G_gpg_vstate.slot+1?"+":" " /* selected*/);
element->text = G_gpg_vstate.menu;
}
}
return element;
}
void ui_idle_sub_slot_action(unsigned int value) {
unsigned char s;
if (value == 128) {
s = G_gpg_vstate.slot;
gpg_nvm_write(&N_gpg_pstate->config_slot[1], &s,1);
value = s+1;
}
else {
s = (unsigned char)(value-1);
G_gpg_vstate.slot = s;
G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot];
}
// redisplay first entry of the idle menu
ui_idle_sub_slot_display(value);
}
/* --------------------------------- INFO UX --------------------------------- */
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
#define STR(x) #x
#define XSTR(x) STR(x)
const ux_menu_entry_t ui_idle_info[] = {
{NULL, NULL, -1, NULL, "OpenPGP Card", NULL, 0, 0},
{NULL, NULL, -1, NULL, "(c) Ledger SAS", NULL, 0, 0},
{NULL, NULL, -1, NULL, "Spec 3.0", NULL, 0, 0},
{NULL, NULL, -1, NULL, "App " XSTR(GPG_VERSION), NULL, 0, 0},
{NULL, ui_idle_main_display, 3, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
/* ----- MAIN UX ----- */
#undef STR
#undef XSTR
/* --------------------------------- MAIN UX --------------------------------- */
const ux_menu_entry_t ui_idle_mainmenu[] = {
{NULL, NULL, 0, NULL, G_gpg_vstate.menu_cur_slot, NULL, 0, 0},
{ui_idle_sub_slot, NULL, 0, NULL, "Select slot", NULL, 0, 0},
{NULL, NULL, 0, NULL, "", NULL, 0, 0},
{NULL, ui_idle_sub_slot_display, 0, NULL, "Select slot", NULL, 0, 0},
{ui_idle_settings, NULL, 0, NULL, "Settings", NULL, 0, 0},
{ui_idle_info, NULL, 0, NULL, "About", NULL, 0, 0},
{NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app" , NULL, 50, 29},
UX_MENU_END
};
const bagl_element_t* ui_idle_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if (entry == &ui_idle_mainmenu[0]) {
if(element->component.userid==0x20) {
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]);
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));
}
}
return element;
}
void ui_idle_main_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_idle_mainmenu, ui_idle_main_preprocessor);
}
void ui_idle_init(void) {
ui_idle_main_display(0);
// setup the first screen changing
UX_CALLBACK_SET_INTERVAL(1000);
}
void io_seproxyhal_display(const bagl_element_t *element) {
io_seproxyhal_display_default((bagl_element_t *)element);
}
void ui_idle_init(void) {
UX_MENU_DISPLAY(0, ui_idle_mainmenu, NULL);
// setup the first screen changing
UX_CALLBACK_SET_INTERVAL(1000);
}
/* ----------------------------------------------------------------------- */
/* --- Application Entry --- */

View File

@ -23,13 +23,13 @@
/* big private DO */
#define GPG_EXT_PRIVATE_DO_LENGTH 1024
#define GPG_EXT_PRIVATE_DO_LENGTH 512
/* will be fixed..1024 is not enougth */
#define GPG_EXT_CARD_HOLDER_CERT_LENTH 4096
#define GPG_EXT_CARD_HOLDER_CERT_LENTH 2560
/* random choice */
#define GPG_EXT_CHALLENGE_LENTH 254
/* accpet long PW, but less than one sha256 block */
#define GPG_MAX_PW_LENGTH 48
#define GPG_MAX_PW_LENGTH 12
#define GPG_KEYS_SLOTS 3
@ -201,6 +201,13 @@ struct gpg_v_state_s {
cx_ecfp_public_key_t public;
cx_ecfp_private_key_t private;
}ecfp256;
struct {
unsigned char md_buffer[GPG_IO_BUFFER_LENGTH-MAX(sizeof(cx_sha3_t),sizeof(cx_sha256_t))];
union {
cx_sha3_t sha3;
cx_sha256_t sha256;
};
} md ;
} work;
/* data state */
@ -213,10 +220,7 @@ struct gpg_v_state_s {
/* ux menus */
char menu_cur_slot[16];
char menu_seed_mode[16];
char menu_template_key[16];
char menu_template_type[16];
char menu[64];
unsigned int ux_key;
unsigned int ux_type;