Merge branch 'trezor-storage-integration'

pull/25/head
Pavol Rusnak 5 years ago
commit 92382d3f2c
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

3
.gitmodules vendored

@ -13,3 +13,6 @@
[submodule "vendor/nanopb"]
path = vendor/nanopb
url = https://github.com/nanopb/nanopb.git
[submodule "vendor/trezor-storage"]
path = vendor/trezor-storage
url = https://github.com/trezor/trezor-storage.git

@ -3,6 +3,8 @@ OBJS += startup.o
endif
OBJS += buttons.o
OBJS += common.o
OBJS += flash.o
OBJS += layout.o
OBJS += oled.o
OBJS += rng.o

@ -72,7 +72,8 @@ CFLAGS += $(OPTFLAGS) \
-I$(TOP_DIR) \
-I$(TOP_DIR)gen \
-I$(TOP_DIR)vendor/trezor-crypto \
-I$(TOP_DIR)vendor/trezor-qrenc
-I$(TOP_DIR)vendor/trezor-qrenc \
-I$(TOP_DIR)vendor/trezor-storage
LDFLAGS += -L$(TOP_DIR) \
$(DBGFLAGS) \

@ -0,0 +1,84 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "common.h"
#include "rng.h"
#include "layout.h"
#include "oled.h"
#include "firmware/usb.h"
uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
static void __attribute__((noreturn)) shutdown(void)
{
for (;;);
}
void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, const char *file, int line_num, const char *func) {
const BITMAP *icon = &bmp_icon_error;
char line[128] = {0};
int y = icon->height + 3;
oledClear();
oledDrawBitmap(0, 0, icon);
oledDrawStringCenter(OLED_WIDTH / 2, (icon->height - FONT_HEIGHT)/2 + 1, "FATAL ERROR", FONT_STANDARD);
snprintf(line, sizeof(line), "Expr: %s", expr ? expr : "(null)");
oledDrawString(0, y, line, FONT_STANDARD);
y += FONT_HEIGHT + 1;
snprintf(line, sizeof(line), "Msg: %s", msg ? msg : "(null)");
oledDrawString(0, y, line, FONT_STANDARD);
y += FONT_HEIGHT + 1;
const char *label = "File: ";
snprintf(line, sizeof(line), "%s:%d", file ? file : "(null)", line_num);
oledDrawStringRight(OLED_WIDTH - 1, y, line, FONT_STANDARD);
oledBox(0, y, oledStringWidth(label, FONT_STANDARD), y + FONT_HEIGHT, false);
oledDrawString(0, y, label, FONT_STANDARD);
y += FONT_HEIGHT + 1;
snprintf(line, sizeof(line), "Func: %s", func ? func : "(null)");
oledDrawString(0, y, line, FONT_STANDARD);
y += FONT_HEIGHT + 1;
oledDrawString(0, y, "Contact TREZOR support.", FONT_STANDARD);
oledRefresh();
shutdown();
for (;;);
}
void __attribute__((noreturn)) error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4) {
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, line3, line4, "Please unplug", "the device.");
shutdown();
for (;;);
}
#ifndef NDEBUG
void __assert_func(const char *file, int line, const char *func, const char *expr) {
__fatal_error(expr, "assert failed", file, line, func);
}
#endif
void hal_delay(uint32_t ms)
{
usbSleep(ms);
}

@ -0,0 +1,36 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TREZORHAL_COMMON_H__
#define __TREZORHAL_COMMON_H__
#include <stdint.h>
#include "secbool.h"
#define HW_ENTROPY_LEN 12
extern uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, const char *file, int line, const char *func);
void __attribute__((noreturn)) error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4);
#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__))
void hal_delay(uint32_t ms);
#endif

@ -3,7 +3,7 @@ EMULATOR := 1
OBJS += setup.o
OBJS += buttons.o
OBJS += flash.o
OBJS += memory.o
OBJS += oled.o
OBJS += rng.o
OBJS += timer.o

@ -27,6 +27,6 @@ uint32_t timer_ms(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
uint32_t msec = t.tv_sec * 1000 + (t.tv_nsec / 1000000);
uint32_t msec = t.tv_sec * 1000 + (t.tv_nsec / 1000000);
return msec;
}

@ -11,7 +11,7 @@ endif
OBJS += u2f.o
OBJS += messages.o
OBJS += storage.o
OBJS += config.o
OBJS += trezor.o
OBJS += pinmatrix.o
OBJS += fsm.o
@ -75,10 +75,18 @@ OBJS += ../vendor/trezor-crypto/aes/aeskey.o
OBJS += ../vendor/trezor-crypto/aes/aestab.o
OBJS += ../vendor/trezor-crypto/aes/aes_modes.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/chacha20poly1305.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/chacha_merged.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/poly1305-donna.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/rfc7539.o
OBJS += ../vendor/trezor-crypto/nem.o
OBJS += ../vendor/trezor-qrenc/qr_encode.o
OBJS += ../vendor/trezor-storage/storage.o
OBJS += ../vendor/trezor-storage/norcow.o
OBJS += ../vendor/nanopb/pb_common.o
OBJS += ../vendor/nanopb/pb_decode.o
OBJS += ../vendor/nanopb/pb_encode.o

