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