Added U2F root key to storage.

Fixes #251.
This commit is contained in:
Jochen Hoenicke 2017-12-13 10:53:27 +01:00 committed by Pavol Rusnak
parent 88563ebaa5
commit 73edc7cb74
3 changed files with 61 additions and 13 deletions

View File

@ -41,6 +41,7 @@
#include "layout2.h"
#include "usb.h"
#include "gettext.h"
#include "u2f.h"
/* magic constant to check validity of storage block */
static const uint32_t storage_magic = 0x726f7473; // 'stor' as uint32_t
@ -108,7 +109,7 @@ static bool sessionPinCached;
static bool sessionPassphraseCached;
static char CONFIDENTIAL sessionPassphrase[51];
#define STORAGE_VERSION 8
#define STORAGE_VERSION 9
void storage_show_error(void)
{
@ -126,6 +127,7 @@ void storage_check_flash_errors(void)
bool storage_from_flash(void)
{
storage_clear_update();
if (memcmp((void *)FLASH_STORAGE_START, &storage_magic, sizeof(storage_magic)) != 0) {
// wrong magic
return false;
@ -140,6 +142,7 @@ bool storage_from_flash(void)
// version 6: since 1.3.6
// version 7: since 1.5.1
// version 8: since 1.5.2
// version 9: since 1.6.1
if (version > STORAGE_VERSION) {
// downgrade -> clear storage
return false;
@ -156,13 +159,20 @@ bool storage_from_flash(void)
old_storage_size = 460;
} else
if (version == 3 || version == 4 || version == 5) {
// added homescreen
old_storage_size = 1488;
} else
if (version == 6 || version == 7) {
// added u2fcounter
old_storage_size = 1496;
} else
if (version == 8) {
// added flags and needsBackup
old_storage_size = 1504;
} else
if (version == 9) {
// added u2froot
old_storage_size = 1704;
}
// erase newly added fields
@ -204,8 +214,15 @@ bool storage_from_flash(void)
storage_u2f_offset++;
u2fword >>= 1;
}
// note: we don't update storage version on flash at this point,
// but it is already upgraded when it comes to content
// force recomputing u2f root for storage version < 9.
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;
}
@ -242,6 +259,29 @@ static uint32_t storage_flash_words(uint32_t addr, const uint32_t *src, int nwor
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 NULL - do not backup original content - essentialy a wipe
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));
storageUpdate.has_mnemonic = storageRom->has_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) {
storageUpdate.has_passphrase_protection = storageRom->has_passphrase_protection;
@ -467,6 +512,12 @@ const uint8_t *storage_getSeed(bool usePassphrase)
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)
{
// if storage has node, decrypt and use it

View File

@ -37,6 +37,7 @@ void storage_loadDevice(LoadDevice *msg);
const uint8_t *storage_getSeed(bool usePassphrase);
bool storage_getU2FRoot(HDNode *node);
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase);
const char *storage_getLabel(void);

View File

@ -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)
// 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
// 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 CONFIDENTIAL HDNode node;
if (!storage_getRootNode(&node, NIST256P1_NAME, false)) {
if (!storage_getU2FRoot(&node)) {
layoutHome();
debugLog(0, "", "ERR: Device not init");
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'
uint32_t key_path[KEY_PATH_ENTRIES];
key_path[0] = U2F_KEY_PATH;
for (uint32_t i = 1; i < KEY_PATH_ENTRIES; i++) {
for (uint32_t i = 0; 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);
memcpy(key_handle, key_path, KEY_PATH_LEN);
// prepare keypair from /random data
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[])
{
uint32_t key_path[KEY_PATH_ENTRIES];
key_path[0] = U2F_KEY_PATH;
memcpy(&key_path[1], key_handle, KEY_PATH_LEN);
for (unsigned int i = 1; i < KEY_PATH_ENTRIES; i++) {
memcpy(key_path, key_handle, KEY_PATH_LEN);
for (unsigned int i = 0; i < KEY_PATH_ENTRIES; i++) {
// check high bit for hardened keys
if (! (key_path[i] & 0x80000000)) {
return NULL;
@ -557,8 +555,6 @@ void u2f_register(const APDU *a)
// First Time request, return not present and display request dialog
if (last_req_state == INIT) {
// wake up crypto system to be ready for signing
getDerivedNode(NULL, 0);
// error: testof-user-presence is required
buttonUpdate(); // Clear button state
if (0 == memcmp(req->appId, BOGUS_APPID, U2F_APPID_SIZE)) {