mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-03 12:00:59 +00:00
parent
88563ebaa5
commit
73edc7cb74
@ -41,6 +41,7 @@
|
|||||||
#include "layout2.h"
|
#include "layout2.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
|
#include "u2f.h"
|
||||||
|
|
||||||
/* magic constant to check validity of storage block */
|
/* magic constant to check validity of storage block */
|
||||||
static const uint32_t storage_magic = 0x726f7473; // 'stor' as uint32_t
|
static const uint32_t storage_magic = 0x726f7473; // 'stor' as uint32_t
|
||||||
@ -108,7 +109,7 @@ static bool sessionPinCached;
|
|||||||
static bool sessionPassphraseCached;
|
static bool sessionPassphraseCached;
|
||||||
static char CONFIDENTIAL sessionPassphrase[51];
|
static char CONFIDENTIAL sessionPassphrase[51];
|
||||||
|
|
||||||
#define STORAGE_VERSION 8
|
#define STORAGE_VERSION 9
|
||||||
|
|
||||||
void storage_show_error(void)
|
void storage_show_error(void)
|
||||||
{
|
{
|
||||||
@ -126,6 +127,7 @@ void storage_check_flash_errors(void)
|
|||||||
|
|
||||||
bool storage_from_flash(void)
|
bool storage_from_flash(void)
|
||||||
{
|
{
|
||||||
|
storage_clear_update();
|
||||||
if (memcmp((void *)FLASH_STORAGE_START, &storage_magic, sizeof(storage_magic)) != 0) {
|
if (memcmp((void *)FLASH_STORAGE_START, &storage_magic, sizeof(storage_magic)) != 0) {
|
||||||
// wrong magic
|
// wrong magic
|
||||||
return false;
|
return false;
|
||||||
@ -140,6 +142,7 @@ bool storage_from_flash(void)
|
|||||||
// version 6: since 1.3.6
|
// version 6: since 1.3.6
|
||||||
// version 7: since 1.5.1
|
// version 7: since 1.5.1
|
||||||
// version 8: since 1.5.2
|
// version 8: since 1.5.2
|
||||||
|
// version 9: since 1.6.1
|
||||||
if (version > STORAGE_VERSION) {
|
if (version > STORAGE_VERSION) {
|
||||||
// downgrade -> clear storage
|
// downgrade -> clear storage
|
||||||
return false;
|
return false;
|
||||||
@ -156,13 +159,20 @@ bool storage_from_flash(void)
|
|||||||
old_storage_size = 460;
|
old_storage_size = 460;
|
||||||
} else
|
} else
|
||||||
if (version == 3 || version == 4 || version == 5) {
|
if (version == 3 || version == 4 || version == 5) {
|
||||||
|
// added homescreen
|
||||||
old_storage_size = 1488;
|
old_storage_size = 1488;
|
||||||
} else
|
} else
|
||||||
if (version == 6 || version == 7) {
|
if (version == 6 || version == 7) {
|
||||||
|
// added u2fcounter
|
||||||
old_storage_size = 1496;
|
old_storage_size = 1496;
|
||||||
} else
|
} else
|
||||||
if (version == 8) {
|
if (version == 8) {
|
||||||
|
// added flags and needsBackup
|
||||||
old_storage_size = 1504;
|
old_storage_size = 1504;
|
||||||
|
} else
|
||||||
|
if (version == 9) {
|
||||||
|
// added u2froot
|
||||||
|
old_storage_size = 1704;
|
||||||
}
|
}
|
||||||
|
|
||||||
// erase newly added fields
|
// erase newly added fields
|
||||||
@ -204,8 +214,15 @@ bool storage_from_flash(void)
|
|||||||
storage_u2f_offset++;
|
storage_u2f_offset++;
|
||||||
u2fword >>= 1;
|
u2fword >>= 1;
|
||||||
}
|
}
|
||||||
// note: we don't update storage version on flash at this point,
|
// force recomputing u2f root for storage version < 9.
|
||||||
// but it is already upgraded when it comes to content
|
if (version < 9) {
|
||||||
|
storageUpdate.has_mnemonic = storageRom->has_mnemonic;
|
||||||
|
strlcpy(storageUpdate.mnemonic, storageRom->mnemonic, sizeof(storageUpdate.mnemonic));
|
||||||
|
}
|
||||||
|
// update storage version on flash
|
||||||
|
if (version != STORAGE_VERSION) {
|
||||||
|
storage_update();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +259,29 @@ static uint32_t storage_flash_words(uint32_t addr, const uint32_t *src, int nwor
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_u2froot_callback(uint32_t iter, uint32_t total)
|
||||||
|
{
|
||||||
|
layoutProgress(_("Updating"), 1000 * iter / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void storage_compute_u2froot(const char* mnemonic, HDNodeType *u2froot) {
|
||||||
|
static CONFIDENTIAL HDNode node;
|
||||||
|
char oldTiny = usbTiny(1);
|
||||||
|
mnemonic_to_seed(mnemonic, "", sessionSeed, get_u2froot_callback); // BIP-0039
|
||||||
|
usbTiny(oldTiny);
|
||||||
|
hdnode_from_seed(sessionSeed, 64, NIST256P1_NAME, &node);
|
||||||
|
hdnode_private_ckd(&node, U2F_KEY_PATH);
|
||||||
|
u2froot->depth = node.depth;
|
||||||
|
u2froot->child_num = U2F_KEY_PATH;
|
||||||
|
u2froot->chain_code.size = sizeof(node.chain_code);
|
||||||
|
memcpy(u2froot->chain_code.bytes, node.chain_code, sizeof(node.chain_code));
|
||||||
|
u2froot->has_private_key = true;
|
||||||
|
u2froot->private_key.size = sizeof(node.private_key);
|
||||||
|
memcpy(u2froot->private_key.bytes, node.private_key, sizeof(node.private_key));
|
||||||
|
memset(&node, 0, sizeof(node));
|
||||||
|
session_clear(false); // invalidate seed cache
|
||||||
|
}
|
||||||
|
|
||||||
// if storage is filled in - update fields that has has_field set to true
|
// if storage is filled in - update fields that has has_field set to true
|
||||||
// if storage is NULL - do not backup original content - essentialy a wipe
|
// if storage is NULL - do not backup original content - essentialy a wipe
|
||||||
static void storage_commit_locked(bool update)
|
static void storage_commit_locked(bool update)
|
||||||
@ -261,6 +301,11 @@ static void storage_commit_locked(bool update)
|
|||||||
memcpy(&storageUpdate.node, &storageRom->node, sizeof(HDNodeType));
|
memcpy(&storageUpdate.node, &storageRom->node, sizeof(HDNodeType));
|
||||||
storageUpdate.has_mnemonic = storageRom->has_mnemonic;
|
storageUpdate.has_mnemonic = storageRom->has_mnemonic;
|
||||||
strlcpy(storageUpdate.mnemonic, storageRom->mnemonic, sizeof(storageUpdate.mnemonic));
|
strlcpy(storageUpdate.mnemonic, storageRom->mnemonic, sizeof(storageUpdate.mnemonic));
|
||||||
|
storageUpdate.has_u2froot = storageRom->has_u2froot;
|
||||||
|
memcpy(&storageUpdate.u2froot, &storageRom->u2froot, sizeof(HDNodeType));
|
||||||
|
} else if (storageUpdate.has_mnemonic) {
|
||||||
|
storageUpdate.has_u2froot = true;
|
||||||
|
storage_compute_u2froot(storageUpdate.mnemonic, &storageUpdate.u2froot);
|
||||||
}
|
}
|
||||||
if (!storageUpdate.has_passphrase_protection) {
|
if (!storageUpdate.has_passphrase_protection) {
|
||||||
storageUpdate.has_passphrase_protection = storageRom->has_passphrase_protection;
|
storageUpdate.has_passphrase_protection = storageRom->has_passphrase_protection;
|
||||||
@ -467,6 +512,12 @@ const uint8_t *storage_getSeed(bool usePassphrase)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool storage_getU2FRoot(HDNode *node)
|
||||||
|
{
|
||||||
|
return storageRom->has_u2froot
|
||||||
|
&& hdnode_from_xprv(storageRom->u2froot.depth, storageRom->u2froot.child_num, storageRom->u2froot.chain_code.bytes, storageRom->u2froot.private_key.bytes, NIST256P1_NAME, node);
|
||||||
|
}
|
||||||
|
|
||||||
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase)
|
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase)
|
||||||
{
|
{
|
||||||
// if storage has node, decrypt and use it
|
// if storage has node, decrypt and use it
|
||||||
|
@ -37,6 +37,7 @@ void storage_loadDevice(LoadDevice *msg);
|
|||||||
|
|
||||||
const uint8_t *storage_getSeed(bool usePassphrase);
|
const uint8_t *storage_getSeed(bool usePassphrase);
|
||||||
|
|
||||||
|
bool storage_getU2FRoot(HDNode *node);
|
||||||
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase);
|
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase);
|
||||||
|
|
||||||
const char *storage_getLabel(void);
|
const char *storage_getLabel(void);
|
||||||
|
@ -59,7 +59,7 @@ static uint8_t u2f_out_packets[U2F_OUT_PKT_BUFFER_LEN][HID_RPT_SIZE];
|
|||||||
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
|
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
|
||||||
|
|
||||||
// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
|
// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
|
||||||
#define KEY_PATH_ENTRIES (1 + KEY_PATH_LEN / sizeof(uint32_t))
|
#define KEY_PATH_ENTRIES (KEY_PATH_LEN / sizeof(uint32_t))
|
||||||
|
|
||||||
// Defined as UsbSignHandler.BOGUS_APP_ID_HASH
|
// Defined as UsbSignHandler.BOGUS_APP_ID_HASH
|
||||||
// in https://github.com/google/u2f-ref-code/blob/master/u2f-chrome-extension/usbsignhandler.js#L118
|
// in https://github.com/google/u2f-ref-code/blob/master/u2f-chrome-extension/usbsignhandler.js#L118
|
||||||
@ -450,7 +450,7 @@ static void getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], const char **a
|
|||||||
static const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count)
|
static const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count)
|
||||||
{
|
{
|
||||||
static CONFIDENTIAL HDNode node;
|
static CONFIDENTIAL HDNode node;
|
||||||
if (!storage_getRootNode(&node, NIST256P1_NAME, false)) {
|
if (!storage_getU2FRoot(&node)) {
|
||||||
layoutHome();
|
layoutHome();
|
||||||
debugLog(0, "", "ERR: Device not init");
|
debugLog(0, "", "ERR: Device not init");
|
||||||
return 0;
|
return 0;
|
||||||
@ -472,14 +472,13 @@ static const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handl
|
|||||||
|
|
||||||
// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
|
// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
|
||||||
uint32_t key_path[KEY_PATH_ENTRIES];
|
uint32_t key_path[KEY_PATH_ENTRIES];
|
||||||
key_path[0] = U2F_KEY_PATH;
|
for (uint32_t i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||||
for (uint32_t i = 1; i < KEY_PATH_ENTRIES; i++) {
|
|
||||||
// high bit for hardened keys
|
// high bit for hardened keys
|
||||||
key_path[i]= 0x80000000 | random32();
|
key_path[i]= 0x80000000 | random32();
|
||||||
}
|
}
|
||||||
|
|
||||||
// First half of keyhandle is key_path
|
// First half of keyhandle is key_path
|
||||||
memcpy(key_handle, &key_path[1], KEY_PATH_LEN);
|
memcpy(key_handle, key_path, KEY_PATH_LEN);
|
||||||
|
|
||||||
// prepare keypair from /random data
|
// prepare keypair from /random data
|
||||||
const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES);
|
const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES);
|
||||||
@ -501,9 +500,8 @@ static const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handl
|
|||||||
static const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[])
|
static const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[])
|
||||||
{
|
{
|
||||||
uint32_t key_path[KEY_PATH_ENTRIES];
|
uint32_t key_path[KEY_PATH_ENTRIES];
|
||||||
key_path[0] = U2F_KEY_PATH;
|
memcpy(key_path, key_handle, KEY_PATH_LEN);
|
||||||
memcpy(&key_path[1], key_handle, KEY_PATH_LEN);
|
for (unsigned int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||||
for (unsigned int i = 1; i < KEY_PATH_ENTRIES; i++) {
|
|
||||||
// check high bit for hardened keys
|
// check high bit for hardened keys
|
||||||
if (! (key_path[i] & 0x80000000)) {
|
if (! (key_path[i] & 0x80000000)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -557,8 +555,6 @@ void u2f_register(const APDU *a)
|
|||||||
|
|
||||||
// First Time request, return not present and display request dialog
|
// First Time request, return not present and display request dialog
|
||||||
if (last_req_state == INIT) {
|
if (last_req_state == INIT) {
|
||||||
// wake up crypto system to be ready for signing
|
|
||||||
getDerivedNode(NULL, 0);
|
|
||||||
// error: testof-user-presence is required
|
// error: testof-user-presence is required
|
||||||
buttonUpdate(); // Clear button state
|
buttonUpdate(); // Clear button state
|
||||||
if (0 == memcmp(req->appId, BOGUS_APPID, U2F_APPID_SIZE)) {
|
if (0 == memcmp(req->appId, BOGUS_APPID, U2F_APPID_SIZE)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user