Async interaction and PIN on screen

Add PIN on screen
Fix strings declarations and usages to avoid PIC fails
Fix IO for Async interaction
Move UX code in dedicated file
This commit is contained in:
Cédric Mesnil 2017-04-12 12:11:00 +02:00
parent b0d13ad6d5
commit 8430858dfb
19 changed files with 1313 additions and 645 deletions

View File

@ -10,7 +10,7 @@ The application supports:
- EDDSA with Ed25519 curve
- ECDH with secp256k1, secp256r1, brainpool 256r1, brainpool 256t1 and curve25519 curves
To compile it, use at least the Nano S SDK 1.3.1.3 on firmware 1.3.1
To compile it, use at least the Nano S SDK 1.3.1.4 on firmware 1.3.1
This release is in beta stage with known missing parts (see also Add-on) :

View File

@ -39,10 +39,12 @@ int gpg_apdu_change_ref_data(int id) ;
int gpg_apdu_reset_retry_counter(void) ;
int gpg_oid2curve(unsigned char* oid, unsigned int len);
int gpg_is_pin_verified(int id);
int gpg_is_pin_blocked(int id);
void gpg_set_pin_verified(int id, int verified);
int gpg_oid2curve(unsigned char* oid, unsigned int len);
int gpg_is_pin_verified(int id);
int gpg_is_pin_blocked(int id);
int gpg_set_pin_verified(int id, int verified);
int gpg_check_pin(int id, unsigned char *pin_val, unsigned int pin_len);
void gpg_change_pin(int id, unsigned char *pin_val, unsigned int pin_len);
/* ----------------------------------------------------------------------- */
/* --- IO ---- */
@ -73,7 +75,7 @@ int gpg_io_fetch_tl(unsigned int *T, unsigned int *L) ;
int gpg_io_fetch_nv(unsigned char* buffer, int len) ;
int gpg_io_fetch(unsigned char* buffer, int len) ;
int gpg_io_do(void) ;
int gpg_io_do(unsigned int io_flags) ;
/* ----------------------------------------------------------------------- */

View File