@ -0,0 +1,905 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdint.h>
#include <libopencm3/stm32/flash.h>
#include "messages.pb.h"
#include "common.h"
#include "trezor.h"
#include "sha2.h"
#include "aes/aes.h"
#include "pbkdf2.h"
#include "hmac.h"
#include "bip32.h"
#include "bip39.h"
#include "curves.h"
#include "util.h"
#include "memory.h"
#include "rng.h"
#include "config.h"
#include "debug.h"
#include "protect.h"
#include "layout2.h"
#include "usb.h"
#include "gettext.h"
#include "u2f.h"
#include "memzero.h"
#include "supervise.h"
#include "storage.h"
/* Magic constants to check validity of storage block for storage versions 1 to 10. */
static const uint32_t CONFIG_MAGIC_V10 = 0x726f7473; // 'stor' as uint32_t
#if !EMULATOR
static const uint32_t META_MAGIC_V10 = 0x525a5254; // 'TRZR' as uint32_t
#else
static const uint32_t META_MAGIC_V10 = 0xFFFFFFFF;
#endif
#define APP 0x0100
#define FLAG_PUBLIC 0x8000
#define FLAGS_WRITE 0xC000
#define KEY_UUID ( 0 | APP | FLAG_PUBLIC) // bytes(12)
#define KEY_VERSION ( 1 | APP) // uint32
#define KEY_MNEMONIC ( 2 | APP) // string(241)
#define KEY_LANGUAGE ( 3 | APP | FLAG_PUBLIC) // string(17)
#define KEY_LABEL ( 4 | APP | FLAG_PUBLIC) // string(33)
#define KEY_PASSPHRASE_PROTECTION ( 5 | APP | FLAG_PUBLIC) // bool
#define KEY_HOMESCREEN ( 6 | APP | FLAG_PUBLIC) // bytes(1024)
#define KEY_NEEDS_BACKUP ( 7 | APP) // bool
#define KEY_FLAGS ( 8 | APP) // uint32
#define KEY_U2F_COUNTER ( 9 | APP | FLAGS_WRITE) // uint32
#define KEY_UNFINISHED_BACKUP ( 11 | APP) // bool
#define KEY_AUTO_LOCK_DELAY_MS ( 12 | APP) // uint32
#define KEY_NO_BACKUP ( 13 | APP) // bool
#define KEY_INITIALIZED ( 14 | APP | FLAG_PUBLIC) // uint32
#define KEY_NODE ( 15 | APP) // node
#define KEY_IMPORTED ( 16 | APP) // bool
#define KEY_U2F_ROOT ( 17 | APP | FLAG_PUBLIC) // node
#define KEY_DEBUG_LINK_PIN (255 | APP | FLAG_PUBLIC) // string(10)
// The PIN value corresponding to an empty PIN.
static const uint32_t PIN_EMPTY = 1;
static uint32_t config_uuid[UUID_SIZE / sizeof(uint32_t)];
_Static_assert(sizeof(config_uuid) == UUID_SIZE, "config_uuid has wrong size");
char config_uuid_str[2*UUID_SIZE + 1];
/*
Old storage layout:
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
The pinfail is a binary number of the form 1...10...0,
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.
*/
/* Current u2f offset, i.e. u2f counter is
* storage.u2f_counter + config_u2f_offset.
* This corresponds to the number of cleared bits in the U2FAREA.
*/
static secbool sessionSeedCached, sessionSeedUsesPassphrase;
static uint8_t CONFIDENTIAL sessionSeed[64];
static secbool sessionPassphraseCached = secfalse;
static char CONFIDENTIAL sessionPassphrase[51];
#define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes
static secbool autoLockDelayMsCached = secfalse;
static uint32_t autoLockDelayMs = autoLockDelayMsDefault;
static const uint32_t CONFIG_VERSION = 11;
static const uint8_t FALSE_BYTE = '\x00';
static const uint8_t TRUE_BYTE = '\x01';
static uint32_t pin_to_int(const char *pin)
{
uint32_t val = 1;
size_t i = 0;
for (i = 0; i < MAX_PIN_LEN && pin[i] != '\0'; ++i) {
if (pin[i] < '0' || pin[i] > '9') {
return 0;
}
val = 10*val + pin[i] - '0';
}
if (pin[i] != '\0') {
return 0;
}
return val;
}
static secbool config_set_bool(uint16_t key, bool value)
{
if (value) {
return storage_set(key, &TRUE_BYTE, sizeof(TRUE_BYTE));
} else {
return storage_set(key, &FALSE_BYTE, sizeof(FALSE_BYTE));
}
}
static secbool config_get_bool(uint16_t key, bool *value)
{
uint8_t val = 0;
uint16_t len = 0;
if (sectrue == storage_get(key, &val, sizeof(val), &len) && len == sizeof(TRUE_BYTE)) {
*value = (val == TRUE_BYTE);
return sectrue;
} else {
*value = false;
return secfalse;
}
}
static secbool config_get_string(uint16_t key, char *dest, uint16_t dest_size)
{
if (dest_size == 0) {
return secfalse;
}
uint16_t len = 0;
if (sectrue != storage_get(key, dest, dest_size - 1, &len)) {
dest[0] = '\0';
return secfalse;
}
dest[len] = '\0';
return sectrue;
}
static secbool config_get_uint32(uint16_t key, uint32_t *value)
{
uint16_t len = 0;
if (sectrue != storage_get(key, value, sizeof(uint32_t), &len) || len != sizeof(uint32_t)) {
*value = 0;
return secfalse;
}
return sectrue;
}
static secbool config_upgrade_v10(void)
{
#define OLD_STORAGE_SIZE(last_member) (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + 3) & ~3)
if (memcmp(FLASH_PTR(FLASH_META_MAGIC), &META_MAGIC_V10, sizeof(META_MAGIC_V10)) != 0 ||
memcmp(FLASH_PTR(FLASH_STORAGE_START), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) {
// wrong magic
return secfalse;
}
Storage config __attribute__((aligned(4)));
_Static_assert((sizeof(config) & 3) == 0, "storage unaligned");
memcpy(config_uuid, FLASH_PTR(FLASH_STORAGE_START + sizeof(CONFIG_MAGIC_V10)), sizeof(config_uuid));
memcpy(&config, FLASH_PTR(FLASH_STORAGE_START + sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)), sizeof(config));
// version 1: since 1.0.0
// version 2: since 1.2.1
// version 3: since 1.3.1
// version 4: since 1.3.2
// version 5: since 1.3.3
// version 6: since 1.3.6
// version 7: since 1.5.1
// version 8: since 1.5.2
// version 9: since 1.6.1
// version 10: since 1.7.2
if (config.version > CONFIG_VERSION) {
// downgrade -> clear storage
config_wipe();
return secfalse;
}
size_t old_config_size = 0;
if (config.version == 0) {
} else if (config.version <= 2) {
old_config_size = OLD_STORAGE_SIZE(imported);
} else if (config.version <= 5) {
// added homescreen
old_config_size = OLD_STORAGE_SIZE(homescreen);
} else if (config.version <= 7) {
// added u2fcounter
old_config_size = OLD_STORAGE_SIZE(u2f_counter);
} else if (config.version <= 8) {
// added flags and needsBackup
old_config_size = OLD_STORAGE_SIZE(flags);
} else if (config.version <= 9) {
// added u2froot, unfinished_backup and auto_lock_delay_ms
old_config_size = OLD_STORAGE_SIZE(auto_lock_delay_ms);
} else if (config.version <= 10) {
// added no_backup
old_config_size = OLD_STORAGE_SIZE(no_backup);
}
// Erase newly added fields.
if (old_config_size != sizeof(Storage)) {
memzero(&config + old_config_size, sizeof(Storage) - old_config_size);
}
const uint32_t FLASH_STORAGE_PINAREA = FLASH_META_START + 0x4000;
uint32_t pin_wait = 0;
if (config.version <= 5) {
// Get PIN failure counter from version 5 format.
uint32_t pinctr = config.has_pin_failed_attempts ? config.pin_failed_attempts : 0;
if (pinctr > 31) {
pinctr = 31;
}
pin_wait = (1 << pinctr) - 1;
} else {
// Get PIN failure counter from version 10 format.
uint32_t flash_pinfails = FLASH_STORAGE_PINAREA;
while (*(const uint32_t*)FLASH_PTR(flash_pinfails) == 0) {
flash_pinfails += sizeof(uint32_t);
}
pin_wait = ~*(const uint32_t*)FLASH_PTR(flash_pinfails);
}
uint32_t u2f_offset = 0;
if (config.has_u2f_counter) {
const uint32_t FLASH_STORAGE_U2FAREA = FLASH_STORAGE_PINAREA + 0x1000;
const uint32_t *u2fptr = (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA);
while (*u2fptr == 0) {
u2fptr++;
}
u2f_offset = 32 * (u2fptr - (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA));
uint32_t u2fword = *u2fptr;
while ((u2fword & 1) == 0) {
u2f_offset++;
u2fword >>= 1;
}
}
storage_init(NULL, HW_ENTROPY_DATA, HW_ENTROPY_LEN);
storage_unlock(PIN_EMPTY);
if (config.has_pin) {
storage_change_pin(PIN_EMPTY, pin_to_int(config.pin));
}
while (pin_wait != 0) {
storage_pin_fails_increase();
pin_wait >>= 1;
}
storage_set(KEY_UUID, config_uuid, sizeof(config_uuid));
storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION));
if (config.has_node) {
if (sectrue == storage_set(KEY_NODE, &config.node, sizeof(config.node))) {
config_set_bool(KEY_INITIALIZED, true);
}
}
if (config.has_mnemonic) {
config_setMnemonic(config.mnemonic);
}
if (config.has_passphrase_protection) {
config_setPassphraseProtection(config.passphrase_protection);
}
if (config.has_language) {
config_setLanguage(config.language);
}
if (config.has_label) {
config_setLabel(config.label);
}
if (config.has_imported) {
config_setImported(config.imported);
}
if (config.has_homescreen) {
config_setHomescreen(config.homescreen.bytes, config.homescreen.size);
}
if (config.has_u2f_counter) {
config_setU2FCounter(config.u2f_counter + u2f_offset);
}
if (config.has_needs_backup) {
config_setNeedsBackup(config.needs_backup);
}
if (config.has_flags) {
config_applyFlags(config.flags);
}
if (config.has_unfinished_backup) {
config_setUnfinishedBackup(config.unfinished_backup);
}
if (config.has_auto_lock_delay_ms) {
config_setAutoLockDelayMs(config.auto_lock_delay_ms);
}
if (config.has_no_backup && config.no_backup) {
config_setNoBackup();
}
memzero(&config, sizeof(config));
session_clear(true);
return sectrue;
}
void config_init(void)
{
config_upgrade_v10();
storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN);
memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA));
uint16_t len = 0;
if (sectrue == storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) && len == sizeof(config_uuid)) {
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
} else {
config_wipe();
}
}
void session_clear(bool lock)
{
sessionSeedCached = secfalse;
memzero(&sessionSeed, sizeof(sessionSeed));
sessionPassphraseCached = secfalse;
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
if (lock) {
storage_lock();
}
}
static void get_u2froot_callback(uint32_t iter, uint32_t total)
{
layoutProgress(_("Updating"), 1000 * iter / total);
}
static void config_compute_u2froot(const char* mnemonic, StorageHDNode *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));
memzero(&node, sizeof(node));
session_clear(false); // invalidate seed cache
}
static void config_setNode(const HDNodeType *node) {
StorageHDNode storageHDNode;
memzero(&storageHDNode, sizeof(storageHDNode));
storageHDNode.depth = node->depth;
storageHDNode.fingerprint = node->fingerprint;
storageHDNode.child_num = node->child_num;
storageHDNode.chain_code.size = 32;
memcpy(storageHDNode.chain_code.bytes, node->chain_code.bytes, 32);
if (node->has_private_key) {
storageHDNode.has_private_key = true;
storageHDNode.private_key.size = 32;
memcpy(storageHDNode.private_key.bytes, node->private_key.bytes, 32);
}
if (sectrue == storage_set(KEY_NODE, &storageHDNode, sizeof(storageHDNode))) {
config_set_bool(KEY_INITIALIZED, true);
}
memzero(&storageHDNode, sizeof(storageHDNode));
}
#if DEBUG_LINK
bool config_dumpNode(HDNodeType *node)
{
memzero(node, sizeof(HDNodeType));
StorageHDNode storageNode;
uint16_t len = 0;
if (sectrue != storage_get(KEY_NODE, &storageNode, sizeof(storageNode), &len) || len != sizeof(StorageHDNode)) {
memzero(&storageNode, sizeof(storageNode));
return false;
}
node->depth = storageNode.depth;
node->fingerprint = storageNode.fingerprint;
node->child_num = storageNode.child_num;
node->chain_code.size = 32;
memcpy(node->chain_code.bytes, storageNode.chain_code.bytes, 32);
if (storageNode.has_private_key) {
node->has_private_key = true;
node->private_key.size = 32;
memcpy(node->private_key.bytes, storageNode.private_key.bytes, 32);
}
memzero(&storageNode, sizeof(storageNode));
return true;
}
#endif
void config_loadDevice(const LoadDevice *msg)
{
session_clear(false);
config_set_bool(KEY_IMPORTED, true);
config_setPassphraseProtection(msg->has_passphrase_protection && msg->passphrase_protection);
if (msg->has_pin) {
config_changePin("", msg->pin);
}
if (msg->has_node) {
storage_delete(KEY_MNEMONIC);
config_setNode(&(msg->node));
} else if (msg->has_mnemonic) {
storage_delete(KEY_NODE);
config_setMnemonic(msg->mnemonic);
}
if (msg->has_language) {
config_setLanguage(msg->language);
}
config_setLabel(msg->has_label ? msg->label : "");
if (msg->has_u2f_counter) {
config_setU2FCounter(msg->u2f_counter);
}
}
void config_setLabel(const char *label)
{
if (label == NULL || label[0] == '\0') {
storage_delete(KEY_LABEL);
} else {
storage_set(KEY_LABEL, label, strnlen(label, MAX_LABEL_LEN));
}
}
void config_setLanguage(const char *lang)
{
if (lang == NULL) {
return;
}
// Sanity check.
if (strcmp(lang, "english") != 0) {
return;
}
storage_set(KEY_LANGUAGE, lang, strnlen(lang, MAX_LANGUAGE_LEN));
}
void config_setPassphraseProtection(bool passphrase_protection)
{
sessionSeedCached = secfalse;
sessionPassphraseCached = secfalse;
config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
}
bool config_getPassphraseProtection(bool *passphrase_protection)
{
return sectrue == config_get_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
}
void config_setHomescreen(const uint8_t *data, uint32_t size)
{
if (data != NULL && size == HOMESCREEN_SIZE) {
storage_set(KEY_HOMESCREEN, data, size);
} else {
storage_delete(KEY_HOMESCREEN);
}
}
static void get_root_node_callback(uint32_t iter, uint32_t total)
{
usbSleep(1);
layoutProgress(_("Waking up"), 1000 * iter / total);
}
const uint8_t *config_getSeed(bool usePassphrase)
{
// root node is properly cached
if (usePassphrase == (sectrue == sessionSeedUsesPassphrase)
&& sectrue == sessionSeedCached) {
return sessionSeed;
}
// if storage has mnemonic, convert it to node and use it
char mnemonic[MAX_MNEMONIC_LEN + 1];
if (config_getMnemonic(mnemonic, sizeof(mnemonic))) {
if (usePassphrase && !protectPassphrase()) {
memzero(mnemonic, sizeof(mnemonic));
return NULL;
}
// if storage was not imported (i.e. it was properly generated or recovered)
bool imported = false;
config_get_bool(KEY_IMPORTED, &imported);
if (!imported) {
// test whether mnemonic is a valid BIP-0039 mnemonic
if (!mnemonic_check(mnemonic)) {
// and if not then halt the device
error_shutdown(_("Storage failure"), _("detected."), NULL, NULL);
}
}
char oldTiny = usbTiny(1);
mnemonic_to_seed(mnemonic, usePassphrase ? sessionPassphrase : "", sessionSeed, get_root_node_callback); // BIP-0039
memzero(mnemonic, sizeof(mnemonic));
usbTiny(oldTiny);
sessionSeedCached = sectrue;
sessionSeedUsesPassphrase = usePassphrase ? sectrue : secfalse;
return sessionSeed;
}
return NULL;
}
static bool config_loadNode(const StorageHDNode *node, const char *curve, HDNode *out) {
return hdnode_from_xprv(node->depth, node->child_num, node->chain_code.bytes, node->private_key.bytes, curve, out);
}
bool config_getU2FRoot(HDNode *node)
{
StorageHDNode u2fNode;
uint16_t len = 0;
if (sectrue != storage_get(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode), &len) || len != sizeof(StorageHDNode)) {
memzero(&u2fNode, sizeof(u2fNode));
return false;
}
bool ret = config_loadNode(&u2fNode, NIST256P1_NAME, node);
memzero(&u2fNode, sizeof(u2fNode));
return ret;
}
bool config_getRootNode(HDNode *node, const char *curve, bool usePassphrase)
{
// if storage has node, decrypt and use it
StorageHDNode storageHDNode;
uint16_t len = 0;
if (strcmp(curve, SECP256K1_NAME) == 0 && sectrue == storage_get(KEY_NODE, &storageHDNode, sizeof(storageHDNode), &len) && len == sizeof(StorageHDNode)) {
if (!protectPassphrase()) {
memzero(&storageHDNode, sizeof(storageHDNode));
return false;
}
if (!config_loadNode(&storageHDNode, curve, node)) {
memzero(&storageHDNode, sizeof(storageHDNode));
return false;
}
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (passphrase_protection && sectrue == sessionPassphraseCached && sessionPassphrase[0] != '\0') {
// decrypt hd node
uint8_t secret[64];
PBKDF2_HMAC_SHA512_CTX pctx;
char oldTiny = usbTiny(1);
pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), (const uint8_t *)"TREZORHD", 8, 1);
get_root_node_callback(0, BIP39_PBKDF2_ROUNDS);
for (int i = 0; i < 8; i++) {
pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8);
get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8, BIP39_PBKDF2_ROUNDS);
}
pbkdf2_hmac_sha512_Final(&pctx, secret);
usbTiny(oldTiny);
aes_decrypt_ctx ctx;
aes_decrypt_key256(secret, &ctx);
aes_cbc_decrypt(node->chain_code, node->chain_code, 32, secret + 32, &ctx);
aes_cbc_decrypt(node->private_key, node->private_key, 32, secret + 32, &ctx);
}
return true;
}
memzero(&storageHDNode, sizeof(storageHDNode));
const uint8_t *seed = config_getSeed(usePassphrase);
if (seed == NULL) {
return false;
}
return hdnode_from_seed(seed, 64, curve, node);
}
bool config_getLabel(char *dest, uint16_t dest_size)
{
return sectrue == config_get_string(KEY_LABEL, dest, dest_size);
}
bool config_getLanguage(char *dest, uint16_t dest_size)
{
return sectrue == config_get_string(KEY_LANGUAGE, dest, dest_size);
}
bool config_getHomescreen(uint8_t *dest, uint16_t dest_size)
{
uint16_t len = 0;
secbool ret = storage_get(KEY_HOMESCREEN, dest, dest_size, &len);
if (sectrue != ret || len != HOMESCREEN_SIZE) {
return false;
}
return true;
}
bool config_setMnemonic(const char *mnemonic)
{
if (mnemonic == NULL) {
return false;
}
if (sectrue != storage_set(KEY_MNEMONIC, mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN))) {
return false;
}
StorageHDNode u2fNode;
memzero(&u2fNode, sizeof(u2fNode));
config_compute_u2froot(mnemonic, &u2fNode);
secbool ret = storage_set(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode));
memzero(&u2fNode, sizeof(u2fNode));
if (sectrue != ret) {
storage_delete(KEY_MNEMONIC);
return false;
}
config_set_bool(KEY_INITIALIZED, true);
return true;
}
bool config_getMnemonic(char *dest, uint16_t dest_size)
{
return sectrue == config_get_string(KEY_MNEMONIC, dest, dest_size);
}
/* Check whether mnemonic matches storage. The mnemonic must be
* a null-terminated string.
*/
bool config_containsMnemonic(const char *mnemonic)
{
uint16_t len = 0;
uint8_t stored_mnemonic[MAX_MNEMONIC_LEN + 1];
if (sectrue != storage_get(KEY_MNEMONIC, stored_mnemonic, MAX_MNEMONIC_LEN, &len)) {
return false;
}
stored_mnemonic[len] = '\0';
/* The execution time of the following code only depends on the
* (public) input. This avoids timing attacks.
*/
char diff = 0;
uint32_t i = 0;
for (; mnemonic[i]; i++) {
diff |= (stored_mnemonic[i] - mnemonic[i]);
}
diff |= stored_mnemonic[i];
memzero(stored_mnemonic, sizeof(stored_mnemonic));
return diff == 0;
}
/* Check whether pin matches storage. The pin must be
* a null-terminated string with at most 9 characters.
*/
bool config_containsPin(const char *pin)
{
return sectrue == storage_unlock(pin_to_int(pin));
}
bool config_hasPin(void)
{
return sectrue == storage_has_pin();
}
bool config_changePin(const char *old_pin, const char *new_pin)
{
uint32_t new_pin_int = pin_to_int(new_pin);
if (new_pin_int == 0) {
return false;
}
secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int);
#if DEBUG_LINK
if (sectrue == ret) {
if (new_pin_int != PIN_EMPTY) {
storage_set(KEY_DEBUG_LINK_PIN, new_pin, strnlen(new_pin, MAX_PIN_LEN));
} else {
storage_delete(KEY_DEBUG_LINK_PIN);
}
}
#endif
memzero(&new_pin_int, sizeof(new_pin_int));
return sectrue == ret;
}
#if DEBUG_LINK
bool config_getPin(char *dest, uint16_t dest_size)
{
return sectrue == config_get_string(KEY_DEBUG_LINK_PIN, dest, dest_size);
}
#endif
void session_cachePassphrase(const char *passphrase)
{
strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase));
sessionPassphraseCached = sectrue;
}
bool session_isPassphraseCached(void)
{
return sectrue == sessionPassphraseCached;
}
bool session_getState(const uint8_t *salt, uint8_t *state, const char *passphrase)
{
if (!passphrase && sectrue != sessionPassphraseCached) {
return false;
} else {
passphrase = sessionPassphrase;
}
if (!salt) {
// if salt is not provided fill the first half of the state with random data
random_buffer(state, 32);
} else {
// if salt is provided fill the first half of the state with salt
memcpy(state, salt, 32);
}
// state[0:32] = salt
// state[32:64] = HMAC(passphrase, salt || device_id)
HMAC_SHA256_CTX ctx;
hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase));
hmac_sha256_Update(&ctx, state, 32);
hmac_sha256_Update(&ctx, (const uint8_t *)config_uuid, sizeof(config_uuid));
hmac_sha256_Final(&ctx, state + 32);
memzero(&ctx, sizeof(ctx));
return true;
}
bool session_isUnlocked(void)
{
return sectrue == storage_is_unlocked();
}
bool config_isInitialized(void)
{
bool initialized = false;
config_get_bool(KEY_INITIALIZED, &initialized);
return initialized;
}
bool config_getImported(bool* imported)
{
return sectrue == config_get_bool(KEY_IMPORTED, imported);
}
void config_setImported(bool imported)
{
config_set_bool(KEY_IMPORTED, imported);
}
bool config_getNeedsBackup(bool *needs_backup)
{
return sectrue == config_get_bool(KEY_NEEDS_BACKUP, needs_backup);
}
void config_setNeedsBackup(bool needs_backup)
{
config_set_bool(KEY_NEEDS_BACKUP, needs_backup);
}
bool config_getUnfinishedBackup(bool *unfinished_backup)
{
return sectrue == config_get_bool(KEY_UNFINISHED_BACKUP, unfinished_backup);
}
void config_setUnfinishedBackup(bool unfinished_backup)
{
config_set_bool(KEY_UNFINISHED_BACKUP, unfinished_backup);
}
bool config_getNoBackup(bool *no_backup)
{
return sectrue == config_get_bool(KEY_NO_BACKUP, no_backup);
}
void config_setNoBackup(void)
{
config_set_bool(KEY_NO_BACKUP, true);
}
void config_applyFlags(uint32_t flags)
{
uint32_t old_flags = 0;
config_get_uint32(KEY_FLAGS, &old_flags);
flags |= old_flags;
if (flags == old_flags) {
return; // no new flags
}
storage_set(KEY_FLAGS, &flags, sizeof(flags));
}
bool config_getFlags(uint32_t *flags)
{
return sectrue == config_get_uint32(KEY_FLAGS, flags);
}
uint32_t config_nextU2FCounter(void)
{
uint32_t u2fcounter = 0;
storage_next_counter(KEY_U2F_COUNTER, &u2fcounter);
return u2fcounter;
}
void config_setU2FCounter(uint32_t u2fcounter)
{
storage_set_counter(KEY_U2F_COUNTER, u2fcounter);
}
uint32_t config_getAutoLockDelayMs()
{
if (sectrue == autoLockDelayMsCached) {
return autoLockDelayMs;
}
if (sectrue != storage_is_unlocked()) {
return autoLockDelayMsDefault;
}
if (sectrue != config_get_uint32(KEY_AUTO_LOCK_DELAY_MS, &autoLockDelayMs)) {
autoLockDelayMs = autoLockDelayMsDefault;
}
autoLockDelayMsCached = sectrue;
return autoLockDelayMs;
}
void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms)
{
const uint32_t min_delay_ms = 10 * 1000U; // 10 seconds
auto_lock_delay_ms = MAX(auto_lock_delay_ms, min_delay_ms);
if (sectrue == storage_set(KEY_AUTO_LOCK_DELAY_MS, &auto_lock_delay_ms, sizeof(auto_lock_delay_ms))) {
autoLockDelayMs = auto_lock_delay_ms;
autoLockDelayMsCached = sectrue;
}
}
void config_wipe(void)
{
storage_wipe();
storage_unlock(PIN_EMPTY);
random_buffer((uint8_t *)config_uuid, sizeof(config_uuid));
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
autoLockDelayMsCached = secfalse;
storage_set(KEY_UUID, config_uuid, sizeof(config_uuid));
storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION));
session_clear(false);
}

