From 9328cad7f191de23c0891600d9a82c3b966762f0 Mon Sep 17 00:00:00 2001 From: Mark Bryars Date: Tue, 3 Nov 2015 00:08:18 +0100 Subject: [PATCH 01/31] Add U2F support --- firmware/Makefile | 1 + firmware/protob/storage.pb.c | 3 +- firmware/protob/storage.pb.h | 11 +- firmware/storage.c | 12 + firmware/storage.h | 2 + firmware/u2f.c | 725 +++++++++++++++++++++++++++++++++++ firmware/u2f.h | 58 +++ firmware/u2f/u2f.h | 141 +++++++ firmware/u2f/u2f_hid.h | 140 +++++++ firmware/u2f/u2f_keys.h | 46 +++ firmware/usb.c | 116 +++++- trezor-common | 2 +- 12 files changed, 1245 insertions(+), 12 deletions(-) create mode 100644 firmware/u2f.c create mode 100644 firmware/u2f.h create mode 100644 firmware/u2f/u2f.h create mode 100644 firmware/u2f/u2f_hid.h create mode 100644 firmware/u2f/u2f_keys.h diff --git a/firmware/Makefile b/firmware/Makefile index fa4aa3e16a..a321a77922 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -3,6 +3,7 @@ APPVER = 1.0.0 NAME = trezor OBJS += usb.o +OBJS += u2f.o OBJS += messages.o OBJS += storage.o OBJS += trezor.o diff --git a/firmware/protob/storage.pb.c b/firmware/protob/storage.pb.c index 68da897f29..67f1619a8f 100644 --- a/firmware/protob/storage.pb.c +++ b/firmware/protob/storage.pb.c @@ -5,7 +5,7 @@ -const pb_field_t Storage_fields[11] = { +const pb_field_t Storage_fields[12] = { PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, Storage, version, version, 0), PB_FIELD2( 2, MESSAGE , OPTIONAL, STATIC , OTHER, Storage, node, version, &HDNodeType_fields), PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, Storage, mnemonic, node, 0), @@ -16,6 +16,7 @@ const pb_field_t Storage_fields[11] = { PB_FIELD2( 8, STRING , OPTIONAL, STATIC , OTHER, Storage, label, language, 0), PB_FIELD2( 9, BOOL , OPTIONAL, STATIC , OTHER, Storage, imported, label, 0), PB_FIELD2( 10, BYTES , OPTIONAL, STATIC , OTHER, Storage, homescreen, imported, 0), + PB_FIELD2( 11, UINT32 , OPTIONAL, STATIC , OTHER, Storage, u2f_counter, homescreen, 0), PB_LAST_FIELD }; diff --git a/firmware/protob/storage.pb.h b/firmware/protob/storage.pb.h index 2f51923fd2..c5d942eb90 100644 --- a/firmware/protob/storage.pb.h +++ b/firmware/protob/storage.pb.h @@ -37,13 +37,15 @@ typedef struct _Storage { bool imported; bool has_homescreen; Storage_homescreen_t homescreen; + bool has_u2f_counter; + uint32_t u2f_counter; } Storage; /* Default values for struct fields */ /* Initializer values for message structs */ -#define Storage_init_default {0, false, HDNodeType_init_default, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}} -#define Storage_init_zero {0, false, HDNodeType_init_zero, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}} +#define Storage_init_default {0, false, HDNodeType_init_default, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0} +#define Storage_init_zero {0, false, HDNodeType_init_zero, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0} /* Field tags (for use in manual encoding/decoding) */ #define Storage_version_tag 1 @@ -56,12 +58,13 @@ typedef struct _Storage { #define Storage_label_tag 8 #define Storage_imported_tag 9 #define Storage_homescreen_tag 10 +#define Storage_u2f_counter_tag 11 /* Struct field encoding specification for nanopb */ -extern const pb_field_t Storage_fields[11]; +extern const pb_field_t Storage_fields[12]; /* Maximum encoded size of messages (where known) */ -#define Storage_size (1359 + HDNodeType_size) +#define Storage_size (1365 + HDNodeType_size) #ifdef __cplusplus } /* extern "C" */ diff --git a/firmware/storage.c b/firmware/storage.c index d5f5bc9f9d..f28bce5891 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -401,3 +401,15 @@ bool storage_isInitialized(void) { return storage.has_node || storage.has_mnemonic; } + +uint32_t storage_nextU2FCounter(void) +{ + if(!storage.has_u2f_counter) { + storage.has_u2f_counter = true; + storage.u2f_counter = 1; + } else { + storage.u2f_counter++; + } + storage_commit(); + return storage.u2f_counter; +} diff --git a/firmware/storage.h b/firmware/storage.h index b1041a8b93..793c8c4ea3 100644 --- a/firmware/storage.h +++ b/firmware/storage.h @@ -58,6 +58,8 @@ void storage_resetPinFails(void); void storage_increasePinFails(void); uint32_t storage_getPinFails(void); +uint32_t storage_nextU2FCounter(void); + bool storage_isInitialized(void); extern Storage storage; diff --git a/firmware/u2f.c b/firmware/u2f.c new file mode 100644 index 0000000000..09f2ca810b --- /dev/null +++ b/firmware/u2f.c @@ -0,0 +1,725 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2015 Mark Bryars + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + + +#include +#include + +#include "debug.h" +#include "storage.h" +#include "bip32.h" +#include "layout2.h" +#include "usb.h" +#include "buttons.h" +#include "trezor.h" +#include "nist256p1.h" +#include "rng.h" + +#include "u2f/u2f.h" +#include "u2f/u2f_hid.h" +#include "u2f/u2f_keys.h" +#include "u2f.h" + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +// About 1/2 Second according to values used in protect.c +#define U2F_TIMEOUT 840000/2 +#define U2F_OUT_PKT_BUFFER_LEN 128 + +// Initialise without a cid +static uint32_t cid = CID_BROADCAST; + +// Circular Output buffer +static uint32_t u2f_out_start = 0; +static uint32_t u2f_out_end = 0; +static uint8_t u2f_out_packets[U2F_OUT_PKT_BUFFER_LEN][HID_RPT_SIZE]; + +#define U2F_PUBKEY_LEN 65 +#define KEY_HANDLE_LEN 64 + +// Auth/Register request state machine +typedef enum { + INIT = 0, + BTN_NO = 1, + BTN_YES = 2, + AUTH = 10, + AUTH_FAIL = 11, + AUTH_PASS = 12, + REG = 20, + REG_FAIL = 21, + REG_PASS = 22 +} U2F_STATE; + +static U2F_STATE last_req_state = INIT; + +typedef struct { + uint8_t reserved; + uint8_t appId[U2F_APPID_SIZE]; + uint8_t chal[U2F_CHAL_SIZE]; + uint8_t keyHandle[KEY_HANDLE_LEN]; + uint8_t pubKey[U2F_PUBKEY_LEN]; +} U2F_REGISTER_SIG_STR; + +typedef struct { + uint8_t appId[U2F_APPID_SIZE]; + uint8_t flags; + uint8_t ctr[4]; + uint8_t chal[U2F_CHAL_SIZE]; +} U2F_AUTHENTICATE_SIG_STR; + +uint8_t buttonState(void) +{ + buttonUpdate(); + + if ((button.NoDown > 10) || button.NoUp) + return BTN_NO; + if ((button.YesDown > 10) || button.YesUp) + return BTN_YES; + return 0; +} + +void int2hex(uint8_t *dst, const uint32_t i) +{ + dst[0] = '0' + ((i >> 28) & 0x0F); + dst[1] = '0' + ((i >> 24) & 0x0F); + dst[2] = '0' + ((i >> 20) & 0x0F); + dst[3] = '0' + ((i >> 16) & 0x0F); + dst[4] = '0' + ((i >> 12) & 0x0F); + dst[5] = '0' + ((i >> 8) & 0x0F); + dst[6] = '0' + ((i >> 4) & 0x0F); + dst[7] = '0' + (i & 0x0F); + dst[8] = '\0'; + + int t = 0; + for (; t < 8; t++) { + if (dst[t] > '9') + dst[t] += 7; // 'A'-'9'+1 + } +} + +char *debugInt(const uint32_t i) +{ + static uint8_t n = 0; + static uint8_t id[8][9]; + int2hex(id[n], i); + debugLog(0, "", (const char *)id[n]); + char *ret = (char *)id[n]; + n = (n + 1) % 8; + return ret; +} + +static uint32_t dialog_timeout = 0; + +void LayoutHomeAfterTimeout(void) +{ + static bool timeoutLock = false; + + if (timeoutLock || dialog_timeout == 0) + return; // Dialog has cleared or already in loop + + timeoutLock = true; + U2F_STATE rs = last_req_state; + U2F_STATE bs = INIT; + while (dialog_timeout-- && rs == last_req_state && bs == 0) { + usbPoll(); // may trigger new request + bs = buttonState(); + } + timeoutLock = false; + + if (rs != last_req_state) + return; // Reset by new request don't clear screen + + if (dialog_timeout == 0) { + last_req_state += BTN_NO; // Timeout is like button no + } + else { + last_req_state += bs; + dialog_timeout = 0; + } + + layoutHome(); +} + +uint32_t next_cid(void) +{ + // extremely unlikely but hey + do { + cid = random32(); + } while (cid == 0 || cid == CID_BROADCAST); + return cid; +} + +void u2fhid_read(const U2FHID_FRAME *f) +{ + static uint8_t seq, cmd; + static uint32_t len; + static uint8_t *buf_ptr; + static uint8_t buf[7609]; + + if ((f->cid != CID_BROADCAST) && (f->cid != cid)) { + return; // Not for us + } + + if (f->type & TYPE_INIT) { + seq = 0; + buf_ptr = buf; + len = MSG_LEN(*f); + cmd = f->type; + memcpy(buf_ptr, f->init.data, sizeof(f->init.data)); + buf_ptr += sizeof(f->init.data); + } + else { + if (f->cont.seq == seq) { + seq++; + memcpy(buf_ptr, f->cont.data, sizeof(f->cont.data)); + buf_ptr += sizeof(f->cont.data); + } + else { + return send_u2fhid_error(ERR_INVALID_SEQ); + } + } + + // Broadcast is reserved for init + if (cid == CID_BROADCAST && cmd != U2FHID_INIT) + return; + + // Check length isnt bigger than spec max + if (len > sizeof(buf)) + return send_u2fhid_error(ERR_INVALID_LEN); + + // Do we need to wait for more data + if ((buf_ptr - buf) < (signed)len) { + // debugLog(0, "", "u2fhid_read wait"); + return; + } + + // We have all the data + switch (cmd) { + case U2FHID_PING: + u2fhid_ping(buf, len); + break; + case U2FHID_MSG: + u2fhid_msg((APDU *)buf, len); + break; + case U2FHID_LOCK: + u2fhid_lock(buf, len); + break; + case U2FHID_INIT: + u2fhid_init((const U2FHID_INIT_REQ *)buf); + break; + case U2FHID_WINK: + u2fhid_wink(buf, len); + break; + // case U2FHID_SYNC: + // u2fhid_sync(buf, len); + break; + default: + send_u2fhid_error(ERR_INVALID_CMD); + break; + } +} + +void u2fhid_ping(const uint8_t *buf, uint32_t len) +{ + debugLog(0, "", "u2fhid_ping"); + send_u2fhid_msg(U2FHID_PING, buf, len); +} + +void u2fhid_wink(const uint8_t *buf, uint32_t len) +{ + debugLog(0, "", "u2fhid_wink"); + (void)buf; + + if (len > 0) + return send_u2fhid_error(ERR_INVALID_LEN); + + if (dialog_timeout > 0) + dialog_timeout = U2F_TIMEOUT; + + U2FHID_FRAME f; + bzero(&f, sizeof(f)); + f.cid = cid; + f.init.cmd = U2FHID_WINK; + f.init.bcntl = 0; + queue_u2f_pkt(&f); +} + +void u2fhid_sync(const uint8_t *buf, uint32_t len) +{ + debugLog(0, "", "u2fhid_sync"); + (void)buf; + + if (len > 0) + return send_u2fhid_error(ERR_INVALID_LEN); + + // Abort things. + dialog_timeout = 0; +} + +void u2fhid_lock(const uint8_t *buf, uint32_t len) +{ + debugLog(0, "", "u2fhid_lock"); + (void)buf; + (void)len; + send_u2fhid_error(ERR_INVALID_CMD); +} + +void u2fhid_init(const U2FHID_INIT_REQ *init_req) +{ + debugLog(0, "", "u2fhid_init"); + U2FHID_FRAME f; + U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)f.init.data; + + bzero(&f, sizeof(f)); + f.cid = CID_BROADCAST; + f.init.cmd = U2FHID_INIT; + f.init.bcnth = 0; + f.init.bcntl = sizeof(U2FHID_INIT_RESP); + + memcpy(resp->nonce, init_req->nonce, sizeof(init_req->nonce)); + resp->cid = next_cid(); + resp->versionInterface = U2FHID_IF_VERSION; + resp->versionMajor = VERSION_MAJOR; + resp->versionMinor = VERSION_MINOR; + resp->versionBuild = VERSION_PATCH; + resp->capFlags = CAPFLAG_WINK; + + queue_u2f_pkt(&f); +} + +void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt) +{ + // debugLog(0, "", "u2f_write_pkt"); + uint32_t next = (u2f_out_end + 1) % U2F_OUT_PKT_BUFFER_LEN; + if (u2f_out_start == next) { + debugLog(0, "", "u2f_write_pkt full"); + return; // Buffer full :( + } + memcpy(u2f_out_packets[u2f_out_end], u2f_pkt, HID_RPT_SIZE); + u2f_out_end = next; +} + +uint8_t *u2f_out_data(void) +{ + if (u2f_out_start == u2f_out_end) + return NULL; // No data + // debugLog(0, "", "u2f_out_data"); + uint32_t t = u2f_out_start; + u2f_out_start = (u2f_out_start + 1) % U2F_OUT_PKT_BUFFER_LEN; + return u2f_out_packets[t]; +} + +void u2fhid_msg(const APDU *a, uint32_t len) +{ + static bool lock = false; + + if ((APDU_LEN(*a) + sizeof(APDU)) > len) { + debugLog(0, "", "BAD APDU LENGTH"); + debugInt(APDU_LEN(*a)); + debugInt(len); + return; + } + + // Very crude locking, incase another message comes in while we wait. This + // actually can probably be removed as no code inside calls usbPoll anymore + if (lock) + return send_u2fhid_error(ERR_CHANNEL_BUSY); + + lock = true; + + switch (a->ins) { + case U2F_REGISTER: + u2f_register(a); + break; + case U2F_AUTHENTICATE: + u2f_authenticate(a); + break; + case U2F_VERSION: + u2f_version(a); + break; + default: + debugLog(0, "", "u2f unknown cmd"); + send_u2f_error(U2F_SW_INS_NOT_SUPPORTED); + } + + lock = false; + + LayoutHomeAfterTimeout(); +} + +void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) +{ + U2FHID_FRAME f; + uint8_t *p = (uint8_t *)data; + uint32_t l = len; + uint32_t psz; + uint8_t seq = 0; + + // debugLog(0, "", "send_u2fhid_msg"); + + bzero(&f, sizeof(f)); + f.cid = cid; + f.init.cmd = cmd; + f.init.bcnth = len >> 8; + f.init.bcntl = len & 0xff; + + // Init packet + psz = MIN(sizeof(f.init.data), l); + memcpy(f.init.data, p, psz); + queue_u2f_pkt(&f); + l -= psz; + p += psz; + + // Cont packet(s) + for (; l > 0; l -= psz, p += psz) { + // debugLog(0, "", "send_u2fhid_msg con"); + bzero(&f.cont.data, sizeof(f.cont.data)); + f.cont.seq = seq++; + psz = MIN(sizeof(f.cont.data), l); + memcpy(f.cont.data, p, psz); + queue_u2f_pkt(&f); + } + + if (data + len != p) { + debugLog(0, "", "send_u2fhid_msg is bad"); + debugInt(data + len - p); + } +} + +void send_u2fhid_error(uint8_t err) +{ + U2FHID_FRAME f; + + bzero(&f, sizeof(f)); + f.cid = cid; + f.init.cmd = U2FHID_ERROR; + f.init.bcntl = 1; + f.init.data[0] = err; + queue_u2f_pkt(&f); +} + +void u2f_version(const APDU *a) +{ + // INCLUDES SW_NO_ERROR + static const uint8_t version_response[] = {'U', '2', 'F', '_', + 'V', '2', 0x90, 0x00}; + (void)a; + debugLog(0, "", "u2f version"); + send_u2f_msg(version_response, sizeof(version_response)); +} + +const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) +{ + static HDNode node; + if (!storage_getRootNode(&node)) { + layoutHome(); + debugLog(0, "", "ERR: Device not init"); + return 0; + } + if (!address_n || address_n_count == 0) { + return &node; + } + if (hdnode_private_ckd_cached(&node, address_n, address_n_count) == 0) { + layoutHome(); + debugLog(0, "", "ERR: Derive private failed"); + return 0; + } + return &node; +} + +const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) +{ + uint8_t keybase[64]; + + // First half of keyhandle is random + random_buffer(key_handle, 32); + + // prepare keypair from /random data + const HDNode *node = + getDerivedNode((uint32_t*)key_handle, 32/sizeof(uint32_t)); + + // For second half of keyhandle + // Signature of app_id and random data + memcpy(&keybase[0], app_id, 32); + memcpy(&keybase[32], key_handle, 32); + uint8_t sig[64]; + ecdsa_sign(&nist256p1, node->private_key, + (uint8_t *)&keybase, sizeof(keybase), sig, + NULL); + + // Copy 32 bytes of signature into keyhandle + memcpy(&key_handle[32], sig, 32); + + // Done! + return node; +} + + +const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[]) +{ + uint8_t keybase[64]; + memcpy(&keybase[0], app_id, 32); + memcpy(&keybase[32], key_handle, 32); + + const HDNode *node = + getDerivedNode((uint32_t*)key_handle, 32/sizeof(uint32_t)); + + uint8_t sig[64]; + ecdsa_sign(&nist256p1, node->private_key, + (uint8_t *)&keybase, sizeof(keybase), sig, + NULL); + + if (memcmp(&key_handle[32], sig, 32) !=0) + return NULL; + + // Done! + return node; +} + + +void u2f_register(const APDU *a) +{ + static U2F_REGISTER_REQ last_req; + const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data; + + // Validate basic request parameters + debugLog(0, "", "u2f register"); + if (APDU_LEN(*a) != sizeof(U2F_REGISTER_REQ)) { + debugLog(0, "", "u2f register - badlen"); + send_u2f_error(U2F_SW_WRONG_DATA); + return; + } + + // If this request is different from last request, reset state machine + if (memcmp(&last_req, req, sizeof(last_req)) != 0) { + memcpy(&last_req, req, sizeof(last_req)); + last_req_state = INIT; + } + + // First Time request, return not present and display request dialog + if (last_req_state == 0) { + // wake up crypto system to be ready for signing + getDerivedNode(NULL, 0); + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + buttonUpdate(); // Clear button state + layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Register", + NULL, "Register U2F", "security key", "", "", "", NULL); + dialog_timeout = U2F_TIMEOUT; + last_req_state = REG; + return; + } + + // Still awaiting Keypress + if (last_req_state == REG) { + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + dialog_timeout = U2F_TIMEOUT; + return; + } + + // Buttons said no! + if (last_req_state == REG_FAIL) { + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + // Buttons said yes + if (last_req_state == REG_PASS) { + uint8_t data[sizeof(U2F_REGISTER_RESP) + 2]; + U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)&data; + bzero(data, sizeof(data)); + + + resp->registerId = U2F_REGISTER_ID; + resp->keyHandleLen = KEY_HANDLE_LEN; + // Generate keypair for this appId + const HDNode *node = + generateKeyHandle(req->appId, (uint8_t*)&resp->keyHandleCertSig); + + if (!node) { + debugLog(0, "", "getDerivedNode Fail"); + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + ecdsa_get_public_key65(&nist256p1, node->private_key, + (uint8_t *)&resp->pubKey); + + memcpy(resp->keyHandleCertSig + resp->keyHandleLen, + U2F_ATT_CERT, sizeof(U2F_ATT_CERT)); + + uint8_t sig[64]; + U2F_REGISTER_SIG_STR sig_base; + sig_base.reserved = 0; + memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE); + memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); + memcpy(sig_base.keyHandle, &resp->keyHandleCertSig, KEY_HANDLE_LEN); + memcpy(sig_base.pubKey, &resp->pubKey, U2F_PUBKEY_LEN); + ecdsa_sign(&nist256p1, U2F_ATT_PRIV_KEY, (uint8_t *)&sig_base, + sizeof(sig_base), sig, NULL); + + // Where to write the signature in the response + uint8_t *resp_sig = resp->keyHandleCertSig + + resp->keyHandleLen + sizeof(U2F_ATT_CERT); + // Convert to der for the response + const uint8_t sig_len = ecdsa_sig_to_der(sig, resp_sig); + + // Append success bytes + memcpy(resp->keyHandleCertSig + resp->keyHandleLen + + sizeof(U2F_ATT_CERT) + sig_len, + "\x90\x00", 2); + + int l = 1 /* registerId */ + U2F_PUBKEY_LEN + + 1 /* keyhandleLen */ + resp->keyHandleLen + + sizeof(U2F_ATT_CERT) + sig_len + 2; + + send_u2f_msg(data, l); + return; + } + + // Didnt expect to get here + dialog_timeout = 0; +} + +void u2f_authenticate(const APDU *a) +{ + const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data; + static U2F_AUTHENTICATE_REQ last_req; + + if (APDU_LEN(*a) < 64) { /// FIXME: decent value + debugLog(0, "", "u2f authenticate - badlen"); + send_u2f_error(U2F_SW_WRONG_DATA); + return; + } + + if (req->keyHandleLen != KEY_HANDLE_LEN) { + debugLog(0, "", "u2f auth - bad keyhandle len"); + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + const HDNode *node = + validateKeyHandle(req->appId, req->keyHandle); + + if (!node) { + debugLog(0, "", "u2f auth - bad keyhandle len"); + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + if (a->p1 == U2F_AUTH_CHECK_ONLY) { + debugLog(0, "", "u2f authenticate check"); + // This is a success for a good keyhandle + // A failed check would have happened earlier + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + return; + } + + if (a->p1 != U2F_AUTH_ENFORCE) { + debugLog(0, "", "u2f authenticate unknown"); + // error:bad key handle + send_u2f_error(U2F_SW_WRONG_DATA); + return; + } + + debugLog(0, "", "u2f authenticate enforce"); + + if (memcmp(&last_req, req, sizeof(last_req)) != 0) { + memcpy(&last_req, req, sizeof(last_req)); + last_req_state = INIT; + } + + if (last_req_state == INIT) { + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + buttonUpdate(); // Clear button state + layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Authenticate", NULL, + "Authenticate U2F", "security key", "", "", "", NULL); + dialog_timeout = U2F_TIMEOUT; + last_req_state = AUTH; + return; + } + + // Awaiting Keypress + if (last_req_state == AUTH) { + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + dialog_timeout = U2F_TIMEOUT; + return; + } + + // Buttons said no! + if (last_req_state == AUTH_FAIL) { + send_u2f_error( + U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + // Buttons said yes + if (last_req_state == AUTH_PASS) { + uint8_t buf[sizeof(U2F_AUTHENTICATE_RESP) + 2]; + U2F_AUTHENTICATE_RESP *resp = + (U2F_AUTHENTICATE_RESP *)&buf; + + const uint32_t ctr = storage_nextU2FCounter(); + resp->flags = U2F_AUTH_FLAG_TUP; + resp->ctr[0] = ctr >> 24 & 0xff; + resp->ctr[1] = ctr >> 16 & 0xff; + resp->ctr[2] = ctr >> 8 & 0xff; + resp->ctr[3] = ctr & 0xff; + + // Build and sign response + U2F_AUTHENTICATE_SIG_STR sig_base; + uint8_t sig[64]; + memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE); + sig_base.flags = resp->flags; + memcpy(sig_base.ctr, resp->ctr, 4); + memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); + ecdsa_sign(&nist256p1, node->private_key, + (uint8_t *)&sig_base, sizeof(sig_base), sig, + NULL); + + // Copy DER encoded signature into response + const uint8_t sig_len = ecdsa_sig_to_der(sig, resp->sig); + + // Append OK + memcpy(buf + sizeof(U2F_AUTHENTICATE_RESP) - + U2F_MAX_EC_SIG_SIZE + sig_len, + "\x90\x00", 2); + send_u2f_msg(buf, sizeof(U2F_AUTHENTICATE_RESP) - + U2F_MAX_EC_SIG_SIZE + sig_len + + 2); + last_req_state = INIT; + } +} + +void send_u2f_error(const uint16_t err) +{ + uint8_t data[2]; + data[0] = err >> 8 & 0xFF; + data[1] = err & 0xFF; + send_u2f_msg(data, 2); +} + +void send_u2f_msg(const uint8_t *data, const uint32_t len) +{ + send_u2fhid_msg(U2FHID_MSG, data, len); +} diff --git a/firmware/u2f.h b/firmware/u2f.h new file mode 100644 index 0000000000..cd1d102bf4 --- /dev/null +++ b/firmware/u2f.h @@ -0,0 +1,58 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2015 Mark Bryars + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef __U2F_H__ +#define __U2F_H__ + +#include +#include +#include "u2f/u2f_hid.h" +#include "trezor.h" + +typedef struct { + uint8_t cla, ins, p1, p2; + uint8_t lc1, lc2, lc3; + uint8_t data[]; +} APDU; + +#define APDU_LEN(A) (uint32_t)(((A).lc1 << 16) + ((A).lc2 << 8) + ((A).lc3)) + +void u2fhid_read(const U2FHID_FRAME *buf); +bool u2fhid_write(uint8_t *buf); +void u2fhid_init(const U2FHID_INIT_REQ *init_req); +void u2fhid_ping(const uint8_t *buf, uint32_t len); +void u2fhid_wink(const uint8_t *buf, uint32_t len); +void u2fhid_sync(const uint8_t *buf, uint32_t len); +void u2fhid_lock(const uint8_t *buf, uint32_t len); +void u2fhid_msg(const APDU *a, uint32_t len); +void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt); + +uint8_t *u2f_out_data(void); +void u2f_register(const APDU *a); +void u2f_version(const APDU *a); +void u2f_authenticate(const APDU *a); + +void send_u2f_msg(const uint8_t *data, const uint32_t len); +void send_u2f_error(const uint16_t err); + +void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, + const uint32_t len); +void send_u2fhid_error(const uint8_t err); + +#endif diff --git a/firmware/u2f/u2f.h b/firmware/u2f/u2f.h new file mode 100644 index 0000000000..4291c594b2 --- /dev/null +++ b/firmware/u2f/u2f.h @@ -0,0 +1,141 @@ +/* + Copyright (C) 2013-2015 Yubico AB + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +// Common U2F raw message format header. +// 2014-08-14 J Ehrensvard, Yubico, Inc. + +#ifndef __U2F_H_INCLUDED__ +#define __U2F_H_INCLUDED__ + +#ifdef _MSC_VER // Windows +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +// General constants + +#define U2F_EC_KEY_SIZE 32 // EC key size in bytes +#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point +#define U2F_MAX_KH_SIZE 128 // Max size of key handle +#define U2F_MAX_ATT_CERT_SIZE 1024 // Max size of attestation certificate +#define U2F_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature +#define U2F_CTR_SIZE 4 // Size of counter field +#define U2F_APPID_SIZE 32 // Size of application id +#define U2F_CHAL_SIZE 32 // Size of challenge + +#define ENC_SIZE(x) ((x + 7) & 0xfff8) + +// EC (uncompressed) point + +#define U2F_POINT_UNCOMPRESSED 0x04 // Uncompressed point format + + typedef struct + { + uint8_t pointFormat; // Point type + uint8_t x[U2F_EC_KEY_SIZE]; // X-value + uint8_t y[U2F_EC_KEY_SIZE]; // Y-value + } U2F_EC_POINT; + +// U2F native commands + +#define U2F_REGISTER 0x01 // Registration command +#define U2F_AUTHENTICATE 0x02 // Authenticate/sign command +#define U2F_VERSION 0x03 // Read version string command + +#define U2F_VENDOR_FIRST 0x40 // First vendor defined command +#define U2F_VENDOR_LAST 0x7f // Last vendor defined command + +// U2F_CMD_REGISTER command defines + +#define U2F_REGISTER_ID 0x05 // Version 2 registration identifier +#define U2F_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier + + typedef struct + { + uint8_t chal[U2F_CHAL_SIZE]; // Challenge + uint8_t appId[U2F_APPID_SIZE]; // Application id + } U2F_REGISTER_REQ; + + typedef struct + { + uint8_t registerId; // Registration identifier (U2F_REGISTER_ID_V2) + U2F_EC_POINT pubKey; // Generated public key + uint8_t keyHandleLen; // Length of key handle + uint8_t keyHandleCertSig[U2F_MAX_KH_SIZE + // Key handle + U2F_MAX_ATT_CERT_SIZE + // Attestation certificate + U2F_MAX_EC_SIG_SIZE]; // Registration signature + } U2F_REGISTER_RESP; + +// U2F_CMD_AUTHENTICATE command defines + +// Authentication control byte + +#define U2F_AUTH_ENFORCE 0x03 // Enforce user presence and sign +#define U2F_AUTH_CHECK_ONLY 0x07 // Check only +#define U2F_AUTH_FLAG_TUP 0x01 // Test of user presence set + + typedef struct + { + uint8_t chal[U2F_CHAL_SIZE]; // Challenge + uint8_t appId[U2F_APPID_SIZE]; // Application id + uint8_t keyHandleLen; // Length of key handle + uint8_t keyHandle[U2F_MAX_KH_SIZE]; // Key handle + } U2F_AUTHENTICATE_REQ; + + typedef struct + { + uint8_t flags; // U2F_AUTH_FLAG_ values + uint8_t ctr[U2F_CTR_SIZE]; // Counter field (big-endian) + uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature + } U2F_AUTHENTICATE_RESP; + +// Common raw message format (ISO7816-4:2005 mapping) + + typedef struct + { + uint8_t cla; // Class - reserved + uint8_t ins; // U2F instruction + uint8_t p1; // U2F parameter 1 + uint8_t p2; // U2F parameter 2 + uint8_t lc1; // Length field, set to zero + uint8_t lc2; // Length field, MSB + uint8_t lc3; // Length field, LSB + uint8_t data[1]; // Data field + } U2F_MSG; + +// Command status responses + +#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR +#define U2F_SW_WRONG_DATA 0x6984 // SW_WRONG_DATA +#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED +#define U2F_SW_INS_NOT_SUPPORTED 0x6d00 // SW_INS_NOT_SUPPORTED +#define U2F_SW_CLA_NOT_SUPPORTED 0x6e00 // SW_CLA_NOT_SUPPORTED + +#ifdef __cplusplus +} +#endif + +#endif // __U2F_H_INCLUDED__ diff --git a/firmware/u2f/u2f_hid.h b/firmware/u2f/u2f_hid.h new file mode 100644 index 0000000000..d576d197c3 --- /dev/null +++ b/firmware/u2f/u2f_hid.h @@ -0,0 +1,140 @@ +/* + Copyright (C) 2013-2015 Yubico AB + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +// Common U2F HID transport header. +// 2014-08-21 J Ehrensvard, Yubico, Inc. + +#ifndef __U2FHID_H_INCLUDED__ +#define __U2FHID_H_INCLUDED__ + +#ifdef _MSC_VER // Windows +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +// Size of HID reports + +#define HID_RPT_SIZE 64 // Default size of raw HID report + +// Frame layout - command- and continuation frames + +#define CID_BROADCAST 0xffffffff // Broadcast channel id + +#define TYPE_MASK 0x80 // Frame type mask +#define TYPE_INIT 0x80 // Initial frame identifier +#define TYPE_CONT 0x00 // Continuation frame identifier + + typedef struct + { + uint32_t cid; // Channel identifier + union + { + uint8_t type; // Frame type - b7 defines type + struct + { + uint8_t cmd; // Command - b7 set + uint8_t bcnth; // Message byte count - high part + uint8_t bcntl; // Message byte count - low part + uint8_t data[HID_RPT_SIZE - 7]; // Data payload + } init; + struct + { + uint8_t seq; // Sequence number - b7 cleared + uint8_t data[HID_RPT_SIZE - 5]; // Data payload + } cont; + }; + } U2FHID_FRAME; + +#define FRAME_TYPE(f) ((f).type & TYPE_MASK) +#define FRAME_CMD(f) ((f).init.cmd & ~TYPE_MASK) +#define MSG_LEN(f) (((f).init.bcnth << 8) + (f).init.bcntl) +#define FRAME_SEQ(f) ((f).cont.seq & ~TYPE_MASK) + +// HID usage- and usage-page definitions + +#define FIDO_USAGE_PAGE 0xf1d0 // FIDO alliance HID usage page +#define FIDO_USAGE_U2FHID 0x01 // U2FHID usage for top-level collection +#define FIDO_USAGE_DATA_IN 0x20 // Raw IN data report +#define FIDO_USAGE_DATA_OUT 0x21 // Raw OUT data report + +// General constants + +#define U2FHID_IF_VERSION 2 // Current interface implementation version +#define U2FHID_FRAME_TIMEOUT 500 // Default frame timeout in ms +#define U2FHID_TRANS_TIMEOUT 3000 // Default message timeout in ms + +// U2FHID native commands + +#define U2FHID_PING (TYPE_INIT | 0x01) // Echo data through local processor only +#define U2FHID_MSG (TYPE_INIT | 0x03) // Send U2F message frame +#define U2FHID_LOCK (TYPE_INIT | 0x04) // Send lock channel command +#define U2FHID_INIT (TYPE_INIT | 0x06) // Channel initialization +#define U2FHID_WINK (TYPE_INIT | 0x08) // Send device identification wink +#define U2FHID_ERROR (TYPE_INIT | 0x3f) // Error response + +#define U2FHID_VENDOR_FIRST (TYPE_INIT | 0x40) // First vendor defined command +#define U2FHID_VENDOR_LAST (TYPE_INIT | 0x7f) // Last vendor defined command + +// U2FHID_INIT command defines + +#define INIT_NONCE_SIZE 8 // Size of channel initialization challenge +#define CAPFLAG_WINK 0x01 // Device supports WINK command +#define CAPFLAG_LOCK 0x02 // Device supports LOCK command + + typedef struct + { + uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce + } U2FHID_INIT_REQ; + + typedef struct + { + uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce + uint32_t cid; // Channel identifier + uint8_t versionInterface; // Interface version + uint8_t versionMajor; // Major version number + uint8_t versionMinor; // Minor version number + uint8_t versionBuild; // Build version number + uint8_t capFlags; // Capabilities flags + } U2FHID_INIT_RESP; + +// Low-level error codes. Return as negatives. + +#define ERR_NONE 0x00 // No error +#define ERR_INVALID_CMD 0x01 // Invalid command +#define ERR_INVALID_PAR 0x02 // Invalid parameter +#define ERR_INVALID_LEN 0x03 // Invalid message length +#define ERR_INVALID_SEQ 0x04 // Invalid message sequencing +#define ERR_MSG_TIMEOUT 0x05 // Message has timed out +#define ERR_CHANNEL_BUSY 0x06 // Channel busy +#define ERR_LOCK_REQUIRED 0x0a // Command requires channel lock +#define ERR_INVALID_CID 0x0b // Command not allowed on this cid +#define ERR_OTHER 0x7f // Other unspecified error + +#ifdef __cplusplus +} +#endif + +#endif // __U2FHID_H_INCLUDED__ diff --git a/firmware/u2f/u2f_keys.h b/firmware/u2f/u2f_keys.h new file mode 100644 index 0000000000..73c8378f71 --- /dev/null +++ b/firmware/u2f/u2f_keys.h @@ -0,0 +1,46 @@ +#ifndef __U2F_KEYS_H_INCLUDED__ +#define __U2F_KEYS_H_INCLUDED__ + +#include + +uint8_t U2F_ATT_PRIV_KEY[] = {0x71, 0x26, 0xac, 0x2b, 0xf6, 0x44, 0xdc, 0x61, + 0x86, 0xad, 0x83, 0xef, 0x1f, 0xcd, 0xf1, 0x2a, + 0x57, 0xb5, 0xcf, 0xa2, 0x00, 0x0b, 0x8a, 0xd0, + 0x27, 0xe9, 0x56, 0xe8, 0x54, 0xc5, 0x0a, 0x8b}; + +uint8_t U2F_ATT_PUB_KEY[] = { + 0x04, 0xd9, 0x18, 0xbd, 0xfa, 0x8a, 0x54, 0xac, 0x92, 0xe9, 0x0d, + 0xa9, 0x1f, 0xca, 0x7a, 0xa2, 0x64, 0x54, 0xc0, 0xd1, 0x73, 0x36, + 0x31, 0x4d, 0xde, 0x83, 0xa5, 0x4b, 0x86, 0xb5, 0xdf, 0x4e, 0xf0, + 0x52, 0x65, 0x9a, 0x1d, 0x6f, 0xfc, 0xb7, 0x46, 0x7f, 0x1a, 0xcd, + 0xdb, 0x8a, 0x33, 0x08, 0x0b, 0x5e, 0xed, 0x91, 0x89, 0x13, 0xf4, + 0x43, 0xa5, 0x26, 0x1b, 0xc7, 0x7b, 0x68, 0x60, 0x6f, 0xc1}; + +uint8_t U2F_ATT_CERT[] = { + 0x30, 0x82, 0x01, 0x19, 0x30, 0x81, 0xC0, 0x02, 0x09, 0x00, 0x8B, 0x3F, + 0xA6, 0x46, 0xDE, 0x01, 0xCB, 0xB8, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0A, 0x54, 0x72, 0x65, 0x7A, 0x6F, + 0x72, 0x20, 0x55, 0x32, 0x46, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x35, 0x31, + 0x30, 0x31, 0x31, 0x32, 0x33, 0x32, 0x33, 0x34, 0x36, 0x5A, 0x17, 0x0D, + 0x32, 0x35, 0x31, 0x30, 0x30, 0x38, 0x32, 0x33, 0x32, 0x33, 0x34, 0x36, + 0x5A, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0C, 0x0A, 0x54, 0x72, 0x65, 0x7A, 0x6F, 0x72, 0x20, 0x55, 0x32, 0x46, + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, + 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0xD9, 0x18, 0xBD, 0xFA, 0x8A, 0x54, 0xAC, 0x92, 0xE9, + 0x0D, 0xA9, 0x1F, 0xCA, 0x7A, 0xA2, 0x64, 0x54, 0xC0, 0xD1, 0x73, 0x36, + 0x31, 0x4D, 0xDE, 0x83, 0xA5, 0x4B, 0x86, 0xB5, 0xDF, 0x4E, 0xF0, 0x52, + 0x65, 0x9A, 0x1D, 0x6F, 0xFC, 0xB7, 0x46, 0x7F, 0x1A, 0xCD, 0xDB, 0x8A, + 0x33, 0x08, 0x0B, 0x5E, 0xED, 0x91, 0x89, 0x13, 0xF4, 0x43, 0xA5, 0x26, + 0x1B, 0xC7, 0x7B, 0x68, 0x60, 0x6F, 0xC1, 0x30, 0x0A, 0x06, 0x08, 0x2A, + 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, + 0x02, 0x21, 0x00, 0x89, 0x38, 0x50, 0x13, 0xBB, 0x13, 0xF1, 0xE9, 0xEF, + 0x7A, 0x1B, 0x47, 0x59, 0x19, 0xBA, 0x18, 0x55, 0x32, 0xE6, 0x76, 0xD1, + 0xAD, 0x69, 0x9D, 0x97, 0x07, 0x89, 0x80, 0x08, 0x4F, 0xAA, 0xE8, 0x02, + 0x20, 0x35, 0x9A, 0x5F, 0x1D, 0xE5, 0x0A, 0x12, 0xA0, 0x9C, 0x96, 0x3D, + 0x5A, 0x39, 0xA8, 0x83, 0x3C, 0xBA, 0x2E, 0xAD, 0xD5, 0x90, 0x23, 0x85, + 0x81, 0x7D, 0xF0, 0xC7, 0x72, 0x8E, 0x79, 0x09, 0xD0, +}; + +#endif // __U2F_KEYS_H_INCLUDED__ diff --git a/firmware/usb.c b/firmware/usb.c index 102b573da3..e084742af7 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -24,6 +24,7 @@ #include "usb.h" #include "debug.h" #include "messages.h" +#include "u2f.h" #include "storage.h" #include "util.h" @@ -31,6 +32,8 @@ #define ENDPOINT_ADDRESS_OUT (0x01) #define ENDPOINT_ADDRESS_DEBUG_IN (0x82) #define ENDPOINT_ADDRESS_DEBUG_OUT (0x02) +#define ENDPOINT_ADDRESS_U2F_IN (0x83) +#define ENDPOINT_ADDRESS_U2F_OUT (0x03) static const struct usb_device_descriptor dev_descr = { .bLength = USB_DT_DEVICE_SIZE, @@ -111,6 +114,25 @@ static const uint8_t hid_report_descriptor[] = { 0x95, 0x13, 0x09, 0x01, 0xB1, 0x02, 0xC0, }; +static const uint8_t hid_report_descriptor_u2f[] = { + 0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance) + 0x09, 0x01, // USAGE (U2F HID Authenticator Device) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x20, // USAGE (Input Report Data) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75,0x08, // REPORT_SIZE (8) + 0x95,0x40, // REPORT_COUNT (64) + 0x81,0x02, // INPUT (Data,Var,Abs) + 0x09,0x21, // USAGE (Output Report Data) + 0x15,0x00, // LOGICAL_MINIMUM (0) + 0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75,0x08, // REPORT_SIZE (8) + 0x95,0x40, // REPORT_COUNT (64) + 0x91,0x02, // OUTPUT (Data,Var,Abs) + 0xc0 // END_COLLECTION +}; + static const struct { struct usb_hid_descriptor hid_descriptor; struct { @@ -131,6 +153,27 @@ static const struct { } }; +static const struct { + struct usb_hid_descriptor hid_descriptor_u2f; + struct { + uint8_t bReportDescriptorType; + uint16_t wDescriptorLength; + } __attribute__((packed)) hid_report_u2f; +} __attribute__((packed)) hid_function_u2f = { + .hid_descriptor_u2f = { + .bLength = sizeof(hid_function_u2f), + .bDescriptorType = USB_DT_HID, + .bcdHID = 0x0111, + .bCountryCode = 0, + .bNumDescriptors = 1, + }, + .hid_report_u2f = { + .bReportDescriptorType = USB_DT_REPORT, + .wDescriptorLength = sizeof(hid_report_descriptor_u2f), + } +}; + + static const struct usb_endpoint_descriptor hid_endpoints[2] = {{ .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -162,6 +205,41 @@ static const struct usb_interface_descriptor hid_iface[] = {{ .extralen = sizeof(hid_function), }}; +static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_U2F_IN, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, +}, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_U2F_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, +}}; + +static const struct usb_interface_descriptor hid_iface_u2f[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, +#if DEBUG_LINK + .bInterfaceNumber = 2, +#else + .bInterfaceNumber = 1, +#endif + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = hid_endpoints_u2f, + .extra = &hid_function_u2f, + .extralen = sizeof(hid_function_u2f), +}}; + #if DEBUG_LINK static const struct usb_endpoint_descriptor hid_endpoints_debug[2] = {{ .bLength = USB_DT_ENDPOINT_SIZE, @@ -198,6 +276,9 @@ static const struct usb_interface_descriptor hid_iface_debug[] = {{ static const struct usb_interface ifaces[] = {{ .num_altsetting = 1, .altsetting = hid_iface, +}, { + .num_altsetting = 1, + .altsetting = hid_iface_u2f, #if DEBUG_LINK }, { .num_altsetting = 1, @@ -210,9 +291,9 @@ static const struct usb_config_descriptor config = { .bDescriptorType = USB_DT_CONFIGURATION, .wTotalLength = 0, #if DEBUG_LINK - .bNumInterfaces = 2, + .bNumInterfaces = 3, #else - .bNumInterfaces = 1, + .bNumInterfaces = 2, #endif .bConfigurationValue = 1, .iConfiguration = 0, @@ -237,11 +318,18 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin (req->wValue != 0x2200)) return 0; - /* Handle the HID report descriptor. */ - *buf = (uint8_t *)hid_report_descriptor; - *len = sizeof(hid_report_descriptor); + if (req->wIndex==1) { + debugLog(0, "", "hid_control_request u2f"); + *buf = (uint8_t *)hid_report_descriptor_u2f; + *len = sizeof(hid_report_descriptor_u2f); + return 1; + } - return 1; + debugLog(0, "", "hid_control_request trezor"); + /* Handle the HID report descriptor. */ + *buf = (uint8_t *)hid_report_descriptor; + *len = sizeof(hid_report_descriptor); + return 1; } static volatile char tiny = 0; @@ -259,6 +347,16 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) } } +static void hid_u2f_rx_callback(usbd_device *dev, uint8_t ep) +{ + (void)ep; + static uint8_t buf[64] __attribute__ ((aligned(4))); + + debugLog(0, "", "hid_u2f_rx_callback"); + if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; + u2fhid_read((const U2FHID_FRAME *)buf); +} + #if DEBUG_LINK static void hid_debug_rx_callback(usbd_device *dev, uint8_t ep) { @@ -280,6 +378,8 @@ static void hid_set_config(usbd_device *dev, uint16_t wValue) usbd_ep_setup(dev, ENDPOINT_ADDRESS_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); usbd_ep_setup(dev, ENDPOINT_ADDRESS_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_rx_callback); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_u2f_rx_callback); #if DEBUG_LINK usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_debug_rx_callback); @@ -311,6 +411,10 @@ void usbPoll(void) if (data) { while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_IN, data, 64) != 64 ) {} } + data = u2f_out_data(); + if (data) { + while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_U2F_IN, data, 64) != 64 ) {} + } #if DEBUG_LINK // write pending debug data data = msg_debug_out_data(); diff --git a/trezor-common b/trezor-common index 3fc31bfe9c..72268e816b 160000 --- a/trezor-common +++ b/trezor-common @@ -1 +1 @@ -Subproject commit 3fc31bfe9c42ebf3c0a63c48f44537994787f9c9 +Subproject commit 72268e816b8e8e06f698b3729223a255c7c74167 From 32f88199978ba0d882c60b0e7b4959e42714274f Mon Sep 17 00:00:00 2001 From: Mark Bryars Date: Thu, 5 Nov 2015 01:24:37 +0100 Subject: [PATCH 02/31] Generate hardened keys in a unique root --- firmware/u2f.c | 23 ++++++++++++++++++----- firmware/u2f.h | 2 ++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index 09f2ca810b..db55c88c0d 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -447,12 +447,20 @@ const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) { uint8_t keybase[64]; - // First half of keyhandle is random - random_buffer(key_handle, 32); + // Derivation path is m/'U2F/'r/'r/'r/'r/'r/'r/'r/'r + uint32_t i, key_path[9]; + key_path[0] = U2F_KEY_PATH; + for (i = 1; i < 9; i++) { + // high bit for hardened keys + key_path[i]= 0x80000000 | random32(); + } + + // First half of keyhandle is key_path + memcpy(key_handle, &key_path[1], 32); // prepare keypair from /random data const HDNode *node = - getDerivedNode((uint32_t*)key_handle, 32/sizeof(uint32_t)); + getDerivedNode(key_path, sizeof(key_path) / sizeof(uint32_t)); // For second half of keyhandle // Signature of app_id and random data @@ -473,12 +481,17 @@ const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[]) { + uint32_t key_path[9]; + key_path[0] = U2F_KEY_PATH; + memcpy(&key_path[1], key_handle, 32); + + const HDNode *node = + getDerivedNode(key_path, sizeof(key_path) / sizeof(uint32_t)); + uint8_t keybase[64]; memcpy(&keybase[0], app_id, 32); memcpy(&keybase[32], key_handle, 32); - const HDNode *node = - getDerivedNode((uint32_t*)key_handle, 32/sizeof(uint32_t)); uint8_t sig[64]; ecdsa_sign(&nist256p1, node->private_key, diff --git a/firmware/u2f.h b/firmware/u2f.h index cd1d102bf4..847710a4c6 100644 --- a/firmware/u2f.h +++ b/firmware/u2f.h @@ -25,6 +25,8 @@ #include "u2f/u2f_hid.h" #include "trezor.h" +#define U2F_KEY_PATH 0x80553246 + typedef struct { uint8_t cla, ins, p1, p2; uint8_t lc1, lc2, lc3; From 1b8bd1852e1dd6fe26d11ebf143d28f5ee0eba5b Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 26 Apr 2016 16:44:08 +0200 Subject: [PATCH 03/31] Adapted U2F to new hdnode API --- firmware/u2f.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index db55c88c0d..3c737cf197 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -28,6 +28,7 @@ #include "usb.h" #include "buttons.h" #include "trezor.h" +#include "curves.h" #include "nist256p1.h" #include "rng.h" @@ -427,7 +428,7 @@ void u2f_version(const APDU *a) const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) { static HDNode node; - if (!storage_getRootNode(&node)) { + if (!storage_getRootNode(&node, NIST256P1_NAME)) { layoutHome(); debugLog(0, "", "ERR: Device not init"); return 0; @@ -467,9 +468,7 @@ const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) memcpy(&keybase[0], app_id, 32); memcpy(&keybase[32], key_handle, 32); uint8_t sig[64]; - ecdsa_sign(&nist256p1, node->private_key, - (uint8_t *)&keybase, sizeof(keybase), sig, - NULL); + hdnode_sign(node, (uint8_t *)&keybase, sizeof(keybase), sig, NULL); // Copy 32 bytes of signature into keyhandle memcpy(&key_handle[32], sig, 32); @@ -494,9 +493,7 @@ const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle uint8_t sig[64]; - ecdsa_sign(&nist256p1, node->private_key, - (uint8_t *)&keybase, sizeof(keybase), sig, - NULL); + hdnode_sign(node, (uint8_t *)&keybase, sizeof(keybase), sig, NULL); if (memcmp(&key_handle[32], sig, 32) !=0) return NULL; @@ -572,7 +569,7 @@ void u2f_register(const APDU *a) return; } - ecdsa_get_public_key65(&nist256p1, node->private_key, + ecdsa_get_public_key65(node->curve->params, node->private_key, (uint8_t *)&resp->pubKey); memcpy(resp->keyHandleCertSig + resp->keyHandleLen, From 01ddb3ff6673e6c74ef77de53ae6ff53a762617e Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 26 Apr 2016 22:57:46 +0200 Subject: [PATCH 04/31] Reduced buffer sizes, moved static info to flash --- firmware/u2f.c | 6 +++--- firmware/u2f/u2f_hid.h | 2 ++ firmware/u2f/u2f_keys.h | 20 +++++++------------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index 3c737cf197..91738340a9 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -41,7 +41,7 @@ // About 1/2 Second according to values used in protect.c #define U2F_TIMEOUT 840000/2 -#define U2F_OUT_PKT_BUFFER_LEN 128 +#define U2F_OUT_PKT_BUFFER_LEN 16 // Initialise without a cid static uint32_t cid = CID_BROADCAST; @@ -171,7 +171,7 @@ void u2fhid_read(const U2FHID_FRAME *f) static uint8_t seq, cmd; static uint32_t len; static uint8_t *buf_ptr; - static uint8_t buf[7609]; + static uint8_t buf[57+7*59]; if ((f->cid != CID_BROADCAST) && (f->cid != cid)) { return; // Not for us @@ -291,7 +291,7 @@ void u2fhid_init(const U2FHID_INIT_REQ *init_req) f.cid = CID_BROADCAST; f.init.cmd = U2FHID_INIT; f.init.bcnth = 0; - f.init.bcntl = sizeof(U2FHID_INIT_RESP); + f.init.bcntl = U2FHID_INIT_RESP_SIZE; memcpy(resp->nonce, init_req->nonce, sizeof(init_req->nonce)); resp->cid = next_cid(); diff --git a/firmware/u2f/u2f_hid.h b/firmware/u2f/u2f_hid.h index d576d197c3..326a3fc223 100644 --- a/firmware/u2f/u2f_hid.h +++ b/firmware/u2f/u2f_hid.h @@ -120,6 +120,8 @@ extern "C" uint8_t capFlags; // Capabilities flags } U2FHID_INIT_RESP; +#define U2FHID_INIT_RESP_SIZE 17 + // Low-level error codes. Return as negatives. #define ERR_NONE 0x00 // No error diff --git a/firmware/u2f/u2f_keys.h b/firmware/u2f/u2f_keys.h index 73c8378f71..94b9302b1d 100644 --- a/firmware/u2f/u2f_keys.h +++ b/firmware/u2f/u2f_keys.h @@ -3,20 +3,14 @@ #include -uint8_t U2F_ATT_PRIV_KEY[] = {0x71, 0x26, 0xac, 0x2b, 0xf6, 0x44, 0xdc, 0x61, - 0x86, 0xad, 0x83, 0xef, 0x1f, 0xcd, 0xf1, 0x2a, - 0x57, 0xb5, 0xcf, 0xa2, 0x00, 0x0b, 0x8a, 0xd0, - 0x27, 0xe9, 0x56, 0xe8, 0x54, 0xc5, 0x0a, 0x8b}; +const uint8_t U2F_ATT_PRIV_KEY[] = { + 0x71, 0x26, 0xac, 0x2b, 0xf6, 0x44, 0xdc, 0x61, + 0x86, 0xad, 0x83, 0xef, 0x1f, 0xcd, 0xf1, 0x2a, + 0x57, 0xb5, 0xcf, 0xa2, 0x00, 0x0b, 0x8a, 0xd0, + 0x27, 0xe9, 0x56, 0xe8, 0x54, 0xc5, 0x0a, 0x8b +}; -uint8_t U2F_ATT_PUB_KEY[] = { - 0x04, 0xd9, 0x18, 0xbd, 0xfa, 0x8a, 0x54, 0xac, 0x92, 0xe9, 0x0d, - 0xa9, 0x1f, 0xca, 0x7a, 0xa2, 0x64, 0x54, 0xc0, 0xd1, 0x73, 0x36, - 0x31, 0x4d, 0xde, 0x83, 0xa5, 0x4b, 0x86, 0xb5, 0xdf, 0x4e, 0xf0, - 0x52, 0x65, 0x9a, 0x1d, 0x6f, 0xfc, 0xb7, 0x46, 0x7f, 0x1a, 0xcd, - 0xdb, 0x8a, 0x33, 0x08, 0x0b, 0x5e, 0xed, 0x91, 0x89, 0x13, 0xf4, - 0x43, 0xa5, 0x26, 0x1b, 0xc7, 0x7b, 0x68, 0x60, 0x6f, 0xc1}; - -uint8_t U2F_ATT_CERT[] = { +const uint8_t U2F_ATT_CERT[] = { 0x30, 0x82, 0x01, 0x19, 0x30, 0x81, 0xC0, 0x02, 0x09, 0x00, 0x8B, 0x3F, 0xA6, 0x46, 0xDE, 0x01, 0xCB, 0xB8, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, From b3bfc64d2fff6ed85664b3dabfd6fde499e13b94 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 27 Apr 2016 12:27:29 +0200 Subject: [PATCH 05/31] Use hmac for checking key integrity --- firmware/u2f.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index 91738340a9..e2ba10f986 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -31,6 +31,7 @@ #include "curves.h" #include "nist256p1.h" #include "rng.h" +#include "hmac.h" #include "u2f/u2f.h" #include "u2f/u2f_hid.h" @@ -467,11 +468,8 @@ const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) // Signature of app_id and random data memcpy(&keybase[0], app_id, 32); memcpy(&keybase[32], key_handle, 32); - uint8_t sig[64]; - hdnode_sign(node, (uint8_t *)&keybase, sizeof(keybase), sig, NULL); - - // Copy 32 bytes of signature into keyhandle - memcpy(&key_handle[32], sig, 32); + hmac_sha256(node->private_key, sizeof(node->private_key), + keybase, sizeof(keybase), &key_handle[32]); // Done! return node; @@ -492,10 +490,11 @@ const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle memcpy(&keybase[32], key_handle, 32); - uint8_t sig[64]; - hdnode_sign(node, (uint8_t *)&keybase, sizeof(keybase), sig, NULL); + uint8_t hmac[32]; + hmac_sha256(node->private_key, sizeof(node->private_key), + keybase, sizeof(keybase), hmac); - if (memcmp(&key_handle[32], sig, 32) !=0) + if (memcmp(&key_handle[32], hmac, 32) != 0) return NULL; // Done! From 5c13e78debaf439ec1690d9869cbf0f1acf6da85 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 27 Apr 2016 12:54:49 +0200 Subject: [PATCH 06/31] Added support for known appid. --- firmware/u2f.c | 48 ++++++++++++++++----------------- firmware/u2f_knownapps.h | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 24 deletions(-) create mode 100644 firmware/u2f_knownapps.h diff --git a/firmware/u2f.c b/firmware/u2f.c index e2ba10f986..a9701f97a0 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -32,10 +32,12 @@ #include "nist256p1.h" #include "rng.h" #include "hmac.h" +#include "util.h" #include "u2f/u2f.h" #include "u2f/u2f_hid.h" #include "u2f/u2f_keys.h" +#include "u2f_knownapps.h" #include "u2f.h" #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -96,31 +98,12 @@ uint8_t buttonState(void) return 0; } -void int2hex(uint8_t *dst, const uint32_t i) -{ - dst[0] = '0' + ((i >> 28) & 0x0F); - dst[1] = '0' + ((i >> 24) & 0x0F); - dst[2] = '0' + ((i >> 20) & 0x0F); - dst[3] = '0' + ((i >> 16) & 0x0F); - dst[4] = '0' + ((i >> 12) & 0x0F); - dst[5] = '0' + ((i >> 8) & 0x0F); - dst[6] = '0' + ((i >> 4) & 0x0F); - dst[7] = '0' + (i & 0x0F); - dst[8] = '\0'; - - int t = 0; - for (; t < 8; t++) { - if (dst[t] > '9') - dst[t] += 7; // 'A'-'9'+1 - } -} - char *debugInt(const uint32_t i) { static uint8_t n = 0; - static uint8_t id[8][9]; - int2hex(id[n], i); - debugLog(0, "", (const char *)id[n]); + static char id[8][9]; + uint32hex(i, id[n]); + debugLog(0, "", id[n]); char *ret = (char *)id[n]; n = (n + 1) % 8; return ret; @@ -426,6 +409,21 @@ void u2f_version(const APDU *a) send_u2f_msg(version_response, sizeof(version_response)); } +static const char *getReadableAppId(const uint8_t appid[32]) { + unsigned int i; + static char buf[6+2+6+1]; + + for (i = 0; i < sizeof(u2f_well_known)/sizeof(U2FWellKnown); i++) { + if (memcmp(appid, u2f_well_known[i].appid, 32) == 0) + return u2f_well_known[i].appname; + } + + data2hex(appid, 3, &buf[0]); + buf[6] = buf[7] = '.'; + data2hex(appid+(sizeof(appid)-3), 3, &buf[8]); + return buf; +} + const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) { static HDNode node; @@ -529,7 +527,8 @@ void u2f_register(const APDU *a) send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Register", - NULL, "Register U2F", "security key", "", "", "", NULL); + NULL, "Register U2F", "security key", + "", getReadableAppId(req->appId), "", NULL); dialog_timeout = U2F_TIMEOUT; last_req_state = REG; return; @@ -661,7 +660,8 @@ void u2f_authenticate(const APDU *a) send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Authenticate", NULL, - "Authenticate U2F", "security key", "", "", "", NULL); + "Authenticate U2F", "security key", + "", getReadableAppId(req->appId), "", NULL); dialog_timeout = U2F_TIMEOUT; last_req_state = AUTH; return; diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h new file mode 100644 index 0000000000..ca3d1732fb --- /dev/null +++ b/firmware/u2f_knownapps.h @@ -0,0 +1,57 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2016 Jochen Hoenicke + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef __U2F_KNOWNAPPS_H_INCLUDED__ +#define __U2F_KNOWNAPPS_H_INCLUDED__ + +#include + +typedef struct { + uint8_t appid[32]; + const char *appname; +} U2FWellKnown; + +static const U2FWellKnown u2f_well_known[3] = { + { + // didn't feel like tracing that one yet + { 0xa5,0x46,0x72,0xb2,0x22,0xc4,0xcf,0x95, + 0xe1,0x51,0xed,0x8d,0x4d,0x3c,0x76,0x7a, + 0x6c,0xc3,0x49,0x43,0x59,0x43,0x79,0x4e, + 0x88,0x4f,0x3d,0x02,0x3a,0x82,0x29,0xfd }, + "Google" + }, + { + // https://github.com/u2f/trusted_facets + { 0x70,0x61,0x7d,0xfe,0xd0,0x65,0x86,0x3a, + 0xf4,0x7c,0x15,0x55,0x6c,0x91,0x79,0x88, + 0x80,0x82,0x8c,0xc4,0x07,0xfd,0xf7,0x0a, + 0xe8,0x50,0x11,0x56,0x94,0x65,0xa0,0x75 }, + "Github" + }, + { + // https://www.dropbox.com/u2f-app-id.json + { 0xc5,0x0f,0x8a,0x7b,0x70,0x8e,0x92,0xf8, + 0x2e,0x7a,0x50,0xe2,0xbd,0xc5,0x5d,0x8f, + 0xd9,0x1a,0x22,0xfe,0x6b,0x29,0xc0,0xcd, + 0xf7,0x80,0x55,0x30,0x84,0x2a,0xf5,0x81 }, + "Dropbox" + } +}; + +#endif // U2F_KNOWNAPPS_INCLUDED From 2abe5d477ee8070094ccd6e539a8766c882bef5b Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 27 Apr 2016 13:37:45 +0200 Subject: [PATCH 07/31] Clean-up. Better checks for buffer overflow. --- firmware/u2f.c | 54 +++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index a9701f97a0..acf4a0ac64 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -168,26 +168,34 @@ void u2fhid_read(const U2FHID_FRAME *f) cmd = f->type; memcpy(buf_ptr, f->init.data, sizeof(f->init.data)); buf_ptr += sizeof(f->init.data); + + // Broadcast is reserved for init + if (cid == CID_BROADCAST && cmd != U2FHID_INIT) + return; + + // Check length isnt bigger than spec max + if (len > sizeof(buf)) { + len = 0; + return send_u2fhid_error(ERR_INVALID_LEN); + } } else { + // Broadcast is reserved for init + if (cid == CID_BROADCAST) + return; + // check out of bounds + if ((buf_ptr - buf) >= (signed) len + || (buf_ptr + sizeof(f->cont.data) - buf) > (signed) sizeof(buf)) + return; if (f->cont.seq == seq) { seq++; memcpy(buf_ptr, f->cont.data, sizeof(f->cont.data)); buf_ptr += sizeof(f->cont.data); - } - else { + } else { return send_u2fhid_error(ERR_INVALID_SEQ); } } - // Broadcast is reserved for init - if (cid == CID_BROADCAST && cmd != U2FHID_INIT) - return; - - // Check length isnt bigger than spec max - if (len > sizeof(buf)) - return send_u2fhid_error(ERR_INVALID_LEN); - // Do we need to wait for more data if ((buf_ptr - buf) < (signed)len) { // debugLog(0, "", "u2fhid_read wait"); @@ -202,18 +210,12 @@ void u2fhid_read(const U2FHID_FRAME *f) case U2FHID_MSG: u2fhid_msg((APDU *)buf, len); break; - case U2FHID_LOCK: - u2fhid_lock(buf, len); - break; case U2FHID_INIT: u2fhid_init((const U2FHID_INIT_REQ *)buf); break; case U2FHID_WINK: u2fhid_wink(buf, len); break; - // case U2FHID_SYNC: - // u2fhid_sync(buf, len); - break; default: send_u2fhid_error(ERR_INVALID_CMD); break; @@ -245,26 +247,6 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) queue_u2f_pkt(&f); } -void u2fhid_sync(const uint8_t *buf, uint32_t len) -{ - debugLog(0, "", "u2fhid_sync"); - (void)buf; - - if (len > 0) - return send_u2fhid_error(ERR_INVALID_LEN); - - // Abort things. - dialog_timeout = 0; -} - -void u2fhid_lock(const uint8_t *buf, uint32_t len) -{ - debugLog(0, "", "u2fhid_lock"); - (void)buf; - (void)len; - send_u2fhid_error(ERR_INVALID_CMD); -} - void u2fhid_init(const U2FHID_INIT_REQ *init_req) { debugLog(0, "", "u2fhid_init"); From c1ff9e1ec70ca7b86152368cfdc69989bff48b94 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 28 Apr 2016 15:00:34 +0200 Subject: [PATCH 08/31] Use more sensible hid descriptor. --- firmware/usb.c | 75 +++++++++++--------------------------------------- 1 file changed, 16 insertions(+), 59 deletions(-) diff --git a/firmware/usb.c b/firmware/usb.c index e084742af7..df087594fa 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -52,66 +52,23 @@ static const struct usb_device_descriptor dev_descr = { .bNumConfigurations = 1, }; -/* got via usbhid-dump from CP2110 */ static const uint8_t hid_report_descriptor[] = { - 0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x09, 0x01, 0x75, 0x08, 0x95, 0x40, 0x26, 0xFF, 0x00, - 0x15, 0x00, 0x85, 0x01, 0x95, 0x01, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x02, - 0x95, 0x02, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x03, 0x95, 0x03, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x04, 0x95, 0x04, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x05, 0x95, 0x05, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x06, - 0x95, 0x06, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x07, 0x95, 0x07, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x08, 0x95, 0x08, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x09, 0x95, 0x09, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0A, - 0x95, 0x0A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0B, 0x95, 0x0B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0C, 0x95, 0x0C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x0D, 0x95, 0x0D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0E, - 0x95, 0x0E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0F, 0x95, 0x0F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x10, 0x95, 0x10, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x11, 0x95, 0x11, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x12, - 0x95, 0x12, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x13, 0x95, 0x13, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x14, 0x95, 0x14, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x15, 0x95, 0x15, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x16, - 0x95, 0x16, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x17, 0x95, 0x17, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x18, 0x95, 0x18, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x19, 0x95, 0x19, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1A, - 0x95, 0x1A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1B, 0x95, 0x1B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1C, 0x95, 0x1C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x1D, 0x95, 0x1D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1E, - 0x95, 0x1E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1F, 0x95, 0x1F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x20, 0x95, 0x20, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x21, 0x95, 0x21, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x22, - 0x95, 0x22, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x23, 0x95, 0x23, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x24, 0x95, 0x24, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x25, 0x95, 0x25, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x26, - 0x95, 0x26, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x27, 0x95, 0x27, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x28, 0x95, 0x28, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x29, 0x95, 0x29, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2A, - 0x95, 0x2A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2B, 0x95, 0x2B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2C, 0x95, 0x2C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x2D, 0x95, 0x2D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2E, - 0x95, 0x2E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2F, 0x95, 0x2F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x30, 0x95, 0x30, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x31, 0x95, 0x31, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x32, - 0x95, 0x32, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x33, 0x95, 0x33, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x34, 0x95, 0x34, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x35, 0x95, 0x35, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x36, - 0x95, 0x36, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x37, 0x95, 0x37, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x38, 0x95, 0x38, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x39, 0x95, 0x39, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3A, - 0x95, 0x3A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3B, 0x95, 0x3B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3C, 0x95, 0x3C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x3D, 0x95, 0x3D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3E, - 0x95, 0x3E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3F, 0x95, 0x3F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x40, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x41, - 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x42, 0x95, 0x06, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x43, - 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x44, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x45, - 0x95, 0x04, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x46, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x47, - 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x50, 0x95, 0x08, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x51, - 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x52, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x60, - 0x95, 0x0A, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x61, 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x62, - 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x63, 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x64, - 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x65, 0x95, 0x3E, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x66, - 0x95, 0x13, 0x09, 0x01, 0xB1, 0x02, 0xC0, + 0x06, 0x00, 0xff, // USAGE_PAGE (Reserved) + 0x09, 0x01, // USAGE + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x20, // USAGE (Input Report Data) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75,0x08, // REPORT_SIZE (8) + 0x95,0x40, // REPORT_COUNT (64) + 0x81,0x02, // INPUT (Data,Var,Abs) + 0x09,0x21, // USAGE (Output Report Data) + 0x15,0x00, // LOGICAL_MINIMUM (0) + 0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75,0x08, // REPORT_SIZE (8) + 0x95,0x40, // REPORT_COUNT (64) + 0x91,0x02, // OUTPUT (Data,Var,Abs) + 0xc0 // END_COLLECTION }; static const uint8_t hid_report_descriptor_u2f[] = { From 6218770e2671028c167170e97d6152435b959552 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 29 Apr 2016 15:32:18 +0200 Subject: [PATCH 09/31] Script to generate key and certificate --- firmware/u2f/genkeys.sh | 45 ++++++++++++++++++++++++++++++++ firmware/u2f/trezordevkey.pem | 5 ++++ firmware/u2f/u2f_keys.h | 48 +++++++++++++++++------------------ vendor/trezor-common | 2 +- vendor/trezor-crypto | 2 +- 5 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 firmware/u2f/genkeys.sh create mode 100644 firmware/u2f/trezordevkey.pem diff --git a/firmware/u2f/genkeys.sh b/firmware/u2f/genkeys.sh new file mode 100644 index 0000000000..3bc7e5b10b --- /dev/null +++ b/firmware/u2f/genkeys.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +cat > u2f_keys.h < + +const uint8_t U2F_ATT_PRIV_KEY[] = { +EOF + +if [ \! -e trezordevkey.pem ]; then + openssl ecparam -genkey -out trezordevkey.pem -name prime256v1 +fi +openssl ec -in trezordevkey.pem -text | + perl -e '$key = "\t"; while (<>) { + if (/priv:/) { $priv = 1 } + elsif (/pub:/) { $priv = 0 } + elsif ($priv) { + while ($_ =~ s/.*?([0-9a-f]{2})//) { + $key .= "0x$1,"; + if ($num++ % 8 == 7) { $key .= "\n\t"; } + else {$key .= " ";} + } + } + } + $key =~ s/,\s*$/\n/s; + print $key;' >> u2f_keys.h +cat >> u2f_keys.h <> u2f_keys.h + +cat >> u2f_keys.h < Date: Sun, 15 May 2016 10:38:02 +0200 Subject: [PATCH 10/31] Fix USB HID descriptor --- firmware/usb.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/firmware/usb.c b/firmware/usb.c index df087594fa..1895da2ea3 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -54,20 +54,17 @@ static const struct usb_device_descriptor dev_descr = { static const uint8_t hid_report_descriptor[] = { 0x06, 0x00, 0xff, // USAGE_PAGE (Reserved) - 0x09, 0x01, // USAGE + 0x09, 0x01, // USAGE (1) 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x20, // USAGE (Input Report Data) 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255) - 0x75,0x08, // REPORT_SIZE (8) - 0x95,0x40, // REPORT_COUNT (64) - 0x81,0x02, // INPUT (Data,Var,Abs) - 0x09,0x21, // USAGE (Output Report Data) - 0x15,0x00, // LOGICAL_MINIMUM (0) - 0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255) - 0x75,0x08, // REPORT_SIZE (8) - 0x95,0x40, // REPORT_COUNT (64) - 0x91,0x02, // OUTPUT (Data,Var,Abs) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x85, 0x3f, // REPORT_ID (63) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x3f, // REPORT_COUNT (63) + 0x09, 0x01, // USAGE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x09, 0x01, // USAGE (1) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0xc0 // END_COLLECTION }; From 117d261a3882cb094db1bb8b8ac352ae2b45abba Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 29 Apr 2016 15:32:18 +0200 Subject: [PATCH 11/31] Script to generate key and certificate --- firmware/u2f/genkeys.sh | 45 ++++++++++++++++++++++++++++++++ firmware/u2f/trezordevkey.pem | 5 ++++ firmware/u2f/u2f_keys.h | 48 +++++++++++++++++------------------ vendor/trezor-common | 2 +- vendor/trezor-crypto | 2 +- 5 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 firmware/u2f/genkeys.sh create mode 100644 firmware/u2f/trezordevkey.pem diff --git a/firmware/u2f/genkeys.sh b/firmware/u2f/genkeys.sh new file mode 100644 index 0000000000..3bc7e5b10b --- /dev/null +++ b/firmware/u2f/genkeys.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +cat > u2f_keys.h < + +const uint8_t U2F_ATT_PRIV_KEY[] = { +EOF + +if [ \! -e trezordevkey.pem ]; then + openssl ecparam -genkey -out trezordevkey.pem -name prime256v1 +fi +openssl ec -in trezordevkey.pem -text | + perl -e '$key = "\t"; while (<>) { + if (/priv:/) { $priv = 1 } + elsif (/pub:/) { $priv = 0 } + elsif ($priv) { + while ($_ =~ s/.*?([0-9a-f]{2})//) { + $key .= "0x$1,"; + if ($num++ % 8 == 7) { $key .= "\n\t"; } + else {$key .= " ";} + } + } + } + $key =~ s/,\s*$/\n/s; + print $key;' >> u2f_keys.h +cat >> u2f_keys.h <> u2f_keys.h + +cat >> u2f_keys.h < Date: Fri, 29 Apr 2016 16:35:23 +0200 Subject: [PATCH 12/31] Only compile debugInt when debugging --- firmware/u2f.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/u2f.c b/firmware/u2f.c index acf4a0ac64..ce3de07cec 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -98,6 +98,7 @@ uint8_t buttonState(void) return 0; } +#if DEBUG_LOG char *debugInt(const uint32_t i) { static uint8_t n = 0; @@ -108,6 +109,9 @@ char *debugInt(const uint32_t i) n = (n + 1) % 8; return ret; } +#else +#define debugInt(I) do{}while(0) +#endif static uint32_t dialog_timeout = 0; From eb2ef2464c58ffe4c09a4b0c8e7068c9a34c4f64 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 29 Apr 2016 19:54:59 +0200 Subject: [PATCH 13/31] CID hacks, not yet finished --- firmware/u2f.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index ce3de07cec..d5b9342997 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -174,8 +174,9 @@ void u2fhid_read(const U2FHID_FRAME *f) buf_ptr += sizeof(f->init.data); // Broadcast is reserved for init - if (cid == CID_BROADCAST && cmd != U2FHID_INIT) + if (f->cid == CID_BROADCAST && cmd != U2FHID_INIT) return; + cid = f->cid; // Check length isnt bigger than spec max if (len > sizeof(buf)) { @@ -184,9 +185,6 @@ void u2fhid_read(const U2FHID_FRAME *f) } } else { - // Broadcast is reserved for init - if (cid == CID_BROADCAST) - return; // check out of bounds if ((buf_ptr - buf) >= (signed) len || (buf_ptr + sizeof(f->cont.data) - buf) > (signed) sizeof(buf)) @@ -258,13 +256,13 @@ void u2fhid_init(const U2FHID_INIT_REQ *init_req) U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)f.init.data; bzero(&f, sizeof(f)); - f.cid = CID_BROADCAST; + f.cid = cid; f.init.cmd = U2FHID_INIT; f.init.bcnth = 0; f.init.bcntl = U2FHID_INIT_RESP_SIZE; memcpy(resp->nonce, init_req->nonce, sizeof(init_req->nonce)); - resp->cid = next_cid(); + resp->cid = cid == CID_BROADCAST ? next_cid() : cid; resp->versionInterface = U2FHID_IF_VERSION; resp->versionMajor = VERSION_MAJOR; resp->versionMinor = VERSION_MINOR; From 2ab950555e4481e893430a674e49015cf865e094 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sat, 7 May 2016 22:12:27 +0200 Subject: [PATCH 14/31] Fixed u2f reentry --- firmware/u2f.c | 167 +++++++++++++++++++++++++++++++++---------------- firmware/u2f.h | 10 +-- firmware/usb.c | 8 +-- 3 files changed, 122 insertions(+), 63 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index d5b9342997..efb7cee6b9 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -47,7 +47,7 @@ #define U2F_OUT_PKT_BUFFER_LEN 16 // Initialise without a cid -static uint32_t cid = CID_BROADCAST; +static uint32_t cid = 0; // Circular Output buffer static uint32_t u2f_out_start = 0; @@ -154,73 +154,130 @@ uint32_t next_cid(void) return cid; } -void u2fhid_read(const U2FHID_FRAME *f) +typedef struct { + uint8_t buf[57+127*59]; + uint8_t *buf_ptr; + uint32_t len; + uint8_t seq; + uint8_t cmd; +} U2F_ReadBuffer; + +U2F_ReadBuffer *reader; + +void u2fhid_read(char tiny, const U2FHID_FRAME *f) { - static uint8_t seq, cmd; - static uint32_t len; - static uint8_t *buf_ptr; - static uint8_t buf[57+7*59]; - - if ((f->cid != CID_BROADCAST) && (f->cid != cid)) { - return; // Not for us - } - - if (f->type & TYPE_INIT) { - seq = 0; - buf_ptr = buf; - len = MSG_LEN(*f); - cmd = f->type; - memcpy(buf_ptr, f->init.data, sizeof(f->init.data)); - buf_ptr += sizeof(f->init.data); - - // Broadcast is reserved for init - if (f->cid == CID_BROADCAST && cmd != U2FHID_INIT) + if (tiny) { + // read continue packet + if (reader == 0 || cid != f->cid) { + send_u2fhid_error(f->cid, ERR_CHANNEL_BUSY); return; - cid = f->cid; - - // Check length isnt bigger than spec max - if (len > sizeof(buf)) { - len = 0; - return send_u2fhid_error(ERR_INVALID_LEN); } - } - else { + + if ((f->type & TYPE_INIT) || reader->seq != f->cont.seq) { + u2fhid_init_cmd(f); + return; + } + // check out of bounds - if ((buf_ptr - buf) >= (signed) len - || (buf_ptr + sizeof(f->cont.data) - buf) > (signed) sizeof(buf)) + if ((reader->buf_ptr - reader->buf) >= (signed) reader->len + || (reader->buf_ptr + sizeof(f->cont.data) - reader->buf) > (signed) sizeof(reader->buf)) return; - if (f->cont.seq == seq) { - seq++; - memcpy(buf_ptr, f->cont.data, sizeof(f->cont.data)); - buf_ptr += sizeof(f->cont.data); - } else { - return send_u2fhid_error(ERR_INVALID_SEQ); - } - } - - // Do we need to wait for more data - if ((buf_ptr - buf) < (signed)len) { - // debugLog(0, "", "u2fhid_read wait"); + reader->seq++; + memcpy(reader->buf_ptr, f->cont.data, sizeof(f->cont.data)); + reader->buf_ptr += sizeof(f->cont.data); return; } - // We have all the data - switch (cmd) { + u2fhid_read_start(f); +} + +void u2fhid_init_cmd(const U2FHID_FRAME *f) { + reader->seq = 0; + reader->buf_ptr = reader->buf; + reader->len = MSG_LEN(*f); + reader->cmd = f->type; + memcpy(reader->buf_ptr, f->init.data, sizeof(f->init.data)); + reader->buf_ptr += sizeof(f->init.data); + cid = f->cid; + // Check length isnt bigger than spec max + if (reader->len > sizeof(reader->buf)) { + reader->len = 0; + return send_u2fhid_error(cid, ERR_INVALID_LEN); + } +} + +void u2fhid_read_start(const U2FHID_FRAME *f) { + U2F_ReadBuffer readbuffer; + if (!(f->type & TYPE_INIT)) { + return; + } + + reader = &readbuffer; + u2fhid_init_cmd(f); + + // Broadcast is reserved for init + if (f->cid == CID_BROADCAST && reader->cmd != U2FHID_INIT) + return; + + usbTiny(1); + for(;;) { + // Do we need to wait for more data + while ((reader->buf_ptr - reader->buf) < (signed)reader->len) { + uint8_t lastseq = reader->seq; + uint8_t lastcmd = reader->cmd; + int counter = U2F_TIMEOUT; + while (reader->seq == lastseq && reader->cmd == lastcmd) { + if (counter-- == 0) { + // timeout + cid = 0; + send_u2fhid_error(f->cid, ERR_MSG_TIMEOUT); + usbTiny(0); + return; + } + usbPoll(); + } + } + + // We have all the data + switch (reader->cmd) { case U2FHID_PING: - u2fhid_ping(buf, len); + u2fhid_ping(reader->buf, reader->len); break; case U2FHID_MSG: - u2fhid_msg((APDU *)buf, len); + u2fhid_msg((APDU *)reader->buf, reader->len); break; case U2FHID_INIT: - u2fhid_init((const U2FHID_INIT_REQ *)buf); + u2fhid_init((const U2FHID_INIT_REQ *)reader->buf); break; case U2FHID_WINK: - u2fhid_wink(buf, len); + u2fhid_wink(reader->buf, reader->len); break; default: - send_u2fhid_error(ERR_INVALID_CMD); + send_u2fhid_error(cid, ERR_INVALID_CMD); break; + } + + // wait for next commmand/ button press + reader->cmd = 0; + uint8_t bs = 0; + while (dialog_timeout-- && bs == 0 && reader->cmd == 0) { + usbPoll(); // may trigger new request + bs = buttonState(); + } + + if (reader->cmd == 0) { + if (dialog_timeout == 0) { + last_req_state += BTN_NO; // Timeout is like button no + } + else { + last_req_state += bs; + dialog_timeout = 0; + } + cid = 0; + usbTiny(0); + layoutHome(); + return; + } } } @@ -236,7 +293,7 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) (void)buf; if (len > 0) - return send_u2fhid_error(ERR_INVALID_LEN); + return send_u2fhid_error(cid, ERR_INVALID_LEN); if (dialog_timeout > 0) dialog_timeout = U2F_TIMEOUT; @@ -308,7 +365,7 @@ void u2fhid_msg(const APDU *a, uint32_t len) // Very crude locking, incase another message comes in while we wait. This // actually can probably be removed as no code inside calls usbPoll anymore if (lock) - return send_u2fhid_error(ERR_CHANNEL_BUSY); + return send_u2fhid_error(cid, ERR_CHANNEL_BUSY); lock = true; @@ -329,7 +386,7 @@ void u2fhid_msg(const APDU *a, uint32_t len) lock = false; - LayoutHomeAfterTimeout(); + //LayoutHomeAfterTimeout(); } void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) @@ -371,12 +428,12 @@ void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) } } -void send_u2fhid_error(uint8_t err) +void send_u2fhid_error(uint32_t fcid, uint8_t err) { U2FHID_FRAME f; bzero(&f, sizeof(f)); - f.cid = cid; + f.cid = fcid; f.init.cmd = U2FHID_ERROR; f.init.bcntl = 1; f.init.data[0] = err; diff --git a/firmware/u2f.h b/firmware/u2f.h index 847710a4c6..6e4c0862c0 100644 --- a/firmware/u2f.h +++ b/firmware/u2f.h @@ -35,7 +35,9 @@ typedef struct { #define APDU_LEN(A) (uint32_t)(((A).lc1 << 16) + ((A).lc2 << 8) + ((A).lc3)) -void u2fhid_read(const U2FHID_FRAME *buf); +void u2fhid_read(char tiny, const U2FHID_FRAME *buf); +void u2fhid_init_cmd(const U2FHID_FRAME *f); +void u2fhid_read_start(const U2FHID_FRAME *f); bool u2fhid_write(uint8_t *buf); void u2fhid_init(const U2FHID_INIT_REQ *init_req); void u2fhid_ping(const uint8_t *buf, uint32_t len); @@ -50,11 +52,11 @@ void u2f_register(const APDU *a); void u2f_version(const APDU *a); void u2f_authenticate(const APDU *a); -void send_u2f_msg(const uint8_t *data, const uint32_t len); -void send_u2f_error(const uint16_t err); +void send_u2f_msg(const uint8_t *data, uint32_t len); +void send_u2f_error(uint16_t err); void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len); -void send_u2fhid_error(const uint8_t err); +void send_u2fhid_error(uint32_t fcid, uint8_t err); #endif diff --git a/firmware/usb.c b/firmware/usb.c index 1895da2ea3..91ed2b847d 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -230,14 +230,14 @@ static const struct usb_interface_descriptor hid_iface_debug[] = {{ static const struct usb_interface ifaces[] = {{ .num_altsetting = 1, .altsetting = hid_iface, -}, { - .num_altsetting = 1, - .altsetting = hid_iface_u2f, #if DEBUG_LINK }, { .num_altsetting = 1, .altsetting = hid_iface_debug, #endif +}, { + .num_altsetting = 1, + .altsetting = hid_iface_u2f, }}; static const struct usb_config_descriptor config = { @@ -308,7 +308,7 @@ static void hid_u2f_rx_callback(usbd_device *dev, uint8_t ep) debugLog(0, "", "hid_u2f_rx_callback"); if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; - u2fhid_read((const U2FHID_FRAME *)buf); + u2fhid_read(tiny, (const U2FHID_FRAME *)buf); } #if DEBUG_LINK From 9006c90a59c761b509d1140e56bc86efae95d86f Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 11 May 2016 14:02:06 +0200 Subject: [PATCH 15/31] use MEMSET_BZERO and U2F_APPID_SIZE macros --- Makefile.include | 2 +- firmware/u2f.c | 19 ++++++++++--------- firmware/u2f_knownapps.h | 2 +- vendor/trezor-crypto | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Makefile.include b/Makefile.include index 5e48ba04f6..f931984328 100644 --- a/Makefile.include +++ b/Makefile.include @@ -14,7 +14,7 @@ DBGFLAGS ?= -g -DNDEBUG CFLAGS += $(OPTFLAGS) \ $(DBGFLAGS) \ - -std=c99 \ + -std=gnu99 \ -W \ -Wall \ -Wextra \ diff --git a/firmware/u2f.c b/firmware/u2f.c index acf4a0ac64..f01f2e956f 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -33,6 +33,7 @@ #include "rng.h" #include "hmac.h" #include "util.h" +#include "macros.h" #include "u2f/u2f.h" #include "u2f/u2f_hid.h" @@ -240,7 +241,7 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) dialog_timeout = U2F_TIMEOUT; U2FHID_FRAME f; - bzero(&f, sizeof(f)); + MEMSET_BZERO(&f, sizeof(f)); f.cid = cid; f.init.cmd = U2FHID_WINK; f.init.bcntl = 0; @@ -253,7 +254,7 @@ void u2fhid_init(const U2FHID_INIT_REQ *init_req) U2FHID_FRAME f; U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)f.init.data; - bzero(&f, sizeof(f)); + MEMSET_BZERO(&f, sizeof(f)); f.cid = CID_BROADCAST; f.init.cmd = U2FHID_INIT; f.init.bcnth = 0; @@ -340,7 +341,7 @@ void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) // debugLog(0, "", "send_u2fhid_msg"); - bzero(&f, sizeof(f)); + MEMSET_BZERO(&f, sizeof(f)); f.cid = cid; f.init.cmd = cmd; f.init.bcnth = len >> 8; @@ -356,7 +357,7 @@ void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) // Cont packet(s) for (; l > 0; l -= psz, p += psz) { // debugLog(0, "", "send_u2fhid_msg con"); - bzero(&f.cont.data, sizeof(f.cont.data)); + MEMSET_BZERO(&f.cont.data, sizeof(f.cont.data)); f.cont.seq = seq++; psz = MIN(sizeof(f.cont.data), l); memcpy(f.cont.data, p, psz); @@ -373,7 +374,7 @@ void send_u2fhid_error(uint8_t err) { U2FHID_FRAME f; - bzero(&f, sizeof(f)); + MEMSET_BZERO(&f, sizeof(f)); f.cid = cid; f.init.cmd = U2FHID_ERROR; f.init.bcntl = 1; @@ -391,18 +392,18 @@ void u2f_version(const APDU *a) send_u2f_msg(version_response, sizeof(version_response)); } -static const char *getReadableAppId(const uint8_t appid[32]) { +static const char *getReadableAppId(const uint8_t appid[U2F_APPID_SIZE]) { unsigned int i; static char buf[6+2+6+1]; for (i = 0; i < sizeof(u2f_well_known)/sizeof(U2FWellKnown); i++) { - if (memcmp(appid, u2f_well_known[i].appid, 32) == 0) + if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) return u2f_well_known[i].appname; } data2hex(appid, 3, &buf[0]); buf[6] = buf[7] = '.'; - data2hex(appid+(sizeof(appid)-3), 3, &buf[8]); + data2hex(appid + (U2F_APPID_SIZE - 3), 3, &buf[8]); return buf; } @@ -534,7 +535,7 @@ void u2f_register(const APDU *a) if (last_req_state == REG_PASS) { uint8_t data[sizeof(U2F_REGISTER_RESP) + 2]; U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)&data; - bzero(data, sizeof(data)); + MEMSET_BZERO(data, sizeof(data)); resp->registerId = U2F_REGISTER_ID; diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h index ca3d1732fb..c3b7142324 100644 --- a/firmware/u2f_knownapps.h +++ b/firmware/u2f_knownapps.h @@ -29,7 +29,7 @@ typedef struct { static const U2FWellKnown u2f_well_known[3] = { { - // didn't feel like tracing that one yet + // https://www.gstatic.com/securitykey/origins.json { 0xa5,0x46,0x72,0xb2,0x22,0xc4,0xcf,0x95, 0xe1,0x51,0xed,0x8d,0x4d,0x3c,0x76,0x7a, 0x6c,0xc3,0x49,0x43,0x59,0x43,0x79,0x4e, diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index 406022acb4..23590c05c6 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit 406022acb4d43a48bb9d8afe392226f1fdeccec2 +Subproject commit 23590c05c652efccdfb7e837a048adbecab5b145 From 041eaa5e4b4c7b6001730de351ab44a10fff5a63 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 16 May 2016 18:58:13 +0200 Subject: [PATCH 16/31] refactor u2f dialogs into a separate function --- firmware/layout2.c | 4 ++++ firmware/layout2.h | 1 + firmware/u2f.c | 8 ++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/firmware/layout2.c b/firmware/layout2.c index 4d77c385ac..73b566c8e0 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -335,3 +335,7 @@ void layoutSignIdentity(const IdentityType *identity, const char *challenge) NULL, NULL); } + +void layoutU2FDialog(const char *verb, const char *appid) { + layoutDialog(DIALOG_ICON_QUESTION, "Cancel", verb, NULL, verb, "U2F security key?", "", appid, "", NULL); +} diff --git a/firmware/layout2.h b/firmware/layout2.h index 1b725d5c5c..6cbe30c436 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -40,5 +40,6 @@ void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address) void layoutAddress(const char *address, const char *desc); void layoutPublicKey(const uint8_t *pubkey); void layoutSignIdentity(const IdentityType *identity, const char *challenge); +void layoutU2FDialog(const char *verb, const char *appid); #endif diff --git a/firmware/u2f.c b/firmware/u2f.c index a40c368c7f..330945c55b 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -568,9 +568,7 @@ void u2f_register(const APDU *a) // error: testof-user-presence is required send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state - layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Register", - NULL, "Register U2F", "security key", - "", getReadableAppId(req->appId), "", NULL); + layoutU2FDialog("Register", getReadableAppId(req->appId)); dialog_timeout = U2F_TIMEOUT; last_req_state = REG; return; @@ -701,9 +699,7 @@ void u2f_authenticate(const APDU *a) // error: testof-user-presence is required send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state - layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Authenticate", NULL, - "Authenticate U2F", "security key", - "", getReadableAppId(req->appId), "", NULL); + layoutU2FDialog("Authenticate", getReadableAppId(req->appId)); dialog_timeout = U2F_TIMEOUT; last_req_state = AUTH; return; From a0571e02a7eeb61bdd552b0d50b2a1e49d1766a7 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 18 May 2016 01:09:13 +0200 Subject: [PATCH 17/31] Removed more magic numbers. `KEY_PATH_LEN`: length of the derivation path in the key handle `KEY_PATH_ENTRIES`: number of entries in derivation path including initial BIP-43 selector. `KEY_HANDLE_LEN`: length of key handle (derivation path + HMAC checksum) --- firmware/u2f.c | 42 +++++++++++++++++++++------------------- firmware/u2f_knownapps.h | 3 ++- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index 330945c55b..2ba366a699 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -56,7 +56,11 @@ static uint32_t u2f_out_end = 0; static uint8_t u2f_out_packets[U2F_OUT_PKT_BUFFER_LEN][HID_RPT_SIZE]; #define U2F_PUBKEY_LEN 65 -#define KEY_HANDLE_LEN 64 +#define KEY_PATH_LEN 32 +#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH) + +// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' +#define KEY_PATH_ENTRIES (1 + KEY_PATH_LEN / sizeof(uint32_t)) // Auth/Register request state machine typedef enum { @@ -487,29 +491,28 @@ const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) { - uint8_t keybase[64]; + uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; - // Derivation path is m/'U2F/'r/'r/'r/'r/'r/'r/'r/'r - uint32_t i, key_path[9]; + // Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' + uint32_t i, key_path[KEY_PATH_ENTRIES]; key_path[0] = U2F_KEY_PATH; - for (i = 1; i < 9; i++) { + for (i = 1; i < KEY_PATH_ENTRIES; i++) { // high bit for hardened keys key_path[i]= 0x80000000 | random32(); } // First half of keyhandle is key_path - memcpy(key_handle, &key_path[1], 32); + memcpy(key_handle, &key_path[1], KEY_PATH_LEN); // prepare keypair from /random data - const HDNode *node = - getDerivedNode(key_path, sizeof(key_path) / sizeof(uint32_t)); + const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); // For second half of keyhandle // Signature of app_id and random data - memcpy(&keybase[0], app_id, 32); - memcpy(&keybase[32], key_handle, 32); + memcpy(&keybase[0], app_id, U2F_APPID_SIZE); + memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); hmac_sha256(node->private_key, sizeof(node->private_key), - keybase, sizeof(keybase), &key_handle[32]); + keybase, sizeof(keybase), &key_handle[KEY_PATH_LEN]); // Done! return node; @@ -518,23 +521,22 @@ const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[]) { - uint32_t key_path[9]; + uint32_t key_path[KEY_PATH_ENTRIES]; key_path[0] = U2F_KEY_PATH; - memcpy(&key_path[1], key_handle, 32); + memcpy(&key_path[1], key_handle, KEY_PATH_LEN); - const HDNode *node = - getDerivedNode(key_path, sizeof(key_path) / sizeof(uint32_t)); + const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); - uint8_t keybase[64]; - memcpy(&keybase[0], app_id, 32); - memcpy(&keybase[32], key_handle, 32); + uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; + memcpy(&keybase[0], app_id, U2F_APPID_SIZE); + memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); - uint8_t hmac[32]; + uint8_t hmac[SHA256_DIGEST_LENGTH]; hmac_sha256(node->private_key, sizeof(node->private_key), keybase, sizeof(keybase), hmac); - if (memcmp(&key_handle[32], hmac, 32) != 0) + if (memcmp(&key_handle[KEY_PATH_LEN], hmac, SHA256_DIGEST_LENGTH) != 0) return NULL; // Done! diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h index c3b7142324..b57bbcb024 100644 --- a/firmware/u2f_knownapps.h +++ b/firmware/u2f_knownapps.h @@ -21,9 +21,10 @@ #define __U2F_KNOWNAPPS_H_INCLUDED__ #include +#include "u2f/u2f.h" typedef struct { - uint8_t appid[32]; + uint8_t appid[U2F_APPID_SIZE]; const char *appname; } U2FWellKnown; From 96f30a0ba708c2f077d536a3b9184355af1c8d9c Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 20 May 2016 01:49:20 +0200 Subject: [PATCH 18/31] Don't ask for passphrase with u2f. --- firmware/fsm.c | 2 +- firmware/storage.c | 16 +++++++++------- firmware/storage.h | 4 ++-- firmware/u2f.c | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 97cb933bed..8a99f0bcb3 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -96,7 +96,7 @@ const CoinType *fsm_getCoin(const char *name) const HDNode *fsm_getDerivedNode(const char *curve, uint32_t *address_n, size_t address_n_count) { static HDNode node; - if (!storage_getRootNode(&node, curve)) { + if (!storage_getRootNode(&node, curve, true)) { fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized or passphrase request cancelled or unsupported curve"); layoutHome(); return 0; diff --git a/firmware/storage.c b/firmware/storage.c index 1d8fb9730f..245ea83916 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -72,7 +72,7 @@ _Static_assert(FLASH_STORAGE_START + FLASH_STORAGE_REALLEN <= FLASH_STORAGE_PINA _Static_assert((sizeof(storage_uuid) & 3) == 0, "storage uuid unaligned"); _Static_assert((sizeof(storage) & 3) == 0, "storage unaligned"); -static bool sessionSeedCached; +static bool sessionSeedCached, sessionSeedUsesPassphrase; static uint8_t sessionSeed[64]; @@ -290,27 +290,29 @@ void get_root_node_callback(uint32_t iter, uint32_t total) layoutProgress("Waking up", 1000 * iter / total); } -const uint8_t *storage_getSeed(void) +const uint8_t *storage_getSeed(bool usePassphrase) { // root node is properly cached - if (sessionSeedCached) { + if (usePassphrase == sessionSeedUsesPassphrase + && sessionSeedCached) { return sessionSeed; } // if storage has mnemonic, convert it to node and use it if (storage.has_mnemonic) { - if (!protectPassphrase()) { + if (usePassphrase && !protectPassphrase()) { return NULL; } - mnemonic_to_seed(storage.mnemonic, sessionPassphrase, sessionSeed, get_root_node_callback); // BIP-0039 + mnemonic_to_seed(storage.mnemonic, usePassphrase ? sessionPassphrase : "", sessionSeed, get_root_node_callback); // BIP-0039 sessionSeedCached = true; + sessionSeedUsesPassphrase = usePassphrase; return sessionSeed; } return NULL; } -bool storage_getRootNode(HDNode *node, const char *curve) +bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase) { // if storage has node, decrypt and use it if (storage.has_node && strcmp(curve, SECP256K1_NAME) == 0) { @@ -339,7 +341,7 @@ bool storage_getRootNode(HDNode *node, const char *curve) return true; } - const uint8_t *seed = storage_getSeed(); + const uint8_t *seed = storage_getSeed(usePassphrase); if (seed == NULL) { return false; } diff --git a/firmware/storage.h b/firmware/storage.h index 304ebf842b..c69d7f0154 100644 --- a/firmware/storage.h +++ b/firmware/storage.h @@ -33,9 +33,9 @@ void session_clear(bool clear_pin); void storage_loadDevice(LoadDevice *msg); -const uint8_t *storage_getSeed(void); +const uint8_t *storage_getSeed(bool usePassphrase); -bool storage_getRootNode(HDNode *node, const char *curve); +bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase); const char *storage_getLabel(void); void storage_setLabel(const char *label); diff --git a/firmware/u2f.c b/firmware/u2f.c index 2ba366a699..b2216870c9 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -473,7 +473,7 @@ static const char *getReadableAppId(const uint8_t appid[U2F_APPID_SIZE]) { const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) { static HDNode node; - if (!storage_getRootNode(&node, NIST256P1_NAME)) { + if (!storage_getRootNode(&node, NIST256P1_NAME, false)) { layoutHome(); debugLog(0, "", "ERR: Device not init"); return 0; From 68b34af19e8f9c22bdd4bc11facce1fd0088529c Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 24 May 2016 00:24:14 +0200 Subject: [PATCH 19/31] More standard conform behaviour Tested with u2f-ref-code/u2f-tests. Known incompatibility: - changed challenge invalidates button press. --- firmware/protect.c | 4 +- firmware/u2f.c | 141 ++++++++++++++++++++++----------------------- firmware/u2f.h | 2 +- firmware/u2f/u2f.h | 4 +- firmware/usb.c | 4 +- 5 files changed, 77 insertions(+), 78 deletions(-) diff --git a/firmware/protect.c b/firmware/protect.c index 0ff821dac9..a39ee07870 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -57,7 +57,7 @@ bool protectButton(ButtonRequestType type, bool confirm_only) // button acked - check buttons if (acked) { - usbDelay(3500); + usbDelay(3300); buttonUpdate(); if (button.YesUp) { result = true; @@ -163,7 +163,7 @@ bool protectPin(bool use_cached) } layoutDialog(DIALOG_ICON_INFO, NULL, NULL, NULL, "Wrong PIN entered", NULL, "Please wait", secstr, "to continue ...", NULL); // wait one second - usbDelay(840000); + usbDelay(800000); wait--; } const char *pin; diff --git a/firmware/u2f.c b/firmware/u2f.c index b2216870c9..46660cae77 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -44,8 +44,8 @@ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) // About 1/2 Second according to values used in protect.c -#define U2F_TIMEOUT 840000/2 -#define U2F_OUT_PKT_BUFFER_LEN 16 +#define U2F_TIMEOUT (800000/2) +#define U2F_OUT_PKT_BUFFER_LEN 128 // Initialise without a cid static uint32_t cid = 0; @@ -120,36 +120,6 @@ char *debugInt(const uint32_t i) static uint32_t dialog_timeout = 0; -void LayoutHomeAfterTimeout(void) -{ - static bool timeoutLock = false; - - if (timeoutLock || dialog_timeout == 0) - return; // Dialog has cleared or already in loop - - timeoutLock = true; - U2F_STATE rs = last_req_state; - U2F_STATE bs = INIT; - while (dialog_timeout-- && rs == last_req_state && bs == 0) { - usbPoll(); // may trigger new request - bs = buttonState(); - } - timeoutLock = false; - - if (rs != last_req_state) - return; // Reset by new request don't clear screen - - if (dialog_timeout == 0) { - last_req_state += BTN_NO; // Timeout is like button no - } - else { - last_req_state += bs; - dialog_timeout = 0; - } - - layoutHome(); -} - uint32_t next_cid(void) { // extremely unlikely but hey @@ -171,6 +141,18 @@ U2F_ReadBuffer *reader; void u2fhid_read(char tiny, const U2FHID_FRAME *f) { + // Always handle init packets directly + if (f->init.cmd == U2FHID_INIT) { + u2fhid_init(f); + if (tiny && reader && f->cid == cid) { + // abort current channel + reader->cmd = 0; + reader->len = 0; + reader->seq = 255; + } + return; + } + if (tiny) { // read continue packet if (reader == 0 || cid != f->cid) { @@ -178,11 +160,19 @@ void u2fhid_read(char tiny, const U2FHID_FRAME *f) return; } - if ((f->type & TYPE_INIT) || reader->seq != f->cont.seq) { + if ((f->type & TYPE_INIT) && reader->seq == 255) { u2fhid_init_cmd(f); return; } + if (reader->seq != f->cont.seq) { + send_u2fhid_error(f->cid, ERR_INVALID_SEQ); + reader->cmd = 0; + reader->len = 0; + reader->seq = 255; + return; + } + // check out of bounds if ((reader->buf_ptr - reader->buf) >= (signed) reader->len || (reader->buf_ptr + sizeof(f->cont.data) - reader->buf) > (signed) sizeof(reader->buf)) @@ -204,11 +194,6 @@ void u2fhid_init_cmd(const U2FHID_FRAME *f) { memcpy(reader->buf_ptr, f->init.data, sizeof(f->init.data)); reader->buf_ptr += sizeof(f->init.data); cid = f->cid; - // Check length isnt bigger than spec max - if (reader->len > sizeof(reader->buf)) { - reader->len = 0; - return send_u2fhid_error(cid, ERR_INVALID_LEN); - } } void u2fhid_read_start(const U2FHID_FRAME *f) { @@ -217,13 +202,20 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { return; } + // Broadcast is reserved for init + if (f->cid == CID_BROADCAST || f->cid == 0) { + send_u2fhid_error(f->cid, ERR_INVALID_CID); + return; + } + + if ((unsigned)MSG_LEN(*f) > sizeof(reader->buf)) { + send_u2fhid_error(f->cid, ERR_INVALID_LEN); + return; + } + reader = &readbuffer; u2fhid_init_cmd(f); - // Broadcast is reserved for init - if (f->cid == CID_BROADCAST && reader->cmd != U2FHID_INIT) - return; - usbTiny(1); for(;;) { // Do we need to wait for more data @@ -234,8 +226,9 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { while (reader->seq == lastseq && reader->cmd == lastcmd) { if (counter-- == 0) { // timeout + send_u2fhid_error(cid, ERR_MSG_TIMEOUT); cid = 0; - send_u2fhid_error(f->cid, ERR_MSG_TIMEOUT); + reader = 0; usbTiny(0); return; } @@ -245,15 +238,15 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { // We have all the data switch (reader->cmd) { + case 0: + // message was aborted by init + break; case U2FHID_PING: u2fhid_ping(reader->buf, reader->len); break; case U2FHID_MSG: u2fhid_msg((APDU *)reader->buf, reader->len); break; - case U2FHID_INIT: - u2fhid_init((const U2FHID_INIT_REQ *)reader->buf); - break; case U2FHID_WINK: u2fhid_wink(reader->buf, reader->len); break; @@ -264,6 +257,7 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { // wait for next commmand/ button press reader->cmd = 0; + reader->seq = 255; uint8_t bs = 0; while (dialog_timeout-- && bs == 0 && reader->cmd == 0) { usbPoll(); // may trigger new request @@ -279,6 +273,7 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { dialog_timeout = 0; } cid = 0; + reader = 0; usbTiny(0); layoutHome(); return; @@ -311,20 +306,27 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) queue_u2f_pkt(&f); } -void u2fhid_init(const U2FHID_INIT_REQ *init_req) +void u2fhid_init(const U2FHID_FRAME *in) { - debugLog(0, "", "u2fhid_init"); + const U2FHID_INIT_REQ *init_req = (const U2FHID_INIT_REQ *)&in->init.data; U2FHID_FRAME f; U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)f.init.data; + debugLog(0, "", "u2fhid_init"); + + if (in->cid == 0) { + send_u2fhid_error(in->cid, ERR_INVALID_CID); + return; + } + MEMSET_BZERO(&f, sizeof(f)); - f.cid = cid; + f.cid = in->cid; f.init.cmd = U2FHID_INIT; f.init.bcnth = 0; f.init.bcntl = U2FHID_INIT_RESP_SIZE; memcpy(resp->nonce, init_req->nonce, sizeof(init_req->nonce)); - resp->cid = cid == CID_BROADCAST ? next_cid() : cid; + resp->cid = in->cid == CID_BROADCAST ? next_cid() : in->cid; resp->versionInterface = U2FHID_IF_VERSION; resp->versionMajor = VERSION_MAJOR; resp->versionMinor = VERSION_MINOR; @@ -358,8 +360,6 @@ uint8_t *u2f_out_data(void) void u2fhid_msg(const APDU *a, uint32_t len) { - static bool lock = false; - if ((APDU_LEN(*a) + sizeof(APDU)) > len) { debugLog(0, "", "BAD APDU LENGTH"); debugInt(APDU_LEN(*a)); @@ -367,12 +367,10 @@ void u2fhid_msg(const APDU *a, uint32_t len) return; } - // Very crude locking, incase another message comes in while we wait. This - // actually can probably be removed as no code inside calls usbPoll anymore - if (lock) - return send_u2fhid_error(cid, ERR_CHANNEL_BUSY); - - lock = true; + if (a->cla != 0) { + send_u2f_error(U2F_SW_CLA_NOT_SUPPORTED); + return; + } switch (a->ins) { case U2F_REGISTER: @@ -388,10 +386,6 @@ void u2fhid_msg(const APDU *a, uint32_t len) debugLog(0, "", "u2f unknown cmd"); send_u2f_error(U2F_SW_INS_NOT_SUPPORTED); } - - lock = false; - - //LayoutHomeAfterTimeout(); } void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) @@ -447,10 +441,15 @@ void send_u2fhid_error(uint32_t fcid, uint8_t err) void u2f_version(const APDU *a) { + if (APDU_LEN(*a) != 0) { + debugLog(0, "", "u2f version - badlen"); + send_u2f_error(U2F_SW_WRONG_LENGTH); + return; + } + // INCLUDES SW_NO_ERROR static const uint8_t version_response[] = {'U', '2', 'F', '_', 'V', '2', 0x90, 0x00}; - (void)a; debugLog(0, "", "u2f version"); send_u2f_msg(version_response, sizeof(version_response)); } @@ -553,7 +552,7 @@ void u2f_register(const APDU *a) debugLog(0, "", "u2f register"); if (APDU_LEN(*a) != sizeof(U2F_REGISTER_REQ)) { debugLog(0, "", "u2f register - badlen"); - send_u2f_error(U2F_SW_WRONG_DATA); + send_u2f_error(U2F_SW_WRONG_LENGTH); return; } @@ -571,7 +570,7 @@ void u2f_register(const APDU *a) send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state layoutU2FDialog("Register", getReadableAppId(req->appId)); - dialog_timeout = U2F_TIMEOUT; + dialog_timeout = 10*U2F_TIMEOUT; last_req_state = REG; return; } @@ -580,7 +579,7 @@ void u2f_register(const APDU *a) if (last_req_state == REG) { // error: testof-user-presence is required send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - dialog_timeout = U2F_TIMEOUT; + dialog_timeout = 10*U2F_TIMEOUT; return; } @@ -640,6 +639,7 @@ void u2f_register(const APDU *a) 1 /* keyhandleLen */ + resp->keyHandleLen + sizeof(U2F_ATT_CERT) + sig_len + 2; + last_req_state = INIT; send_u2f_msg(data, l); return; } @@ -655,7 +655,7 @@ void u2f_authenticate(const APDU *a) if (APDU_LEN(*a) < 64) { /// FIXME: decent value debugLog(0, "", "u2f authenticate - badlen"); - send_u2f_error(U2F_SW_WRONG_DATA); + send_u2f_error(U2F_SW_WRONG_LENGTH); return; } @@ -699,19 +699,16 @@ void u2f_authenticate(const APDU *a) if (last_req_state == INIT) { // error: testof-user-presence is required - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state layoutU2FDialog("Authenticate", getReadableAppId(req->appId)); - dialog_timeout = U2F_TIMEOUT; last_req_state = AUTH; - return; } // Awaiting Keypress if (last_req_state == AUTH) { // error: testof-user-presence is required send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - dialog_timeout = U2F_TIMEOUT; + dialog_timeout = 10*U2F_TIMEOUT; return; } @@ -753,10 +750,10 @@ void u2f_authenticate(const APDU *a) memcpy(buf + sizeof(U2F_AUTHENTICATE_RESP) - U2F_MAX_EC_SIG_SIZE + sig_len, "\x90\x00", 2); + last_req_state = INIT; send_u2f_msg(buf, sizeof(U2F_AUTHENTICATE_RESP) - U2F_MAX_EC_SIG_SIZE + sig_len + 2); - last_req_state = INIT; } } diff --git a/firmware/u2f.h b/firmware/u2f.h index 6e4c0862c0..6ed7f02f06 100644 --- a/firmware/u2f.h +++ b/firmware/u2f.h @@ -39,7 +39,7 @@ void u2fhid_read(char tiny, const U2FHID_FRAME *buf); void u2fhid_init_cmd(const U2FHID_FRAME *f); void u2fhid_read_start(const U2FHID_FRAME *f); bool u2fhid_write(uint8_t *buf); -void u2fhid_init(const U2FHID_INIT_REQ *init_req); +void u2fhid_init(const U2FHID_FRAME *in); void u2fhid_ping(const uint8_t *buf, uint32_t len); void u2fhid_wink(const uint8_t *buf, uint32_t len); void u2fhid_sync(const uint8_t *buf, uint32_t len); diff --git a/firmware/u2f/u2f.h b/firmware/u2f/u2f.h index 4291c594b2..62979a3160 100644 --- a/firmware/u2f/u2f.h +++ b/firmware/u2f/u2f.h @@ -129,8 +129,10 @@ extern "C" // Command status responses #define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR -#define U2F_SW_WRONG_DATA 0x6984 // SW_WRONG_DATA +#define U2F_SW_WRONG_LENGTH 0x6700 // SW_WRONG_LENGTH +#define U2F_SW_DATA_INVALID 0x6984 // SW_WRONG_DATA #define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED +#define U2F_SW_WRONG_DATA 0x6a80 // SW_WRONG_DATA #define U2F_SW_INS_NOT_SUPPORTED 0x6d00 // SW_INS_NOT_SUPPORTED #define U2F_SW_CLA_NOT_SUPPORTED 0x6e00 // SW_CLA_NOT_SUPPORTED diff --git a/firmware/usb.c b/firmware/usb.c index 91ed2b847d..d1dadd71b3 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -165,14 +165,14 @@ static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = {{ .bEndpointAddress = ENDPOINT_ADDRESS_U2F_IN, .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, .wMaxPacketSize = 64, - .bInterval = 1, + .bInterval = 2, }, { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_U2F_OUT, .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, .wMaxPacketSize = 64, - .bInterval = 1, + .bInterval = 2, }}; static const struct usb_interface_descriptor hid_iface_u2f[] = {{ From 053fe7cb66d0588127b09365567896e9d6ce0435 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 24 May 2016 01:54:08 +0200 Subject: [PATCH 20/31] Remove Cancel Option U2F doesn't allow cancellation on device. Also fix button state in protect. This fixes the following bug: 1. wipe device 2. press and hold right button, click left button to cancel. 3. release all buttons. 4. wipe device again, now automatic. --- firmware/layout2.c | 2 +- firmware/protect.c | 1 + firmware/u2f.c | 55 ++++++++++++---------------------------------- 3 files changed, 16 insertions(+), 42 deletions(-) diff --git a/firmware/layout2.c b/firmware/layout2.c index 73b566c8e0..7805258b66 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -337,5 +337,5 @@ void layoutSignIdentity(const IdentityType *identity, const char *challenge) } void layoutU2FDialog(const char *verb, const char *appid) { - layoutDialog(DIALOG_ICON_QUESTION, "Cancel", verb, NULL, verb, "U2F security key?", "", appid, "", NULL); + layoutDialog(DIALOG_ICON_QUESTION, NULL, verb, NULL, verb, "U2F security key?", "", appid, "", NULL); } diff --git a/firmware/protect.c b/firmware/protect.c index a39ee07870..a1b127ffbb 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -44,6 +44,7 @@ bool protectButton(ButtonRequestType type, bool confirm_only) resp.has_code = true; resp.code = type; usbTiny(1); + buttonUpdate(); // Clear button state msg_write(MessageType_MessageType_ButtonRequest, &resp); for (;;) { diff --git a/firmware/u2f.c b/firmware/u2f.c index 46660cae77..5ce8b75be7 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -65,14 +65,10 @@ static uint8_t u2f_out_packets[U2F_OUT_PKT_BUFFER_LEN][HID_RPT_SIZE]; // Auth/Register request state machine typedef enum { INIT = 0, - BTN_NO = 1, - BTN_YES = 2, AUTH = 10, - AUTH_FAIL = 11, - AUTH_PASS = 12, + AUTH_PASS = 11, REG = 20, - REG_FAIL = 21, - REG_PASS = 22 + REG_PASS = 21 } U2F_STATE; static U2F_STATE last_req_state = INIT; @@ -92,16 +88,6 @@ typedef struct { uint8_t chal[U2F_CHAL_SIZE]; } U2F_AUTHENTICATE_SIG_STR; -uint8_t buttonState(void) -{ - buttonUpdate(); - - if ((button.NoDown > 10) || button.NoUp) - return BTN_NO; - if ((button.YesDown > 10) || button.YesUp) - return BTN_YES; - return 0; -} #if DEBUG_LOG char *debugInt(const uint32_t i) @@ -259,18 +245,19 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { reader->cmd = 0; reader->seq = 255; uint8_t bs = 0; - while (dialog_timeout-- && bs == 0 && reader->cmd == 0) { + while (dialog_timeout && bs == 0 && reader->cmd == 0) { + dialog_timeout--; usbPoll(); // may trigger new request - bs = buttonState(); + buttonUpdate(); + if (button.YesUp && + (last_req_state == AUTH || last_req_state == REG)) { + last_req_state++; + } } if (reader->cmd == 0) { if (dialog_timeout == 0) { - last_req_state += BTN_NO; // Timeout is like button no - } - else { - last_req_state += bs; - dialog_timeout = 0; + last_req_state = INIT; } cid = 0; reader = 0; @@ -296,7 +283,7 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) return send_u2fhid_error(cid, ERR_INVALID_LEN); if (dialog_timeout > 0) - dialog_timeout = U2F_TIMEOUT; + dialog_timeout = 10*U2F_TIMEOUT; U2FHID_FRAME f; MEMSET_BZERO(&f, sizeof(f)); @@ -563,16 +550,13 @@ void u2f_register(const APDU *a) } // First Time request, return not present and display request dialog - if (last_req_state == 0) { + if (last_req_state == INIT) { // wake up crypto system to be ready for signing getDerivedNode(NULL, 0); // error: testof-user-presence is required - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); buttonUpdate(); // Clear button state layoutU2FDialog("Register", getReadableAppId(req->appId)); - dialog_timeout = 10*U2F_TIMEOUT; last_req_state = REG; - return; } // Still awaiting Keypress @@ -583,12 +567,6 @@ void u2f_register(const APDU *a) return; } - // Buttons said no! - if (last_req_state == REG_FAIL) { - send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle - return; - } - // Buttons said yes if (last_req_state == REG_PASS) { uint8_t data[sizeof(U2F_REGISTER_RESP) + 2]; @@ -640,6 +618,7 @@ void u2f_register(const APDU *a) sizeof(U2F_ATT_CERT) + sig_len + 2; last_req_state = INIT; + dialog_timeout = 0; send_u2f_msg(data, l); return; } @@ -712,13 +691,6 @@ void u2f_authenticate(const APDU *a) return; } - // Buttons said no! - if (last_req_state == AUTH_FAIL) { - send_u2f_error( - U2F_SW_WRONG_DATA); // error:bad key handle - return; - } - // Buttons said yes if (last_req_state == AUTH_PASS) { uint8_t buf[sizeof(U2F_AUTHENTICATE_RESP) + 2]; @@ -751,6 +723,7 @@ void u2f_authenticate(const APDU *a) U2F_MAX_EC_SIG_SIZE + sig_len, "\x90\x00", 2); last_req_state = INIT; + dialog_timeout = 0; send_u2f_msg(buf, sizeof(U2F_AUTHENTICATE_RESP) - U2F_MAX_EC_SIG_SIZE + sig_len + 2); From e09337112943d46f854cca64eeb523b67883b760 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 24 May 2016 23:44:40 +0200 Subject: [PATCH 21/31] Added storage area for u2f counter. To prevent flashing for every u2f operation just clear one bit in the u2f area to indicate an increased counter. --- firmware/storage.c | 98 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/firmware/storage.c b/firmware/storage.c index f3d8fbb183..32e269b8be 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -48,13 +48,15 @@ char storage_uuid_str[25]; /* storage layout: - offset | type/length | description ---------+-------------+------------------------------- - 0x0000 | 4 bytes | magic = 'stor' - 0x0004 | 12 bytes | uuid - 0x0010 | ? | Storage structure - 0x4000 | 4 kbytes | area for pin failures - 0x5000 | 12 kbytes | reserved + offset | type/length | description +--------+--------------+------------------------------- + 0x0000 | 4 bytes | magic = 'stor' + 0x0004 | 12 bytes | uuid + 0x0010 | ? bytes | Storage structure +--------+--------------+------------------------------- + 0x4000 | 4 kbytes | area for pin failures + 0x5000 | 256 bytes | area for u2f counter updates + 0x5100 | 11.75 kbytes | reserved The area for pin failures looks like this: 0 ... 0 pinfail 0xffffffff .. 0xffffffff @@ -63,15 +65,28 @@ the number of zeros is the number of pin failures. This layout is used because we can only clear bits without erasing the flash. +The area for u2f counter updates is just a sequence of zero-bits +followed by a sequence of one-bits. The bits in a byte are numbered +from LSB to MSB. The number of zero bits is the offset that should +be added to the storage u2f_counter to get the real counter value. + */ #define FLASH_STORAGE_PINAREA (FLASH_META_START + 0x4000) #define FLASH_STORAGE_PINAREA_LEN (0x1000) +#define FLASH_STORAGE_U2FAREA (FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN) +#define FLASH_STORAGE_U2FAREA_LEN (0x100) #define FLASH_STORAGE_REALLEN (4 + sizeof(storage_uuid) + sizeof(Storage)) _Static_assert(FLASH_STORAGE_START + FLASH_STORAGE_REALLEN <= FLASH_STORAGE_PINAREA, "Storage struct is too large for TREZOR flash"); _Static_assert((sizeof(storage_uuid) & 3) == 0, "storage uuid unaligned"); _Static_assert((sizeof(storage) & 3) == 0, "storage unaligned"); +/* Current u2f offset, i.e. u2f counter is + * storage.u2f_counter + storage_u2f_offset. + * This corresponds to the number of cleared bits in the U2FAREA. + */ +static uint32_t storage_u2f_offset; + static bool sessionSeedCached, sessionSeedUsesPassphrase; static uint8_t sessionSeed[64]; @@ -132,6 +147,15 @@ bool storage_from_flash(void) storage.has_pin_failed_attempts = false; storage.pin_failed_attempts = 0; } + uint32_t *u2fptr = (uint32_t*) FLASH_STORAGE_U2FAREA; + while (*u2fptr == 0) + u2fptr++; + storage_u2f_offset = 32 * (u2fptr - (uint32_t*) FLASH_STORAGE_U2FAREA); + uint32_t u2fword = *u2fptr; + while ((u2fword & 1) == 0) { + storage_u2f_offset++; + u2fword >>= 1; + } // upgrade storage version if (version != STORAGE_VERSION) { storage.version = STORAGE_VERSION; @@ -185,15 +209,13 @@ static uint32_t storage_flash_words(uint32_t addr, uint32_t *src, int nwords) { return addr; } -void storage_commit(void) +static void storage_commit_locked(void) { uint8_t meta_backup[FLASH_META_DESC_LEN]; // backup meta memcpy(meta_backup, (uint8_t*)FLASH_META_START, FLASH_META_DESC_LEN); - flash_clear_status_flags(); - flash_unlock(); // erase storage flash_erase_sector(FLASH_META_SECTOR_FIRST, FLASH_CR_PROGRAM_X32); // copy meta @@ -209,6 +231,13 @@ void storage_commit(void) flash_program_word(flash, 0); flash += 4; } +} + +void storage_commit(void) +{ + flash_clear_status_flags(); + flash_unlock(); + storage_commit_locked(); flash_lock(); storage_check_flash_errors(); } @@ -429,23 +458,38 @@ bool session_isPinCached(void) return sessionPinCached; } -void storage_clearPinArea() +void storage_clearPinArea(void) { flash_clear_status_flags(); flash_unlock(); flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32); flash_lock(); storage_check_flash_errors(); + storage_u2f_offset = 0; +} + +// called when u2f area or pin area overflows +static void storage_area_recycle(uint32_t new_pinfails) +{ + // erase storage sector + flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32); + flash_program_word(FLASH_STORAGE_PINAREA, new_pinfails); + if (storage_u2f_offset > 0) { + storage.has_u2f_counter = true; + storage.u2f_counter += storage_u2f_offset; + storage_commit_locked(); + storage_u2f_offset = 0; + } } void storage_resetPinFails(uint32_t *pinfailsptr) { flash_clear_status_flags(); flash_unlock(); - if ((uint32_t) (pinfailsptr + 1) - FLASH_STORAGE_PINAREA - >= FLASH_STORAGE_PINAREA_LEN) { - // erase extra storage sector - flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32); + if ((uint32_t) (pinfailsptr + 1) + >= FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN) { + // recycle extra storage sector + storage_area_recycle(0xffffffff); } else { flash_program_word((uint32_t) pinfailsptr, 0); } @@ -483,14 +527,24 @@ bool storage_isInitialized(void) return storage.has_node || storage.has_mnemonic; } +uint32_t storage_getU2FCounter(void) +{ + return storage.u2f_counter + storage_u2f_offset; +} + uint32_t storage_nextU2FCounter(void) { - if(!storage.has_u2f_counter) { - storage.has_u2f_counter = true; - storage.u2f_counter = 1; - } else { - storage.u2f_counter++; + uint32_t *ptr = ((uint32_t *) FLASH_STORAGE_U2FAREA) + (storage_u2f_offset / 32); + uint32_t newval = 0xfffffffe << (storage_u2f_offset & 31); + + flash_clear_status_flags(); + flash_unlock(); + flash_program_word((uint32_t) ptr, newval); + storage_u2f_offset++; + if (storage_u2f_offset >= 8 * FLASH_STORAGE_U2FAREA_LEN) { + storage_area_recycle(*storage_getPinFailsPtr()); } - storage_commit(); - return storage.u2f_counter; + flash_lock(); + storage_check_flash_errors(); + return storage.u2f_counter + storage_u2f_offset; } From 18d549c83d6bea8fe9ab52ee2482755fc0ddb15e Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 26 May 2016 20:02:54 +0200 Subject: [PATCH 22/31] Fix U2F hid interface index for debug link --- firmware/usb.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/firmware/usb.c b/firmware/usb.c index 0264491b21..ad3bcb4ff1 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -28,6 +28,12 @@ #include "storage.h" #include "util.h" +#if DEBUG_LINK +#define USB_INTERFACE_INDEX_U2F 2 +#else +#define USB_INTERFACE_INDEX_U2F 1 +#endif + #define ENDPOINT_ADDRESS_IN (0x81) #define ENDPOINT_ADDRESS_OUT (0x01) #define ENDPOINT_ADDRESS_DEBUG_IN (0x82) @@ -181,11 +187,7 @@ static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = {{ static const struct usb_interface_descriptor hid_iface_u2f[] = {{ .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, -#if DEBUG_LINK - .bInterfaceNumber = 2, -#else - .bInterfaceNumber = 1, -#endif + .bInterfaceNumber = USB_INTERFACE_INDEX_U2F, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_HID, @@ -275,18 +277,18 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin (req->wValue != 0x2200)) return 0; - if (req->wIndex==1) { - debugLog(0, "", "hid_control_request u2f"); - *buf = (uint8_t *)hid_report_descriptor_u2f; - *len = sizeof(hid_report_descriptor_u2f); - return 1; - } + if (req->wIndex == USB_INTERFACE_INDEX_U2F) { + debugLog(0, "", "hid_control_request u2f"); + *buf = (uint8_t *)hid_report_descriptor_u2f; + *len = sizeof(hid_report_descriptor_u2f); + return 1; + } - debugLog(0, "", "hid_control_request trezor"); - /* Handle the HID report descriptor. */ - *buf = (uint8_t *)hid_report_descriptor; - *len = sizeof(hid_report_descriptor); - return 1; + debugLog(0, "", "hid_control_request trezor"); + /* Handle the HID report descriptor. */ + *buf = (uint8_t *)hid_report_descriptor; + *len = sizeof(hid_report_descriptor); + return 1; } static volatile char tiny = 0; From be0858b7d724a5efa3f57c2e4c097791e04e2951 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 26 May 2016 20:27:40 +0200 Subject: [PATCH 23/31] Updated protobuf --- firmware/protob/messages.options | 3 + firmware/protob/messages.pb.c | 61 +++++++++++++- firmware/protob/messages.pb.h | 136 ++++++++++++++++++++++++++++++- 3 files changed, 198 insertions(+), 2 deletions(-) diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options index c707aac054..34ed1e21d0 100644 --- a/firmware/protob/messages.options +++ b/firmware/protob/messages.options @@ -116,3 +116,6 @@ DebugLinkState.recovery_fake_word max_size:12 DebugLinkLog.bucket max_size:33 DebugLinkLog.text max_size:256 + +DebugLinkMemory.memory max_size:1024 +DebugLinkMemoryWrite.memory max_size:1024 diff --git a/firmware/protob/messages.pb.c b/firmware/protob/messages.pb.c index c1420d6048..a527f31224 100644 --- a/firmware/protob/messages.pb.c +++ b/firmware/protob/messages.pb.c @@ -149,11 +149,22 @@ const pb_field_t GetAddress_fields[5] = { PB_LAST_FIELD }; +const pb_field_t EthereumGetAddress_fields[3] = { + PB_FIELD2( 1, UINT32 , REPEATED, CALLBACK, FIRST, EthereumGetAddress, address_n, address_n, 0), + PB_FIELD2( 2, BOOL , OPTIONAL, STATIC , OTHER, EthereumGetAddress, show_display, address_n, 0), + PB_LAST_FIELD +}; + const pb_field_t Address_fields[2] = { PB_FIELD2( 1, STRING , REQUIRED, STATIC , FIRST, Address, address, address, 0), PB_LAST_FIELD }; +const pb_field_t EthereumAddress_fields[2] = { + PB_FIELD2( 1, BYTES , REQUIRED, CALLBACK, FIRST, EthereumAddress, address, address, 0), + PB_LAST_FIELD +}; + const pb_field_t WipeDevice_fields[1] = { PB_LAST_FIELD }; @@ -317,6 +328,31 @@ const pb_field_t TxAck_fields[2] = { PB_LAST_FIELD }; +const pb_field_t EthereumSignTx_fields[9] = { + PB_FIELD2( 1, UINT32 , REPEATED, CALLBACK, FIRST, EthereumSignTx, address_n, address_n, 0), + PB_FIELD2( 2, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumSignTx, nonce, address_n, 0), + PB_FIELD2( 3, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumSignTx, gas_price, nonce, 0), + PB_FIELD2( 4, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumSignTx, gas_limit, gas_price, 0), + PB_FIELD2( 5, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumSignTx, to, gas_limit, 0), + PB_FIELD2( 6, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumSignTx, value, to, 0), + PB_FIELD2( 7, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumSignTx, data_initial_chunk, value, 0), + PB_FIELD2( 8, UINT32 , OPTIONAL, STATIC , OTHER, EthereumSignTx, data_length, data_initial_chunk, 0), + PB_LAST_FIELD +}; + +const pb_field_t EthereumTxRequest_fields[5] = { + PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, EthereumTxRequest, data_length, data_length, 0), + PB_FIELD2( 2, UINT32 , OPTIONAL, STATIC , OTHER, EthereumTxRequest, signature_v, data_length, 0), + PB_FIELD2( 3, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumTxRequest, signature_r, signature_v, 0), + PB_FIELD2( 4, BYTES , OPTIONAL, CALLBACK, OTHER, EthereumTxRequest, signature_s, signature_r, 0), + PB_LAST_FIELD +}; + +const pb_field_t EthereumTxAck_fields[2] = { + PB_FIELD2( 1, BYTES , OPTIONAL, CALLBACK, FIRST, EthereumTxAck, data_chunk, data_chunk, 0), + PB_LAST_FIELD +}; + const pb_field_t SignIdentity_fields[5] = { PB_FIELD2( 1, MESSAGE , OPTIONAL, STATIC , FIRST, SignIdentity, identity, identity, &IdentityType_fields), PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, SignIdentity, challenge_hidden, identity, 0), @@ -375,6 +411,29 @@ const pb_field_t DebugLinkLog_fields[4] = { PB_LAST_FIELD }; +const pb_field_t DebugLinkMemoryRead_fields[3] = { + PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, DebugLinkMemoryRead, address, address, 0), + PB_FIELD2( 2, UINT32 , OPTIONAL, STATIC , OTHER, DebugLinkMemoryRead, length, address, 0), + PB_LAST_FIELD +}; + +const pb_field_t DebugLinkMemory_fields[2] = { + PB_FIELD2( 1, BYTES , OPTIONAL, STATIC , FIRST, DebugLinkMemory, memory, memory, 0), + PB_LAST_FIELD +}; + +const pb_field_t DebugLinkMemoryWrite_fields[4] = { + PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, DebugLinkMemoryWrite, address, address, 0), + PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, DebugLinkMemoryWrite, memory, address, 0), + PB_FIELD2( 3, BOOL , OPTIONAL, STATIC , OTHER, DebugLinkMemoryWrite, flash, memory, 0), + PB_LAST_FIELD +}; + +const pb_field_t DebugLinkFlashErase_fields[2] = { + PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, DebugLinkFlashErase, sector, sector, 0), + PB_LAST_FIELD +}; + /* Check that field information fits in pb_field_t */ #if !defined(PB_FIELD_32BIT) @@ -385,7 +444,7 @@ const pb_field_t DebugLinkLog_fields[4] = { * numbers or field sizes that are larger than what can fit in 8 or 16 bit * field descriptors. */ -STATIC_ASSERT((pb_membersize(Features, coins[0]) < 65536 && pb_membersize(PublicKey, node) < 65536 && pb_membersize(GetAddress, multisig) < 65536 && pb_membersize(LoadDevice, node) < 65536 && pb_membersize(SimpleSignTx, inputs[0]) < 65536 && pb_membersize(SimpleSignTx, outputs[0]) < 65536 && pb_membersize(SimpleSignTx, transactions[0]) < 65536 && pb_membersize(TxRequest, details) < 65536 && pb_membersize(TxRequest, serialized) < 65536 && pb_membersize(TxAck, tx) < 65536 && pb_membersize(SignIdentity, identity) < 65536 && pb_membersize(DebugLinkState, node) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_Initialize_GetFeatures_Features_ClearSession_ApplySettings_ChangePin_Ping_Success_Failure_ButtonRequest_ButtonAck_PinMatrixRequest_PinMatrixAck_Cancel_PassphraseRequest_PassphraseAck_GetEntropy_Entropy_GetPublicKey_PublicKey_GetAddress_Address_WipeDevice_LoadDevice_ResetDevice_EntropyRequest_EntropyAck_RecoveryDevice_WordRequest_WordAck_SignMessage_VerifyMessage_MessageSignature_EncryptMessage_EncryptedMessage_DecryptMessage_DecryptedMessage_CipherKeyValue_CipheredKeyValue_EstimateTxSize_TxSize_SignTx_SimpleSignTx_TxRequest_TxAck_SignIdentity_SignedIdentity_FirmwareErase_FirmwareUpload_DebugLinkDecision_DebugLinkGetState_DebugLinkState_DebugLinkStop_DebugLinkLog) +STATIC_ASSERT((pb_membersize(Features, coins[0]) < 65536 && pb_membersize(PublicKey, node) < 65536 && pb_membersize(GetAddress, multisig) < 65536 && pb_membersize(LoadDevice, node) < 65536 && pb_membersize(SimpleSignTx, inputs[0]) < 65536 && pb_membersize(SimpleSignTx, outputs[0]) < 65536 && pb_membersize(SimpleSignTx, transactions[0]) < 65536 && pb_membersize(TxRequest, details) < 65536 && pb_membersize(TxRequest, serialized) < 65536 && pb_membersize(TxAck, tx) < 65536 && pb_membersize(SignIdentity, identity) < 65536 && pb_membersize(DebugLinkState, node) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_Initialize_GetFeatures_Features_ClearSession_ApplySettings_ChangePin_Ping_Success_Failure_ButtonRequest_ButtonAck_PinMatrixRequest_PinMatrixAck_Cancel_PassphraseRequest_PassphraseAck_GetEntropy_Entropy_GetPublicKey_PublicKey_GetAddress_EthereumGetAddress_Address_EthereumAddress_WipeDevice_LoadDevice_ResetDevice_EntropyRequest_EntropyAck_RecoveryDevice_WordRequest_WordAck_SignMessage_VerifyMessage_MessageSignature_EncryptMessage_EncryptedMessage_DecryptMessage_DecryptedMessage_CipherKeyValue_CipheredKeyValue_EstimateTxSize_TxSize_SignTx_SimpleSignTx_TxRequest_TxAck_EthereumSignTx_EthereumTxRequest_EthereumTxAck_SignIdentity_SignedIdentity_FirmwareErase_FirmwareUpload_DebugLinkDecision_DebugLinkGetState_DebugLinkState_DebugLinkStop_DebugLinkLog_DebugLinkMemoryRead_DebugLinkMemory_DebugLinkMemoryWrite_DebugLinkFlashErase) #endif #if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) diff --git a/firmware/protob/messages.pb.h b/firmware/protob/messages.pb.h index f4cd33ff1a..e3c8485e05 100644 --- a/firmware/protob/messages.pb.h +++ b/firmware/protob/messages.pb.h @@ -61,11 +61,20 @@ typedef enum _MessageType { MessageType_MessageType_SignIdentity = 53, MessageType_MessageType_SignedIdentity = 54, MessageType_MessageType_GetFeatures = 55, + MessageType_MessageType_EthereumGetAddress = 56, + MessageType_MessageType_EthereumAddress = 57, + MessageType_MessageType_EthereumSignTx = 58, + MessageType_MessageType_EthereumTxRequest = 59, + MessageType_MessageType_EthereumTxAck = 60, MessageType_MessageType_DebugLinkDecision = 100, MessageType_MessageType_DebugLinkGetState = 101, MessageType_MessageType_DebugLinkState = 102, MessageType_MessageType_DebugLinkStop = 103, - MessageType_MessageType_DebugLinkLog = 104 + MessageType_MessageType_DebugLinkLog = 104, + MessageType_MessageType_DebugLinkMemoryRead = 110, + MessageType_MessageType_DebugLinkMemory = 111, + MessageType_MessageType_DebugLinkMemoryWrite = 112, + MessageType_MessageType_DebugLinkFlashErase = 113 } MessageType; /* Struct definitions */ @@ -190,6 +199,11 @@ typedef struct _DebugLinkDecision { bool yes_no; } DebugLinkDecision; +typedef struct _DebugLinkFlashErase { + bool has_sector; + uint32_t sector; +} DebugLinkFlashErase; + typedef struct _DebugLinkLog { bool has_level; uint32_t level; @@ -199,6 +213,37 @@ typedef struct _DebugLinkLog { char text[256]; } DebugLinkLog; +typedef struct { + size_t size; + uint8_t bytes[1024]; +} DebugLinkMemory_memory_t; + +typedef struct _DebugLinkMemory { + bool has_memory; + DebugLinkMemory_memory_t memory; +} DebugLinkMemory; + +typedef struct _DebugLinkMemoryRead { + bool has_address; + uint32_t address; + bool has_length; + uint32_t length; +} DebugLinkMemoryRead; + +typedef struct { + size_t size; + uint8_t bytes[1024]; +} DebugLinkMemoryWrite_memory_t; + +typedef struct _DebugLinkMemoryWrite { + bool has_address; + uint32_t address; + bool has_memory; + DebugLinkMemoryWrite_memory_t memory; + bool has_flash; + bool flash; +} DebugLinkMemoryWrite; + typedef struct { size_t size; uint8_t bytes[1024]; @@ -343,6 +388,41 @@ typedef struct _EstimateTxSize { char coin_name[17]; } EstimateTxSize; +typedef struct _EthereumAddress { + pb_callback_t address; +} EthereumAddress; + +typedef struct _EthereumGetAddress { + pb_callback_t address_n; + bool has_show_display; + bool show_display; +} EthereumGetAddress; + +typedef struct _EthereumSignTx { + pb_callback_t address_n; + pb_callback_t nonce; + pb_callback_t gas_price; + pb_callback_t gas_limit; + pb_callback_t to; + pb_callback_t value; + pb_callback_t data_initial_chunk; + bool has_data_length; + uint32_t data_length; +} EthereumSignTx; + +typedef struct _EthereumTxAck { + pb_callback_t data_chunk; +} EthereumTxAck; + +typedef struct _EthereumTxRequest { + bool has_data_length; + uint32_t data_length; + bool has_signature_v; + uint32_t signature_v; + pb_callback_t signature_r; + pb_callback_t signature_s; +} EthereumTxRequest; + typedef struct _Failure { bool has_code; FailureType code; @@ -681,7 +761,9 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define GetPublicKey_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0} #define PublicKey_init_default {HDNodeType_init_default, false, ""} #define GetAddress_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "Bitcoin", false, 0, false, MultisigRedeemScriptType_init_default} +#define EthereumGetAddress_init_default {{{NULL}, NULL}, false, 0} #define Address_init_default {""} +#define EthereumAddress_init_default {{{NULL}, NULL}} #define WipeDevice_init_default {0} #define LoadDevice_init_default {false, "", false, HDNodeType_init_default, false, "", false, 0, false, "english", false, "", false, 0} #define ResetDevice_init_default {false, 0, false, 256u, false, 0, false, 0, false, "english", false, ""} @@ -705,6 +787,9 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define SimpleSignTx_init_default {0, {}, 0, {}, 0, {}, false, "Bitcoin", false, 1u, false, 0u} #define TxRequest_init_default {false, (RequestType)0, false, TxRequestDetailsType_init_default, false, TxRequestSerializedType_init_default} #define TxAck_init_default {false, TransactionType_init_default} +#define EthereumSignTx_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, 0} +#define EthereumTxRequest_init_default {false, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}} +#define EthereumTxAck_init_default {{{NULL}, NULL}} #define SignIdentity_init_default {false, IdentityType_init_default, false, {0, {0}}, false, "", false, ""} #define SignedIdentity_init_default {false, "", false, {0, {0}}, false, {0, {0}}} #define FirmwareErase_init_default {0} @@ -714,6 +799,10 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define DebugLinkState_init_default {false, {0, {0}}, false, "", false, "", false, "", false, HDNodeType_init_default, false, 0, false, "", false, {0, {0}}, false, "", false, 0} #define DebugLinkStop_init_default {0} #define DebugLinkLog_init_default {false, 0, false, "", false, ""} +#define DebugLinkMemoryRead_init_default {false, 0, false, 0} +#define DebugLinkMemory_init_default {false, {0, {0}}} +#define DebugLinkMemoryWrite_init_default {false, 0, false, {0, {0}}, false, 0} +#define DebugLinkFlashErase_init_default {false, 0} #define Initialize_init_zero {0} #define GetFeatures_init_zero {0} #define Features_init_zero {false, "", false, 0, false, 0, false, 0, false, 0, false, "", false, 0, false, 0, false, "", false, "", 0, {CoinType_init_zero, CoinType_init_zero, CoinType_init_zero, CoinType_init_zero, CoinType_init_zero, CoinType_init_zero}, false, 0, false, {0, {0}}, false, {0, {0}}, false, 0, false, 0, false, 0} @@ -735,7 +824,9 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define GetPublicKey_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0} #define PublicKey_init_zero {HDNodeType_init_zero, false, ""} #define GetAddress_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0, false, MultisigRedeemScriptType_init_zero} +#define EthereumGetAddress_init_zero {{{NULL}, NULL}, false, 0} #define Address_init_zero {""} +#define EthereumAddress_init_zero {{{NULL}, NULL}} #define WipeDevice_init_zero {0} #define LoadDevice_init_zero {false, "", false, HDNodeType_init_zero, false, "", false, 0, false, "", false, "", false, 0} #define ResetDevice_init_zero {false, 0, false, 0, false, 0, false, 0, false, "", false, ""} @@ -759,6 +850,9 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define SimpleSignTx_init_zero {0, {}, 0, {}, 0, {}, false, "", false, 0, false, 0} #define TxRequest_init_zero {false, (RequestType)0, false, TxRequestDetailsType_init_zero, false, TxRequestSerializedType_init_zero} #define TxAck_init_zero {false, TransactionType_init_zero} +#define EthereumSignTx_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, 0} +#define EthereumTxRequest_init_zero {false, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}} +#define EthereumTxAck_init_zero {{{NULL}, NULL}} #define SignIdentity_init_zero {false, IdentityType_init_zero, false, {0, {0}}, false, "", false, ""} #define SignedIdentity_init_zero {false, "", false, {0, {0}}, false, {0, {0}}} #define FirmwareErase_init_zero {0} @@ -768,6 +862,10 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define DebugLinkState_init_zero {false, {0, {0}}, false, "", false, "", false, "", false, HDNodeType_init_zero, false, 0, false, "", false, {0, {0}}, false, "", false, 0} #define DebugLinkStop_init_zero {0} #define DebugLinkLog_init_zero {false, 0, false, "", false, ""} +#define DebugLinkMemoryRead_init_zero {false, 0, false, 0} +#define DebugLinkMemory_init_zero {false, {0, {0}}} +#define DebugLinkMemoryWrite_init_zero {false, 0, false, {0, {0}}, false, 0} +#define DebugLinkFlashErase_init_zero {false, 0} /* Field tags (for use in manual encoding/decoding) */ #define Address_address_tag 1 @@ -787,9 +885,16 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define CipherKeyValue_iv_tag 7 #define CipheredKeyValue_value_tag 1 #define DebugLinkDecision_yes_no_tag 1 +#define DebugLinkFlashErase_sector_tag 1 #define DebugLinkLog_level_tag 1 #define DebugLinkLog_bucket_tag 2 #define DebugLinkLog_text_tag 3 +#define DebugLinkMemory_memory_tag 1 +#define DebugLinkMemoryRead_address_tag 1 +#define DebugLinkMemoryRead_length_tag 2 +#define DebugLinkMemoryWrite_address_tag 1 +#define DebugLinkMemoryWrite_memory_tag 2 +#define DebugLinkMemoryWrite_flash_tag 3 #define DebugLinkState_layout_tag 1 #define DebugLinkState_pin_tag 2 #define DebugLinkState_matrix_tag 3 @@ -819,6 +924,22 @@ extern const uint32_t SimpleSignTx_lock_time_default; #define EstimateTxSize_outputs_count_tag 1 #define EstimateTxSize_inputs_count_tag 2 #define EstimateTxSize_coin_name_tag 3 +#define EthereumAddress_address_tag 1 +#define EthereumGetAddress_address_n_tag 1 +#define EthereumGetAddress_show_display_tag 2 +#define EthereumSignTx_address_n_tag 1 +#define EthereumSignTx_nonce_tag 2 +#define EthereumSignTx_gas_price_tag 3 +#define EthereumSignTx_gas_limit_tag 4 +#define EthereumSignTx_to_tag 5 +#define EthereumSignTx_value_tag 6 +#define EthereumSignTx_data_initial_chunk_tag 7 +#define EthereumSignTx_data_length_tag 8 +#define EthereumTxAck_data_chunk_tag 1 +#define EthereumTxRequest_data_length_tag 1 +#define EthereumTxRequest_signature_v_tag 2 +#define EthereumTxRequest_signature_r_tag 3 +#define EthereumTxRequest_signature_s_tag 4 #define Failure_code_tag 1 #define Failure_message_tag 2 #define Features_vendor_tag 1 @@ -932,7 +1053,9 @@ extern const pb_field_t Entropy_fields[2]; extern const pb_field_t GetPublicKey_fields[4]; extern const pb_field_t PublicKey_fields[3]; extern const pb_field_t GetAddress_fields[5]; +extern const pb_field_t EthereumGetAddress_fields[3]; extern const pb_field_t Address_fields[2]; +extern const pb_field_t EthereumAddress_fields[2]; extern const pb_field_t WipeDevice_fields[1]; extern const pb_field_t LoadDevice_fields[8]; extern const pb_field_t ResetDevice_fields[7]; @@ -956,6 +1079,9 @@ extern const pb_field_t SignTx_fields[6]; extern const pb_field_t SimpleSignTx_fields[7]; extern const pb_field_t TxRequest_fields[4]; extern const pb_field_t TxAck_fields[2]; +extern const pb_field_t EthereumSignTx_fields[9]; +extern const pb_field_t EthereumTxRequest_fields[5]; +extern const pb_field_t EthereumTxAck_fields[2]; extern const pb_field_t SignIdentity_fields[5]; extern const pb_field_t SignedIdentity_fields[4]; extern const pb_field_t FirmwareErase_fields[1]; @@ -965,6 +1091,10 @@ extern const pb_field_t DebugLinkGetState_fields[1]; extern const pb_field_t DebugLinkState_fields[11]; extern const pb_field_t DebugLinkStop_fields[1]; extern const pb_field_t DebugLinkLog_fields[4]; +extern const pb_field_t DebugLinkMemoryRead_fields[3]; +extern const pb_field_t DebugLinkMemory_fields[2]; +extern const pb_field_t DebugLinkMemoryWrite_fields[4]; +extern const pb_field_t DebugLinkFlashErase_fields[2]; /* Maximum encoded size of messages (where known) */ #define Initialize_size 0 @@ -1021,6 +1151,10 @@ extern const pb_field_t DebugLinkLog_fields[4]; #define DebugLinkState_size (1468 + HDNodeType_size) #define DebugLinkStop_size 0 #define DebugLinkLog_size 300 +#define DebugLinkMemoryRead_size 12 +#define DebugLinkMemory_size 1027 +#define DebugLinkMemoryWrite_size 1035 +#define DebugLinkFlashErase_size 6 #ifdef __cplusplus } /* extern "C" */ From 78d11cf06029360e49d4663ccf2c019655f5af74 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 26 May 2016 20:28:11 +0200 Subject: [PATCH 24/31] New memory access over debug link --- firmware/fsm.c | 38 ++++++++++++++++++++++++++++++++++++++ firmware/fsm.h | 3 +++ firmware/messages.c | 4 ++++ 3 files changed, 45 insertions(+) diff --git a/firmware/fsm.c b/firmware/fsm.c index 7d9b72d440..0b08e1d18b 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -46,6 +46,7 @@ #include "ripemd160.h" #include "curves.h" #include "secp256k1.h" +#include // message methods @@ -975,4 +976,41 @@ void fsm_msgDebugLinkStop(DebugLinkStop *msg) (void)msg; } +void fsm_msgDebugLinkMemoryRead(DebugLinkMemoryRead *msg) +{ + RESP_INIT(DebugLinkMemory); + + uint32_t length = 1024; + if (msg->has_length && msg->length < length) + length = msg->length; + resp->has_memory = true; + memcpy(resp->memory.bytes, (void*) msg->address, length); + resp->memory.size = length; + msg_debug_write(MessageType_MessageType_DebugLinkMemory, resp); +} + +void fsm_msgDebugLinkMemoryWrite(DebugLinkMemoryWrite *msg) +{ + uint32_t length = msg->memory.size; + if (msg->flash) { + flash_clear_status_flags(); + flash_unlock(); + uint32_t* src = (uint32_t *) msg->memory.bytes; + for (unsigned int i = 0; i < length; i += 4) { + flash_program_word(msg->address + i, *src); + src++; + } + flash_lock(); + } else { + memcpy((void *) msg->address, msg->memory.bytes, length); + } +} + +void fsm_msgDebugLinkFlashErase(DebugLinkFlashErase *msg) +{ + flash_clear_status_flags(); + flash_unlock(); + flash_erase_sector(msg->sector, FLASH_CR_PROGRAM_X32); + flash_lock(); +} #endif diff --git a/firmware/fsm.h b/firmware/fsm.h index 1a3fcf8f1e..5b6eef971e 100644 --- a/firmware/fsm.h +++ b/firmware/fsm.h @@ -63,6 +63,9 @@ void fsm_msgWordAck(WordAck *msg); //void fsm_msgDebugLinkDecision(DebugLinkDecision *msg); void fsm_msgDebugLinkGetState(DebugLinkGetState *msg); void fsm_msgDebugLinkStop(DebugLinkStop *msg); +void fsm_msgDebugLinkMemoryWrite(DebugLinkMemoryWrite *msg); +void fsm_msgDebugLinkMemoryRead(DebugLinkMemoryRead *msg); +void fsm_msgDebugLinkFlashErase(DebugLinkFlashErase *msg); #endif #endif diff --git a/firmware/messages.c b/firmware/messages.c index 1994f61a78..57c311151d 100644 --- a/firmware/messages.c +++ b/firmware/messages.c @@ -97,9 +97,13 @@ static const struct MessagesMap_t MessagesMap[] = { // {'d', 'i', MessageType_MessageType_DebugLinkDecision, DebugLinkDecision_fields, (void (*)(void *))fsm_msgDebugLinkDecision}, {'d', 'i', MessageType_MessageType_DebugLinkGetState, DebugLinkGetState_fields, (void (*)(void *))fsm_msgDebugLinkGetState}, {'d', 'i', MessageType_MessageType_DebugLinkStop, DebugLinkStop_fields, (void (*)(void *))fsm_msgDebugLinkStop}, + {'d', 'i', MessageType_MessageType_DebugLinkMemoryRead, DebugLinkMemoryRead_fields, (void (*)(void *))fsm_msgDebugLinkMemoryRead}, + {'d', 'i', MessageType_MessageType_DebugLinkMemoryWrite, DebugLinkMemoryWrite_fields, (void (*)(void *))fsm_msgDebugLinkMemoryWrite}, + {'d', 'i', MessageType_MessageType_DebugLinkFlashErase, DebugLinkFlashErase_fields, (void (*)(void *))fsm_msgDebugLinkFlashErase}, // debug out messages {'d', 'o', MessageType_MessageType_DebugLinkState, DebugLinkState_fields, 0}, {'d', 'o', MessageType_MessageType_DebugLinkLog, DebugLinkLog_fields, 0}, + {'d', 'o', MessageType_MessageType_DebugLinkMemory, DebugLinkMemory_fields, 0}, #endif // end {0, 0, 0, 0, 0} From a366700332802108e814825a6d189069fc01bcd1 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 26 May 2016 21:21:08 +0200 Subject: [PATCH 25/31] fix indentation --- firmware/u2f.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index 5ce8b75be7..c910ae5a48 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -477,31 +477,31 @@ const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) { - uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; + uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; - // Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' - uint32_t i, key_path[KEY_PATH_ENTRIES]; - key_path[0] = U2F_KEY_PATH; - for (i = 1; i < KEY_PATH_ENTRIES; i++) { - // high bit for hardened keys - key_path[i]= 0x80000000 | random32(); - } + // Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' + uint32_t i, key_path[KEY_PATH_ENTRIES]; + key_path[0] = U2F_KEY_PATH; + for (i = 1; i < KEY_PATH_ENTRIES; i++) { + // high bit for hardened keys + key_path[i]= 0x80000000 | random32(); + } - // First half of keyhandle is key_path - memcpy(key_handle, &key_path[1], KEY_PATH_LEN); + // First half of keyhandle is key_path + memcpy(key_handle, &key_path[1], KEY_PATH_LEN); - // prepare keypair from /random data - const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + // prepare keypair from /random data + const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); - // For second half of keyhandle - // Signature of app_id and random data - memcpy(&keybase[0], app_id, U2F_APPID_SIZE); - memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); - hmac_sha256(node->private_key, sizeof(node->private_key), + // For second half of keyhandle + // Signature of app_id and random data + memcpy(&keybase[0], app_id, U2F_APPID_SIZE); + memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); + hmac_sha256(node->private_key, sizeof(node->private_key), keybase, sizeof(keybase), &key_handle[KEY_PATH_LEN]); - // Done! - return node; + // Done! + return node; } From c4e8bd0d0ef0e9fbd0e46566c34236bc3bde0b1f Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 27 May 2016 14:03:20 +0200 Subject: [PATCH 26/31] More robust storage recycle sector Clear storage marker first before clearing the second sector to prevent leaving a state where only PIN failures have been cleared but storage is still present. --- firmware/storage.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/firmware/storage.c b/firmware/storage.c index 32e269b8be..e7bb56182d 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -98,12 +98,17 @@ static char sessionPassphrase[51]; #define STORAGE_VERSION 6 +void storage_show_error(void) +{ + layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Storage failure", "detected.", NULL, "Please unplug", "the device.", NULL); + for (;;) { } +} + void storage_check_flash_errors(void) { // flash operation failed if (FLASH_SR & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Storage failure", "detected.", NULL, "Please unplug", "the device.", NULL); - for (;;) { } + storage_show_error(); } } @@ -471,15 +476,26 @@ void storage_clearPinArea(void) // called when u2f area or pin area overflows static void storage_area_recycle(uint32_t new_pinfails) { + // first clear storage marker. In case of a failure below it is better + // to clear the storage than to allow restarting with zero PIN failures + flash_program_word(FLASH_STORAGE_START, 0); + if (*(uint32_t *)FLASH_STORAGE_START != 0) { + storage_show_error(); + } + // erase storage sector flash_erase_sector(FLASH_META_SECTOR_LAST, FLASH_CR_PROGRAM_X32); flash_program_word(FLASH_STORAGE_PINAREA, new_pinfails); + if (*(uint32_t *)FLASH_STORAGE_PINAREA != new_pinfails) { + storage_show_error(); + } + if (storage_u2f_offset > 0) { storage.has_u2f_counter = true; storage.u2f_counter += storage_u2f_offset; - storage_commit_locked(); storage_u2f_offset = 0; } + storage_commit_locked(); } void storage_resetPinFails(uint32_t *pinfailsptr) @@ -527,11 +543,6 @@ bool storage_isInitialized(void) return storage.has_node || storage.has_mnemonic; } -uint32_t storage_getU2FCounter(void) -{ - return storage.u2f_counter + storage_u2f_offset; -} - uint32_t storage_nextU2FCounter(void) { uint32_t *ptr = ((uint32_t *) FLASH_STORAGE_U2FAREA) + (storage_u2f_offset / 32); From 2929bfbd48e8171c9444f194745746a1dcbc9ee3 Mon Sep 17 00:00:00 2001 From: Ondrej Sika Date: Mon, 6 Jun 2016 14:34:35 +0200 Subject: [PATCH 27/31] u2f - add Slush Pool to WellKnown --- firmware/u2f_knownapps.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h index b57bbcb024..4320910afc 100644 --- a/firmware/u2f_knownapps.h +++ b/firmware/u2f_knownapps.h @@ -28,7 +28,7 @@ typedef struct { const char *appname; } U2FWellKnown; -static const U2FWellKnown u2f_well_known[3] = { +static const U2FWellKnown u2f_well_known[4] = { { // https://www.gstatic.com/securitykey/origins.json { 0xa5,0x46,0x72,0xb2,0x22,0xc4,0xcf,0x95, @@ -52,6 +52,14 @@ static const U2FWellKnown u2f_well_known[3] = { 0xd9,0x1a,0x22,0xfe,0x6b,0x29,0xc0,0xcd, 0xf7,0x80,0x55,0x30,0x84,0x2a,0xf5,0x81 }, "Dropbox" + }, + { + // https://slushpool.com/static/security/u2f.json + { 0x08,0xb2,0xa3,0xd4,0x19,0x39,0xaa,0x31, + 0x66,0x84,0x93,0xcb,0x36,0xcd,0xcc,0x4f, + 0x16,0xc4,0xd9,0xb4,0xc8,0x23,0x8b,0x73, + 0xc2,0xf6,0x72,0xc0,0x33,0x00,0x71,0x97 }, + "Slush Pool" } }; From 9aaf0d37baf9f516fb25e7d239a408e0b0bc49cc Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 8 Jun 2016 17:21:16 +0200 Subject: [PATCH 28/31] add u2f icons --- firmware/u2f_knownapps.h | 54 +++++++++++++++++++--------------- gen/bitmaps.c | 8 +++++ gen/bitmaps.h | 4 +++ gen/bitmaps/u2f_dropbox.png | Bin 0 -> 263 bytes gen/bitmaps/u2f_github.png | Bin 0 -> 236 bytes gen/bitmaps/u2f_google.png | Bin 0 -> 240 bytes gen/bitmaps/u2f_slushpool.png | Bin 0 -> 214 bytes 7 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 gen/bitmaps/u2f_dropbox.png create mode 100644 gen/bitmaps/u2f_github.png create mode 100644 gen/bitmaps/u2f_google.png create mode 100644 gen/bitmaps/u2f_slushpool.png diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h index 4320910afc..32386c73b1 100644 --- a/firmware/u2f_knownapps.h +++ b/firmware/u2f_knownapps.h @@ -22,45 +22,51 @@ #include #include "u2f/u2f.h" +#include "bitmaps.h" typedef struct { - uint8_t appid[U2F_APPID_SIZE]; + const uint8_t appid[U2F_APPID_SIZE]; const char *appname; + const BITMAP *icon; } U2FWellKnown; static const U2FWellKnown u2f_well_known[4] = { - { - // https://www.gstatic.com/securitykey/origins.json - { 0xa5,0x46,0x72,0xb2,0x22,0xc4,0xcf,0x95, + { + // https://www.gstatic.com/securitykey/origins.json + { 0xa5,0x46,0x72,0xb2,0x22,0xc4,0xcf,0x95, 0xe1,0x51,0xed,0x8d,0x4d,0x3c,0x76,0x7a, 0x6c,0xc3,0x49,0x43,0x59,0x43,0x79,0x4e, 0x88,0x4f,0x3d,0x02,0x3a,0x82,0x29,0xfd }, - "Google" - }, - { - // https://github.com/u2f/trusted_facets - { 0x70,0x61,0x7d,0xfe,0xd0,0x65,0x86,0x3a, + "Google", + &bmp_u2f_google + }, + { + // https://github.com/u2f/trusted_facets + { 0x70,0x61,0x7d,0xfe,0xd0,0x65,0x86,0x3a, 0xf4,0x7c,0x15,0x55,0x6c,0x91,0x79,0x88, 0x80,0x82,0x8c,0xc4,0x07,0xfd,0xf7,0x0a, 0xe8,0x50,0x11,0x56,0x94,0x65,0xa0,0x75 }, - "Github" - }, - { - // https://www.dropbox.com/u2f-app-id.json - { 0xc5,0x0f,0x8a,0x7b,0x70,0x8e,0x92,0xf8, + "Github", + &bmp_u2f_github + }, + { + // https://www.dropbox.com/u2f-app-id.json + { 0xc5,0x0f,0x8a,0x7b,0x70,0x8e,0x92,0xf8, 0x2e,0x7a,0x50,0xe2,0xbd,0xc5,0x5d,0x8f, 0xd9,0x1a,0x22,0xfe,0x6b,0x29,0xc0,0xcd, 0xf7,0x80,0x55,0x30,0x84,0x2a,0xf5,0x81 }, - "Dropbox" - }, - { - // https://slushpool.com/static/security/u2f.json - { 0x08,0xb2,0xa3,0xd4,0x19,0x39,0xaa,0x31, - 0x66,0x84,0x93,0xcb,0x36,0xcd,0xcc,0x4f, - 0x16,0xc4,0xd9,0xb4,0xc8,0x23,0x8b,0x73, - 0xc2,0xf6,0x72,0xc0,0x33,0x00,0x71,0x97 }, - "Slush Pool" - } + "Dropbox", + &bmp_u2f_dropbox + }, + { + // https://slushpool.com/static/security/u2f.json + { 0x08,0xb2,0xa3,0xd4,0x19,0x39,0xaa,0x31, + 0x66,0x84,0x93,0xcb,0x36,0xcd,0xcc,0x4f, + 0x16,0xc4,0xd9,0xb4,0xc8,0x23,0x8b,0x73, + 0xc2,0xf6,0x72,0xc0,0x33,0x00,0x71,0x97 }, + "Slush Pool", + &bmp_u2f_slushpool + } }; #endif // U2F_KNOWNAPPS_INCLUDED diff --git a/gen/bitmaps.c b/gen/bitmaps.c index 64808987fa..264064dc8c 100644 --- a/gen/bitmaps.c +++ b/gen/bitmaps.c @@ -23,6 +23,10 @@ const uint8_t bmp_logo48_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x const uint8_t bmp_logo48_empty_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x81, 0x80, 0x00, 0x00, 0x06, 0x00, 0x60, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x10, 0x7e, 0x08, 0x00, 0x00, 0x21, 0x81, 0x84, 0x00, 0x00, 0x22, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x22, 0x00, 0x00, 0x48, 0x00, 0x12, 0x00, 0x00, 0x88, 0x00, 0x11, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x03, 0x9f, 0xff, 0xf9, 0xc0, 0x04, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x10, 0x08, 0x3f, 0xff, 0xfc, 0x10, 0x08, 0x40, 0x00, 0x02, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x60, 0x00, 0x06, 0x10, 0x08, 0x1c, 0x00, 0x38, 0x10, 0x08, 0x03, 0x81, 0xc0, 0x10, 0x07, 0x00, 0x7e, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0x07, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x00, 0x03, 0x81, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; const uint8_t bmp_logo64_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x3f, 0xc0, 0x07, 0xf8, 0x00, 0x00, 0x7f, 0x80, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0x80, 0x03, 0xff, 0xf0, 0x1f, 0xc0, 0x00, 0x00, 0x07, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xfc, 0x00, 0x00, 0x7f, 0xf0, 0x1f, 0xff, 0x00, 0x01, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x1f, 0xff, 0xfc, 0x7f, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; const uint8_t bmp_logo64_empty_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x08, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x0f, 0xe0, 0x10, 0x00, 0x00, 0x20, 0x30, 0x18, 0x08, 0x00, 0x00, 0x20, 0x40, 0x04, 0x08, 0x00, 0x00, 0x40, 0x80, 0x02, 0x04, 0x00, 0x00, 0x41, 0x00, 0x01, 0x04, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x87, 0xff, 0xff, 0xc2, 0x00, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x7f, 0xfc, 0x00, 0x10, 0x10, 0x3f, 0x80, 0x03, 0xf8, 0x10, 0x10, 0x40, 0x00, 0x00, 0x04, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x60, 0x00, 0x00, 0x0c, 0x10, 0x10, 0x1c, 0x00, 0x00, 0x70, 0x10, 0x10, 0x03, 0x00, 0x01, 0x80, 0x10, 0x10, 0x00, 0xf0, 0x1e, 0x00, 0x10, 0x10, 0x00, 0x0c, 0x60, 0x00, 0x10, 0x0e, 0x00, 0x03, 0x80, 0x00, 0xe0, 0x01, 0xc0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0e, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x01, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +const uint8_t bmp_u2f_dropbox_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x07, 0xf8, 0x1f, 0xe0, 0x0f, 0xfc, 0x3f, 0xf8, 0x3f, 0xfe, 0x7f, 0xfc, 0x7f, 0xfe, 0x7f, 0xff, 0x7f, 0xfc, 0x1f, 0xfe, 0x3f, 0xf0, 0x0f, 0xfc, 0x1f, 0xe0, 0x03, 0xf8, 0x07, 0x80, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x60, 0x07, 0x80, 0x01, 0xf0, 0x1f, 0xe0, 0x03, 0xf8, 0x3f, 0xf0, 0x0f, 0xfc, 0x7f, 0xfc, 0x3f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x1f, 0xfe, 0x7f, 0xfc, 0x0f, 0xfc, 0x3f, 0xf0, 0x03, 0xf9, 0x9f, 0xc0, 0x00, 0xe3, 0xc7, 0x80, 0x00, 0x47, 0xe2, 0x00, 0x03, 0x1f, 0xf8, 0x40, 0x03, 0xff, 0xfd, 0xc0, 0x01, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, }; +const uint8_t bmp_u2f_github_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x1f, 0x80, 0x01, 0xf8, 0x1f, 0xe0, 0x03, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x1f, 0xf8, 0x20, 0x04, 0x1f, 0xf0, 0x70, 0x0e, 0x0f, 0x70, 0xf0, 0x0f, 0x0e, 0x70, 0xf8, 0x1f, 0x0e, 0x70, 0x70, 0x0e, 0x0e, 0x30, 0x70, 0x0e, 0x0c, 0x38, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00, 0x70, 0x07, 0x80, 0x01, 0xe0, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +const uint8_t bmp_u2f_google_data[] = { 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 0x07, 0xf0, 0x0f, 0xe0, 0x0f, 0xc0, 0x03, 0xf0, 0x1f, 0x00, 0x01, 0xf8, 0x3e, 0x00, 0x00, 0xfc, 0x3c, 0x00, 0x01, 0xfc, 0x7c, 0x0f, 0xf3, 0xfe, 0x78, 0x1f, 0xff, 0xfe, 0x78, 0x3f, 0xff, 0xfe, 0xf0, 0x3f, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xf0, 0x7f, 0x00, 0x1f, 0xf0, 0x7f, 0x00, 0x1f, 0xf0, 0x7f, 0x00, 0x1f, 0xf0, 0x7f, 0x00, 0x1f, 0xf0, 0x7f, 0xfe, 0x1f, 0xf0, 0x3f, 0xfe, 0x1f, 0x78, 0x3f, 0xfc, 0x1e, 0x78, 0x1f, 0xf8, 0x3e, 0x7c, 0x07, 0xf0, 0x3e, 0x3c, 0x00, 0x00, 0x7c, 0x3e, 0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xf8, 0x0f, 0xc0, 0x03, 0xf0, 0x07, 0xf0, 0x0f, 0xe0, 0x03, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x0f, 0xf0, 0x00, }; +const uint8_t bmp_u2f_slushpool_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x01, 0xee, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xf8, 0x00, 0xfe, 0x0f, 0xf8, 0x00, 0xfe, 0x03, 0xf8, 0x00, 0xfc, 0x03, 0xf8, 0x00, 0xfc, 0x03, 0xf8, 0x01, 0xfc, 0x03, 0xf8, 0x01, 0xfc, 0x03, 0xf0, 0x01, 0xfc, 0x07, 0xf0, 0x01, 0xfc, 0x1f, 0xf0, 0x0f, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; const BITMAP bmp_digit0 = {16, 16, bmp_digit0_data}; const BITMAP bmp_digit1 = {16, 16, bmp_digit1_data}; @@ -47,3 +51,7 @@ const BITMAP bmp_logo48 = {40, 48, bmp_logo48_data}; const BITMAP bmp_logo48_empty = {40, 48, bmp_logo48_empty_data}; const BITMAP bmp_logo64 = {48, 64, bmp_logo64_data}; const BITMAP bmp_logo64_empty = {48, 64, bmp_logo64_empty_data}; +const BITMAP bmp_u2f_dropbox = {32, 32, bmp_u2f_dropbox_data}; +const BITMAP bmp_u2f_github = {32, 32, bmp_u2f_github_data}; +const BITMAP bmp_u2f_google = {32, 32, bmp_u2f_google_data}; +const BITMAP bmp_u2f_slushpool = {32, 32, bmp_u2f_slushpool_data}; diff --git a/gen/bitmaps.h b/gen/bitmaps.h index 9eff4cd592..9c3db2bff2 100644 --- a/gen/bitmaps.h +++ b/gen/bitmaps.h @@ -31,5 +31,9 @@ extern const BITMAP bmp_logo48; extern const BITMAP bmp_logo48_empty; extern const BITMAP bmp_logo64; extern const BITMAP bmp_logo64_empty; +extern const BITMAP bmp_u2f_dropbox; +extern const BITMAP bmp_u2f_github; +extern const BITMAP bmp_u2f_google; +extern const BITMAP bmp_u2f_slushpool; #endif diff --git a/gen/bitmaps/u2f_dropbox.png b/gen/bitmaps/u2f_dropbox.png new file mode 100644 index 0000000000000000000000000000000000000000..9d78cadb004addcb8344a54cd62690849256c0cb GIT binary patch literal 263 zcmV+i0r>ujP)*Bi47H{Zla*jJXXUhY zYcWW{RwfmHc!A-Oego-^_%8hk?*_|yOZ N002ovPDHLkV1l8qa>4)r literal 0 HcmV?d00001 diff --git a/gen/bitmaps/u2f_github.png b/gen/bitmaps/u2f_github.png new file mode 100644 index 0000000000000000000000000000000000000000..9f9be253e30c34e220ea3cac2145152b0189a3f7 GIT binary patch literal 236 zcmVo5iB zVHX|v@n&##lix2uQx+Oi6Fuc1qx{`OzJt8-je~Zd6Z)`{3Iz_vCY>b16WJl0gM-LS zU`2leD-9CL996ouil7PSdzpLq*buDeM=4xCVsx m4V+jBA_UP z8z^byR*3WzXoUmk=$ylI)vebB6!&?fF?3)o-6!@+L3{e`YG+&_w%ixRA9df%M#;b- zZn-@y?2s|1P>Rbt9UMtAY=VT>is-kjq45^o5a0000RmFP;X;f6Ka+7>t=K<}%hC+Am>L`_^2;*j|5iR4LG* N44$rjF6*2UngASlQk?(* literal 0 HcmV?d00001 From b1e3c52b0874b52f0aa819a01df074bad4cd98e6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 8 Jun 2016 17:55:25 +0200 Subject: [PATCH 29/31] remove DialogIcon enum, use bitmap structure directly --- bootloader/bootloader.c | 10 +++++----- bootloader/usb.c | 14 +++++++------- demo/demo.c | 2 +- firmware/fsm.c | 22 +++++++++++----------- firmware/layout2.c | 26 +++++++++++++------------- firmware/layout2.h | 3 ++- firmware/protect.c | 4 ++-- firmware/recovery.c | 4 ++-- firmware/reset.c | 10 +++++----- firmware/storage.c | 2 +- firmware/trezor.c | 2 +- layout.c | 28 ++++------------------------ layout.h | 12 ++---------- 13 files changed, 56 insertions(+), 83 deletions(-) diff --git a/bootloader/bootloader.c b/bootloader/bootloader.c index b6b389ad99..30abc4bc54 100644 --- a/bootloader/bootloader.c +++ b/bootloader/bootloader.c @@ -45,18 +45,18 @@ void layoutFirmwareHash(uint8_t *hash) for (i = 0; i < 4; i++) { data2hex(hash + i * 8, 8, str[i]); } - layoutDialog(DIALOG_ICON_QUESTION, "Abort", "Continue", "Compare fingerprints", str[0], str[1], str[2], str[3], NULL, NULL); + layoutDialog(&bmp_icon_question, "Abort", "Continue", "Compare fingerprints", str[0], str[1], str[2], str[3], NULL, NULL); } void show_halt(void) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Unofficial firmware", "aborted.", NULL, "Unplug your TREZOR", "and see our support", "page at mytrezor.com"); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Unofficial firmware", "aborted.", NULL, "Unplug your TREZOR", "and see our support", "page at mytrezor.com"); system_halt(); } void show_unofficial_warning(uint8_t *hash) { - layoutDialog(DIALOG_ICON_WARNING, "Abort", "I'll take the risk", NULL, "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, NULL); + layoutDialog(&bmp_icon_warning, "Abort", "I'll take the risk", NULL, "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, NULL); do { delay(100000); @@ -124,7 +124,7 @@ void check_firmware_sanity(void) broken++; } if (broken) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Firmware appears", "to be broken.", NULL, "Unplug your TREZOR", "and see our support", "page at mytrezor.com"); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Firmware appears", "to be broken.", NULL, "Unplug your TREZOR", "and see our support", "page at mytrezor.com"); system_halt(); } } @@ -133,7 +133,7 @@ uint32_t __stack_chk_guard; void __attribute__((noreturn)) __stack_chk_fail(void) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL); for (;;) {} // loop forever } diff --git a/bootloader/usb.c b/bootloader/usb.c index f4a73533e5..3745b0cacd 100644 --- a/bootloader/usb.c +++ b/bootloader/usb.c @@ -272,7 +272,7 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) if (flash_state == STATE_OPEN) { if (msg_id == 0x0006) { // FirmwareErase message (id 6) - layoutDialog(DIALOG_ICON_QUESTION, "Abort", "Continue", NULL, "Install new", "firmware?", NULL, "Never do this without", "your recovery card!", NULL); + layoutDialog(&bmp_icon_question, "Abort", "Continue", NULL, "Install new", "firmware?", NULL, "Never do this without", "your recovery card!", NULL); do { delay(100000); buttonUpdate(); @@ -299,7 +299,7 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) } send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(DIALOG_ICON_WARNING, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You may now", "unplug your TREZOR.", NULL); + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You may now", "unplug your TREZOR.", NULL); return; } return; @@ -310,7 +310,7 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) if (buf[9] != 0x0a) { // invalid contents send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); return; } // read payload length @@ -319,7 +319,7 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) if (flash_len > FLASH_TOTAL_SIZE + FLASH_META_DESC_LEN - (FLASH_APP_START - FLASH_ORIGIN)) { // firmware is too big send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Firmware is too big.", NULL, "Get official firmware", "from mytrezor.com", NULL, NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Firmware is too big.", NULL, "Get official firmware", "from mytrezor.com", NULL, NULL); return; } sha256_Init(&ctx); @@ -348,7 +348,7 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) if (buf[0] != '?') { // invalid contents send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); return; } p = buf + 1; @@ -432,10 +432,10 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) } flash_state = STATE_END; if (hash_check_ok) { - layoutDialog(DIALOG_ICON_OK, NULL, NULL, NULL, "New firmware", "successfully installed.", NULL, "You may now", "unplug your TREZOR.", NULL); + layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware", "successfully installed.", NULL, "You may now", "unplug your TREZOR.", NULL); send_msg_success(dev); } else { - layoutDialog(DIALOG_ICON_WARNING, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You need to repeat", "the procedure with", "the correct firmware."); + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You need to repeat", "the procedure with", "the correct firmware."); send_msg_failure(dev); } return; diff --git a/demo/demo.c b/demo/demo.c index cfed6d6f68..f4ee18ac22 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -242,7 +242,7 @@ uint32_t __stack_chk_guard; void __attribute__((noreturn)) __stack_chk_fail(void) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL); for (;;) {} // loop forever } diff --git a/firmware/fsm.c b/firmware/fsm.c index 0b08e1d18b..b7c6e5025a 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -161,7 +161,7 @@ void fsm_msgPing(Ping *msg) RESP_INIT(Success); if (msg->has_button_protection && msg->button_protection) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "answer to ping?", NULL, NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "answer to ping?", NULL, NULL, NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Ping cancelled"); layoutHome(); @@ -196,16 +196,16 @@ void fsm_msgChangePin(ChangePin *msg) bool removal = msg->has_remove && msg->remove; if (removal) { if (storage_hasPin()) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "remove current PIN?", NULL, NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "remove current PIN?", NULL, NULL, NULL, NULL); } else { fsm_sendSuccess("PIN removed"); return; } } else { if (storage_hasPin()) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change current PIN?", NULL, NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "change current PIN?", NULL, NULL, NULL, NULL); } else { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "set new PIN?", NULL, NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "set new PIN?", NULL, NULL, NULL, NULL); } } if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { @@ -233,7 +233,7 @@ void fsm_msgChangePin(ChangePin *msg) void fsm_msgWipeDevice(WipeDevice *msg) { (void)msg; - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "wipe the device?", NULL, "All data will be lost.", NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "wipe the device?", NULL, "All data will be lost.", NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_WipeDevice, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Wipe cancelled"); layoutHome(); @@ -263,7 +263,7 @@ void fsm_msgFirmwareUpload(FirmwareUpload *msg) void fsm_msgGetEntropy(GetEntropy *msg) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "send entropy?", NULL, NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "send entropy?", NULL, NULL, NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Entropy cancelled"); layoutHome(); @@ -332,7 +332,7 @@ void fsm_msgLoadDevice(LoadDevice *msg) return; } - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "I take the risk", NULL, "Loading private seed", "is not recommended.", "Continue only if you", "know what you are", "doing!", NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "I take the risk", NULL, "Loading private seed", "is not recommended.", "Continue only if you", "know what you are", "doing!", NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Load cancelled"); layoutHome(); @@ -489,7 +489,7 @@ void fsm_msgClearSession(ClearSession *msg) void fsm_msgApplySettings(ApplySettings *msg) { if (msg->has_label) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "?", NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "?", NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled"); layoutHome(); @@ -497,7 +497,7 @@ void fsm_msgApplySettings(ApplySettings *msg) } } if (msg->has_language) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change language to", msg->language, "?", NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "change language to", msg->language, "?", NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled"); layoutHome(); @@ -505,7 +505,7 @@ void fsm_msgApplySettings(ApplySettings *msg) } } if (msg->has_use_passphrase) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", msg->use_passphrase ? "enable passphrase" : "disable passphrase", "encryption?", NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", msg->use_passphrase ? "enable passphrase" : "disable passphrase", "encryption?", NULL, NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled"); layoutHome(); @@ -513,7 +513,7 @@ void fsm_msgApplySettings(ApplySettings *msg) } } if (msg->has_homescreen) { - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change the home", "screen ?", NULL, NULL, NULL); + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "change the home", "screen ?", NULL, NULL, NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled"); layoutHome(); diff --git a/firmware/layout2.c b/firmware/layout2.c index 7805258b66..16b867441a 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -31,7 +31,7 @@ void *layoutLast = layoutHome; -void layoutDialogSwipe(LayoutDialogIcon icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) +void layoutDialogSwipe(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) { layoutLast = layoutDialogSwipe; oledSwipeLeft(); @@ -119,7 +119,7 @@ void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out) static char first_half[17 + 1]; strlcpy(first_half, out->address, sizeof(first_half)); const char *str_out = str_amount(out->amount, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_out, sizeof(buf_out)); - layoutDialogSwipe(DIALOG_ICON_QUESTION, + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, @@ -136,7 +136,7 @@ void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_ { const char *str_out = str_amount(amount_out, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_out, sizeof(buf_out)); const char *str_fee = str_amount(amount_fee, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_fee, sizeof(buf_fee)); - layoutDialogSwipe(DIALOG_ICON_QUESTION, + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, @@ -153,7 +153,7 @@ void layoutFeeOverThreshold(const CoinType *coin, uint64_t fee, uint32_t kb) { (void)kb; const char *str_out = str_amount(fee, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_out, sizeof(buf_out)); - layoutDialogSwipe(DIALOG_ICON_QUESTION, + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, @@ -191,7 +191,7 @@ const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen) void layoutSignMessage(const uint8_t *msg, uint32_t len) { const char **str = split_message(msg, len, 16); - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", "Sign message?", str[0], str[1], str[2], str[3], NULL, NULL); } @@ -199,7 +199,7 @@ void layoutSignMessage(const uint8_t *msg, uint32_t len) void layoutVerifyAddress(const char *address) { const char **str = split_message((const uint8_t *)address, strlen(address), 17); - layoutDialogSwipe(DIALOG_ICON_INFO, "Cancel", "Confirm", + layoutDialogSwipe(&bmp_icon_info, "Cancel", "Confirm", "Confirm address?", "Message signed by:", NULL, str[0], str[1], str[2], NULL); @@ -208,7 +208,7 @@ void layoutVerifyAddress(const char *address) void layoutVerifyMessage(const uint8_t *msg, uint32_t len) { const char **str = split_message(msg, len, 16); - layoutDialogSwipe(DIALOG_ICON_INFO, "Cancel", "Confirm", + layoutDialogSwipe(&bmp_icon_info, "Cancel", "Confirm", "Verified message", str[0], str[1], str[2], str[3], NULL, NULL); } @@ -216,7 +216,7 @@ void layoutVerifyMessage(const uint8_t *msg, uint32_t len) void layoutCipherKeyValue(bool encrypt, const char *key) { const char **str = split_message((const uint8_t *)key, strlen(key), 16); - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", encrypt ? "Encode value of this key?" : "Decode value of this key?", str[0], str[1], str[2], str[3], NULL, NULL); } @@ -224,7 +224,7 @@ void layoutCipherKeyValue(bool encrypt, const char *key) void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing) { const char **str = split_message(msg, len, 16); - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", signing ? "Encrypt+Sign message?" : "Encrypt message?", str[0], str[1], str[2], str[3], NULL, NULL); } @@ -232,7 +232,7 @@ void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing) void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address) { const char **str = split_message(msg, len, 16); - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "OK", + layoutDialogSwipe(&bmp_icon_info, NULL, "OK", address ? "Decrypted signed message" : "Decrypted message", str[0], str[1], str[2], str[3], NULL, NULL); } @@ -286,7 +286,7 @@ void layoutPublicKey(const uint8_t *pubkey) data2hex(pubkey, 1, desc + 12); data2hex(pubkey + 1, 32, hex); const char **str = split_message((const uint8_t *)hex, 32*2, 16); - layoutDialogSwipe(DIALOG_ICON_QUESTION, NULL, "Continue", NULL, + layoutDialogSwipe(&bmp_icon_question, NULL, "Continue", NULL, desc, str[0], str[1], str[2], str[3], NULL); } @@ -326,7 +326,7 @@ void layoutSignIdentity(const IdentityType *identity, const char *challenge) row_user[0] = 0; } - layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", "Do you want to sign in?", row_proto[0] ? row_proto : NULL, row_hostport[0] ? row_hostport : NULL, @@ -337,5 +337,5 @@ void layoutSignIdentity(const IdentityType *identity, const char *challenge) } void layoutU2FDialog(const char *verb, const char *appid) { - layoutDialog(DIALOG_ICON_QUESTION, NULL, verb, NULL, verb, "U2F security key?", "", appid, "", NULL); + layoutDialog(&bmp_icon_question, NULL, verb, NULL, verb, "U2F security key?", "", appid, "", NULL); } diff --git a/firmware/layout2.h b/firmware/layout2.h index 6cbe30c436..d17ec7faaf 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -22,8 +22,9 @@ #include "layout.h" #include "types.pb.h" +#include "bitmaps.h" -void layoutDialogSwipe(LayoutDialogIcon icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); +void layoutDialogSwipe(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); void layoutProgressSwipe(const char *desc, int permil); void layoutScreensaver(void); diff --git a/firmware/protect.c b/firmware/protect.c index a1b127ffbb..f92010915d 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -162,7 +162,7 @@ bool protectPin(bool use_cached) if (wait == 1) { secstrbuf[16] = 0; } - layoutDialog(DIALOG_ICON_INFO, NULL, NULL, NULL, "Wrong PIN entered", NULL, "Please wait", secstr, "to continue ...", NULL); + layoutDialog(&bmp_icon_info, NULL, NULL, NULL, "Wrong PIN entered", NULL, "Please wait", secstr, "to continue ...", NULL); // wait one second usbDelay(800000); wait--; @@ -216,7 +216,7 @@ bool protectPassphrase(void) usbTiny(1); msg_write(MessageType_MessageType_PassphraseRequest, &resp); - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Please enter your", "passphrase using", "the computer's", "keyboard.", NULL, NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, "Please enter your", "passphrase using", "the computer's", "keyboard.", NULL, NULL); bool result; for (;;) { diff --git a/firmware/recovery.c b/firmware/recovery.c index bd5bda70f7..7ddceed63a 100644 --- a/firmware/recovery.c +++ b/firmware/recovery.c @@ -41,7 +41,7 @@ void next_word(void) { if (word_pos == 0) { const char * const *wl = mnemonic_wordlist(); strlcpy(fake_word, wl[random_uniform(2048)], sizeof(fake_word)); - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Please enter the word", NULL, fake_word, NULL, "on your computer", NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, "Please enter the word", NULL, fake_word, NULL, "on your computer", NULL); } else { fake_word[0] = 0; char desc[] = "##th word"; @@ -60,7 +60,7 @@ void next_word(void) { if (word_pos == 3 || word_pos == 23) { desc[2] = 'r'; desc[3] = 'd'; } - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Please enter the", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, "of your mnemonic", NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, "Please enter the", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, "of your mnemonic", NULL); } WordRequest resp; memset(&resp, 0, sizeof(WordRequest)); diff --git a/firmware/reset.c b/firmware/reset.c index 1ecad15f4d..7b2fa72232 100644 --- a/firmware/reset.c +++ b/firmware/reset.c @@ -52,7 +52,7 @@ void reset_init(bool display_random, uint32_t _strength, bool passphrase_protect data2hex(int_entropy + 24, 8, ent_str[3]); if (display_random) { - layoutDialogSwipe(DIALOG_ICON_INFO, "Cancel", "Continue", NULL, "Internal entropy:", ent_str[0], ent_str[1], ent_str[2], ent_str[3], NULL); + layoutDialogSwipe(&bmp_icon_info, "Cancel", "Continue", NULL, "Internal entropy:", ent_str[0], ent_str[1], ent_str[2], ent_str[3], NULL); if (!protectButton(ButtonRequestType_ButtonRequest_ResetDevice, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Reset cancelled"); layoutHome(); @@ -129,15 +129,15 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len) current_word_display[j + 1] = 0; if (word_pos == (int)strength/32*3) { // last word if (pass == 1) { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Finish", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, "Finish", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } else { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Again", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, "Again", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } } else { if (pass == 1) { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, "Next", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } else { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); + layoutDialogSwipe(&bmp_icon_info, NULL, "Next", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } } if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) { diff --git a/firmware/storage.c b/firmware/storage.c index e7bb56182d..f1a21196aa 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -100,7 +100,7 @@ static char sessionPassphrase[51]; void storage_show_error(void) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Storage failure", "detected.", NULL, "Please unplug", "the device.", NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Storage failure", "detected.", NULL, "Please unplug", "the device.", NULL); for (;;) { } } diff --git a/firmware/trezor.c b/firmware/trezor.c index aa154d6e61..e54d81c7cc 100644 --- a/firmware/trezor.c +++ b/firmware/trezor.c @@ -32,7 +32,7 @@ uint32_t __stack_chk_guard; void __attribute__((noreturn)) __stack_chk_fail(void) { - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL); for (;;) {} // loop forever } diff --git a/layout.c b/layout.c index ecb97210b6..28a71ac9f5 100644 --- a/layout.c +++ b/layout.c @@ -22,33 +22,13 @@ #include "layout.h" #include "oled.h" -void layoutDialog(LayoutDialogIcon icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) +void layoutDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) { int left = 0; oledClear(); - switch (icon) { - case DIALOG_NOICON: - break; - case DIALOG_ICON_ERROR: - oledDrawBitmap(0, 0, &bmp_icon_error); - left = 20; - break; - case DIALOG_ICON_INFO: - oledDrawBitmap(0, 0, &bmp_icon_info); - left = 20; - break; - case DIALOG_ICON_QUESTION: - oledDrawBitmap(0, 0, &bmp_icon_question); - left = 20; - break; - case DIALOG_ICON_WARNING: - oledDrawBitmap(0, 0, &bmp_icon_warning); - left = 20; - break; - case DIALOG_ICON_OK: - oledDrawBitmap(0, 0, &bmp_icon_ok); - left = 20; - break; + if (icon) { + oledDrawBitmap(0, 0, icon); + left = icon->width + 4; } if (line1) oledDrawString(left, 0 * 9, line1); if (line2) oledDrawString(left, 1 * 9, line2); diff --git a/layout.h b/layout.h index c7f127a92d..27f81d49e6 100644 --- a/layout.h +++ b/layout.h @@ -22,17 +22,9 @@ #include #include +#include "bitmaps.h" -typedef enum { - DIALOG_NOICON = 0, - DIALOG_ICON_ERROR, - DIALOG_ICON_INFO, - DIALOG_ICON_QUESTION, - DIALOG_ICON_WARNING, - DIALOG_ICON_OK, -} LayoutDialogIcon; - -void layoutDialog(LayoutDialogIcon icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); +void layoutDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); void layoutProgressUpdate(bool refresh); void layoutProgress(const char *desc, int permil); From da067913c2b1177898588ad87acbd08a727a2386 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 9 Jun 2016 18:11:19 +0200 Subject: [PATCH 30/31] show app icons in u2f dialog --- firmware/layout2.c | 7 +++++-- firmware/layout2.h | 2 +- firmware/u2f.c | 30 ++++++++++++++++++++---------- firmware/u2f_knownapps.h | 2 +- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/firmware/layout2.c b/firmware/layout2.c index 16b867441a..4c0a3a3cae 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -336,6 +336,9 @@ void layoutSignIdentity(const IdentityType *identity, const char *challenge) NULL); } -void layoutU2FDialog(const char *verb, const char *appid) { - layoutDialog(&bmp_icon_question, NULL, verb, NULL, verb, "U2F security key?", "", appid, "", NULL); +void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appicon) { + if (!appicon) { + appicon = &bmp_icon_question; + } + layoutDialog(appicon, NULL, verb, NULL, verb, "U2F security key?", "", appname, "", NULL); } diff --git a/firmware/layout2.h b/firmware/layout2.h index d17ec7faaf..975d530bc2 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -41,6 +41,6 @@ void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address) void layoutAddress(const char *address, const char *desc); void layoutPublicKey(const uint8_t *pubkey); void layoutSignIdentity(const IdentityType *identity, const char *challenge); -void layoutU2FDialog(const char *verb, const char *appid); +void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appicon); #endif diff --git a/firmware/u2f.c b/firmware/u2f.c index c910ae5a48..cb05370c70 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -441,19 +441,23 @@ void u2f_version(const APDU *a) send_u2f_msg(version_response, sizeof(version_response)); } -static const char *getReadableAppId(const uint8_t appid[U2F_APPID_SIZE]) { +void getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], const char **appname, const BITMAP **appicon) { unsigned int i; - static char buf[6+2+6+1]; + static char buf[8+2+8+1]; for (i = 0; i < sizeof(u2f_well_known)/sizeof(U2FWellKnown); i++) { - if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) - return u2f_well_known[i].appname; + if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) { + *appname = u2f_well_known[i].appname; + *appicon = u2f_well_known[i].appicon; + return; + } } - data2hex(appid, 3, &buf[0]); - buf[6] = buf[7] = '.'; - data2hex(appid + (U2F_APPID_SIZE - 3), 3, &buf[8]); - return buf; + data2hex(appid, 4, &buf[0]); + buf[8] = buf[9] = '.'; + data2hex(appid + (U2F_APPID_SIZE - 4), 4, &buf[10]); + *appname = buf; + *appicon = NULL; } const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) @@ -555,7 +559,10 @@ void u2f_register(const APDU *a) getDerivedNode(NULL, 0); // error: testof-user-presence is required buttonUpdate(); // Clear button state - layoutU2FDialog("Register", getReadableAppId(req->appId)); + const char *appname; + const BITMAP *appicon; + getReadableAppId(req->appId, &appname, &appicon); + layoutU2FDialog("Register", appname, appicon); last_req_state = REG; } @@ -679,7 +686,10 @@ void u2f_authenticate(const APDU *a) if (last_req_state == INIT) { // error: testof-user-presence is required buttonUpdate(); // Clear button state - layoutU2FDialog("Authenticate", getReadableAppId(req->appId)); + const char *appname; + const BITMAP *appicon; + getReadableAppId(req->appId, &appname, &appicon); + layoutU2FDialog("Authenticate", appname, appicon); last_req_state = AUTH; } diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h index 32386c73b1..947594012c 100644 --- a/firmware/u2f_knownapps.h +++ b/firmware/u2f_knownapps.h @@ -27,7 +27,7 @@ typedef struct { const uint8_t appid[U2F_APPID_SIZE]; const char *appname; - const BITMAP *icon; + const BITMAP *appicon; } U2FWellKnown; static const U2FWellKnown u2f_well_known[4] = { From caafefc0fbb8955786d9c84b8fdfb47d5e081535 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sun, 12 Jun 2016 21:25:35 +0200 Subject: [PATCH 31/31] Tweaked timeouts, handle null nodes. --- firmware/u2f.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/firmware/u2f.c b/firmware/u2f.c index cb05370c70..76eb80f511 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -216,12 +216,13 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { cid = 0; reader = 0; usbTiny(0); + layoutHome(); return; } usbPoll(); } } - + // We have all the data switch (reader->cmd) { case 0: @@ -244,21 +245,20 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { // wait for next commmand/ button press reader->cmd = 0; reader->seq = 255; - uint8_t bs = 0; - while (dialog_timeout && bs == 0 && reader->cmd == 0) { + while (dialog_timeout > 0 && reader->cmd == 0) { dialog_timeout--; usbPoll(); // may trigger new request buttonUpdate(); if (button.YesUp && (last_req_state == AUTH || last_req_state == REG)) { last_req_state++; + // standard requires to remember button press for 10 seconds. + dialog_timeout = 10 * U2F_TIMEOUT; } } if (reader->cmd == 0) { - if (dialog_timeout == 0) { - last_req_state = INIT; - } + last_req_state = INIT; cid = 0; reader = 0; usbTiny(0); @@ -283,7 +283,7 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) return send_u2fhid_error(cid, ERR_INVALID_LEN); if (dialog_timeout > 0) - dialog_timeout = 10*U2F_TIMEOUT; + dialog_timeout = U2F_TIMEOUT; U2FHID_FRAME f; MEMSET_BZERO(&f, sizeof(f)); @@ -496,6 +496,8 @@ const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) // prepare keypair from /random data const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + if (!node) + return NULL; // For second half of keyhandle // Signature of app_id and random data @@ -516,6 +518,8 @@ const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle memcpy(&key_path[1], key_handle, KEY_PATH_LEN); const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + if (!node) + return NULL; uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; memcpy(&keybase[0], app_id, U2F_APPID_SIZE); @@ -539,6 +543,11 @@ void u2f_register(const APDU *a) static U2F_REGISTER_REQ last_req; const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data; + if (!storage_isInitialized()) { + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + return; + } + // Validate basic request parameters debugLog(0, "", "u2f register"); if (APDU_LEN(*a) != sizeof(U2F_REGISTER_REQ)) { @@ -570,7 +579,7 @@ void u2f_register(const APDU *a) if (last_req_state == REG) { // error: testof-user-presence is required send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - dialog_timeout = 10*U2F_TIMEOUT; + dialog_timeout = U2F_TIMEOUT; return; } @@ -639,6 +648,11 @@ void u2f_authenticate(const APDU *a) const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data; static U2F_AUTHENTICATE_REQ last_req; + if (!storage_isInitialized()) { + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + return; + } + if (APDU_LEN(*a) < 64) { /// FIXME: decent value debugLog(0, "", "u2f authenticate - badlen"); send_u2f_error(U2F_SW_WRONG_LENGTH); @@ -697,7 +711,7 @@ void u2f_authenticate(const APDU *a) if (last_req_state == AUTH) { // error: testof-user-presence is required send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - dialog_timeout = 10*U2F_TIMEOUT; + dialog_timeout = U2F_TIMEOUT; return; }