diff --git a/.gitmodules b/.gitmodules index 920894a059..ff5aff640d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/Makefile b/Makefile index 140c3faf54..fce0a317c5 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/Makefile.include b/Makefile.include index 1f175f0e28..11e922652f 100644 --- a/Makefile.include +++ b/Makefile.include @@ -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) \ diff --git a/common.c b/common.c new file mode 100644 index 0000000000..0530ee3fa0 --- /dev/null +++ b/common.c @@ -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 . + */ + +#include +#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); +} diff --git a/common.h b/common.h new file mode 100644 index 0000000000..912f73c54d --- /dev/null +++ b/common.h @@ -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 . + */ + +#ifndef __TREZORHAL_COMMON_H__ +#define __TREZORHAL_COMMON_H__ + +#include +#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 diff --git a/emulator/Makefile b/emulator/Makefile index 1f2976dd01..1243a7fe54 100644 --- a/emulator/Makefile +++ b/emulator/Makefile @@ -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 diff --git a/emulator/flash.c b/emulator/memory.c similarity index 100% rename from emulator/flash.c rename to emulator/memory.c diff --git a/emulator/timer.c b/emulator/timer.c index eadbf76cce..efb95564bd 100644 --- a/emulator/timer.c +++ b/emulator/timer.c @@ -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; } diff --git a/firmware/Makefile b/firmware/Makefile index c8095160ce..90e136324d 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -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 diff --git a/firmware/config.c b/firmware/config.c new file mode 100644 index 0000000000..e7de2d0409 --- /dev/null +++ b/firmware/config.c @@ -0,0 +1,905 @@ +/* + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (C) 2014 Pavol Rusnak + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include +#include +#include + +#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); +} diff --git a/firmware/storage.h b/firmware/config.h similarity index 52% rename from firmware/storage.h rename to firmware/config.h index 3004e89901..fc97ef0f0a 100644 --- a/firmware/storage.h +++ b/firmware/config.h @@ -17,8 +17,8 @@ * along with this library. If not, see . */ -#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 diff --git a/firmware/fsm.c b/firmware/fsm.c index 50fd0fc45c..ce0732fa15 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -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; diff --git a/firmware/fsm_msg_common.h b/firmware/fsm_msg_common.h index 524d12b114..10d61a1dc6 100644 --- a/firmware/fsm_msg_common.h +++ b/firmware/fsm_msg_common.h @@ -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(); } diff --git a/firmware/fsm_msg_debug.h b/firmware/fsm_msg_debug.h index 2ac38d4727..cf1973ddbd 100644 --- a/firmware/fsm_msg_debug.h +++ b/firmware/fsm_msg_debug.h @@ -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); } diff --git a/firmware/layout2.c b/firmware/layout2.c index 15bc7ef41c..1080ef2787 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -22,7 +22,7 @@ #include #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); } diff --git a/firmware/protect.c b/firmware/protect.c index 4a1d12c7ed..51f0754a05 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -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; - 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 + // 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; + } + + 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)) { + if (!ret) { fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); - return false; - } - if (storage_containsPin(pin)) { - session_cachePin(); - storage_resetPinFails(fails); - return true; - } else { - protectCheckMaxTry(storage_getPinWait(fails)); - 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)); + 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)); - pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, _("Please re-enter new PIN:")); + 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; + } - const bool result = pin && (strncmp(pin_compare, pin, sizeof(pin_compare)) == 0); - - 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; } diff --git a/firmware/protect.h b/firmware/protect.h index 8a3af45108..bcabdac674 100644 --- a/firmware/protect.h +++ b/firmware/protect.h @@ -22,10 +22,12 @@ #include #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; diff --git a/firmware/recovery.c b/firmware/recovery.c index f67af3bcdc..dd3b7edb09 100644 --- a/firmware/recovery.c +++ b/firmware/recovery.c @@ -21,7 +21,7 @@ #include #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) { diff --git a/firmware/reset.c b/firmware/reset.c index dd7fe102bf..82bf7191d6 100644 --- a/firmware/reset.c +++ b/firmware/reset.c @@ -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(); } diff --git a/firmware/reset.h b/firmware/reset.h index f4638ac36f..3ceea7533b 100644 --- a/firmware/reset.h +++ b/firmware/reset.h @@ -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); diff --git a/firmware/stellar.c b/firmware/stellar.c index 662cd5922b..5ee64d8574 100644 --- a/firmware/stellar.c +++ b/firmware/stellar.c @@ -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 diff --git a/firmware/storage.c b/firmware/storage.c deleted file mode 100644 index 6e7f66db32..0000000000 --- a/firmware/storage.c +++ /dev/null @@ -1,936 +0,0 @@ -/* - * This file is part of the TREZOR project, https://trezor.io/ - * - * Copyright (C) 2014 Pavol Rusnak - * - * This library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - */ - -#include -#include - -#include - -#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(); -} diff --git a/firmware/trezor.c b/firmware/trezor.c index 4d256eaece..c26ec0deda 100644 --- a/firmware/trezor.c +++ b/firmware/trezor.c @@ -17,13 +17,14 @@ * along with this library. If not, see . */ +#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 /* 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 (;;) { diff --git a/firmware/u2f.c b/firmware/u2f.c index 456ca3f147..048b52a15f 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -21,7 +21,7 @@ #include #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; diff --git a/firmware/usb.c b/firmware/usb.c index a40ce122b2..9da00ea913 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -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") \ diff --git a/flash.c b/flash.c new file mode 100644 index 0000000000..1501b6aa2a --- /dev/null +++ b/flash.c @@ -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 . + */ + +#include +#include + +#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; +} diff --git a/flash.h b/flash.h new file mode 100644 index 0000000000..24e1a3ad64 --- /dev/null +++ b/flash.h @@ -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 . + */ + +#ifndef FLASH_H +#define FLASH_H + +#include +#include +#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 diff --git a/norcow_config.h b/norcow_config.h new file mode 100644 index 0000000000..ba57dfb4b2 --- /dev/null +++ b/norcow_config.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 . + */ + +#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 diff --git a/secbool.h b/secbool.h new file mode 100644 index 0000000000..76dfb38dc1 --- /dev/null +++ b/secbool.h @@ -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 . + */ + +#ifndef TREZORHAL_SECBOOL_H +#define TREZORHAL_SECBOOL_H + +#include + +typedef uint32_t secbool; +#define sectrue 0xAAAAAAAAU +#define secfalse 0x00000000U + +#ifndef __wur +#define __wur __attribute__ ((warn_unused_result)) +#endif + +#endif diff --git a/vendor/trezor-storage b/vendor/trezor-storage new file mode 160000 index 0000000000..5688a9e47e --- /dev/null +++ b/vendor/trezor-storage @@ -0,0 +1 @@ +Subproject commit 5688a9e47e6d0d21b63ac22924199de2da696db7