@ -17,8 +17,8 @@
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STORAGE_H__
#define __STORAGE_H__
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include "bip32.h"
#include "messages-management.pb.h"
@ -76,85 +76,81 @@ typedef struct _Storage {
STORAGE_BOOL (no_backup)
} Storage;
extern Storage storageUpdate;
extern Storage configUpdate;
void storage_init(void);
void storage_generate_uuid(void);
void storage_clear_update(void);
void storage_update(void);
void session_clear(bool clear_pin);
#define MAX_PIN_LEN 9
#define MAX_LABEL_LEN 32
#define MAX_LANGUAGE_LEN 16
#define MAX_MNEMONIC_LEN 240
#define HOMESCREEN_SIZE 1024
#define UUID_SIZE 12
void storage_loadDevice(const LoadDevice *msg);
void config_init(void);
void session_clear(bool lock);
const uint8_t *storage_getSeed(bool usePassphrase);
void config_loadDevice(const LoadDevice *msg);
bool storage_getU2FRoot(HDNode *node);
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase);
const uint8_t *config_getSeed(bool usePassphrase);
const char *storage_getLabel(void);
void storage_setLabel(const char *label);
bool config_getU2FRoot(HDNode *node);
bool config_getRootNode(HDNode *node, const char *curve, bool usePassphrase);
const char *storage_getLanguage(void);
void storage_setLanguage(const char *lang);
bool config_getLabel(char *dest, uint16_t dest_size);
void config_setLabel(const char *label);
void storage_setPassphraseProtection(bool passphrase_protection);
bool storage_hasPassphraseProtection(void);
bool config_getLanguage(char *dest, uint16_t dest_size);
void config_setLanguage(const char *lang);
const uint8_t *storage_getHomescreen(void);
void storage_setHomescreen(const uint8_t *data, uint32_t size);
void config_setPassphraseProtection(bool passphrase_protection);
bool config_getPassphraseProtection(bool *passphrase_protection);
bool config_getHomescreen(uint8_t *dest, uint16_t dest_size);
void config_setHomescreen(const uint8_t *data, uint32_t size);
void session_cachePassphrase(const char *passphrase);
bool session_isPassphraseCached(void);
bool session_getState(const uint8_t *salt, uint8_t *state, const char *passphrase);
void storage_setMnemonic(const char *mnemonic);
bool storage_containsMnemonic(const char *mnemonic);
bool storage_hasMnemonic(void);
const char *storage_getMnemonic(void);
bool config_setMnemonic(const char *mnemonic);
bool config_containsMnemonic(const char *mnemonic);
bool config_getMnemonic(char *dest, uint16_t dest_size);
bool storage_hasNode(void);
#if DEBUG_LINK
void storage_dumpNode(HDNodeType *node);
bool config_dumpNode(HDNodeType *node);
bool config_getPin(char *dest, uint16_t dest_size);
#endif
bool storage_containsPin(const char *pin);
bool storage_hasPin(void);
const char *storage_getPin(void);
void storage_setPin(const char *pin);
void session_cachePin(void);
void session_uncachePin(void);
bool session_isPinCached(void);
void storage_clearPinArea(void);
void storage_resetPinFails(uint32_t flash_pinfails);
bool storage_increasePinFails(uint32_t flash_pinfails);
uint32_t storage_getPinWait(uint32_t flash_pinfails);
uint32_t storage_getPinFailsOffset(void);
bool config_containsPin(const char *pin);
bool config_hasPin(void);
void config_setPin(const char *pin);
bool config_changePin(const char *old_pin, const char *new_pin);
bool session_isUnlocked(void);
uint32_t storage_nextU2FCounter(void);
void storage_setU2FCounter(uint32_t u2fcounter);
uint32_t config_nextU2FCounter(void);
void config_setU2FCounter(uint32_t u2fcounter);
bool storage_isInitialized(void);
bool config_isInitialized(void);
bool storage_isImported(void);
void storage_setImported(bool imported);
bool config_getImported(bool *imported);
void config_setImported(bool imported);
bool storage_needsBackup(void);
void storage_setNeedsBackup(bool needs_backup);
bool config_getNeedsBackup(bool *needs_backup);
void config_setNeedsBackup(bool needs_backup);
bool storage_unfinishedBackup(void);
void storage_setUnfinishedBackup(bool unfinished_backup);
bool config_getUnfinishedBackup(bool *unfinished_backup);
void config_setUnfinishedBackup(bool unfinished_backup);
bool storage_noBackup(void);
void storage_setNoBackup(void);
bool config_getNoBackup(bool *no_backup);
void config_setNoBackup(void);
void storage_applyFlags(uint32_t flags);
uint32_t storage_getFlags(void);
void config_applyFlags(uint32_t flags);
bool config_getFlags(uint32_t *flags);
uint32_t storage_getAutoLockDelayMs(void);
void storage_setAutoLockDelayMs(uint32_t auto_lock_delay_ms);
uint32_t config_getAutoLockDelayMs(void);
void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms);
void storage_wipe(void);
void config_wipe(void);
extern char storage_uuid_str[25];
extern char config_uuid_str[2*UUID_SIZE + 1];
#endif