@ -619,7 +619,7 @@ int gpg_apdu_put_data(unsigned int ref) {
/* ----------------- RC ----------------- */
case 0xD3:
sw = gpg_apdu_change_ref_data(ID_RC);
sw = gpg_apdu_change_ref_data(PIN_ID_RC);
break;
/* ----------------- UIF ----------------- */

View File

@ -42,7 +42,7 @@ void gpg_check_access_ins() {
return;
case INS_RESET_RETRY_COUNTER:
if (gpg_is_verified(ID_PW3) || gpg_is_verified(ID_RC)) {
if (gpg_is_verified(PIN_ID_PW3) || gpg_is_verified(PIN_ID_RC)) {
return;
}
@ -54,27 +54,27 @@ void gpg_check_access_ins() {
if (G_gpg_vstate.io_p1 == 0x81) {
return;
}
if (gpg_is_verified(ID_PW3)) {
if (gpg_is_verified(PIN_ID_PW3)) {
return;
}
break;
case INS_PSO:
if ((ref == 0x9e9a) && gpg_is_verified(ID_PW1)) {
if ((ref == 0x9e9a) && gpg_is_verified(PIN_ID_PW1)) {
//pso:sign
if (N_gpg_pstate->PW_status[0] == 0) {
gpg_set_pin_verified(ID_PW1,0);
gpg_set_pin_verified(PIN_ID_PW1,0);
}
return;
}
if ((ref == 0x8086 ) && gpg_is_verified(ID_PW2)) {
if ((ref == 0x8086 ) && gpg_is_verified(PIN_ID_PW2)) {
//pso:dec
return;
}
break;
case INS_INTERNAL_AUTHENTICATE:
if (gpg_is_verified(ID_PW2)) {
if (gpg_is_verified(PIN_ID_PW2)) {
return;
}
break;
@ -83,7 +83,7 @@ void gpg_check_access_ins() {
return;
case INS_TERMINATE_DF:
if (gpg_is_pin_verified(ID_PW3) || gpg_is_pin_blocked(ID_PW3)) {
if (gpg_is_pin_verified(PIN_ID_PW3) || gpg_is_pin_blocked(PIN_ID_PW3)) {
return;
}
break;
@ -140,14 +140,14 @@ void gpg_check_access_read_DO() {
//PW1
case 0x0103:
if (gpg_is_verified(ID_PW2)) {
if (gpg_is_verified(PIN_ID_PW2)) {
return;
}
break;
//PW3
case 0x0104:
if (gpg_is_verified(ID_PW3)) {
if (gpg_is_verified(PIN_ID_PW3)) {
return;
}
break;
@ -156,6 +156,8 @@ void gpg_check_access_read_DO() {
THROW(SW_CONDITIONS_NOT_SATISFIED);
}
char debugbuff[5];
void gpg_check_access_write_DO() {
unsigned int ref;
ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ;
@ -166,7 +168,7 @@ void gpg_check_access_write_DO() {
case 0x0101:
case 0x0103:
case 0x01F2:
if (gpg_is_verified(ID_PW2)) {
if (gpg_is_verified(PIN_ID_PW2)) {
return;
}
break;
@ -207,7 +209,7 @@ void gpg_check_access_write_DO() {
case 0x00D6:
case 0x00D7:
case 0x00D8:
if (gpg_is_verified(ID_PW3)) {
if (gpg_is_verified(PIN_ID_PW3)) {
return;
}
break;
@ -247,7 +249,7 @@ int gpg_dispatch() {
case INS_TERMINATE_DF:
gpg_io_discard(0);
if (G_gpg_vstate.verified_pin[ID_PW3] || (N_gpg_pstate->PW3.counter == 0)) {
if (G_gpg_vstate.verified_pin[PIN_ID_PW3] || (N_gpg_pstate->PW3.counter == 0)) {
gpg_install(STATE_TERMINATE);
return(SW_OK);
break;
@ -331,7 +333,8 @@ int gpg_dispatch() {
sw = gpg_apdu_verify(G_gpg_vstate.io_p2&0x0F);
break;
}
THROW(SW_INCORRECT_P1P2);
THROW(0x9BF0);
//THROW(SW_INCORRECT_P1P2);
case INS_CHANGE_REFERENCE_DATA:
if ((G_gpg_vstate.io_p2 == 0x81) ||

View File

@ -214,6 +214,8 @@ void gpg_init() {
//key conf
G_gpg_vstate.slot = N_gpg_pstate->config_slot[1];
G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot];
//pin conf
G_gpg_vstate.pinmode = N_gpg_pstate->config_pin[0];
//ux conf
gpg_init_ux();
}
@ -229,7 +231,6 @@ void gpg_init_ux() {
int gpg_install(unsigned char app_state) {
gpg_pin_t pin;
unsigned int l;
unsigned char config[4];
//full reset data
gpg_nvm_write(N_gpg_pstate, NULL, sizeof(gpg_nv_state_t));
@ -268,12 +269,17 @@ int gpg_install(unsigned char app_state) {
G_gpg_vstate.work.io_buffer[1] = GPG_MAX_PW_LENGTH;
G_gpg_vstate.work.io_buffer[2] = GPG_MAX_PW_LENGTH;
G_gpg_vstate.work.io_buffer[3] = GPG_MAX_PW_LENGTH;
gpg_nvm_write(&N_gpg_pstate->PW_status, G_gpg_vstate.work.io_buffer, 4);
gpg_nvm_write(&N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer, 4);
//config slot
config[0] = GPG_KEYS_SLOTS;
config[1] = 0;
config[2] = 3; // 3: selection by APDU and screen
G_gpg_vstate.work.io_buffer[0] = GPG_KEYS_SLOTS;
G_gpg_vstate.work.io_buffer[1] = 0;
G_gpg_vstate.work.io_buffer[2] = 3; // 3: selection by APDU and screen
gpg_nvm_write(&N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer, 3);
//config pin
G_gpg_vstate.work.io_buffer[0] = PIN_MODE_SCREEN;
gpg_nvm_write(&N_gpg_pstate->config_pin, G_gpg_vstate.work.io_buffer, 1);
//default key template: RSA 2048)

View File

@ -231,44 +231,55 @@ int gpg_io_fetch(unsigned char* buffer, int len) {
/* REAL IO */
/* ----------------------------------------------------------------------- */
int gpg_io_do() {
#define MAX_OUT GPG_APDU_LENGTH
int gpg_io_do(unsigned int io_flags) {
//if pending input chaining
if (G_gpg_vstate.io_cla & 0x01) {
goto in_chaining;
}
// --- full out chaining ---
G_gpg_vstate.io_offset = 0;
while(G_gpg_vstate.io_length > MAX_OUT) {
unsigned int tx,xx;
//send chunk
tx = MAX_OUT-2;
os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, tx);
G_gpg_vstate.io_length -= tx;
G_gpg_vstate.io_offset += tx;
G_io_apdu_buffer[tx] = 0x61;
if (G_gpg_vstate.io_length > MAX_OUT-2) {
xx = MAX_OUT-2;
} else {
xx = G_gpg_vstate.io_length-2;
}
G_io_apdu_buffer[tx+1] = xx;
gpg_io_exchange(CHANNEL_APDU | G_gpg_vstate.io_flags, tx+2);
//check get response
if ((G_io_apdu_buffer[0] != 0x00) ||
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);
} else {
// --- full out chaining ---
G_gpg_vstate.io_offset = 0;
while(G_gpg_vstate.io_length > MAX_OUT) {
unsigned int tx,xx;
//send chunk
tx = MAX_OUT-2;
os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, tx);
G_gpg_vstate.io_length -= tx;
G_gpg_vstate.io_offset += tx;
G_io_apdu_buffer[tx] = 0x61;
if (G_gpg_vstate.io_length > MAX_OUT-2) {
xx = MAX_OUT-2;
} else {
xx = G_gpg_vstate.io_length-2;
}
G_io_apdu_buffer[tx+1] = xx;
gpg_io_exchange(CHANNEL_APDU, tx+2);
//check get response
if ((G_io_apdu_buffer[0] != 0x00) ||
(G_io_apdu_buffer[1] != 0xc0) ||
(G_io_apdu_buffer[2] != 0x00) ||
(G_io_apdu_buffer[3] != 0x00) ) {
THROW(SW_COMMAND_NOT_ALLOWED);
THROW(SW_COMMAND_NOT_ALLOWED);
}
}
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);
return 0;
} else {
gpg_io_exchange(CHANNEL_APDU, G_gpg_vstate.io_length);
}
}
os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_length);
gpg_io_exchange(CHANNEL_APDU | G_gpg_vstate.io_flags, G_gpg_vstate.io_length);
//--- full in chaining ---
G_gpg_vstate.io_offset = 0;
@ -292,7 +303,16 @@ int gpg_io_do() {
if (G_gpg_vstate.io_p1 == 0) {
break;
}
case INS_VERIFY:
case INS_CHANGE_REFERENCE_DATA:
if (G_io_apdu_buffer[4] == 0) {
break;
}
goto _default;
default:
_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);
G_gpg_vstate.io_length = G_gpg_vstate.io_lc;
@ -303,7 +323,7 @@ int gpg_io_do() {
G_io_apdu_buffer[0] = 0x90;
G_io_apdu_buffer[1] = 0x00;
gpg_io_exchange(CHANNEL_APDU | G_gpg_vstate.io_flags, 2);
gpg_io_exchange(CHANNEL_APDU, 2);
in_chaining:
if (((G_io_apdu_buffer[0] & 0xEF) != (G_gpg_vstate.io_cla& 0xEF)) ||
(G_io_apdu_buffer[1] != G_gpg_vstate.io_ins) ||

View File

@ -21,575 +21,26 @@
#include "gpg_api.h"
#include "gpg_vars.h"
#include "gpg_ux_nanos.h"
//#include "gpg_ux_blue.h"
#include "os_io_seproxyhal.h"
#include "string.h"
#include "glyphs.h"
/* ----------------------------------------------------------------------- */
/* --- Blue UI layout --- */
/* ----------------------------------------------------------------------- */
/* screeen size:
blue; 320x480
nanoS: 128x32
*/
#if 0
static const bagl_element_t const ui_idle_blue[] = {
{{BAGL_RECTANGLE, 0x00, 0, 60, 320, 420, 0, 0,
BAGL_FILL, 0xf9f9f9, 0xf9f9f9,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0,
BAGL_FILL, 0x1d2028, 0x1d2028,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0,
BAGL_FILL, 0xFFFFFF, 0x1d2028,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
"GPG Card",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 190, 215, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"Exit",
0,
0x37ae99,
0xF9F9F9,
gpg_io_seproxyhal_touch_exit,
NULL,
NULL},
#ifdef GPG_DEBUG
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 20, 215, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"Init",
0,
0x37ae99,
0xF9F9F9,
gpg_io_seproxyhal_touch_debug,
NULL,
NULL}
#endif
};
const bagl_element_t ui_idle_nanos[] = {
// type
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0,
BAGL_FILL, 0x000000, 0xFFFFFF,
0,
0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x00, 0, 12, 128, 32, 0, 0,
0 , 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER,
0 },
"GPGCard",
0,
0,
0,
NULL,
NULL,
NULL },
//{{BAGL_LABELINE , 0x02, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Waiting for requests...", 0, 0, 0, NULL, NULL, NULL },
{{BAGL_ICON , 0x00, 3, 12, 7,
7, 0, 0,
0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS },
NULL,
0,
0,
0,
NULL,
NULL,
NULL },
};
unsigned int gpg_io_seproxyhal_touch_exit(const bagl_element_t *e) {
// Go back to the dashboard
os_sched_exit(0);
return 0; // do not redraw the widget
}
unsigned int ui_idle_blue_button(unsigned int button_mask,
unsigned int button_mask_counter) {
return 0;
}
#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_CCID_reset(void) {
//INSERT CODE HERE TO REMOVE/INSERT THE TOKEN
}
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(entry, ui_dogsays, NULL);
};
/* ------------------------------- template UX ------------------------------- */
#define LABEL_SIG "Signature"
#define LABEL_AUT "Authentication"
#define LABEL_DEC "Decryption"
#define LABEL_RSA2048 "RSA 2048"
#define LABEL_RSA3072 "RSA 3072"
#define LABEL_RSA4096 "RSA 4096"
#define LABEL_NISTP256 "NIST P256"
#define LABEL_BPOOLR1 "Brainpool R1"
#define LABEL_Ed25519 "Ed25519"
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
};
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) {
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_set_action(unsigned int value) {
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
gpg_key_t* dest;
char* err;
err = NULL;
os_memset(&attributes, 0, sizeof(attributes));
switch (G_gpg_vstate.ux_type) {
case 2048:
case 3072:
case 4096:
attributes.value[0] = 0x01;
attributes.value[1] = (G_gpg_vstate.ux_type>>8) &0xFF;
attributes.value[2] = G_gpg_vstate.ux_type&0xFF;
attributes.value[3] = 0x00;
attributes.value[4] = 0x20;
attributes.value[5] = 0x01;
attributes.length = 6;
break;
case CX_CURVE_SECP256R1:
if (G_gpg_vstate.ux_key == 2) {
attributes.value[0] = 18; //ecdh
} else {
attributes.value[0] = 19; //ecdsa
}
os_memmove(attributes.value+1, C_OID_SECP256R1, sizeof(C_OID_SECP256R1));
attributes.length = 1+sizeof(C_OID_SECP256R1);
break;
case CX_CURVE_BrainPoolP256R1:
if (G_gpg_vstate.ux_key == 2) {
attributes.value[0] = 18; //ecdh
} else {
attributes.value[0] = 19; //ecdsa
}
os_memmove(attributes.value+1, C_OID_BRAINPOOL256R1, sizeof(C_OID_BRAINPOOL256R1));
attributes.length = 1+sizeof(C_OID_BRAINPOOL256R1);
break;
case CX_CURVE_Ed25519:
if (G_gpg_vstate.ux_key == 2) {
attributes.value[0] = 18; //ecdh
os_memmove(attributes.value+1, C_OID_cv25519, sizeof(C_OID_cv25519));
attributes.length = 1+sizeof(C_OID_cv25519);
} else {
attributes.value[0] = 22; //eddsa
os_memmove(attributes.value+1, C_OID_Ed25519, sizeof(C_OID_Ed25519));
attributes.length = 1+sizeof(C_OID_Ed25519);
}
break;
default:
err = "type";
goto ERROR;
}
dest = NULL;
switch(G_gpg_vstate.ux_key) {
case 1:
dest = &G_gpg_vstate.kslot->sig;
break;
case 2:
dest = &G_gpg_vstate.kslot->dec;
break;
case 3:
dest = &G_gpg_vstate.kslot->aut;
break;
default:
err = "key";
goto ERROR;
}
gpg_nvm_write(dest, NULL, sizeof(gpg_key_t));
gpg_nvm_write(&dest->attributes, &attributes, sizeof(attributes));
ui_info("Template set!", NULL, ui_idle_sub_template_display, 0);
return;
ERROR:
ui_info("Invalid selection:", err, ui_idle_sub_template_display, 0);
}
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_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, "", 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_settings, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
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},
UX_MENU_END
};
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_CCID_reset();
ui_idle_main_display(0);
}
/* ------------------------------- SETTINGS UX ------------------------------- */
const ux_menu_entry_t ui_idle_settings[] = {
{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},
{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);
if (s!= G_gpg_vstate.slot) {
G_gpg_vstate.slot = s;
G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot];
ui_CCID_reset();
}
}
// 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
};
#undef STR
#undef XSTR
/* --------------------------------- MAIN UX --------------------------------- */
const ux_menu_entry_t ui_idle_mainmenu[] = {
{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] |(G_gpg_vstate.slot+1));
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);
}
/* ----------------------------------------------------------------------- */
/* --- Application Entry --- */
/* ----------------------------------------------------------------------- */
void gpg_main(void) {
unsigned int io_flags;
io_flags = 0;
for (;;) {
volatile unsigned short sw = 0;
BEGIN_TRY {
TRY {
gpg_io_do();
gpg_io_do(io_flags);
sw = gpg_dispatch();
}
CATCH_OTHER(e) {
@ -603,7 +54,12 @@ void gpg_main(void) {
}
}
FINALLY {
gpg_io_insert_u16(sw);
if (sw) {
gpg_io_insert_u16(sw);
io_flags = 0;
} else {
io_flags = IO_ASYNCH_REPLY;
}
}
}
END_TRY;
@ -714,7 +170,7 @@ __attribute__((section(".boot"))) int main(void) {
gpg_init();
//set up initial screen
ui_idle_init();
ui_init();

View File

@ -19,15 +19,16 @@
#include "gpg_api.h"
#include "gpg_vars.h"
#include "gpg_ux_nanos.h"
static gpg_pin_t *gpg_get_pin(int id) {
switch (id) {
case ID_PW1 :
case ID_PW2 :
case PIN_ID_PW1 :
case PIN_ID_PW2 :
return &N_gpg_pstate->PW1;
case ID_PW3:
case PIN_ID_PW3:
return &N_gpg_pstate->PW3;
case ID_RC:
case PIN_ID_RC:
return &N_gpg_pstate->RC;
}
return NULL;
@ -35,11 +36,8 @@ static gpg_pin_t *gpg_get_pin(int id) {
void gpg_set_pin_verified(int id, int verified) {
G_gpg_vstate.verified_pin[id] = verified;
}
static void gpg_check_pin(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) {
static int gpg_check_pin_internal(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) {
cx_sha256_t sha256;
unsigned int counter;
@ -53,11 +51,20 @@ static void gpg_check_pin(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) {
cx_sha256_init(&sha256);
cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, NULL);
if (os_memcmp(sha256.acc, pin->value, 32)) {
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
return 0;
}
counter = 3;
gpg_nvm_write(&(pin->counter), &counter, sizeof(int));
return 1;
}
static void gpg_checkthrow_pin(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) {
if (gpg_check_pin_internal(pin,pin_val,pin_len)) {
return;
}
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
}
static void gpg_set_pin(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len) {
@ -84,6 +91,27 @@ static void gpg_unblock_pin(int id) {
*/
int gpg_set_pin_verified(int id, int verified) {
G_gpg_vstate.verified_pin[id] = verified;
return verified;
}
int gpg_check_pin(int id, unsigned char *pin_val, unsigned int pin_len) {
gpg_pin_t *pin;
pin = gpg_get_pin(id);
return gpg_set_pin_verified(id, gpg_check_pin_internal(pin,pin_val,pin_len)?1:0);
}
/*
*
*/
void gpg_change_pin(int id, unsigned char *pin_val, unsigned int pin_len) {
gpg_pin_t *pin;
pin = gpg_get_pin(id);
gpg_set_pin(pin, pin_val, pin_len);
}
/*
*
*/
@ -118,11 +146,29 @@ int gpg_apdu_verify(int id) {
THROW(SW_PIN_BLOCKED);
return 0;
}
gpg_check_pin(pin,
if (G_gpg_vstate.io_length == 0) {
if (G_gpg_vstate.pinmode == PIN_MODE_SCREEN) {
//Delegate pin check to ui
gpg_io_discard(1);
ui_menu_pinentry_display(0);
return 0;
}
if (G_gpg_vstate.pinmode == PIN_MODE_CONFIRM) {
//Delegate pin check to ui
gpg_io_discard(1);
ui_menu_pinconfirm_display(0);
return 0;
}
if (G_gpg_vstate.pinmode == PIN_MODE_TRUST) {
gpg_set_pin_verified(id,1);
gpg_io_discard(1);
return SW_OK;
}
}
gpg_checkthrow_pin(pin,
G_gpg_vstate.work.io_buffer+ G_gpg_vstate.io_offset,
G_gpg_vstate.io_length);
gpg_set_pin_verified(id,1);
gpg_set_pin_verified(id,1);
gpg_io_discard(1);
return SW_OK;
}
@ -140,7 +186,7 @@ int gpg_apdu_change_ref_data(int id) {
// --- RC pin ---
if (id == ID_RC) {
if (id == PIN_ID_RC) {
newlen = G_gpg_vstate.io_length;
if (newlen == 0) {
gpg_nvm_write(pin, NULL, sizeof(gpg_pin_t));
@ -170,14 +216,14 @@ int gpg_apdu_change_ref_data(int id) {
len = pin->length;
}
gpg_check_pin(pin,
gpg_checkthrow_pin(pin,
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,
len);
newlen = G_gpg_vstate.io_length-len;
if ( (newlen > GPG_MAX_PW_LENGTH) ||
((id == ID_PW1) && (newlen < 6)) ||
((id == ID_PW3) && (newlen < 8)) ) {
((id == PIN_ID_PW1) && (newlen < 6)) ||
((id == PIN_ID_PW3) && (newlen < 8)) ) {
THROW(SW_WRONG_DATA);
}
gpg_set_pin(pin,
@ -192,16 +238,16 @@ int gpg_apdu_reset_retry_counter() {
gpg_pin_t *pin_rc;
int rc_len, pw1_len;
pin_pw1 = gpg_get_pin(ID_PW1);
pin_pw1 = gpg_get_pin(PIN_ID_PW1);
if (G_gpg_vstate.io_p1 == 2) {
if (!G_gpg_vstate.verified_pin[ID_PW3]) {
if (!G_gpg_vstate.verified_pin[PIN_ID_PW3]) {
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
}
rc_len = 0;
pw1_len = G_gpg_vstate.io_length;
} else {
pin_rc = gpg_get_pin(ID_RC);
pin_rc = gpg_get_pin(PIN_ID_RC);
//avoid any-overflow whitout giving info
if (pin_rc->length > G_gpg_vstate.io_length) {
rc_len = G_gpg_vstate.io_length;
@ -209,7 +255,7 @@ int gpg_apdu_reset_retry_counter() {
rc_len = pin_rc->length;
}
pw1_len = G_gpg_vstate.io_length-rc_len;
gpg_check_pin(pin_rc,
gpg_checkthrow_pin(pin_rc,
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,
rc_len);
}

View File

@ -32,3 +32,4 @@ int apdu_n;
gpg_v_state_t G_gpg_vstate;

View File

@ -28,10 +28,10 @@ int gpg_apdu_select() {
G_gpg_vstate.DO_reccord = 0;
G_gpg_vstate.DO_offset = 0;
if ( G_gpg_vstate.selected == 0) {
G_gpg_vstate.verified_pin[ID_PW1] = 0;
G_gpg_vstate.verified_pin[ID_PW2] = 0;
G_gpg_vstate.verified_pin[ID_PW3] = 0;
G_gpg_vstate.verified_pin[ID_RC] = 0;
G_gpg_vstate.verified_pin[PIN_ID_PW1] = 0;
G_gpg_vstate.verified_pin[PIN_ID_PW2] = 0;
G_gpg_vstate.verified_pin[PIN_ID_PW3] = 0;
G_gpg_vstate.verified_pin[PIN_ID_RC] = 0;
}
gpg_io_discard(0);
sw = SW_OK;

View File

@ -16,6 +16,7 @@
#ifndef GPG_TYPES_H
#define GPG_TYPES_H
#include "os_io_seproxyhal.h"
/* cannot send more that F0 bytes in CCID, why? do not know for now
* So set up length to F0 minus 2 bytes for SW
*/
@ -107,6 +108,9 @@ struct gpg_nv_state_s {
/* magic */
unsigned char magic[8];
/* pin mode */
unsigned char config_pin[1];
/* 01F1 (01F2 is volatile)*/
unsigned char config_slot[3];
@ -178,7 +182,6 @@ struct gpg_v_state_s {
unsigned short io_length;
unsigned short io_offset;
unsigned short io_mark;
unsigned short io_flags;
union {
unsigned char io_buffer[GPG_IO_BUFFER_LENGTH];
struct {
@ -217,13 +220,14 @@ struct gpg_v_state_s {
/* PINs state */
unsigned char verified_pin[5];
unsigned char pinmode;
/* ux menus */
char menu[64];
unsigned char ux_pinentry[12];
unsigned int ux_key;
unsigned int ux_type;
ux_menu_entry_t ui_dogsays[2] ;
#ifdef GPG_DEBUG
unsigned char print;
@ -240,10 +244,6 @@ typedef struct gpg_v_state_s gpg_v_state_t;
/* --- IDentifiers --- */
#define ID_PW1 1
#define ID_PW2 2
#define ID_PW3 3
#define ID_RC 4
#define ID_AUTH 1
#define ID_DEC 2
@ -256,6 +256,17 @@ typedef struct gpg_v_state_s gpg_v_state_t;
#define IO_OFFSET_END (unsigned int)-1
#define IO_OFFSET_MARK (unsigned int)-2
#define PIN_ID_PW1 1
#define PIN_ID_PW2 2
#define PIN_ID_PW3 3
#define PIN_ID_RC 4
#define PIN_MODE_HOST 1
#define PIN_MODE_SCREEN 2
#define PIN_MODE_CONFIRM 3
#define PIN_MODE_TRUST 4
/* --- INS --- */
#define INS_SELECT 0xa4

155
src/gpg_ux_blue.c Normal file
View File

@ -0,0 +1,155 @@
/* Copyright 2017 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.
*/
#include "os.h"
#include "cx.h"
#include "gpg_types.h"
#include "gpg_api.h"
#include "gpg_vars.h"
#include "os_io_seproxyhal.h"
#include "string.h"
#include "glyphs.h"
/* ----------------------------------------------------------------------- */
/* --- Blue UI layout --- */
/* ----------------------------------------------------------------------- */
/* screeen size:
blue; 320x480
nanoS: 128x32
*/
#if 0
static const bagl_element_t const ui_idle_blue[] = {
{{BAGL_RECTANGLE, 0x00, 0, 60, 320, 420, 0, 0,
BAGL_FILL, 0xf9f9f9, 0xf9f9f9,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0,
BAGL_FILL, 0x1d2028, 0x1d2028,
0, 0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0,
BAGL_FILL, 0xFFFFFF, 0x1d2028,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
"GPG Card",
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 190, 215, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"Exit",
0,
0x37ae99,
0xF9F9F9,
gpg_io_seproxyhal_touch_exit,
NULL,
NULL},
#ifdef GPG_DEBUG
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 20, 215, 120, 40, 0, 6,
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE,
0},
"Init",
0,
0x37ae99,
0xF9F9F9,
gpg_io_seproxyhal_touch_debug,
NULL,
NULL}
#endif
};
const bagl_element_t ui_idle_nanos[] = {
// type
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0,
BAGL_FILL, 0x000000, 0xFFFFFF,
0,
0},
NULL,
0,
0,
0,
NULL,
NULL,
NULL},
{{BAGL_LABELINE, 0x00, 0, 12, 128, 32, 0, 0,
0 , 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER,
0 },
"GPGCard",
0,
0,
0,
NULL,
NULL,
NULL },
//{{BAGL_LABELINE , 0x02, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Waiting for requests...", 0, 0, 0, NULL, NULL, NULL },
{{BAGL_ICON , 0x00, 3, 12, 7,
7, 0, 0,
0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS },
NULL,
0,
0,
0,
NULL,
NULL,
NULL },
};
unsigned int gpg_io_seproxyhal_touch_exit(const bagl_element_t *e) {
// Go back to the dashboard
os_sched_exit(0);
return 0; // do not redraw the widget
}
unsigned int ui_idle_blue_button(unsigned int button_mask,
unsigned int button_mask_counter) {
return 0;
}
#endif

36
src/gpg_ux_msg.c Normal file
View File

@ -0,0 +1,36 @@
/* Copyright 2017 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.
*/
const char * const C_TEMPLATE_TYPE = "Key type";
const char * const C_TEMPLATE_KEY = "Key";
const char * const C_INVALID_SELECTION = "Invalid selection";
const char * const C_OK = "OK";
const char * const C_NOK = "NOK";
const char * const C_WRONG_PIN = "PIN Incorrect";
const char * const C_RIGHT_PIN = "PIN Correct";
const char * const C_PIN_CHANGED = "PIN changed";
const char * const C_PIN_DIFFERS = "2 PINs differs";
const char * const C_PIN_USER = "User PIN";
const char * const C_PIN_ADMIN = "Admin PIN";
const char * const C_VERIFIED = "Verified";
const char * const C_NOT_VERIFIED = "Not Verified";
const char * const C_ALLOWED = "Allowed";
const char * const C_NOT_ALLOWED = "Not Allowed ";
const char * const C_DEFAULT_MODE = "Default mode";

58
src/gpg_ux_msg.h Normal file
View File

@ -0,0 +1,58 @@
/* Copyright 2017 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.
*/
#ifndef GPG_UX_MSG_H
#define GPG_UX_MSG_H
extern const char * const C_TEMPLATE_TYPE;
extern const char * const C_TEMPLATE_KEY;
extern const char * const C_INVALID_SELECTION;
extern const char * const C_OK;
extern const char * const C_NOK;
extern const char * const C_WRONG_PIN;
extern const char * const C_RIGHT_PIN;
extern const char * const C_PIN_CHANGED;
extern const char * const C_PIN_DIFFERS ;
extern const char * const C_PIN_USER;
extern const char * const C_PIN_ADMIN;
extern const char * const C_VERIFIED;
extern const char * const C_NOT_VERIFIED;
extern const char * const C_NOT_ALLOWED;
extern const char * const C_DEFAULT_MODE;
#define PICSTR(x) ((char*)PIC(x))
#define TEMPLATE_TYPE PICSTR(C_TEMPLATE_TYPE)
#define TEMPLATE_KEY PICSTR(C_TEMPLATE_KEY)
#define INVALID_SELECTION PICSTR(C_INVALID_SELECTION)
#define OK PICSTR(C_OK)
#define NOK PICSTR(C_NOK)
#define WRONG_PIN PICSTR(C_WRONG_PIN)
#define RIGHT_PIN PICSTR(C_RIGHT_PIN)
#define PIN_CHANGED PICSTR(C_PIN_CHANGED)
#define PIN_DIFFERS PICSTR(C_PIN_DIFFERS)
#define PIN_USER PICSTR(C_PIN_USER)
#define PIN_ADMIN PICSTR(C_PIN_ADMIN)
#define VERIFIED PICSTR(C_VERIFIED)
#define NOT_VERIFIED PICSTR(C_NOT_VERIFIED)
#define ALLOWED PICSTR(C_ALLOWED)
#define NOT_ALLOWED PICSTR(C_NOT_ALLOWED)
#define DEFAULT_MODE PICSTR(C_DEFAULT_MODE)
#endif

837
src/gpg_ux_nanos.c Normal file
View File

@ -0,0 +1,837 @@
/* Copyright 2017 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.
*/
#include "os.h"
#include "cx.h"
#include "gpg_types.h"
#include "gpg_api.h"
#include "gpg_vars.h"
#include "gpg_ux_msg.h"
#include "os_io_seproxyhal.h"
#include "usbd_ccid_impl.h"
#include "string.h"
#include "glyphs.h"
/* ----------------------------------------------------------------------- */
/* --- NanoS UI layout --- */
/* ----------------------------------------------------------------------- */
#define PICSTR(x) ((char*)PIC(x))
#define TEMPLATE_TYPE PICSTR(C_TEMPLATE_TYPE)
#define TEMPLATE_KEY PICSTR(C_TEMPLATE_KEY)
#define INVALID_SELECTION PICSTR(C_INVALID_SELECTION)
#define OK PICSTR(C_OK)
#define NOK PICSTR(C_NOK)
#define WRONG_PIN PICSTR(C_WRONG_PIN)
#define RIGHT_PIN PICSTR(C_RIGHT_PIN)
#define PIN_CHANGED PICSTR(C_PIN_CHANGED)
#define PIN_DIFFERS PICSTR(C_PIN_DIFFERS)
#define PIN_USER PICSTR(C_PIN_USER)
#define PIN_ADMIN PICSTR(C_PIN_ADMIN)
#define VERIFIED PICSTR(C_VERIFIED)
#define NOT_VERIFIED PICSTR(C_NOT_VERIFIED)
#define ALLOWED PICSTR(C_ALLOWED)
#define NOT_ALLOWED PICSTR(C_NOT_ALLOWED)
#define DEFAULT_MODE PICSTR(C_DEFAULT_MODE)
const ux_menu_entry_t ui_menu_template[];
void ui_menu_template_display(unsigned int value);
const bagl_element_t* ui_menu_template_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
void ui_menu_tmpl_set_action(unsigned int value) ;
const ux_menu_entry_t ui_menu_tmpl_key[];
void ui_menu_tmpl_key_action(unsigned int value);
const ux_menu_entry_t ui_menu_tmpl_type[];
void ui_menu_tmpl_type_action(unsigned int value);
const ux_menu_entry_t ui_menu_seed[];
void ui_menu_seed_display(unsigned int value);
const bagl_element_t* ui_menu_seed_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) ;
void ui_menu_seed_action(unsigned int value) ;
const ux_menu_entry_t ui_menu_reset[] ;
void ui_menu_reset_action(unsigned int value);
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);
const ux_menu_entry_t ui_menu_settings[] ;
const ux_menu_entry_t ui_menu_main[];
void ui_menu_main_display(unsigned int value) ;
const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
const bagl_element_t ui_pinconfirm_nanos[];
void ui_menu_pinconfirm_action(unsigned int value);
unsigned int ui_pinconfirm_nanos_button(unsigned int button_mask, unsigned int button_mask_counter);
unsigned int ui_pinconfirm_prepro(const bagl_element_t* element) ;
const bagl_element_t ui_pinentry_nanos[];
void ui_menu_pinentry_display(unsigned int value) ;
void ui_menu_pinentry_action(unsigned int value);
unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int button_mask_counter);
unsigned int ui_pinentry_prepro(const bagl_element_t* element) ;
static unsigned int validate_pin();
/* ------------------------------- Helpers UX ------------------------------- */
void ui_CCID_reset(void) {
//INSERT CODE HERE TO REMOVE/INSERT THE TOKEN
}
void ui_info(const char* msg1, const char* msg2, const void *menu_display, unsigned int value) {
os_memset(&G_gpg_vstate.ui_dogsays[0], 0, sizeof(ux_menu_entry_t));
G_gpg_vstate.ui_dogsays[0].callback = menu_display;
G_gpg_vstate.ui_dogsays[0].userid = value;
G_gpg_vstate.ui_dogsays[0].line1 = msg1;
G_gpg_vstate.ui_dogsays[0].line2 = msg2;
os_memset(&G_gpg_vstate.ui_dogsays[1],0, sizeof(ux_menu_entry_t));
UX_MENU_DISPLAY(0, G_gpg_vstate.ui_dogsays, NULL);
};
/* ------------------------------ PIN CONFIRM UX ----------------------------- */
const bagl_element_t ui_pinconfirm_nanos[] = {
// type userid x y w h str rad fill fg bg font_id icon_id
{ {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0},
NULL,
0,
0, 0,
NULL, NULL, NULL},
{ {BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS },
NULL,
0,
0, 0,
NULL, NULL, NULL },
{ {BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK },
NULL,
0,
0, 0,
NULL, NULL, NULL },
{ {BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 },
G_gpg_vstate.menu,
0,
0, 0,
NULL, NULL, NULL },
{ {BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 },
G_gpg_vstate.menu,
0,
0, 0,
NULL, NULL, NULL },
};
void ui_menu_pinconfirm_display(unsigned int value) {
UX_DISPLAY(ui_pinconfirm_nanos, (void*)ui_pinconfirm_prepro);
}
unsigned int ui_pinconfirm_prepro(const bagl_element_t* element) {
if (element->component.userid == 1) {
if ((G_gpg_vstate.io_p2 == 0x81) || (G_gpg_vstate.io_p2 == 0x82) || (G_gpg_vstate.io_p2 == 0x83)) {
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Confirm PIN");
return 1;
}
}
if (element->component.userid == 2) {
if ((G_gpg_vstate.io_p2 == 0x81) || (G_gpg_vstate.io_p2 == 0x82)|| (G_gpg_vstate.io_p2 == 0x83)) {
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s %x", G_gpg_vstate.io_p2 == 0x83?"Admin":"User",G_gpg_vstate.io_p2);
return 1;
}
}
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Please Cancel");
return 1;
}
unsigned int ui_pinconfirm_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) {
unsigned int sw;
sw = 0x6985;
switch(button_mask) {
case BUTTON_EVT_RELEASED|BUTTON_LEFT: // CANCEL
gpg_set_pin_verified(G_gpg_vstate.io_p2&0x0F,0);
sw = 0x6985;
break;
case BUTTON_EVT_RELEASED|BUTTON_RIGHT: // OK
gpg_set_pin_verified(G_gpg_vstate.io_p2&0x0F,1);
sw = 0x9000;
break;
default:
return 0;
}
gpg_io_discard(0);
gpg_io_insert_u16(sw);
gpg_io_do(IO_RETURN_AFTER_TX);
ui_menu_main_display(0);
return 0;
}
/* ------------------------------- PIN ENTRY UX ------------------------------ */
const bagl_element_t ui_pinentry_nanos[] = {
// type userid x y w h str rad fill fg bg font_id icon_id
{ {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0},
NULL,
0,
0, 0,
NULL, NULL, NULL},
{ {BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_DOWN },
NULL,
0,
0, 0,
NULL, NULL, NULL },
{ {BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_UP },
NULL,
0,
0, 0,
NULL, NULL, NULL },
{ {BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 },
G_gpg_vstate.menu,
0,
0, 0,
NULL, NULL, NULL },
{ {BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 },
G_gpg_vstate.menu,
0,
0, 0,
NULL, NULL, NULL },
};
static const char C_pin_digit[] = {'0','1','2','3','4','5','6','7','8','9','C','A','V'};
void ui_menu_pinentry_display(unsigned int value) {
if (value == 0) {
os_memset(G_gpg_vstate.ux_pinentry, 0, sizeof(G_gpg_vstate.ux_pinentry));
G_gpg_vstate.ux_pinentry[0]=1;
G_gpg_vstate.ux_pinentry[1]=5;
}
UX_DISPLAY(ui_pinentry_nanos, (void*)ui_pinentry_prepro);
}
unsigned int ui_pinentry_prepro(const bagl_element_t* element) {
if (element->component.userid == 1) {
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
}
else if (element->component.userid == 2) {
unsigned int i;
G_gpg_vstate.menu[0] = ' ';
for (i = 1; i<= G_gpg_vstate.ux_pinentry[0]; i++) {
G_gpg_vstate.menu[i] = C_pin_digit[G_gpg_vstate.ux_pinentry[i]];
}
for (; i<= GPG_MAX_PW_LENGTH;i++) {
G_gpg_vstate.menu[i] = '-';
}
G_gpg_vstate.menu[i] = 0;
}
return 1;
}
unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) {
unsigned int offset = G_gpg_vstate.ux_pinentry[0];
char digit ;
switch(button_mask) {
case BUTTON_EVT_RELEASED|BUTTON_LEFT: // Down
if (G_gpg_vstate.ux_pinentry[offset]) {
G_gpg_vstate.ux_pinentry[offset]--;
} else {
G_gpg_vstate.ux_pinentry[offset] = sizeof(C_pin_digit)-1;
}
break;
case BUTTON_EVT_RELEASED|BUTTON_RIGHT: //up
G_gpg_vstate.ux_pinentry[offset]++;
if (G_gpg_vstate.ux_pinentry[offset] == sizeof(C_pin_digit)) {
G_gpg_vstate.ux_pinentry[offset] = 0;
}
break;
case BUTTON_EVT_RELEASED|BUTTON_LEFT|BUTTON_RIGHT:
digit = C_pin_digit[G_gpg_vstate.ux_pinentry[offset]];
//next digit
if ((digit >= '0') && (digit <= '9')) {
offset ++;
G_gpg_vstate.ux_pinentry[0] = offset;
if (offset == GPG_MAX_PW_LENGTH+1) {
return validate_pin() ;
}
G_gpg_vstate.ux_pinentry[offset] = 5;
}
//cancel digit
else if (digit == 'C') {
if (offset > 1) {
offset--;
G_gpg_vstate.ux_pinentry[0] = offset;
}
}
//validate pin
else if (digit == 'V') {
G_gpg_vstate.ux_pinentry[0] = offset-1;
return validate_pin() ;
}
//cancel input without check
else { //(digit == 'A')
gpg_io_discard(0);
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
gpg_io_do(IO_RETURN_AFTER_TX);
ui_menu_main_display(0);
return 0;
}
}
ui_menu_pinentry_display(1);
return 0;
}
static unsigned int validate_pin() {
unsigned int offset, len, sw;
for (offset = 1; offset< G_gpg_vstate.ux_pinentry[0];offset++) {
G_gpg_vstate.menu[offset] = C_pin_digit[G_gpg_vstate.ux_pinentry[offset]];
}
if (G_gpg_vstate.io_ins == 0x20) {
if (gpg_check_pin(G_gpg_vstate.io_p2&0x0F, (unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0])) {
sw = SW_OK;
} else {
sw = SW_CONDITIONS_NOT_SATISFIED;
}
gpg_io_discard(1);
gpg_io_insert_u16(sw);
gpg_io_do(IO_RETURN_AFTER_TX);
if (sw == SW_CONDITIONS_NOT_SATISFIED) {
ui_info(WRONG_PIN, NULL, ui_menu_main_display, 0);
} else {
ui_info(RIGHT_PIN, NULL, ui_menu_main_display, 0);
}
return 0;
}
if (G_gpg_vstate.io_ins == 0x24) {
if (G_gpg_vstate.work.io_buffer[0] <= 2) {
gpg_io_insert_u8(G_gpg_vstate.ux_pinentry[0]);
gpg_io_insert((unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0]);
G_gpg_vstate.work.io_buffer[0]++;
}
if (G_gpg_vstate.work.io_buffer[0] == 3) {
if (!gpg_check_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+1, G_gpg_vstate.work.io_buffer[0])) {
gpg_io_discard(1);
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
gpg_io_do(IO_RETURN_AFTER_TX);
ui_info(WRONG_PIN, NULL, ui_menu_main_display, 0);
return 0;
}
offset = 1+G_gpg_vstate.work.io_buffer[0];
len = G_gpg_vstate.work.io_buffer[offset];
if ((len != G_gpg_vstate.work.io_buffer[offset+ 1+len]) ||
(os_memcmp(G_gpg_vstate.work.io_buffer+offset + 1, G_gpg_vstate.work.io_buffer+offset+ 1+len+1, len) != 0)) {
gpg_io_discard(1);
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
gpg_io_do(IO_RETURN_AFTER_TX);
ui_info(PIN_DIFFERS, NULL, ui_menu_main_display, 0);
}
gpg_change_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+offset+ 1, len);
gpg_io_discard(1);
gpg_io_insert_u16(SW_OK);
gpg_io_do(IO_RETURN_AFTER_TX);
ui_info(PIN_CHANGED, NULL, ui_menu_main_display, 0);
return 0;
}
}
return 0;
}
/* ------------------------------- template UX ------------------------------- */
#define LABEL_SIG "Signature"
#define LABEL_AUT "Authentication"
#define LABEL_DEC "Decryption"
#define LABEL_RSA2048 "RSA 2048"
#define LABEL_RSA3072 "RSA 3072"
#define LABEL_RSA4096 "RSA 4096"
#define LABEL_NISTP256 "NIST P256"
#define LABEL_BPOOLR1 "Brainpool R1"
#define LABEL_Ed25519 "Ed25519"
const ux_menu_entry_t ui_menu_template[] = {
{ui_menu_tmpl_key, NULL, -1, NULL, "Choose key...", NULL, 0, 0},
{ui_menu_tmpl_type, NULL, -1, NULL, "Choose type...", NULL, 0, 0},
{NULL, ui_menu_tmpl_set_action, -1, NULL, "Set template", NULL, 0, 0},
{ui_menu_settings, NULL, 0, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_menu_template_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_menu_template, ui_menu_template_preprocessor);
}
const bagl_element_t* ui_menu_template_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if(element->component.userid==0x20) {
if (entry == &ui_menu_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_menu_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_menu_tmpl_set_action(unsigned int value) {
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
gpg_key_t* dest;
const char* err;
err = NULL;
os_memset(&attributes, 0, sizeof(attributes));
switch (G_gpg_vstate.ux_type) {
case 2048:
case 3072:
case 4096:
attributes.value[0] = 0x01;
attributes.value[1] = (G_gpg_vstate.ux_type>>8) &0xFF;
attributes.value[2] = G_gpg_vstate.ux_type&0xFF;
attributes.value[3] = 0x00;
attributes.value[4] = 0x20;
attributes.value[5] = 0x01;
attributes.length = 6;
break;
case CX_CURVE_SECP256R1:
if (G_gpg_vstate.ux_key == 2) {
attributes.value[0] = 18; //ecdh
} else {
attributes.value[0] = 19; //ecdsa
}
os_memmove(attributes.value+1, C_OID_SECP256R1, sizeof(C_OID_SECP256R1));
attributes.length = 1+sizeof(C_OID_SECP256R1);
break;
case CX_CURVE_BrainPoolP256R1:
if (G_gpg_vstate.ux_key == 2) {
attributes.value[0] = 18; //ecdh
} else {
attributes.value[0] = 19; //ecdsa
}
os_memmove(attributes.value+1, C_OID_BRAINPOOL256R1, sizeof(C_OID_BRAINPOOL256R1));
attributes.length = 1+sizeof(C_OID_BRAINPOOL256R1);
break;
case CX_CURVE_Ed25519:
if (G_gpg_vstate.ux_key == 2) {
attributes.value[0] = 18; //ecdh
os_memmove(attributes.value+1, C_OID_cv25519, sizeof(C_OID_cv25519));
attributes.length = 1+sizeof(C_OID_cv25519);
} else {
attributes.value[0] = 22; //eddsa
os_memmove(attributes.value+1, C_OID_Ed25519, sizeof(C_OID_Ed25519));
attributes.length = 1+sizeof(C_OID_Ed25519);
}
break;
default:
err = TEMPLATE_TYPE;
goto ERROR;
}
dest = NULL;
switch(G_gpg_vstate.ux_key) {
case 1:
dest = &G_gpg_vstate.kslot->sig;
break;
case 2:
dest = &G_gpg_vstate.kslot->dec;
break;
case 3:
dest = &G_gpg_vstate.kslot->aut;
break;
default:
err = TEMPLATE_KEY;
goto ERROR;
}
gpg_nvm_write(dest, NULL, sizeof(gpg_key_t));
gpg_nvm_write(&dest->attributes, &attributes, sizeof(attributes));
ui_info(OK, NULL, ui_menu_template_display, 0);
return;
ERROR:
ui_info(INVALID_SELECTION, err, ui_menu_template_display, 0);
}
const ux_menu_entry_t ui_menu_tmpl_key[] = {
{NULL, ui_menu_tmpl_key_action, 1, NULL, LABEL_SIG, NULL, 0, 0},
{NULL, ui_menu_tmpl_key_action, 2, NULL, LABEL_DEC, NULL, 0, 0},
{NULL, ui_menu_tmpl_key_action, 3, NULL, LABEL_AUT, NULL, 0, 0},
{ui_menu_template, NULL, 0, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_menu_tmpl_key_action(unsigned int value) {
G_gpg_vstate.ux_key = value;
ui_menu_template_display(0);
}
const ux_menu_entry_t ui_menu_tmpl_type[] = {
{NULL, ui_menu_tmpl_type_action, 2048, NULL, LABEL_RSA2048, NULL, 0, 0},
{NULL, ui_menu_tmpl_type_action, 3072, NULL, LABEL_RSA3072, NULL, 0, 0},
{NULL, ui_menu_tmpl_type_action, 4096, NULL, LABEL_RSA4096, NULL, 0, 0},
{NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0},
{NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOLR1, NULL, 0, 0},
{NULL, ui_menu_tmpl_type_action, CX_CURVE_Ed25519, NULL, LABEL_Ed25519, NULL, 0, 0},
{ui_menu_template, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_menu_tmpl_type_action(unsigned int value) {
G_gpg_vstate.ux_type = value;
ui_menu_template_display(1);
}
/* --------------------------------- SEED UX --------------------------------- */
const ux_menu_entry_t ui_menu_seed[] = {
#if GPG_KEYS_SLOTS != 3
#error menu definition not correct for current value of GPG_KEYS_SLOTS
#endif
{NULL, NULL, 0, NULL, "", NULL, 0, 0},
{NULL, ui_menu_seed_action, 1, NULL, "Set on", NULL, 0, 0},
{NULL, ui_menu_seed_action, 0, NULL, "Set off", NULL, 0, 0},
{ui_menu_settings, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_menu_seed_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_menu_seed, ui_menu_seed_preprocessor);
}
const bagl_element_t* ui_menu_seed_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if(element->component.userid==0x20) {
if (entry == &ui_menu_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_menu_seed_action(unsigned int value) {
G_gpg_vstate.seed_mode = value;
ui_menu_seed_display(0);
}
/* ------------------------------- PIN MODE UX ------------------------------ */
const ux_menu_entry_t ui_menu_pinmode[];
void ui_menu_pinmode_display(unsigned int value);
const bagl_element_t* ui_menu_pinmode_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element);
void ui_menu_pinmode_action(unsigned int value);
const ux_menu_entry_t ui_menu_pinmode[] = {
{NULL, NULL, -1, NULL, "Choose:", NULL, 0, 0},
{NULL, ui_menu_pinmode_action, 0x8000|PIN_MODE_HOST, NULL, "Host", NULL, 0, 0},
{NULL, ui_menu_pinmode_action, 0x8000|PIN_MODE_SCREEN, NULL, "On Screen", NULL, 0, 0},
{NULL, ui_menu_pinmode_action, 0x8000|PIN_MODE_CONFIRM, NULL, "Confirm only", NULL, 0, 0},
{NULL, ui_menu_pinmode_action, 0x8000|PIN_MODE_TRUST, NULL, "Trust", NULL, 0, 0},
{NULL, ui_menu_pinmode_action, 128, NULL, "Set Default", NULL, 0, 0},
{ui_menu_settings, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_menu_pinmode_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_menu_pinmode, ui_menu_pinmode_preprocessor);
}
const bagl_element_t* ui_menu_pinmode_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
if (element->component.userid==0x20) {
if ((entry->userid >= (0x8000|PIN_MODE_HOST)) && (entry->userid<=(0x8000|PIN_MODE_TRUST))) {
unsigned char id = entry->userid&0x7FFFF;
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s %s %s",
(char*)PIC(entry->line1),
id == N_gpg_pstate->config_pin[0] ? "#" : " ", /* default */
id == G_gpg_vstate.pinmode ? "+" : " " /* selected*/);
element->text = G_gpg_vstate.menu;
element->component.height = 32;
}
}
return element;
}
void ui_menu_pinmode_action(unsigned int value) {
unsigned char s;
value = value&0x7FFF;
if (value == 128) {
if (G_gpg_vstate.pinmode != N_gpg_pstate->config_pin[0]) {
if (G_gpg_vstate.pinmode == PIN_MODE_TRUST) {
ui_info(DEFAULT_MODE, NOT_ALLOWED, ui_menu_pinmode_display,0);
return;
}
//set new mode
s = G_gpg_vstate.pinmode;
gpg_nvm_write(&N_gpg_pstate->config_pin[0], &s,1);
//disactivate pinpad if any
if (G_gpg_vstate.pinmode == PIN_MODE_HOST) {
s = 0;
} else {
s = 3;
}
USBD_CCID_activate_pinpad(s);
}
}
else {
switch (value) {
case PIN_MODE_HOST:
case PIN_MODE_SCREEN:
if (!gpg_is_pin_verified(PIN_ID_PW1)) {
ui_info(PIN_USER, NOT_VERIFIED, ui_menu_pinmode_display,0);
return;
}
break;
case PIN_MODE_CONFIRM:
case PIN_MODE_TRUST:
if (!gpg_is_pin_verified(PIN_ID_PW3)) {
ui_info(PIN_ADMIN, NOT_VERIFIED, ui_menu_pinmode_display,0);
return;
}
break;
default:
ui_info(INVALID_SELECTION, NULL, ui_menu_pinmode_display,0);
return;
}
G_gpg_vstate.pinmode = value;
}
// redisplay first entry of the idle menu
ui_menu_pinmode_display(0);
}
/* -------------------------------- RESET UX --------------------------------- */
const ux_menu_entry_t ui_menu_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_menu_main_display, 0, &C_badge_back, "Oh No!", NULL, 61, 40},
{NULL, ui_menu_reset_action, 0, NULL, "Yes!", NULL, 0, 0},
UX_MENU_END
};
void ui_menu_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_CCID_reset();
ui_menu_main_display(0);
}
/* ------------------------------- SETTINGS UX ------------------------------- */
const ux_menu_entry_t ui_menu_settings[] = {
{NULL, ui_menu_template_display, 0, NULL, "Key template", NULL, 0, 0},
{NULL, ui_menu_seed_display, 0, NULL, "Seed mode", NULL, 0, 0},
{NULL, ui_menu_pinmode_display, 0, NULL, "PIN mode", NULL, 0, 0},
{ui_menu_reset, NULL, 0, NULL, "Reset", NULL, 0, 0},
{NULL, ui_menu_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_menu_slot[] = {
{NULL, NULL, -1, NULL, "Choose:", NULL, 0, 0},
{NULL, ui_menu_slot_action, 1, NULL, "", NULL, 0, 0},
{NULL, ui_menu_slot_action, 2, NULL, "", NULL, 0, 0},
{NULL, ui_menu_slot_action, 3, NULL, "", NULL, 0, 0},
{NULL, ui_menu_slot_action, 128, NULL, "Set Default", NULL, 0, 0},
{NULL, ui_menu_main_display, 1, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
void ui_menu_slot_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_menu_slot, ui_menu_slot_preprocessor);
}
const bagl_element_t* ui_menu_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_menu_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_menu_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);
if (s!= G_gpg_vstate.slot) {
G_gpg_vstate.slot = s;
G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot];
ui_CCID_reset();
}
}
// redisplay first entry of the idle menu
ui_menu_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_menu_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_menu_main_display, 3, &C_badge_back, "Back", NULL, 61, 40},
UX_MENU_END
};
#undef STR
#undef XSTR
/* --------------------------------- MAIN UX --------------------------------- */
const ux_menu_entry_t ui_menu_main[] = {
{NULL, NULL, 0, NULL, "", NULL, 0, 0},
{NULL, ui_menu_slot_display, 0, NULL, "Select slot", NULL, 0, 0},
{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},
UX_MENU_END
};
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) {
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));
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_menu_main_display(unsigned int value) {
UX_MENU_DISPLAY(value, ui_menu_main, ui_menu_main_preprocessor);
}
void ui_init(void) {
ui_menu_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);
}

25
src/gpg_ux_nanos.h Normal file
View File

@ -0,0 +1,25 @@
/* Copyright 2017 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.
*/
#ifndef GPG_UX_NANOS_H
#define GPG_UX_NANOS_H
void ui_init(void);
void ui_main_display(unsigned int value);
void ui_menu_pinconfirm_display(unsigned int value);
void ui_menu_pinentry_display(unsigned int value);
#endif

View File

@ -18,6 +18,7 @@
#include "os.h"
#include "cx.h"
#include "os_io_seproxyhal.h"
#include "gpg_types.h"
#include "gpg_api.h"
@ -39,5 +40,5 @@ extern gpg_nv_state_t N_state_pic;
extern int apdu_n;
#endif
extern ux_state_t ux;
#endif

View File

@ -160,7 +160,8 @@ static const uint8_t const USBD_DeviceDesc[]= {
/* USB Mass storage device Configuration Descriptor */
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
static const uint8_t USBD_CfgDesc[] =
#define USBD_OFFSET_CfgDesc_bPINSupport 70
static const uint8_t N_USBD_CfgDesc[] =
{
0x09, /* bLength: Configuration Descriptor size */
@ -252,9 +253,10 @@ static const uint8_t USBD_CfgDesc[] =
0x00, /* bClassGetResponse*/
0x00, /* bClassEnvelope */
0x00,0x00, /* wLcdLayout : 0000h no LCD. */
0x01, /* bPINSupport : no PIN verif and modif */
0x03, /* bPINSupport : no PIN verif and modif */ //<= offset: 70
0x01, /* bMaxCCIDBusySlots */
//72
/******************** CCID Endpoints ********************/
0x07, /*Endpoint descriptor length = 7*/
0x05, /*Endpoint descriptor type */
@ -283,10 +285,11 @@ static const uint8_t USBD_CfgDesc[] =
};
static uint8_t *USBD_GetCfgDesc_impl (uint16_t *length)
{
*length = sizeof (USBD_CfgDesc);
return (uint8_t*)USBD_CfgDesc;
*length = sizeof (N_USBD_CfgDesc);
return (uint8_t*)(N_USBD_CfgDesc);
}
/**
@ -432,6 +435,12 @@ static const USBD_ClassTypeDef USBD_CCID =
};
void USBD_CCID_activate_pinpad(int enabled) {
unsigned char e;
e = enabled?3:0;
nvm_write(USBD_GetCfgDesc_impl+USBD_OFFSET_CfgDesc_bPINSupport, &e,1);
}
void USB_CCID_power(unsigned char enabled) {
os_memset(&USBD_Device, 0, sizeof(USBD_Device));

View File

@ -17,6 +17,8 @@
#define CCID_INTR_EPIN_SIZE 16
#define CCID_EP0_BUFF_SIZ 64
void USB_CCID_power(unsigned char enabled);
void USBD_CCID_activate_pinpad(int enabled);
#endif // USBD_CCID_IMPL_H