@ -23,12 +23,11 @@
#include "fsm.h"
#include "messages.h"
#include "bip32.h"
#include "storage.h"
#include "config.h"
#include "coins.h"
#include "debug.h"
#include "transaction.h"
#include "rng.h"
#include "storage.h"
#include "oled.h"
#include "protect.h"
#include "pinmatrix.h"
@ -70,13 +69,13 @@ static uint8_t msg_resp[MSG_OUT_SIZE] __attribute__ ((aligned));
memzero(resp, sizeof(TYPE));
#define CHECK_INITIALIZED \
if (!storage_isInitialized()) { \
if (!config_isInitialized()) { \
fsm_sendFailure(FailureType_Failure_NotInitialized, NULL); \
return; \
}
#define CHECK_NOT_INITIALIZED \
if (storage_isInitialized()) { \
if (config_isInitialized()) { \
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Device is already initialized. Use Wipe first.")); \
return; \
}
@ -207,7 +206,7 @@ static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n,
if (fingerprint) {
*fingerprint = 0;
}
if (!storage_getRootNode(&node, curve, true)) {
if (!config_getRootNode(&node, curve, true)) {
fsm_sendFailure(FailureType_Failure_NotInitialized, _("Device not initialized or passphrase request cancelled or unsupported curve"));
layoutHome();
return 0;

@ -45,31 +45,25 @@ void fsm_msgGetFeatures(const GetFeatures *msg)
resp->has_major_version = true; resp->major_version = VERSION_MAJOR;
resp->has_minor_version = true; resp->minor_version = VERSION_MINOR;
resp->has_patch_version = true; resp->patch_version = VERSION_PATCH;
resp->has_device_id = true; strlcpy(resp->device_id, storage_uuid_str, sizeof(resp->device_id));
resp->has_pin_protection = true; resp->pin_protection = storage_hasPin();
resp->has_passphrase_protection = true; resp->passphrase_protection = storage_hasPassphraseProtection();
resp->has_device_id = true; strlcpy(resp->device_id, config_uuid_str, sizeof(resp->device_id));
resp->has_pin_protection = true; resp->pin_protection = config_hasPin();
resp->has_passphrase_protection = true; config_getPassphraseProtection(&(resp->passphrase_protection));
#ifdef SCM_REVISION
int len = sizeof(SCM_REVISION) - 1;
resp->has_revision = true; memcpy(resp->revision.bytes, SCM_REVISION, len); resp->revision.size = len;
#endif
resp->has_bootloader_hash = true; resp->bootloader_hash.size = memory_bootloader_hash(resp->bootloader_hash.bytes);
if (storage_getLanguage()) {
resp->has_language = true;
strlcpy(resp->language, storage_getLanguage(), sizeof(resp->language));
}
if (storage_getLabel()) {
resp->has_label = true;
strlcpy(resp->label, storage_getLabel(), sizeof(resp->label));
}
resp->has_initialized = true; resp->initialized = storage_isInitialized();
resp->has_imported = true; resp->imported = storage_isImported();
resp->has_pin_cached = true; resp->pin_cached = session_isPinCached();
resp->has_language = config_getLanguage(resp->language, sizeof(resp->language));
resp->has_label = config_getLabel(resp->label, sizeof(resp->label));
resp->has_initialized = true; resp->initialized = config_isInitialized();
resp->has_imported = config_getImported(&(resp->imported));
resp->has_pin_cached = true; resp->pin_cached = session_isUnlocked() && config_hasPin();
resp->has_passphrase_cached = true; resp->passphrase_cached = session_isPassphraseCached();
resp->has_needs_backup = true; resp->needs_backup = storage_needsBackup();
resp->has_unfinished_backup = true; resp->unfinished_backup = storage_unfinishedBackup();
resp->has_no_backup = true; resp->no_backup = storage_noBackup();
resp->has_flags = true; resp->flags = storage_getFlags();
resp->has_needs_backup = true; config_getNeedsBackup(&(resp->needs_backup));
resp->has_unfinished_backup = true; config_getUnfinishedBackup(&(resp->unfinished_backup));
resp->has_no_backup = true; config_getNoBackup(&(resp->no_backup));
resp->has_flags = config_getFlags(&(resp->flags));
resp->has_model = true; strlcpy(resp->model, "1", sizeof(resp->model));
msg_write(MessageType_MessageType_Features, resp);
@ -111,14 +105,14 @@ void fsm_msgChangePin(const ChangePin *msg)
{
bool removal = msg->has_remove && msg->remove;
if (removal) {
if (storage_hasPin()) {
if (config_hasPin()) {
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()) {
if (config_hasPin()) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change current PIN?"), NULL, NULL, NULL, NULL);
} else {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("set new PIN?"), NULL, NULL, NULL, NULL);
@ -130,19 +124,14 @@ void fsm_msgChangePin(const ChangePin *msg)
return;
}
CHECK_PIN_UNCACHED
if (protectChangePin(removal)) {
if (removal) {
fsm_sendSuccess(_("PIN removed"));
} else {
fsm_sendSuccess(_("PIN changed"));
}
}
if (removal) {
storage_setPin("");
storage_update();
fsm_sendSuccess(_("PIN removed"));
} else {
if (protectChangePin()) {
fsm_sendSuccess(_("PIN changed"));
} else {
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
}
}
layoutHome();
}
@ -155,7 +144,7 @@ void fsm_msgWipeDevice(const WipeDevice *msg)
layoutHome();
return;
}
storage_wipe();
config_wipe();
// the following does not work on Mac anyway :-/ Linux/Windows are fine, so it is not needed
// usbReconnect(); // force re-enumeration because of the serial number change
fsm_sendSuccess(_("Device wiped"));
@ -185,6 +174,8 @@ void fsm_msgGetEntropy(const GetEntropy *msg)
void fsm_msgLoadDevice(const LoadDevice *msg)
{
CHECK_PIN
CHECK_NOT_INITIALIZED
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);
@ -202,13 +193,15 @@ void fsm_msgLoadDevice(const LoadDevice *msg)
}
}
storage_loadDevice(msg);
config_loadDevice(msg);
fsm_sendSuccess(_("Device loaded"));
layoutHome();
}
void fsm_msgResetDevice(const ResetDevice *msg)
{
CHECK_PIN
CHECK_NOT_INITIALIZED
CHECK_PARAM(!msg->has_strength || msg->strength == 128 || msg->strength == 192 || msg->strength == 256, _("Invalid seed strength"));
@ -242,7 +235,11 @@ void fsm_msgBackupDevice(const BackupDevice *msg)
CHECK_PIN_UNCACHED
(void)msg;
reset_backup(true);
char mnemonic[MAX_MNEMONIC_LEN + 1];
if (config_getMnemonic(mnemonic, sizeof(mnemonic))) {
reset_backup(true, mnemonic);
}
memzero(mnemonic, sizeof(mnemonic));
}
void fsm_msgCancel(const Cancel *msg)
@ -312,39 +309,40 @@ void fsm_msgApplySettings(const ApplySettings *msg)
}
if (msg->has_label) {
storage_setLabel(msg->label);
config_setLabel(msg->label);
}
if (msg->has_language) {
storage_setLanguage(msg->language);
config_setLanguage(msg->language);
}
if (msg->has_use_passphrase) {
storage_setPassphraseProtection(msg->use_passphrase);
config_setPassphraseProtection(msg->use_passphrase);
}
if (msg->has_homescreen) {
storage_setHomescreen(msg->homescreen.bytes, msg->homescreen.size);
config_setHomescreen(msg->homescreen.bytes, msg->homescreen.size);
}
if (msg->has_auto_lock_delay_ms) {
storage_setAutoLockDelayMs(msg->auto_lock_delay_ms);
config_setAutoLockDelayMs(msg->auto_lock_delay_ms);
}
storage_update();
fsm_sendSuccess(_("Settings applied"));
layoutHome();
}
void fsm_msgApplyFlags(const ApplyFlags *msg)
{
CHECK_PIN
if (msg->has_flags) {
storage_applyFlags(msg->flags);
config_applyFlags(msg->flags);
}
fsm_sendSuccess(_("Flags applied"));
}
void fsm_msgRecoveryDevice(const RecoveryDevice *msg)
{
CHECK_PIN
const bool dry_run = msg->has_dry_run ? msg->dry_run : false;
if (dry_run) {
CHECK_PIN
} else {
if (!dry_run) {
CHECK_NOT_INITIALIZED
}
@ -376,8 +374,7 @@ void fsm_msgSetU2FCounter(const SetU2FCounter *msg)
layoutHome();
return;
}
storage_setU2FCounter(msg->u2f_counter);
storage_update();
config_setU2FCounter(msg->u2f_counter);
fsm_sendSuccess(_("U2F counter set"));
layoutHome();
}

@ -32,10 +32,7 @@ void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg)
resp.layout.size = OLED_BUFSIZE;
memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE);
if (storage_hasPin()) {
resp.has_pin = true;
strlcpy(resp.pin, storage_getPin(), sizeof(resp.pin));
}
resp.has_pin = config_getPin(resp.pin, sizeof(resp.pin));
resp.has_matrix = true;
strlcpy(resp.matrix, pinmatrix_get(), sizeof(resp.matrix));
@ -52,18 +49,11 @@ void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg)
resp.has_recovery_word_pos = true;
resp.recovery_word_pos = recovery_get_word_pos();
if (storage_hasMnemonic()) {
resp.has_mnemonic = true;
strlcpy(resp.mnemonic, storage_getMnemonic(), sizeof(resp.mnemonic));
}
resp.has_mnemonic = config_getMnemonic(resp.mnemonic, sizeof(resp.mnemonic));
if (storage_hasNode()) {
resp.has_node = true;
storage_dumpNode(&(resp.node));
}
resp.has_node = config_dumpNode(&(resp.node));
resp.has_passphrase_protection = true;
resp.passphrase_protection = storage_hasPassphraseProtection();
resp.has_passphrase_protection = config_getPassphraseProtection(&(resp.passphrase_protection));
msg_debug_write(MessageType_MessageType_DebugLinkState, &resp);
}

@ -22,7 +22,7 @@
#include <ctype.h>
#include "layout2.h"
#include "storage.h"
#include "config.h"
#include "oled.h"
#include "bitmaps.h"
#include "string.h"
@ -235,31 +235,41 @@ void layoutHome(void)
layoutSwipe();
}
layoutLast = layoutHome;
const char *label = storage_isInitialized() ? storage_getLabel() : _("Go to trezor.io/start");
const uint8_t *homescreen = storage_getHomescreen();
if (homescreen) {
char label[MAX_LABEL_LEN + 1] = _("Go to trezor.io/start");
if (config_isInitialized()) {
config_getLabel(label, sizeof(label));
}
uint8_t homescreen[HOMESCREEN_SIZE];
if (config_getHomescreen(homescreen, sizeof(homescreen))) {
BITMAP b;
b.width = 128;
b.height = 64;
b.data = homescreen;
oledDrawBitmap(0, 0, &b);
} else {
if (label && strlen(label) > 0) {
if (label[0] != '\0') {
oledDrawBitmap(44, 4, &bmp_logo48);
oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 8, label, FONT_STANDARD);
} else {
oledDrawBitmap(40, 0, &bmp_logo64);
}
}
if (storage_noBackup()) {
bool no_backup = false;
bool unfinished_backup = false;
bool needs_backup = false;
config_getNoBackup(&no_backup);
config_getUnfinishedBackup(&unfinished_backup);
config_getNeedsBackup(&needs_backup);
if (no_backup) {
oledBox(0, 0, 127, 8, false);
oledDrawStringCenter(OLED_WIDTH / 2, 0, "SEEDLESS", FONT_STANDARD);
} else
if (storage_unfinishedBackup()) {
} else if (unfinished_backup) {
oledBox(0, 0, 127, 8, false);
oledDrawStringCenter(OLED_WIDTH / 2, 0, "BACKUP FAILED!", FONT_STANDARD);
} else
if (storage_needsBackup()) {
} else if (needs_backup) {
oledBox(0, 0, 127, 8, false);
oledDrawStringCenter(OLED_WIDTH / 2, 0, "NEEDS BACKUP!", FONT_STANDARD);
}

@ -18,7 +18,7 @@
*/
#include "protect.h"
#include "storage.h"
#include "config.h"
#include "memory.h"
#include "messages.h"
#include "usb.h"
@ -147,102 +147,118 @@ const char *requestPin(PinMatrixRequestType type, const char *text)
}
}
static void protectCheckMaxTry(uint32_t wait) {
if (wait < (1 << MAX_WRONG_PINS))
return;
secbool protectPinUiCallback(uint32_t wait, uint32_t progress)
{
(void) progress;
// Convert wait to secstr string.
char secstrbuf[] = _("________0 seconds");
char *secstr = secstrbuf + 9;
uint32_t secs = wait;
do {
secstr--;
*secstr = (secs % 10) + '0';
secs /= 10;
} while (secs > 0 && secstr >= secstrbuf);
if (wait == 1) {
// Change "seconds" to "second".
secstrbuf[16] = 0;
}
layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Verifying PIN"), NULL, _("Please wait"), secstr, _("to continue ..."), NULL);
// Check for Cancel / Initialize.
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize = (msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
msg_tiny_id = 0xFFFF;
usbTiny(0);
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return sectrue;
}
storage_wipe();
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Too many wrong PIN"), _("attempts. Storage has"), _("been wiped."), NULL, _("Please unplug"), _("the device."));
for (;;) {} // loop forever
return secfalse;
}
bool protectPin(bool use_cached)
{
if (!storage_hasPin() || (use_cached && session_isPinCached())) {
if (use_cached && session_isUnlocked()) {
return true;
}
uint32_t fails = storage_getPinFailsOffset();
uint32_t wait = storage_getPinWait(fails);
protectCheckMaxTry(wait);
usbTiny(1);
while (wait > 0) {
// convert wait to secstr string
char secstrbuf[20];
strlcpy(secstrbuf, _("________0 seconds"), sizeof(secstrbuf));
char *secstr = secstrbuf + 9;
uint32_t secs = wait;
while (secs > 0 && secstr >= secstrbuf) {
secstr--;
*secstr = (secs % 10) + '0';
secs /= 10;
}
if (wait == 1) {
secstrbuf[16] = 0;
}
layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Wrong PIN entered"), NULL, _("Please wait"), secstr, _("to continue ..."), NULL);
// wait one second
usbSleep(1000);
if (msg_tiny_id == MessageType_MessageType_Initialize) {
protectAbortedByCancel = false;
protectAbortedByInitialize = true;
msg_tiny_id = 0xFFFF;
usbTiny(0);
const char *pin = "";
if (config_hasPin()) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:"));
if (!pin) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
wait--;
}
usbTiny(1);
bool ret = config_containsPin(pin);
usbTiny(0);
const char *pin;
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:"));
if (!pin) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
if (!storage_increasePinFails(fails)) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
return false;
}
if (storage_containsPin(pin)) {
session_cachePin();
storage_resetPinFails(fails);
return true;
} else {
protectCheckMaxTry(storage_getPinWait(fails));
if (!ret) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
return false;
}
return ret;
}
bool protectChangePin(void)
bool protectChangePin(bool removal)
{
static CONFIDENTIAL char pin_compare[17];
static CONFIDENTIAL char old_pin[MAX_PIN_LEN + 1] = "";
static CONFIDENTIAL char new_pin[MAX_PIN_LEN + 1] = "";
const char* pin = NULL;
const char *pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst, _("Please enter new PIN:"));
if (!pin) {
return false;
if (config_hasPin()) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:"));
if (pin == NULL) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
strlcpy(old_pin, pin, sizeof(old_pin));
}
strlcpy(pin_compare, pin, sizeof(pin_compare));
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, _("Please re-enter new PIN:"));
if (!removal) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst, _("Please enter new PIN:"));
if (pin == NULL) {
memzero(old_pin, sizeof(old_pin));
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
strlcpy(new_pin, pin, sizeof(new_pin));
const bool result = pin && (strncmp(pin_compare, pin, sizeof(pin_compare)) == 0);
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, _("Please re-enter new PIN:"));
if (pin == NULL) {
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
if (result) {
storage_setPin(pin_compare);
storage_update();
if (strncmp(new_pin, pin, sizeof(new_pin)) != 0) {
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
return false;
}
}
memzero(pin_compare, sizeof(pin_compare));
return result;
usbTiny(1);
bool ret = config_changePin(old_pin, new_pin);
usbTiny(0);
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
if (ret == false) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
}
return ret;
}
bool protectPassphrase(void)
{
if (!storage_hasPassphraseProtection() || session_isPassphraseCached()) {
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (!passphrase_protection || session_isPassphraseCached()) {
return true;
}

@ -22,10 +22,12 @@
#include <stdbool.h>
#include "messages-common.pb.h"
#include "secbool.h"
bool protectButton(ButtonRequestType type, bool confirm_only);
secbool protectPinUiCallback(uint32_t wait, uint32_t progress);
bool protectPin(bool use_cached);
bool protectChangePin(void);
bool protectChangePin(bool removal);
bool protectPassphrase(void);
extern bool protectAbortedByCancel;

@ -21,7 +21,7 @@
#include <ctype.h>
#include "recovery.h"
#include "fsm.h"
#include "storage.h"
#include "config.h"
#include "layout2.h"
#include "protect.h"
#include "messages.h"
@ -44,7 +44,7 @@ static uint32_t word_count;
*/
static int awaiting_word = 0;
/* True if we should not write anything back to storage
/* True if we should not write anything back to config
* (can be used for testing seed for correctness).
*/
static bool dry_run;
@ -153,7 +153,7 @@ static void recovery_request(void) {
* Check mnemonic and send success/failure.
*/
static void recovery_done(void) {
char new_mnemonic[241] = {0}; // TODO: remove constant
char new_mnemonic[MAX_MNEMONIC_LEN + 1] = {0};
strlcpy(new_mnemonic, words[0], sizeof(new_mnemonic));
for (uint32_t i = 1; i < word_count; i++) {
@ -163,18 +163,20 @@ static void recovery_done(void) {
if (!enforce_wordlist || mnemonic_check(new_mnemonic)) {
// New mnemonic is valid.
if (!dry_run) {
// Update mnemonic on storage.
storage_setMnemonic(new_mnemonic);
memzero(new_mnemonic, sizeof(new_mnemonic));
if (!enforce_wordlist) {
// not enforcing => mark storage as imported
storage_setImported(true);
// Update mnemonic on config.
if (config_setMnemonic(new_mnemonic)) {
if (!enforce_wordlist) {
// not enforcing => mark config as imported
config_setImported(true);
}
fsm_sendSuccess(_("Device recovered"));
} else {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to store mnemonic"));
}
storage_update();
fsm_sendSuccess(_("Device recovered"));
memzero(new_mnemonic, sizeof(new_mnemonic));
} else {
// Inform the user about new mnemonic correctness (as well as whether it is the same as the current one).
bool match = (storage_isInitialized() && storage_containsMnemonic(new_mnemonic));
bool match = (config_isInitialized() && config_containsMnemonic(new_mnemonic));
memzero(new_mnemonic, sizeof(new_mnemonic));
if (match) {
layoutDialog(&bmp_icon_ok, NULL, _("Confirm"), NULL,
@ -466,17 +468,15 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_pr
}
if (!dry_run) {
if (pin_protection && !protectChangePin()) {
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
if (pin_protection && !protectChangePin(false)) {
layoutHome();
return;
}
storage_setPassphraseProtection(passphrase_protection);
storage_setLanguage(language);
storage_setLabel(label);
storage_setU2FCounter(u2f_counter);
storage_update();
config_setPassphraseProtection(passphrase_protection);
config_setLanguage(language);
config_setLabel(label);
config_setU2FCounter(u2f_counter);
}
if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) {

@ -18,7 +18,7 @@
*/
#include "reset.h"
#include "storage.h"
#include "config.h"
#include "rng.h"
#include "sha2.h"
#include "messages.h"
@ -75,17 +75,15 @@ void reset_init(bool display_random, uint32_t _strength, bool passphrase_protect
}
}
if (pin_protection && !protectChangePin()) {
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
if (pin_protection && !protectChangePin(false)) {
layoutHome();
return;
}
storage_setPassphraseProtection(passphrase_protection);
storage_setLanguage(language);
storage_setLabel(label);
storage_setU2FCounter(u2f_counter);
storage_update();
config_setPassphraseProtection(passphrase_protection);
config_setLanguage(language);
config_setLabel(label);
config_setU2FCounter(u2f_counter);
EntropyRequest resp;
memzero(&resp, sizeof(EntropyRequest));
@ -99,49 +97,50 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len)
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Reset mode"));
return;
}
awaiting_entropy = false;
SHA256_CTX ctx;
sha256_Init(&ctx);
sha256_Update(&ctx, int_entropy, 32);
sha256_Update(&ctx, ext_entropy, len);
sha256_Final(&ctx, int_entropy);
if (no_backup) {
storage_setNoBackup();
} else {
storage_setNeedsBackup(true);
}
storage_setMnemonic(mnemonic_from_data(int_entropy, strength / 8));
mnemonic_clear();
const char* mnemonic = mnemonic_from_data(int_entropy, strength / 8);
memzero(int_entropy, 32);
awaiting_entropy = false;
if (skip_backup || no_backup) {
storage_update();
fsm_sendSuccess(_("Device successfully initialized"));
if (no_backup) {
config_setNoBackup();
} else {
config_setNeedsBackup(true);
}
if (config_setMnemonic(mnemonic)) {
fsm_sendSuccess(_("Device successfully initialized"));
} else {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to store mnemonic"));
}
layoutHome();
} else {
reset_backup(false);
reset_backup(false, mnemonic);
}
mnemonic_clear();
}
static char current_word[10];
// separated == true if called as a separate workflow via BackupMessage
void reset_backup(bool separated)
void reset_backup(bool separated, const char* mnemonic)
{
if (!storage_needsBackup()) {
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Seed already backed up"));
return;
}
storage_setUnfinishedBackup(true);
storage_setNeedsBackup(false);
if (separated) {
storage_update();
}
bool needs_backup = false;
config_getNeedsBackup(&needs_backup);
if (!needs_backup) {
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Seed already backed up"));
return;
}
const char *mnemonic = storage_getMnemonic();
config_setUnfinishedBackup(true);
config_setNeedsBackup(false);
}
for (int pass = 0; pass < 2; pass++) {
int i = 0, word_pos = 1;
@ -159,7 +158,6 @@ void reset_backup(bool separated)
layoutResetWord(current_word, pass, word_pos, mnemonic[i] == 0);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) {
if (!separated) {
storage_clear_update();
session_clear(true);
}
layoutHome();
@ -170,13 +168,17 @@ void reset_backup(bool separated)
}
}
storage_setUnfinishedBackup(false);
storage_update();
config_setUnfinishedBackup(false);
if (separated) {
fsm_sendSuccess(_("Seed successfully backed up"));
} else {
fsm_sendSuccess(_("Device successfully initialized"));
config_setNeedsBackup(false);
if (config_setMnemonic(mnemonic)) {
fsm_sendSuccess(_("Device successfully initialized"));
} else {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to store mnemonic"));
}
}
layoutHome();
}

@ -25,7 +25,7 @@
void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label, uint32_t u2f_counter, bool _skip_backup, bool _no_backup);
void reset_entropy(const uint8_t *ext_entropy, uint32_t len);
void reset_backup(bool separated);
void reset_backup(bool separated, const char* mnemonic);
uint32_t reset_get_int_entropy(uint8_t *entropy);
const char *reset_get_word(void);

@ -38,7 +38,7 @@
#include "bignum.h"
#include "oled.h"
#include "base32.h"
#include "storage.h"
#include "config.h"
#include "fsm.h"
#include "protect.h"
#include "util.h"
@ -1514,7 +1514,7 @@ const HDNode *stellar_deriveNode(const uint32_t *address_n, size_t address_n_cou
const char *curve = "ed25519";
// Device not initialized, passphrase request cancelled, or unsupported curve
if (!storage_getRootNode(&node, curve, true)) {
if (!config_getRootNode(&node, curve, true)) {
return 0;
}
// Failed to derive private key

@ -1,936 +0,0 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdint.h>
#include <libopencm3/stm32/flash.h>
#include "messages.pb.h"
#include "trezor.h"
#include "sha2.h"
#include "aes/aes.h"
#include "pbkdf2.h"
#include "hmac.h"
#include "bip32.h"
#include "bip39.h"
#include "curves.h"
#include "util.h"
#include "memory.h"
#include "rng.h"
#include "storage.h"
#include "debug.h"
#include "protect.h"
#include "layout2.h"
#include "usb.h"
#include "gettext.h"
#include "u2f.h"
#include "memzero.h"
#include "supervise.h"
/* magic constant to check validity of storage block */
static const uint32_t storage_magic = 0x726f7473; // 'stor' as uint32_t
static uint32_t storage_uuid[12 / sizeof(uint32_t)];
_Static_assert(sizeof(storage_uuid) == 12, "storage_uuid has wrong size");
Storage CONFIDENTIAL storageUpdate __attribute__((aligned(4)));
_Static_assert((sizeof(storageUpdate) & 3) == 0, "storage unaligned");
#define FLASH_STORAGE (FLASH_STORAGE_START + sizeof(storage_magic) + sizeof(storage_uuid))
#define storageRom ((const Storage *) FLASH_PTR(FLASH_STORAGE))
char storage_uuid_str[25];
/*
storage layout:
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
The pinfail is a binary number of the form 1...10...0,
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 (sizeof(storage_magic) + sizeof(storage_uuid) + sizeof(Storage))
#if !EMULATOR
// TODO: Fix this for emulator
_Static_assert(FLASH_STORAGE_START + FLASH_STORAGE_REALLEN <= FLASH_STORAGE_PINAREA, "Storage struct is too large for TREZOR flash");
#endif
/* 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 CONFIDENTIAL sessionSeed[64];
static bool sessionPinCached = false;
static bool sessionPassphraseCached = false;
static char CONFIDENTIAL sessionPassphrase[51];
#define STORAGE_VERSION 10
void storage_show_error(void)
{
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Storage failure"), _("detected."), NULL, _("Please unplug"), _("the device."), NULL);
shutdown();
}
void storage_check_flash_errors(uint32_t status)
{
// flash operation failed
if (status & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) {
storage_show_error();
}
}
bool storage_from_flash(void)
{
storage_clear_update();
if (memcmp(FLASH_PTR(FLASH_STORAGE_START), &storage_magic, sizeof(storage_magic)) != 0) {
// wrong magic
return false;
}
const uint32_t version = storageRom->version;
// version 1: since 1.0.0
// version 2: since 1.2.1
// version 3: since 1.3.1
// version 4: since 1.3.2
// version 5: since 1.3.3
// version 6: since 1.3.6
// version 7: since 1.5.1
// version 8: since 1.5.2
// version 9: since 1.6.1
// version 10: since 1.7.2
if (version > STORAGE_VERSION) {
// downgrade -> clear storage
return false;
}
// load uuid
memcpy(storage_uuid, FLASH_PTR(FLASH_STORAGE_START + sizeof(storage_magic)), sizeof(storage_uuid));
data2hex(storage_uuid, sizeof(storage_uuid), storage_uuid_str);
#define OLD_STORAGE_SIZE(last_member) (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + 3) & ~3)
// copy storage
size_t old_storage_size = 0;
if (version == 0) {
} else if (version <= 2) {
old_storage_size = OLD_STORAGE_SIZE(imported);
} else if (version <= 5) {
// added homescreen
old_storage_size = OLD_STORAGE_SIZE(homescreen);
} else if (version <= 7) {
// added u2fcounter
old_storage_size = OLD_STORAGE_SIZE(u2f_counter);
} else if (version <= 8) {
// added flags and needsBackup
old_storage_size = OLD_STORAGE_SIZE(flags);
} else if (version <= 9) {
// added u2froot, unfinished_backup and auto_lock_delay_ms
old_storage_size = OLD_STORAGE_SIZE(auto_lock_delay_ms);
} else if (version <= 10) {
// added no_backup
old_storage_size = OLD_STORAGE_SIZE(no_backup);
}
// erase newly added fields
if (old_storage_size != sizeof(Storage)) {
svc_flash_unlock();
svc_flash_program(FLASH_CR_PROGRAM_X32);
for (uint32_t offset = old_storage_size; offset < sizeof(Storage); offset += sizeof(uint32_t)) {
flash_write32(FLASH_STORAGE_START + sizeof(storage_magic) + sizeof(storage_uuid) + offset, 0);
}
storage_check_flash_errors(svc_flash_lock());
}
if (version <= 5) {
// convert PIN failure counter from version 5 format
uint32_t pinctr = storageRom->has_pin_failed_attempts ? storageRom->pin_failed_attempts : 0;
if (pinctr > 31) {
pinctr = 31;
}
svc_flash_unlock();
// erase extra storage sector
svc_flash_erase_sector(FLASH_META_SECTOR_LAST);
svc_flash_program(FLASH_CR_PROGRAM_X32);
flash_write32(FLASH_STORAGE_PINAREA, 0xffffffff << pinctr);
// storageRom.has_pin_failed_attempts and storageRom.pin_failed_attempts
// are erased by storage_update below
storage_check_flash_errors(svc_flash_lock());
}
const uint32_t *u2fptr = (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA);
while (*u2fptr == 0) {
u2fptr++;
}
storage_u2f_offset = 32 * (u2fptr - (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA));
uint32_t u2fword = *u2fptr;
while ((u2fword & 1) == 0) {
storage_u2f_offset++;
u2fword >>= 1;
}
// force recomputing u2f root for storage version < 9.
// this is done by re-setting the mnemonic, which triggers the computation
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;
}
void storage_init(void)
{
if (!storage_from_flash()) {
storage_wipe();
}
}
void storage_generate_uuid(void)
{
// set random uuid
random_buffer((uint8_t *)storage_uuid, sizeof(storage_uuid));
data2hex(storage_uuid, sizeof(storage_uuid), storage_uuid_str);
}
void session_clear(bool clear_pin)
{
sessionSeedCached = false;
memzero(&sessionSeed, sizeof(sessionSeed));
sessionPassphraseCached = false;
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
if (clear_pin) {
session_uncachePin();
}
}
static uint32_t storage_flash_words(uint32_t addr, const uint32_t *src, int nwords) {
for (int i = 0; i < nwords; i++) {
flash_write32(addr, *src++);
addr += sizeof(uint32_t);
}
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, StorageHDNode *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));
memzero(&node, 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)
{
if (update) {
if (storageUpdate.has_passphrase_protection) {
sessionSeedCached = false;
sessionPassphraseCached = false;
}
storageUpdate.version = STORAGE_VERSION;
if (!storageUpdate.has_node && !storageUpdate.has_mnemonic) {
storageUpdate.has_node = storageRom->has_node;
memcpy(&storageUpdate.node, &storageRom->node, sizeof(StorageHDNode));
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(StorageHDNode));
} 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;
storageUpdate.passphrase_protection = storageRom->passphrase_protection;
}
if (!storageUpdate.has_pin) {
storageUpdate.has_pin = storageRom->has_pin;
strlcpy(storageUpdate.pin, storageRom->pin, sizeof(storageUpdate.pin));
} else if (!storageUpdate.pin[0]) {
storageUpdate.has_pin = false;
}
if (!storageUpdate.has_language) {
storageUpdate.has_language = storageRom->has_language;
strlcpy(storageUpdate.language, storageRom->language, sizeof(storageUpdate.language));
}
if (!storageUpdate.has_label) {
storageUpdate.has_label = storageRom->has_label;
strlcpy(storageUpdate.label, storageRom->label, sizeof(storageUpdate.label));
} else if (!storageUpdate.label[0]) {
storageUpdate.has_label = false;
}
if (!storageUpdate.has_imported) {
storageUpdate.has_imported = storageRom->has_imported;
storageUpdate.imported = storageRom->imported;
}
if (!storageUpdate.has_homescreen) {
storageUpdate.has_homescreen = storageRom->has_homescreen;
memcpy(&storageUpdate.homescreen, &storageRom->homescreen, sizeof(storageUpdate.homescreen));
} else if (storageUpdate.homescreen.size == 0) {
storageUpdate.has_homescreen = false;
}
if (!storageUpdate.has_u2f_counter) {
storageUpdate.has_u2f_counter = storageRom->has_u2f_counter;
storageUpdate.u2f_counter = storageRom->u2f_counter;
}
if (!storageUpdate.has_needs_backup) {
storageUpdate.has_needs_backup = storageRom->has_needs_backup;
storageUpdate.needs_backup = storageRom->needs_backup;
}
if (!storageUpdate.has_unfinished_backup) {
storageUpdate.has_unfinished_backup = storageRom->has_unfinished_backup;
storageUpdate.unfinished_backup = storageRom->unfinished_backup;
}
if (!storageUpdate.has_no_backup) {
storageUpdate.has_no_backup = storageRom->has_no_backup;
storageUpdate.no_backup = storageRom->no_backup;
}
if (!storageUpdate.has_flags) {
storageUpdate.has_flags = storageRom->has_flags;
storageUpdate.flags = storageRom->flags;
}
}
// backup meta
uint32_t meta_backup[FLASH_META_DESC_LEN / sizeof(uint32_t)];
memcpy(meta_backup, FLASH_PTR(FLASH_META_START), FLASH_META_DESC_LEN);
// erase storage
svc_flash_erase_sector(FLASH_META_SECTOR_FIRST);
svc_flash_program(FLASH_CR_PROGRAM_X32);
// copy meta back
uint32_t flash = FLASH_META_START;
flash = storage_flash_words(flash, meta_backup, FLASH_META_DESC_LEN / sizeof(uint32_t));
// copy storage
flash = storage_flash_words(flash, &storage_magic, sizeof(storage_magic) / sizeof(uint32_t));
flash = storage_flash_words(flash, storage_uuid, sizeof(storage_uuid) / sizeof(uint32_t));
if (update) {
flash = storage_flash_words(flash, (const uint32_t *)&storageUpdate, sizeof(storageUpdate) / sizeof(uint32_t));
}
storage_clear_update();
// fill remainder with zero for future extensions
while (flash < FLASH_STORAGE_PINAREA) {
flash_write32(flash, 0);
flash += sizeof(uint32_t);
}
}
void storage_clear_update(void)
{
memzero(&storageUpdate, sizeof(storageUpdate));
}
void storage_update(void)
{
svc_flash_unlock();
storage_commit_locked(true);
storage_check_flash_errors(svc_flash_lock());
}
static void storage_setNode(const HDNodeType *node) {
storageUpdate.node.depth = node->depth;
storageUpdate.node.fingerprint = node->fingerprint;
storageUpdate.node.child_num = node->child_num;
storageUpdate.node.chain_code.size = 32;
memcpy(storageUpdate.node.chain_code.bytes, node->chain_code.bytes, 32);
if (node->has_private_key) {
storageUpdate.node.has_private_key = true;
storageUpdate.node.private_key.size = 32;
memcpy(storageUpdate.node.private_key.bytes, node->private_key.bytes, 32);
}
}
#if DEBUG_LINK
void storage_dumpNode(HDNodeType *node) {
node->depth = storageRom->node.depth;
node->fingerprint = storageRom->node.fingerprint;
node->child_num = storageRom->node.child_num;
node->chain_code.size = 32;
memcpy(node->chain_code.bytes, storageRom->node.chain_code.bytes, 32);
if (storageRom->node.has_private_key) {
node->has_private_key = true;
node->private_key.size = 32;
memcpy(node->private_key.bytes, storageRom->node.private_key.bytes, 32);
}
}
#endif
void storage_loadDevice(const LoadDevice *msg)
{
session_clear(true);
storageUpdate.has_imported = true;
storageUpdate.imported = true;
storage_setPin(msg->has_pin ? msg->pin : "");
storage_setPassphraseProtection(msg->has_passphrase_protection && msg->passphrase_protection);
if (msg->has_node) {
storageUpdate.has_node = true;
storageUpdate.has_mnemonic = false;
storage_setNode(&(msg->node));
sessionSeedCached = false;
memzero(&sessionSeed, sizeof(sessionSeed));
} else if (msg->has_mnemonic) {
storageUpdate.has_mnemonic = true;
storageUpdate.has_node = false;
strlcpy(storageUpdate.mnemonic, msg->mnemonic, sizeof(storageUpdate.mnemonic));
sessionSeedCached = false;
memzero(&sessionSeed, sizeof(sessionSeed));
}
if (msg->has_language) {
storageUpdate.has_language = true;
strlcpy(storageUpdate.language, msg->language, sizeof(storageUpdate.language));
}
storage_setLabel(msg->has_label ? msg->label : "");
if (msg->has_u2f_counter) {
storageUpdate.has_u2f_counter = true;
storageUpdate.u2f_counter = msg->u2f_counter - storage_u2f_offset;
}
storage_update();
}
void storage_setLabel(const char *label)
{
storageUpdate.has_label = true;
if (!label) return;
strlcpy(storageUpdate.label, label, sizeof(storageUpdate.label));
}
void storage_setLanguage(const char *lang)
{
if (!lang) return;
// sanity check
if (strcmp(lang, "english") == 0) {
storageUpdate.has_language = true;
strlcpy(storageUpdate.language, lang, sizeof(storageUpdate.language));
}
}
void storage_setPassphraseProtection(bool passphrase_protection)
{
sessionSeedCached = false;
sessionPassphraseCached = false;
storageUpdate.has_passphrase_protection = true;
storageUpdate.passphrase_protection = passphrase_protection;
}
bool storage_hasPassphraseProtection(void)
{
return storageRom->has_passphrase_protection && storageRom->passphrase_protection;
}
void storage_setHomescreen(const uint8_t *data, uint32_t size)
{
storageUpdate.has_homescreen = true;
if (data && size == 1024) {
memcpy(storageUpdate.homescreen.bytes, data, size);
storageUpdate.homescreen.size = size;
} else {
memzero(storageUpdate.homescreen.bytes, sizeof(storageUpdate.homescreen.bytes));
storageUpdate.homescreen.size = 0;
}
}
static void get_root_node_callback(uint32_t iter, uint32_t total)
{
usbSleep(1);
layoutProgress(_("Waking up"), 1000 * iter / total);
}
const uint8_t *storage_getSeed(bool usePassphrase)
{
// root node is properly cached
if (usePassphrase == sessionSeedUsesPassphrase
&& sessionSeedCached) {
return sessionSeed;
}
// if storage has mnemonic, convert it to node and use it
if (storageRom->has_mnemonic) {
if (usePassphrase && !protectPassphrase()) {
return NULL;
}
// if storage was not imported (i.e. it was properly generated or recovered)
if (!storageRom->has_imported || !storageRom->imported) {
// test whether mnemonic is a valid BIP-0039 mnemonic
if (!mnemonic_check(storageRom->mnemonic)) {
// and if not then halt the device
storage_show_error();
}
}
char oldTiny = usbTiny(1);
mnemonic_to_seed(storageRom->mnemonic, usePassphrase ? sessionPassphrase : "", sessionSeed, get_root_node_callback); // BIP-0039
usbTiny(oldTiny);
sessionSeedCached = true;
sessionSeedUsesPassphrase = usePassphrase;
return sessionSeed;
}
return NULL;
}
static bool storage_loadNode(const StorageHDNode *node, const char *curve, HDNode *out) {
return hdnode_from_xprv(node->depth, node->child_num, node->chain_code.bytes, node->private_key.bytes, curve, out);
}
bool storage_getU2FRoot(HDNode *node)
{
return storageRom->has_u2froot && storage_loadNode(&storageRom->u2froot, NIST256P1_NAME, node);
}
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase)
{
// if storage has node, decrypt and use it
if (storageRom->has_node && strcmp(curve, SECP256K1_NAME) == 0) {
if (!protectPassphrase()) {
return false;
}
if (!storage_loadNode(&storageRom->node, curve, node)) {
return false;
}
if (storageRom->has_passphrase_protection && storageRom->passphrase_protection && sessionPassphraseCached && strlen(sessionPassphrase) > 0) {
// decrypt hd node
uint8_t secret[64];
PBKDF2_HMAC_SHA512_CTX pctx;
char oldTiny = usbTiny(1);
pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), (const uint8_t *)"TREZORHD", 8, 1);
get_root_node_callback(0, BIP39_PBKDF2_ROUNDS);
for (int i = 0; i < 8; i++) {
pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8);
get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8, BIP39_PBKDF2_ROUNDS);
}
pbkdf2_hmac_sha512_Final(&pctx, secret);
usbTiny(oldTiny);
aes_decrypt_ctx ctx;
aes_decrypt_key256(secret, &ctx);
aes_cbc_decrypt(node->chain_code, node->chain_code, 32, secret + 32, &ctx);
aes_cbc_decrypt(node->private_key, node->private_key, 32, secret + 32, &ctx);
}
return true;
}
const uint8_t *seed = storage_getSeed(usePassphrase);
if (seed == NULL) {
return false;
}
return hdnode_from_seed(seed, 64, curve, node);
}
const char *storage_getLabel(void)
{
return storageRom->has_label ? storageRom->label : 0;
}
const char *storage_getLanguage(void)
{
return storageRom->has_language ? storageRom->language : 0;
}
const uint8_t *storage_getHomescreen(void)
{
return (storageRom->has_homescreen && storageRom->homescreen.size == 1024) ? storageRom->homescreen.bytes : 0;
}
void storage_setMnemonic(const char *mnemonic)
{
storageUpdate.has_mnemonic = true;
strlcpy(storageUpdate.mnemonic, mnemonic, sizeof(storageUpdate.mnemonic));
}
bool storage_hasNode(void)
{
return storageRom->has_node;
}
bool storage_hasMnemonic(void)
{
return storageRom->has_mnemonic;
}
const char *storage_getMnemonic(void)
{
return storageUpdate.has_mnemonic ? storageUpdate.mnemonic
: storageRom->has_mnemonic ? storageRom->mnemonic : 0;
}
/* Check whether mnemonic matches storage. The mnemonic must be
* a null-terminated string.
*/
bool storage_containsMnemonic(const char *mnemonic) {
/* The execution time of the following code only depends on the
* (public) input. This avoids timing attacks.
*/
char diff = 0;
uint32_t i = 0;
for (; mnemonic[i]; i++) {
diff |= (storageRom->mnemonic[i] - mnemonic[i]);
}
diff |= storageRom->mnemonic[i];
return diff == 0;
}
/* Check whether pin matches storage. The pin must be
* a null-terminated string with at most 9 characters.
*/
bool storage_containsPin(const char *pin)
{
/* The execution time of the following code only depends on the
* (public) input. This avoids timing attacks.
*/
char diff = 0;
uint32_t i = 0;
while (pin[i]) {
diff |= storageRom->pin[i] - pin[i];
i++;
}
diff |= storageRom->pin[i];
return diff == 0;
}
bool storage_hasPin(void)
{
return storageRom->has_pin && storageRom->pin[0] != 0;
}
void storage_setPin(const char *pin)
{
storageUpdate.has_pin = true;
strlcpy(storageUpdate.pin, pin, sizeof(storageUpdate.pin));
session_cachePin();
}
const char *storage_getPin(void)
{
return storageRom->has_pin ? storageRom->pin : 0;
}
void session_cachePassphrase(const char *passphrase)
{
strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase));
sessionPassphraseCached = true;
}
bool session_isPassphraseCached(void)
{
return sessionPassphraseCached;
}
bool session_getState(const uint8_t *salt, uint8_t *state, const char *passphrase)
{
if (!passphrase && !sessionPassphraseCached) {
return false;
} else {
passphrase = sessionPassphrase;
}
if (!salt) {
// if salt is not provided fill the first half of the state with random data
random_buffer(state, 32);
} else {
// if salt is provided fill the first half of the state with salt
memcpy(state, salt, 32);
}
// state[0:32] = salt
// state[32:64] = HMAC(passphrase, salt || device_id)
HMAC_SHA256_CTX ctx;
hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase));
hmac_sha256_Update(&ctx, state, 32);
hmac_sha256_Update(&ctx, (const uint8_t *)storage_uuid, sizeof(storage_uuid));
hmac_sha256_Final(&ctx, state + 32);
memzero(&ctx, sizeof(ctx));
return true;
}
void session_cachePin(void)
{
sessionPinCached = true;
}
void session_uncachePin(void)
{
sessionPinCached = false;
}
bool session_isPinCached(void)
{
return sessionPinCached;
}
void storage_clearPinArea(void)
{
svc_flash_unlock();
svc_flash_erase_sector(FLASH_META_SECTOR_LAST);
storage_check_flash_errors(svc_flash_lock());
storage_u2f_offset = 0;
}
// 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
svc_flash_program(FLASH_CR_PROGRAM_X32);
flash_write32(FLASH_STORAGE_START, 0);
if (*(const uint32_t *)FLASH_PTR(FLASH_STORAGE_START) != 0) {
storage_show_error();
}
// erase pinarea/u2f sector
svc_flash_erase_sector(FLASH_META_SECTOR_LAST);
flash_write32(FLASH_STORAGE_PINAREA, new_pinfails);
if (*(const volatile uint32_t *)FLASH_PTR(FLASH_STORAGE_PINAREA) != new_pinfails) {
storage_show_error();
}
// restore storage sector
storageUpdate.has_u2f_counter = true;
storageUpdate.u2f_counter += storage_u2f_offset;
storage_u2f_offset = 0;
storage_commit_locked(true);
}
void storage_resetPinFails(uint32_t flash_pinfails)
{
svc_flash_unlock();
if (flash_pinfails + sizeof(uint32_t)
>= FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN) {
// recycle extra storage sector
storage_area_recycle(0xffffffff);
} else {
svc_flash_program(FLASH_CR_PROGRAM_X32);
flash_write32(flash_pinfails, 0);
}
storage_check_flash_errors(svc_flash_lock());
}
bool storage_increasePinFails(uint32_t flash_pinfails)
{
uint32_t newctr = *(const uint32_t*)FLASH_PTR(flash_pinfails) << 1;
// counter already at maximum, we do not increase it any more
// return success so that a good pin is accepted
if (!newctr)
return true;
svc_flash_unlock();
svc_flash_program(FLASH_CR_PROGRAM_X32);
flash_write32(flash_pinfails, newctr);
storage_check_flash_errors(svc_flash_lock());
return *(const uint32_t*)FLASH_PTR(flash_pinfails) == newctr;
}
uint32_t storage_getPinWait(uint32_t flash_pinfails)
{
// The pin failure word is the inverted wait time in seconds.
// It's inverted because flash allows changing 1 to 0 but not vice versa.
return ~*(const uint32_t*)FLASH_PTR(flash_pinfails);
}
uint32_t storage_getPinFailsOffset(void)
{
uint32_t flash_pinfails = FLASH_STORAGE_PINAREA;
while (*(const uint32_t*)FLASH_PTR(flash_pinfails) == 0)
flash_pinfails += sizeof(uint32_t);
return flash_pinfails;
}
bool storage_isInitialized(void)
{
return storageRom->has_node || storageRom->has_mnemonic;
}
bool storage_isImported(void)
{
return storageRom->has_imported && storageRom->imported;
}
void storage_setImported(bool imported)
{
storageUpdate.has_imported = true;
storageUpdate.imported = imported;
}
bool storage_needsBackup(void)
{
return storageUpdate.has_needs_backup ? storageUpdate.needs_backup
: storageRom->has_needs_backup && storageRom->needs_backup;
}
void storage_setNeedsBackup(bool needs_backup)
{
storageUpdate.has_needs_backup = true;
storageUpdate.needs_backup = needs_backup;
}
bool storage_unfinishedBackup(void)
{
return storageUpdate.has_unfinished_backup ? storageUpdate.unfinished_backup
: storageRom->has_unfinished_backup && storageRom->unfinished_backup;
}
void storage_setUnfinishedBackup(bool unfinished_backup)
{
storageUpdate.has_unfinished_backup = true;
storageUpdate.unfinished_backup = unfinished_backup;
}
bool storage_noBackup(void)
{
return storageUpdate.has_no_backup ? storageUpdate.no_backup
: storageRom->has_no_backup && storageRom->no_backup;
}
void storage_setNoBackup(void)
{
storageUpdate.has_no_backup = true;
storageUpdate.no_backup = true;
}
void storage_applyFlags(uint32_t flags)
{
if ((storageRom->flags | flags) == storageRom->flags) {
return; // no new flags
}
storageUpdate.has_flags = true;
storageUpdate.flags |= flags;
storage_update();
}
uint32_t storage_getFlags(void)
{
return storageRom->has_flags ? storageRom->flags : 0;
}
uint32_t storage_nextU2FCounter(void)
{
uint32_t flash_u2f_offset = FLASH_STORAGE_U2FAREA +
sizeof(uint32_t) * (storage_u2f_offset / 32);
uint32_t newval = 0xfffffffe << (storage_u2f_offset & 31);
svc_flash_unlock();
svc_flash_program(FLASH_CR_PROGRAM_X32);
flash_write32(flash_u2f_offset, newval);
storage_u2f_offset++;
if (storage_u2f_offset >= 8 * FLASH_STORAGE_U2FAREA_LEN) {
storage_area_recycle(*(const uint32_t*)
FLASH_PTR(storage_getPinFailsOffset()));
}
storage_check_flash_errors(svc_flash_lock());
return storageRom->u2f_counter + storage_u2f_offset;
}
void storage_setU2FCounter(uint32_t u2fcounter)
{
storageUpdate.has_u2f_counter = true;
storageUpdate.u2f_counter = u2fcounter - storage_u2f_offset;
}
uint32_t storage_getAutoLockDelayMs()
{
const uint32_t default_delay_ms = 10 * 60 * 1000U; // 10 minutes
return storageRom->has_auto_lock_delay_ms ? storageRom->auto_lock_delay_ms : default_delay_ms;
}
void storage_setAutoLockDelayMs(uint32_t auto_lock_delay_ms)
{
const uint32_t min_delay_ms = 10 * 1000U; // 10 seconds
auto_lock_delay_ms = MAX(auto_lock_delay_ms, min_delay_ms);
storageUpdate.has_auto_lock_delay_ms = true;
storageUpdate.auto_lock_delay_ms = auto_lock_delay_ms;
}
void storage_wipe(void)
{
session_clear(true);
storage_generate_uuid();
svc_flash_unlock();
storage_commit_locked(false);
storage_check_flash_errors(svc_flash_lock());
storage_clearPinArea();
}

@ -17,13 +17,14 @@
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "trezor.h"
#include "oled.h"
#include "bitmaps.h"
#include "util.h"
#include "usb.h"
#include "setup.h"
#include "storage.h"
#include "config.h"
#include "layout.h"
#include "layout2.h"
#include "rng.h"
@ -31,6 +32,8 @@
#include "buttons.h"
#include "gettext.h"
#include "bl_check.h"
#include "memzero.h"
#include <libopencm3/stm32/desig.h>
/* Screen timeout */
uint32_t system_millis_lock_start;
@ -76,7 +79,7 @@ void check_lock_screen(void)
// if homescreen is shown for too long
if (layoutLast == layoutHome) {
if ((timer_ms() - system_millis_lock_start) >= storage_getAutoLockDelayMs()) {
if ((timer_ms() - system_millis_lock_start) >= config_getAutoLockDelayMs()) {
// lock the screen
session_clear(true);
layoutScreensaver();
@ -97,24 +100,31 @@ int main(void)
#endif
if (!is_mode_unprivileged()) {
desig_get_unique_id((uint32_t*)HW_ENTROPY_DATA);
timer_init();
#ifdef APPVER
// enable MPU (Memory Protection Unit)
mpu_config();
#endif
} else {
#if EMULATOR
memzero(HW_ENTROPY_DATA, HW_ENTROPY_LEN);
#else
// we are running in unprivileged mode
// use fixed HW_ENTROPY
memset(HW_ENTROPY_DATA, 0x3C, HW_ENTROPY_LEN);
#endif
}
#if DEBUG_LINK
oledSetDebugLink(1);
storage_wipe();
config_wipe();
#endif
oledDrawBitmap(40, 0, &bmp_logo64);
oledRefresh();
storage_init();
config_init();
layoutHome();
usbInit();
for (;;) {

@ -21,7 +21,7 @@
#include <ecdsa.h>
#include "debug.h"
#include "storage.h"
#include "config.h"
#include "bip32.h"
#include "layout2.h"
#include "usb.h"
@ -460,7 +460,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_getU2FRoot(&node)) {
if (!config_getU2FRoot(&node)) {
layoutHome();
debugLog(0, "", "ERR: Device not init");
return 0;
@ -546,7 +546,7 @@ 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()) {
if (!config_isInitialized()) {
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
return;
}
@ -654,7 +654,7 @@ 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()) {
if (!config_isInitialized()) {
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
return;
}
@ -727,7 +727,7 @@ void u2f_authenticate(const APDU *a)
U2F_AUTHENTICATE_RESP *resp =
(U2F_AUTHENTICATE_RESP *)&buf;
const uint32_t ctr = storage_nextU2FCounter();
const uint32_t ctr = config_nextU2FCounter();
resp->flags = U2F_AUTH_FLAG_TUP;
resp->ctr[0] = ctr >> 24 & 0xff;
resp->ctr[1] = ctr >> 16 & 0xff;

@ -25,7 +25,7 @@
#include "debug.h"
#include "messages.h"
#include "u2f.h"
#include "storage.h"
#include "config.h"
#include "util.h"
#include "timer.h"
@ -56,7 +56,7 @@
#define USB_STRINGS \
X(MANUFACTURER, "SatoshiLabs") \
X(PRODUCT, "TREZOR") \
X(SERIAL_NUMBER, storage_uuid_str) \
X(SERIAL_NUMBER, config_uuid_str) \
X(INTERFACE_MAIN, "TREZOR Interface") \
X(INTERFACE_DEBUG, "TREZOR Debug Link Interface") \
X(INTERFACE_U2F, "TREZOR U2F Interface") \

@ -0,0 +1,137 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <libopencm3/stm32/flash.h>
#include "common.h"
#include "flash.h"
#include "memory.h"
#include "supervise.h"
static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = {
[ 0] = 0x08000000, // - 0x08003FFF | 16 KiB
[ 1] = 0x08004000, // - 0x08007FFF | 16 KiB
[ 2] = 0x08008000, // - 0x0800BFFF | 16 KiB
[ 3] = 0x0800C000, // - 0x0800FFFF | 16 KiB
[ 4] = 0x08010000, // - 0x0801FFFF | 64 KiB
[ 5] = 0x08020000, // - 0x0803FFFF | 128 KiB
[ 6] = 0x08040000, // - 0x0805FFFF | 128 KiB
[ 7] = 0x08060000, // - 0x0807FFFF | 128 KiB
[ 8] = 0x08080000, // - 0x0809FFFF | 128 KiB
[ 9] = 0x080A0000, // - 0x080BFFFF | 128 KiB
[10] = 0x080C0000, // - 0x080DFFFF | 128 KiB
[11] = 0x080E0000, // - 0x080FFFFF | 128 KiB
[12] = 0x08100000, // last element - not a valid sector
};
static secbool flash_check_success(uint32_t status)
{
return (status & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) ? secfalse : sectrue;
}
void flash_init(void)
{
}
secbool flash_unlock_write(void)
{
svc_flash_unlock();
return sectrue;
}
secbool flash_lock_write(void)
{
return flash_check_success(svc_flash_lock());
}
const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size)
{
if (sector >= FLASH_SECTOR_COUNT) {
return NULL;
}
const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset;
const uint32_t next = FLASH_SECTOR_TABLE[sector + 1];
if (addr + size > next) {
return NULL;
}
return (const void *) FLASH_PTR(addr);
}
secbool flash_erase(uint8_t sector)
{
ensure(flash_unlock_write(), NULL);
svc_flash_erase_sector(sector);
ensure(flash_lock_write(), NULL);
// Check whether the sector was really deleted (contains only 0xFF).
const uint32_t addr_start = FLASH_SECTOR_TABLE[sector], addr_end = FLASH_SECTOR_TABLE[sector + 1];
for (uint32_t addr = addr_start; addr < addr_end; addr += 4) {
if (*((const uint32_t *)FLASH_PTR(addr)) != 0xFFFFFFFF) {
return secfalse;
}
}
return sectrue;
}
secbool flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data)
{
uint8_t *address = (uint8_t *) flash_get_address(sector, offset, 1);
if (address == NULL) {
return secfalse;
}
if ((*address & data) != data) {
return secfalse;
}
svc_flash_program(FLASH_CR_PROGRAM_X8);
*(volatile uint8_t *) address = data;
if (*address != data) {
return secfalse;
}
return sectrue;
}
secbool flash_write_word(uint8_t sector, uint32_t offset, uint32_t data)
{
uint32_t *address = (uint32_t *) flash_get_address(sector, offset, 4);
if (address == NULL) {
return secfalse;
}
if (offset % 4 != 0) {
return secfalse;
}
if ((*address & data) != data) {
return secfalse;
}
svc_flash_program(FLASH_CR_PROGRAM_X32);
*(volatile uint32_t *) address = data;
if (*address != data) {
return secfalse;
}
return sectrue;
}

@ -0,0 +1,47 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FLASH_H
#define FLASH_H
#include <stdint.h>
#include <stdlib.h>
#include "secbool.h"
#define FLASH_SECTOR_COUNT 24
// note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427) (reference RM0090 section 3.7.5)
#ifndef STM32F427xx
#define FLASH_SR_RDERR 0
#endif
#define FLASH_STATUS_ALL_FLAGS (FLASH_SR_RDERR | FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR | FLASH_SR_SOP | FLASH_SR_EOP)
void flash_init(void);
secbool __wur flash_unlock_write(void);
secbool __wur flash_lock_write(void);
const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size);
secbool __wur flash_erase(uint8_t sector);
secbool __wur flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data);
secbool __wur flash_write_word(uint8_t sector, uint32_t offset, uint32_t data);
#endif // FLASH_H

@ -0,0 +1,39 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __NORCOW_CONFIG_H__
#define __NORCOW_CONFIG_H__
#include "flash.h"
#define NORCOW_SECTOR_COUNT 2
#define NORCOW_SECTOR_SIZE (16*1024)
#define NORCOW_SECTORS {2, 3}
/*
* The length of the sector header in bytes. The header is preserved between sector erasures.
*/
#define NORCOW_HEADER_LEN (0x100)
/*
* Current storage version.
*/
#define NORCOW_VERSION ((uint32_t)0x00000001)
#endif

@ -0,0 +1,33 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TREZORHAL_SECBOOL_H
#define TREZORHAL_SECBOOL_H
#include <stdint.h>
typedef uint32_t secbool;
#define sectrue 0xAAAAAAAAU
#define secfalse 0x00000000U
#ifndef __wur
#define __wur __attribute__ ((warn_unused_result))
#endif
#endif

@ -0,0 +1 @@
Subproject commit 5688a9e47e6d0d21b63ac22924199de2da696db7
Loading…
Cancel
Save