mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-23 23:08:14 +00:00
firmware: integrate trezor-storage
This commit is contained in:
parent
5137f4ec00
commit
4f32cb5083
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,3 +13,6 @@
|
||||
[submodule "vendor/nanopb"]
|
||||
path = vendor/nanopb
|
||||
url = https://github.com/nanopb/nanopb.git
|
||||
[submodule "vendor/trezor-storage"]
|
||||
path = vendor/trezor-storage
|
||||
url = https://github.com/trezor/trezor-storage.git
|
||||
|
2
Makefile
2
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
|
||||
|
@ -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) \
|
||||
|
65
common.c
Normal file
65
common.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
#include "rng.h"
|
||||
#include "layout.h"
|
||||
#include "firmware/usb.h"
|
||||
|
||||
void shutdown(void);
|
||||
|
||||
void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, const char *file, int line_num, const char *func) {
|
||||
char line[4][128] = {{0}};
|
||||
int i = 0;
|
||||
if (expr != NULL) {
|
||||
snprintf(line[i], sizeof(line[0]), "Expr: %s", expr);
|
||||
i++;
|
||||
}
|
||||
if (msg != NULL) {
|
||||
snprintf(line[i], sizeof(line[0]), "Msg: %s", msg);
|
||||
i++;
|
||||
}
|
||||
if (file != NULL) {
|
||||
snprintf(line[i], sizeof(line[0]), "File: %s:%d", file, line_num);
|
||||
i++;
|
||||
}
|
||||
if (func != NULL) {
|
||||
snprintf(line[i], sizeof(line[0]), "Func: %s", func);
|
||||
i++;
|
||||
}
|
||||
error_shutdown("FATAL ERROR:", NULL, line[0], line[1], line[2], line[3]);
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) {
|
||||
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, line3, line4, line5, line6);
|
||||
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);
|
||||
}
|
43
common.h
Normal file
43
common.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TREZORHAL_COMMON_H__
|
||||
#define __TREZORHAL_COMMON_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "secbool.h"
|
||||
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
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, const char *line5, const char *line6);
|
||||
|
||||
#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__))
|
||||
|
||||
void hal_delay(uint32_t ms);
|
||||
|
||||
#endif
|
@ -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
|
||||
|
854
firmware/config.c
Normal file
854
firmware/config.c
Normal file
@ -0,0 +1,854 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
|
||||
#include "messages.pb.h"
|
||||
|
||||
#include "trezor.h"
|
||||
#include "sha2.h"
|
||||
#include "aes/aes.h"
|
||||
#include "pbkdf2.h"
|
||||
#include "hmac.h"
|
||||
#include "bip32.h"
|
||||
#include "bip39.h"
|
||||
#include "curves.h"
|
||||
#include "util.h"
|
||||
#include "memory.h"
|
||||
#include "rng.h"
|
||||
#include "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 constant to check validity of storage block for storage versions 1 to 10. */
|
||||
static const uint32_t CONFIG_MAGIC_V10 = 0x726f7473; // 'stor' as uint32_t
|
||||
|
||||
#define APP 0x0100
|
||||
#define FLAG_PUBLIC 0x8000
|
||||
|
||||
static const uint16_t KEY_UUID = 0 | APP | FLAG_PUBLIC; // bytes(12)
|
||||
static const uint16_t KEY_VERSION = 1 | APP; // uint32
|
||||
static const uint16_t KEY_NODE = 2 | APP; // node
|
||||
static const uint16_t KEY_MNEMONIC = 3 | APP; // string(241)
|
||||
static const uint16_t KEY_PASSPHRASE_PROTECTION = 4 | APP; // bool
|
||||
static const uint16_t KEY_LANGUAGE = 5 | APP | FLAG_PUBLIC; // string(17)
|
||||
static const uint16_t KEY_LABEL = 6 | APP | FLAG_PUBLIC; // string(33)
|
||||
static const uint16_t KEY_IMPORTED = 7 | APP; // bool
|
||||
static const uint16_t KEY_HOMESCREEN = 8 | APP | FLAG_PUBLIC; // bytes(1024)
|
||||
static const uint16_t KEY_U2F_COUNTER = 9 | APP | FLAG_PUBLIC; // uint32
|
||||
static const uint16_t KEY_NEEDS_BACKUP = 10 | APP; // bool
|
||||
static const uint16_t KEY_FLAGS = 11 | APP; // uint32
|
||||
static const uint16_t KEY_U2F_ROOT = 12 | APP; // node
|
||||
static const uint16_t KEY_UNFINISHED_BACKUP = 13 | APP; // bool
|
||||
static const uint16_t KEY_AUTO_LOCK_DELAY_MS = 14 | APP; // uint32
|
||||
static const uint16_t KEY_NO_BACKUP = 15 | APP; // bool
|
||||
|
||||
// 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 bool sessionSeedCached, sessionSeedUsesPassphrase;
|
||||
|
||||
static uint8_t CONFIDENTIAL sessionSeed[64];
|
||||
|
||||
static bool sessionPassphraseCached = false;
|
||||
static char CONFIDENTIAL sessionPassphrase[51];
|
||||
|
||||
static const uint32_t CONFIG_VERSION = 10;
|
||||
|
||||
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 bool config_set_bool(uint16_t key, bool value)
|
||||
{
|
||||
if (value) {
|
||||
return (sectrue == storage_set(key, &TRUE_BYTE, sizeof(TRUE_BYTE)));
|
||||
} else {
|
||||
return (sectrue == storage_set(key, &FALSE_BYTE, sizeof(FALSE_BYTE)));
|
||||
}
|
||||
}
|
||||
|
||||
static bool config_get_bool(uint16_t key)
|
||||
{
|
||||
uint8_t value = 0;
|
||||
uint16_t len = 0;
|
||||
secbool ret = storage_get(key, &value, sizeof(value), &len);
|
||||
return (sectrue == ret && len == 1 && value == TRUE_BYTE);
|
||||
}
|
||||
|
||||
static bool config_has_key(uint16_t key)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
return sectrue == storage_get(key, NULL, 0, &len);
|
||||
}
|
||||
|
||||
static bool config_get_string(uint16_t key, char *dest, uint16_t dest_size) {
|
||||
dest[0] = '\0';
|
||||
uint16_t len = 0;
|
||||
if (sectrue != storage_get(key, dest, dest_size - 1, &len)) {
|
||||
return false;
|
||||
}
|
||||
dest[len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t config_get_uint32(uint16_t key) {
|
||||
uint32_t value = 0;
|
||||
uint16_t len = 0;
|
||||
if (sectrue != storage_get(key, &value, sizeof(value), &len) || len != sizeof(value)) {
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void config_show_error(void)
|
||||
{
|
||||
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Storage failure"), _("detected."), NULL, _("Please unplug"), _("the device."), NULL);
|
||||
shutdown();
|
||||
}
|
||||
|
||||
static bool 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_STORAGE_START), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) {
|
||||
// wrong magic
|
||||
return false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Add salt.
|
||||
storage_init(NULL, (const uint8_t*)"", 0);
|
||||
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) {
|
||||
storage_set(KEY_NODE, &config.node, sizeof(config.node));
|
||||
}
|
||||
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 true;
|
||||
}
|
||||
|
||||
void config_init(void)
|
||||
{
|
||||
config_upgrade_v10();
|
||||
|
||||
// TODO Add salt.
|
||||
storage_init(&protectPinUiCallback, (const uint8_t*)"", 0);
|
||||
|
||||
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 = false;
|
||||
memzero(&sessionSeed, sizeof(sessionSeed));
|
||||
sessionPassphraseCached = false;
|
||||
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);
|
||||
}
|
||||
storage_set(KEY_NODE, &storageHDNode, sizeof(storageHDNode));
|
||||
}
|
||||
|
||||
#if DEBUG_LINK
|
||||
void 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)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
#endif
|
||||
|
||||
void config_loadDevice(const LoadDevice *msg)
|
||||
{
|
||||
session_clear(true);
|
||||
// TODO We can't set anything with the storage locked. Shouldn't we wipe?
|
||||
|
||||
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_LABEL, lang, strnlen(lang, MAX_LANGUAGE_LEN));
|
||||
}
|
||||
|
||||
void config_setPassphraseProtection(bool passphrase_protection)
|
||||
{
|
||||
sessionSeedCached = false;
|
||||
sessionPassphraseCached = false;
|
||||
config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
|
||||
}
|
||||
|
||||
bool config_hasPassphraseProtection(void)
|
||||
{
|
||||
return config_get_bool(KEY_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 == sessionSeedUsesPassphrase
|
||||
&& 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)
|
||||
if (!config_get_bool(KEY_IMPORTED)) {
|
||||
// test whether mnemonic is a valid BIP-0039 mnemonic
|
||||
if (!mnemonic_check(mnemonic)) {
|
||||
// and if not then halt the device
|
||||
config_show_error();
|
||||
}
|
||||
}
|
||||
char oldTiny = usbTiny(1);
|
||||
mnemonic_to_seed(mnemonic, usePassphrase ? sessionPassphrase : "", sessionSeed, get_root_node_callback); // BIP-0039
|
||||
memzero(mnemonic, sizeof(mnemonic));
|
||||
usbTiny(oldTiny);
|
||||
sessionSeedCached = true;
|
||||
sessionSeedUsesPassphrase = usePassphrase;
|
||||
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;
|
||||
}
|
||||
if (config_hasPassphraseProtection() && 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 config_get_string(KEY_LABEL, dest, dest_size);
|
||||
}
|
||||
|
||||
bool config_getLanguage(char *dest, uint16_t dest_size)
|
||||
{
|
||||
return 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;
|
||||
}
|
||||
|
||||
void config_setMnemonic(const char *mnemonic)
|
||||
{
|
||||
if (mnemonic == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sectrue != storage_set(KEY_MNEMONIC, mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN))) {
|
||||
return;
|
||||
}
|
||||
|
||||
StorageHDNode u2fNode;
|
||||
memzero(&u2fNode, sizeof(u2fNode));
|
||||
config_compute_u2froot(mnemonic, &u2fNode);
|
||||
storage_set(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode));
|
||||
memzero(&u2fNode, sizeof(u2fNode));
|
||||
}
|
||||
|
||||
bool config_hasNode(void)
|
||||
{
|
||||
return config_has_key(KEY_NODE);
|
||||
}
|
||||
|
||||
bool config_hasMnemonic(void)
|
||||
{
|
||||
return config_has_key(KEY_MNEMONIC);
|
||||
}
|
||||
|
||||
bool config_getMnemonic(char *dest, uint16_t dest_size)
|
||||
{
|
||||
return 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 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;
|
||||
}
|
||||
|
||||
if (sectrue == storage_change_pin(pin_to_int(old_pin), new_pin_int)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 *)config_uuid, sizeof(config_uuid));
|
||||
hmac_sha256_Final(&ctx, state + 32);
|
||||
|
||||
memzero(&ctx, sizeof(ctx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool session_isPinCached(void)
|
||||
{
|
||||
return storage_is_unlocked();
|
||||
}
|
||||
|
||||
bool config_isInitialized(void)
|
||||
{
|
||||
return config_has_key(KEY_NODE) || config_has_key(KEY_MNEMONIC);
|
||||
}
|
||||
|
||||
bool config_isImported(void)
|
||||
{
|
||||
return config_get_bool(KEY_IMPORTED);
|
||||
}
|
||||
|
||||
void config_setImported(bool imported)
|
||||
{
|
||||
config_set_bool(KEY_IMPORTED, imported);
|
||||
}
|
||||
|
||||
bool config_needsBackup(void)
|
||||
{
|
||||
return config_get_bool(KEY_NEEDS_BACKUP);
|
||||
}
|
||||
|
||||
void config_setNeedsBackup(bool needs_backup)
|
||||
{
|
||||
config_set_bool(KEY_NEEDS_BACKUP, needs_backup);
|
||||
}
|
||||
|
||||
bool config_unfinishedBackup(void)
|
||||
{
|
||||
return config_get_bool(KEY_UNFINISHED_BACKUP);
|
||||
}
|
||||
|
||||
void config_setUnfinishedBackup(bool unfinished_backup)
|
||||
{
|
||||
config_set_bool(KEY_UNFINISHED_BACKUP, unfinished_backup);
|
||||
}
|
||||
|
||||
bool config_noBackup(void)
|
||||
{
|
||||
return config_get_bool(KEY_NO_BACKUP);
|
||||
}
|
||||
|
||||
void config_setNoBackup(void)
|
||||
{
|
||||
config_set_bool(KEY_NO_BACKUP, true);
|
||||
}
|
||||
|
||||
void config_applyFlags(uint32_t flags)
|
||||
{
|
||||
uint32_t old_flags = config_get_uint32(KEY_FLAGS);
|
||||
flags |= old_flags;
|
||||
if (flags == old_flags) {
|
||||
return; // no new flags
|
||||
}
|
||||
storage_set(KEY_FLAGS, &flags, sizeof(flags));
|
||||
}
|
||||
|
||||
uint32_t config_getFlags(void)
|
||||
{
|
||||
return config_get_uint32(KEY_FLAGS);
|
||||
}
|
||||
|
||||
uint32_t config_nextU2FCounter(void)
|
||||
{
|
||||
// TODO Implement efficient version.
|
||||
uint32_t counter = 0;
|
||||
uint16_t len = 0;
|
||||
storage_get(KEY_U2F_COUNTER, &counter, sizeof(counter), &len);
|
||||
counter++;
|
||||
storage_set(KEY_U2F_COUNTER, &counter, sizeof(counter));
|
||||
return counter;
|
||||
}
|
||||
|
||||
void config_setU2FCounter(uint32_t u2fcounter)
|
||||
{
|
||||
storage_set(KEY_U2F_COUNTER, &u2fcounter, sizeof(u2fcounter));
|
||||
}
|
||||
|
||||
uint32_t config_getAutoLockDelayMs()
|
||||
{
|
||||
const uint32_t default_delay_ms = 10 * 60 * 1000U; // 10 minutes
|
||||
uint32_t delay_ms = config_get_uint32(KEY_AUTO_LOCK_DELAY_MS);
|
||||
return (delay_ms != 0) ? delay_ms : default_delay_ms;
|
||||
}
|
||||
|
||||
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);
|
||||
storage_set(KEY_AUTO_LOCK_DELAY_MS, &auto_lock_delay_ms, sizeof(auto_lock_delay_ms));
|
||||
}
|
||||
|
||||
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);
|
||||
storage_set(KEY_UUID, config_uuid, sizeof(config_uuid));
|
||||
storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION));
|
||||
session_clear(true);
|
||||
}
|
@ -17,8 +17,8 @@
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __STORAGE_H__
|
||||
#define __STORAGE_H__
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#include "bip32.h"
|
||||
#include "messages-management.pb.h"
|
||||
@ -76,85 +76,82 @@ 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_hasPassphraseProtection(void);
|
||||
|
||||
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);
|
||||
void config_setMnemonic(const char *mnemonic);
|
||||
bool config_containsMnemonic(const char *mnemonic);
|
||||
bool config_hasMnemonic(void);
|
||||
bool config_getMnemonic(char *dest, uint16_t dest_size);
|
||||
|
||||
bool storage_hasNode(void);
|
||||
bool config_hasNode(void);
|
||||
#if DEBUG_LINK
|
||||
void storage_dumpNode(HDNodeType *node);
|
||||
void config_dumpNode(HDNodeType *node);
|
||||
#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 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_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);
|
||||
|
||||
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_isImported(void);
|
||||
void config_setImported(bool imported);
|
||||
|
||||
bool storage_needsBackup(void);
|
||||
void storage_setNeedsBackup(bool needs_backup);
|
||||
bool config_needsBackup(void);
|
||||
void config_setNeedsBackup(bool needs_backup);
|
||||
|
||||
bool storage_unfinishedBackup(void);
|
||||
void storage_setUnfinishedBackup(bool unfinished_backup);
|
||||
bool config_unfinishedBackup(void);
|
||||
void config_setUnfinishedBackup(bool unfinished_backup);
|
||||
|
||||
bool storage_noBackup(void);
|
||||
void storage_setNoBackup(void);
|
||||
bool config_noBackup(void);
|
||||
void config_setNoBackup(void);
|
||||
|
||||
void storage_applyFlags(uint32_t flags);
|
||||
uint32_t storage_getFlags(void);
|
||||
void config_applyFlags(uint32_t flags);
|
||||
uint32_t config_getFlags(void);
|
||||
|
||||
uint32_t storage_getAutoLockDelayMs(void);
|
||||
void storage_setAutoLockDelayMs(uint32_t auto_lock_delay_ms);
|
||||
uint32_t config_getAutoLockDelayMs(void);
|
||||
void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms);
|
||||
|
||||
void storage_wipe(void);
|
||||
void config_wipe(void);
|
||||
|
||||
extern char storage_uuid_str[25];
|
||||
extern char config_uuid_str[2*UUID_SIZE + 1];
|
||||
|
||||
#endif
|
@ -23,12 +23,11 @@
|
||||
#include "fsm.h"
|
||||
#include "messages.h"
|
||||
#include "bip32.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "coins.h"
|
||||
#include "debug.h"
|
||||
#include "transaction.h"
|
||||
#include "rng.h"
|
||||
#include "storage.h"
|
||||
#include "oled.h"
|
||||
#include "protect.h"
|
||||
#include "pinmatrix.h"
|
||||
@ -70,13 +69,13 @@ static uint8_t msg_resp[MSG_OUT_SIZE] __attribute__ ((aligned));
|
||||
memzero(resp, sizeof(TYPE));
|
||||
|
||||
#define CHECK_INITIALIZED \
|
||||
if (!storage_isInitialized()) { \
|
||||
if (!config_isInitialized()) { \
|
||||
fsm_sendFailure(FailureType_Failure_NotInitialized, NULL); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define CHECK_NOT_INITIALIZED \
|
||||
if (storage_isInitialized()) { \
|
||||
if (config_isInitialized()) { \
|
||||
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Device is already initialized. Use Wipe first.")); \
|
||||
return; \
|
||||
}
|
||||
@ -207,7 +206,7 @@ static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n,
|
||||
if (fingerprint) {
|
||||
*fingerprint = 0;
|
||||
}
|
||||
if (!storage_getRootNode(&node, curve, true)) {
|
||||
if (!config_getRootNode(&node, curve, true)) {
|
||||
fsm_sendFailure(FailureType_Failure_NotInitialized, _("Device not initialized or passphrase request cancelled or unsupported curve"));
|
||||
layoutHome();
|
||||
return 0;
|
||||
|
@ -45,31 +45,31 @@ 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; resp->passphrase_protection = config_hasPassphraseProtection();
|
||||
#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()) {
|
||||
|
||||
if (config_getLanguage(resp->language, sizeof(resp->language))) {
|
||||
resp->has_language = true;
|
||||
}
|
||||
|
||||
if (config_getLabel(resp->label, sizeof(resp->label))) {
|
||||
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_initialized = true; resp->initialized = config_isInitialized();
|
||||
resp->has_imported = true; resp->imported = config_isImported();
|
||||
resp->has_pin_cached = true; resp->pin_cached = session_isPinCached();
|
||||
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; resp->needs_backup = config_needsBackup();
|
||||
resp->has_unfinished_backup = true; resp->unfinished_backup = config_unfinishedBackup();
|
||||
resp->has_no_backup = true; resp->no_backup = config_noBackup();
|
||||
resp->has_flags = true; resp->flags = config_getFlags();
|
||||
resp->has_model = true; strlcpy(resp->model, "1", sizeof(resp->model));
|
||||
|
||||
msg_write(MessageType_MessageType_Features, resp);
|
||||
@ -111,14 +111,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 +130,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 +150,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"));
|
||||
@ -202,7 +197,7 @@ void fsm_msgLoadDevice(const LoadDevice *msg)
|
||||
}
|
||||
}
|
||||
|
||||
storage_loadDevice(msg);
|
||||
config_loadDevice(msg);
|
||||
fsm_sendSuccess(_("Device loaded"));
|
||||
layoutHome();
|
||||
}
|
||||
@ -242,7 +237,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,21 +311,20 @@ 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();
|
||||
}
|
||||
@ -334,7 +332,7 @@ void fsm_msgApplySettings(const ApplySettings *msg)
|
||||
void fsm_msgApplyFlags(const ApplyFlags *msg)
|
||||
{
|
||||
if (msg->has_flags) {
|
||||
storage_applyFlags(msg->flags);
|
||||
config_applyFlags(msg->flags);
|
||||
}
|
||||
fsm_sendSuccess(_("Flags applied"));
|
||||
}
|
||||
@ -376,8 +374,7 @@ void fsm_msgSetU2FCounter(const SetU2FCounter *msg)
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
storage_setU2FCounter(msg->u2f_counter);
|
||||
storage_update();
|
||||
config_setU2FCounter(msg->u2f_counter);
|
||||
fsm_sendSuccess(_("U2F counter set"));
|
||||
layoutHome();
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg)
|
||||
resp.layout.size = OLED_BUFSIZE;
|
||||
memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE);
|
||||
|
||||
if (storage_hasPin()) {
|
||||
if (config_hasPin()) {
|
||||
resp.has_pin = true;
|
||||
strlcpy(resp.pin, storage_getPin(), sizeof(resp.pin));
|
||||
strlcpy(resp.pin, "1", sizeof(resp.pin));
|
||||
}
|
||||
|
||||
resp.has_matrix = true;
|
||||
@ -52,18 +52,18 @@ void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg)
|
||||
resp.has_recovery_word_pos = true;
|
||||
resp.recovery_word_pos = recovery_get_word_pos();
|
||||
|
||||
if (storage_hasMnemonic()) {
|
||||
if (config_hasMnemonic()) {
|
||||
resp.has_mnemonic = true;
|
||||
strlcpy(resp.mnemonic, storage_getMnemonic(), sizeof(resp.mnemonic));
|
||||
strlcpy(resp.mnemonic, config_getMnemonic(), sizeof(resp.mnemonic));
|
||||
}
|
||||
|
||||
if (storage_hasNode()) {
|
||||
if (config_hasNode()) {
|
||||
resp.has_node = true;
|
||||
storage_dumpNode(&(resp.node));
|
||||
config_dumpNode(&(resp.node));
|
||||
}
|
||||
|
||||
resp.has_passphrase_protection = true;
|
||||
resp.passphrase_protection = storage_hasPassphraseProtection();
|
||||
resp.passphrase_protection = config_hasPassphraseProtection();
|
||||
|
||||
msg_debug_write(MessageType_MessageType_DebugLinkState, &resp);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "layout2.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "oled.h"
|
||||
#include "bitmaps.h"
|
||||
#include "string.h"
|
||||
@ -235,31 +235,36 @@ 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()) {
|
||||
if (config_noBackup()) {
|
||||
oledBox(0, 0, 127, 8, false);
|
||||
oledDrawStringCenter(OLED_WIDTH / 2, 0, "SEEDLESS", FONT_STANDARD);
|
||||
} else
|
||||
if (storage_unfinishedBackup()) {
|
||||
if (config_unfinishedBackup()) {
|
||||
oledBox(0, 0, 127, 8, false);
|
||||
oledDrawStringCenter(OLED_WIDTH / 2, 0, "BACKUP FAILED!", FONT_STANDARD);
|
||||
} else
|
||||
if (storage_needsBackup()) {
|
||||
if (config_needsBackup()) {
|
||||
oledBox(0, 0, 127, 8, false);
|
||||
oledDrawStringCenter(OLED_WIDTH / 2, 0, "NEEDS BACKUP!", FONT_STANDARD);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "protect.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "messages.h"
|
||||
#include "usb.h"
|
||||
@ -147,102 +147,115 @@ const char *requestPin(PinMatrixRequestType type, const char *text)
|
||||
}
|
||||
}
|
||||
|
||||
static void protectCheckMaxTry(uint32_t wait) {
|
||||
if (wait < (1 << MAX_WRONG_PINS))
|
||||
return;
|
||||
void 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;
|
||||
while (secs > 0 && secstr >= secstrbuf) {
|
||||
secstr--;
|
||||
*secstr = (secs % 10) + '0';
|
||||
secs /= 10;
|
||||
}
|
||||
if (wait == 1) {
|
||||
// Change "seconds" to "second".
|
||||
secstrbuf[16] = 0;
|
||||
}
|
||||
layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Wrong PIN entered"), NULL, _("Please wait"), secstr, _("to continue ..."), NULL);
|
||||
|
||||
/* TODO
|
||||
if (msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||
protectAbortedByCancel = false;
|
||||
protectAbortedByInitialize = true;
|
||||
msg_tiny_id = 0xFFFF;
|
||||
usbTiny(0);
|
||||
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool protectPin(bool use_cached)
|
||||
{
|
||||
if (!storage_hasPin() || (use_cached && session_isPinCached())) {
|
||||
if (!config_hasPin() || (use_cached && session_isPinCached())) {
|
||||
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);
|
||||
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
|
||||
return false;
|
||||
}
|
||||
wait--;
|
||||
}
|
||||
usbTiny(0);
|
||||
const char *pin;
|
||||
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:"));
|
||||
|
||||
// TODO If maximum number of PIN attempts:
|
||||
// error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.", NULL, "Please unplug", "the device.");
|
||||
|
||||
const char *pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:"));
|
||||
if (!pin) {
|
||||
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
|
||||
return false;
|
||||
}
|
||||
if (!storage_increasePinFails(fails)) {
|
||||
|
||||
usbTiny(1);
|
||||
bool ret = config_containsPin(pin);
|
||||
usbTiny(0);
|
||||
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 (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));
|
||||
}
|
||||
|
||||
if (!pin) {
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
|
||||
strlcpy(pin_compare, pin, sizeof(pin_compare));
|
||||
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;
|
||||
}
|
||||
|
||||
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, _("Please re-enter new PIN:"));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const bool result = pin && (strncmp(pin_compare, pin, sizeof(pin_compare)) == 0);
|
||||
|
||||
if (result) {
|
||||
storage_setPin(pin_compare);
|
||||
storage_update();
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (!config_hasPassphraseProtection() || session_isPassphraseCached()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,9 @@
|
||||
#include "messages-common.pb.h"
|
||||
|
||||
bool protectButton(ButtonRequestType type, bool confirm_only);
|
||||
void protectPinUiCallback(uint32_t wait, uint32_t progress);
|
||||
bool protectPin(bool use_cached);
|
||||
bool protectChangePin(void);
|
||||
bool protectChangePin(bool removal);
|
||||
bool protectPassphrase(void);
|
||||
|
||||
extern bool protectAbortedByCancel;
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <ctype.h>
|
||||
#include "recovery.h"
|
||||
#include "fsm.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "layout2.h"
|
||||
#include "protect.h"
|
||||
#include "messages.h"
|
||||
@ -44,7 +44,7 @@ static uint32_t word_count;
|
||||
*/
|
||||
static int awaiting_word = 0;
|
||||
|
||||
/* True if we should not write anything back to storage
|
||||
/* True if we should not write anything back to config
|
||||
* (can be used for testing seed for correctness).
|
||||
*/
|
||||
static bool dry_run;
|
||||
@ -163,18 +163,17 @@ 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);
|
||||
// Update mnemonic on config.
|
||||
config_setMnemonic(new_mnemonic);
|
||||
memzero(new_mnemonic, sizeof(new_mnemonic));
|
||||
if (!enforce_wordlist) {
|
||||
// not enforcing => mark storage as imported
|
||||
storage_setImported(true);
|
||||
// not enforcing => mark config as imported
|
||||
config_setImported(true);
|
||||
}
|
||||
storage_update();
|
||||
fsm_sendSuccess(_("Device recovered"));
|
||||
} 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 +465,15 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_pr
|
||||
}
|
||||
|
||||
if (!dry_run) {
|
||||
if (pin_protection && !protectChangePin()) {
|
||||
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
|
||||
if (pin_protection && !protectChangePin(false)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
storage_setPassphraseProtection(passphrase_protection);
|
||||
storage_setLanguage(language);
|
||||
storage_setLabel(label);
|
||||
storage_setU2FCounter(u2f_counter);
|
||||
storage_update();
|
||||
config_setPassphraseProtection(passphrase_protection);
|
||||
config_setLanguage(language);
|
||||
config_setLabel(label);
|
||||
config_setU2FCounter(u2f_counter);
|
||||
}
|
||||
|
||||
if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) {
|
||||
|
@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "reset.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "rng.h"
|
||||
#include "sha2.h"
|
||||
#include "messages.h"
|
||||
@ -75,17 +75,15 @@ void reset_init(bool display_random, uint32_t _strength, bool passphrase_protect
|
||||
}
|
||||
}
|
||||
|
||||
if (pin_protection && !protectChangePin()) {
|
||||
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
|
||||
if (pin_protection && !protectChangePin(false)) {
|
||||
layoutHome();
|
||||
return;
|
||||
}
|
||||
|
||||
storage_setPassphraseProtection(passphrase_protection);
|
||||
storage_setLanguage(language);
|
||||
storage_setLabel(label);
|
||||
storage_setU2FCounter(u2f_counter);
|
||||
storage_update();
|
||||
config_setPassphraseProtection(passphrase_protection);
|
||||
config_setLanguage(language);
|
||||
config_setLabel(label);
|
||||
config_setU2FCounter(u2f_counter);
|
||||
|
||||
EntropyRequest resp;
|
||||
memzero(&resp, sizeof(EntropyRequest));
|
||||
@ -104,45 +102,41 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len)
|
||||
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();
|
||||
if (no_backup) {
|
||||
config_setNoBackup();
|
||||
} else {
|
||||
config_setNeedsBackup(true);
|
||||
}
|
||||
config_setMnemonic(mnemonic);
|
||||
fsm_sendSuccess(_("Device successfully initialized"));
|
||||
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()) {
|
||||
if (!config_needsBackup()) {
|
||||
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Seed already backed up"));
|
||||
return;
|
||||
}
|
||||
|
||||
storage_setUnfinishedBackup(true);
|
||||
storage_setNeedsBackup(false);
|
||||
|
||||
if (separated) {
|
||||
storage_update();
|
||||
config_setUnfinishedBackup(true);
|
||||
config_setNeedsBackup(false);
|
||||
}
|
||||
|
||||
const char *mnemonic = storage_getMnemonic();
|
||||
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
int i = 0, word_pos = 1;
|
||||
while (mnemonic[i] != 0) {
|
||||
@ -159,7 +153,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,12 +163,14 @@ void reset_backup(bool separated)
|
||||
}
|
||||
}
|
||||
|
||||
storage_setUnfinishedBackup(false);
|
||||
config_setUnfinishedBackup(false);
|
||||
storage_update();
|
||||
|
||||
if (separated) {
|
||||
fsm_sendSuccess(_("Seed successfully backed up"));
|
||||
} else {
|
||||
config_setNeedsBackup(false);
|
||||
config_setMnemonic(mnemonic);
|
||||
fsm_sendSuccess(_("Device successfully initialized"));
|
||||
}
|
||||
layoutHome();
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label, uint32_t u2f_counter, bool _skip_backup, bool _no_backup);
|
||||
void reset_entropy(const uint8_t *ext_entropy, uint32_t len);
|
||||
void reset_backup(bool separated);
|
||||
void reset_backup(bool separated, const char* mnemonic);
|
||||
uint32_t reset_get_int_entropy(uint8_t *entropy);
|
||||
const char *reset_get_word(void);
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "bignum.h"
|
||||
#include "oled.h"
|
||||
#include "base32.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "fsm.h"
|
||||
#include "protect.h"
|
||||
#include "util.h"
|
||||
@ -1514,7 +1514,7 @@ const HDNode *stellar_deriveNode(const uint32_t *address_n, size_t address_n_cou
|
||||
const char *curve = "ed25519";
|
||||
|
||||
// Device not initialized, passphrase request cancelled, or unsupported curve
|
||||
if (!storage_getRootNode(&node, curve, true)) {
|
||||
if (!config_getRootNode(&node, curve, true)) {
|
||||
return 0;
|
||||
}
|
||||
// Failed to derive private key
|
||||
|
@ -1,936 +0,0 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
|
||||
#include "messages.pb.h"
|
||||
|
||||
#include "trezor.h"
|
||||
#include "sha2.h"
|
||||
#include "aes/aes.h"
|
||||
#include "pbkdf2.h"
|
||||
#include "hmac.h"
|
||||
#include "bip32.h"
|
||||
#include "bip39.h"
|
||||
#include "curves.h"
|
||||
#include "util.h"
|
||||
#include "memory.h"
|
||||
#include "rng.h"
|
||||
#include "storage.h"
|
||||
#include "debug.h"
|
||||
#include "protect.h"
|
||||
#include "layout2.h"
|
||||
#include "usb.h"
|
||||
#include "gettext.h"
|
||||
#include "u2f.h"
|
||||
#include "memzero.h"
|
||||
#include "supervise.h"
|
||||
|
||||
/* magic constant to check validity of storage block */
|
||||
static const uint32_t storage_magic = 0x726f7473; // 'stor' as uint32_t
|
||||
|
||||
static uint32_t storage_uuid[12 / sizeof(uint32_t)];
|
||||
_Static_assert(sizeof(storage_uuid) == 12, "storage_uuid has wrong size");
|
||||
|
||||
Storage CONFIDENTIAL storageUpdate __attribute__((aligned(4)));
|
||||
_Static_assert((sizeof(storageUpdate) & 3) == 0, "storage unaligned");
|
||||
|
||||
#define FLASH_STORAGE (FLASH_STORAGE_START + sizeof(storage_magic) + sizeof(storage_uuid))
|
||||
#define storageRom ((const Storage *) FLASH_PTR(FLASH_STORAGE))
|
||||
|
||||
char storage_uuid_str[25];
|
||||
|
||||
/*
|
||||
storage layout:
|
||||
|
||||
offset | type/length | description
|
||||
--------+--------------+-------------------------------
|
||||
0x0000 | 4 bytes | magic = 'stor'
|
||||
0x0004 | 12 bytes | uuid
|
||||
0x0010 | ? bytes | Storage structure
|
||||
--------+--------------+-------------------------------
|
||||
0x4000 | 4 kbytes | area for pin failures
|
||||
0x5000 | 256 bytes | area for u2f counter updates
|
||||
0x5100 | 11.75 kbytes | reserved
|
||||
|
||||
The area for pin failures looks like this:
|
||||
0 ... 0 pinfail 0xffffffff .. 0xffffffff
|
||||
The pinfail is a binary number of the form 1...10...0,
|
||||
the number of zeros is the number of pin failures.
|
||||
This layout is used because we can only clear bits without
|
||||
erasing the flash.
|
||||
|
||||
The area for u2f counter updates is just a sequence of zero-bits
|
||||
followed by a sequence of one-bits. The bits in a byte are numbered
|
||||
from LSB to MSB. The number of zero bits is the offset that should
|
||||
be added to the storage u2f_counter to get the real counter value.
|
||||
|
||||
*/
|
||||
|
||||
#define FLASH_STORAGE_PINAREA (FLASH_META_START + 0x4000)
|
||||
#define FLASH_STORAGE_PINAREA_LEN (0x1000)
|
||||
#define FLASH_STORAGE_U2FAREA (FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN)
|
||||
#define FLASH_STORAGE_U2FAREA_LEN (0x100)
|
||||
#define FLASH_STORAGE_REALLEN (sizeof(storage_magic) + sizeof(storage_uuid) + sizeof(Storage))
|
||||
|
||||
#if !EMULATOR
|
||||
// TODO: Fix this for emulator
|
||||
_Static_assert(FLASH_STORAGE_START + FLASH_STORAGE_REALLEN <= FLASH_STORAGE_PINAREA, "Storage struct is too large for TREZOR flash");
|
||||
#endif
|
||||
|
||||
/* Current u2f offset, i.e. u2f counter is
|
||||
* storage.u2f_counter + storage_u2f_offset.
|
||||
* This corresponds to the number of cleared bits in the U2FAREA.
|
||||
*/
|
||||
static uint32_t storage_u2f_offset;
|
||||
|
||||
static bool sessionSeedCached, sessionSeedUsesPassphrase;
|
||||
|
||||
static uint8_t CONFIDENTIAL sessionSeed[64];
|
||||
|
||||
static bool sessionPinCached = false;
|
||||
|
||||
static bool sessionPassphraseCached = false;
|
||||
static char CONFIDENTIAL sessionPassphrase[51];
|
||||
|
||||
#define STORAGE_VERSION 10
|
||||
|
||||
void storage_show_error(void)
|
||||
{
|
||||
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Storage failure"), _("detected."), NULL, _("Please unplug"), _("the device."), NULL);
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void storage_check_flash_errors(uint32_t status)
|
||||
{
|
||||
// flash operation failed
|
||||
if (status & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) {
|
||||
storage_show_error();
|
||||
}
|
||||
}
|
||||
|
||||
bool storage_from_flash(void)
|
||||
{
|
||||
storage_clear_update();
|
||||
if (memcmp(FLASH_PTR(FLASH_STORAGE_START), &storage_magic, sizeof(storage_magic)) != 0) {
|
||||
// wrong magic
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t version = storageRom->version;
|
||||
// version 1: since 1.0.0
|
||||
// version 2: since 1.2.1
|
||||
// version 3: since 1.3.1
|
||||
// version 4: since 1.3.2
|
||||
// version 5: since 1.3.3
|
||||
// version 6: since 1.3.6
|
||||
// version 7: since 1.5.1
|
||||
// version 8: since 1.5.2
|
||||
// version 9: since 1.6.1
|
||||
// version 10: since 1.7.2
|
||||
if (version > STORAGE_VERSION) {
|
||||
// downgrade -> clear storage
|
||||
return false;
|
||||
}
|
||||
|
||||
// load uuid
|
||||
memcpy(storage_uuid, FLASH_PTR(FLASH_STORAGE_START + sizeof(storage_magic)), sizeof(storage_uuid));
|
||||
data2hex(storage_uuid, sizeof(storage_uuid), storage_uuid_str);
|
||||
|
||||
#define OLD_STORAGE_SIZE(last_member) (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + 3) & ~3)
|
||||
|
||||
// copy storage
|
||||
size_t old_storage_size = 0;
|
||||
|
||||
if (version == 0) {
|
||||
} else if (version <= 2) {
|
||||
old_storage_size = OLD_STORAGE_SIZE(imported);
|
||||
} else if (version <= 5) {
|
||||
// added homescreen
|
||||
old_storage_size = OLD_STORAGE_SIZE(homescreen);
|
||||
} else if (version <= 7) {
|
||||
// added u2fcounter
|
||||
old_storage_size = OLD_STORAGE_SIZE(u2f_counter);
|
||||
} else if (version <= 8) {
|
||||
// added flags and needsBackup
|
||||
old_storage_size = OLD_STORAGE_SIZE(flags);
|
||||
} else if (version <= 9) {
|
||||
// added u2froot, unfinished_backup and auto_lock_delay_ms
|
||||
old_storage_size = OLD_STORAGE_SIZE(auto_lock_delay_ms);
|
||||
} else if (version <= 10) {
|
||||
// added no_backup
|
||||
old_storage_size = OLD_STORAGE_SIZE(no_backup);
|
||||
}
|
||||
|
||||
// erase newly added fields
|
||||
if (old_storage_size != sizeof(Storage)) {
|
||||
svc_flash_unlock();
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
for (uint32_t offset = old_storage_size; offset < sizeof(Storage); offset += sizeof(uint32_t)) {
|
||||
flash_write32(FLASH_STORAGE_START + sizeof(storage_magic) + sizeof(storage_uuid) + offset, 0);
|
||||
}
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
}
|
||||
|
||||
if (version <= 5) {
|
||||
// convert PIN failure counter from version 5 format
|
||||
uint32_t pinctr = storageRom->has_pin_failed_attempts ? storageRom->pin_failed_attempts : 0;
|
||||
if (pinctr > 31) {
|
||||
pinctr = 31;
|
||||
}
|
||||
svc_flash_unlock();
|
||||
// erase extra storage sector
|
||||
svc_flash_erase_sector(FLASH_META_SECTOR_LAST);
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
flash_write32(FLASH_STORAGE_PINAREA, 0xffffffff << pinctr);
|
||||
// storageRom.has_pin_failed_attempts and storageRom.pin_failed_attempts
|
||||
// are erased by storage_update below
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
}
|
||||
const uint32_t *u2fptr = (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA);
|
||||
while (*u2fptr == 0) {
|
||||
u2fptr++;
|
||||
}
|
||||
storage_u2f_offset = 32 * (u2fptr - (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA));
|
||||
uint32_t u2fword = *u2fptr;
|
||||
while ((u2fword & 1) == 0) {
|
||||
storage_u2f_offset++;
|
||||
u2fword >>= 1;
|
||||
}
|
||||
// force recomputing u2f root for storage version < 9.
|
||||
// this is done by re-setting the mnemonic, which triggers the computation
|
||||
if (version < 9) {
|
||||
storageUpdate.has_mnemonic = storageRom->has_mnemonic;
|
||||
strlcpy(storageUpdate.mnemonic, storageRom->mnemonic, sizeof(storageUpdate.mnemonic));
|
||||
}
|
||||
// update storage version on flash
|
||||
if (version != STORAGE_VERSION) {
|
||||
storage_update();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void storage_init(void)
|
||||
{
|
||||
if (!storage_from_flash()) {
|
||||
storage_wipe();
|
||||
}
|
||||
}
|
||||
|
||||
void storage_generate_uuid(void)
|
||||
{
|
||||
// set random uuid
|
||||
random_buffer((uint8_t *)storage_uuid, sizeof(storage_uuid));
|
||||
data2hex(storage_uuid, sizeof(storage_uuid), storage_uuid_str);
|
||||
}
|
||||
|
||||
void session_clear(bool clear_pin)
|
||||
{
|
||||
sessionSeedCached = false;
|
||||
memzero(&sessionSeed, sizeof(sessionSeed));
|
||||
sessionPassphraseCached = false;
|
||||
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
|
||||
if (clear_pin) {
|
||||
session_uncachePin();
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t storage_flash_words(uint32_t addr, const uint32_t *src, int nwords) {
|
||||
for (int i = 0; i < nwords; i++) {
|
||||
flash_write32(addr, *src++);
|
||||
addr += sizeof(uint32_t);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void get_u2froot_callback(uint32_t iter, uint32_t total)
|
||||
{
|
||||
layoutProgress(_("Updating"), 1000 * iter / total);
|
||||
}
|
||||
|
||||
static void storage_compute_u2froot(const char* mnemonic, StorageHDNode *u2froot) {
|
||||
static CONFIDENTIAL HDNode node;
|
||||
char oldTiny = usbTiny(1);
|
||||
mnemonic_to_seed(mnemonic, "", sessionSeed, get_u2froot_callback); // BIP-0039
|
||||
usbTiny(oldTiny);
|
||||
hdnode_from_seed(sessionSeed, 64, NIST256P1_NAME, &node);
|
||||
hdnode_private_ckd(&node, U2F_KEY_PATH);
|
||||
u2froot->depth = node.depth;
|
||||
u2froot->child_num = U2F_KEY_PATH;
|
||||
u2froot->chain_code.size = sizeof(node.chain_code);
|
||||
memcpy(u2froot->chain_code.bytes, node.chain_code, sizeof(node.chain_code));
|
||||
u2froot->has_private_key = true;
|
||||
u2froot->private_key.size = sizeof(node.private_key);
|
||||
memcpy(u2froot->private_key.bytes, node.private_key, sizeof(node.private_key));
|
||||
memzero(&node, sizeof(node));
|
||||
session_clear(false); // invalidate seed cache
|
||||
}
|
||||
|
||||
// if storage is filled in - update fields that has has_field set to true
|
||||
// if storage is NULL - do not backup original content - essentialy a wipe
|
||||
static void storage_commit_locked(bool update)
|
||||
{
|
||||
if (update) {
|
||||
if (storageUpdate.has_passphrase_protection) {
|
||||
sessionSeedCached = false;
|
||||
sessionPassphraseCached = false;
|
||||
}
|
||||
|
||||
storageUpdate.version = STORAGE_VERSION;
|
||||
if (!storageUpdate.has_node && !storageUpdate.has_mnemonic) {
|
||||
storageUpdate.has_node = storageRom->has_node;
|
||||
memcpy(&storageUpdate.node, &storageRom->node, sizeof(StorageHDNode));
|
||||
storageUpdate.has_mnemonic = storageRom->has_mnemonic;
|
||||
strlcpy(storageUpdate.mnemonic, storageRom->mnemonic, sizeof(storageUpdate.mnemonic));
|
||||
storageUpdate.has_u2froot = storageRom->has_u2froot;
|
||||
memcpy(&storageUpdate.u2froot, &storageRom->u2froot, sizeof(StorageHDNode));
|
||||
} else if (storageUpdate.has_mnemonic) {
|
||||
storageUpdate.has_u2froot = true;
|
||||
storage_compute_u2froot(storageUpdate.mnemonic, &storageUpdate.u2froot);
|
||||
}
|
||||
if (!storageUpdate.has_passphrase_protection) {
|
||||
storageUpdate.has_passphrase_protection = storageRom->has_passphrase_protection;
|
||||
storageUpdate.passphrase_protection = storageRom->passphrase_protection;
|
||||
}
|
||||
if (!storageUpdate.has_pin) {
|
||||
storageUpdate.has_pin = storageRom->has_pin;
|
||||
strlcpy(storageUpdate.pin, storageRom->pin, sizeof(storageUpdate.pin));
|
||||
} else if (!storageUpdate.pin[0]) {
|
||||
storageUpdate.has_pin = false;
|
||||
}
|
||||
if (!storageUpdate.has_language) {
|
||||
storageUpdate.has_language = storageRom->has_language;
|
||||
strlcpy(storageUpdate.language, storageRom->language, sizeof(storageUpdate.language));
|
||||
}
|
||||
if (!storageUpdate.has_label) {
|
||||
storageUpdate.has_label = storageRom->has_label;
|
||||
strlcpy(storageUpdate.label, storageRom->label, sizeof(storageUpdate.label));
|
||||
} else if (!storageUpdate.label[0]) {
|
||||
storageUpdate.has_label = false;
|
||||
}
|
||||
if (!storageUpdate.has_imported) {
|
||||
storageUpdate.has_imported = storageRom->has_imported;
|
||||
storageUpdate.imported = storageRom->imported;
|
||||
}
|
||||
if (!storageUpdate.has_homescreen) {
|
||||
storageUpdate.has_homescreen = storageRom->has_homescreen;
|
||||
memcpy(&storageUpdate.homescreen, &storageRom->homescreen, sizeof(storageUpdate.homescreen));
|
||||
} else if (storageUpdate.homescreen.size == 0) {
|
||||
storageUpdate.has_homescreen = false;
|
||||
}
|
||||
if (!storageUpdate.has_u2f_counter) {
|
||||
storageUpdate.has_u2f_counter = storageRom->has_u2f_counter;
|
||||
storageUpdate.u2f_counter = storageRom->u2f_counter;
|
||||
}
|
||||
if (!storageUpdate.has_needs_backup) {
|
||||
storageUpdate.has_needs_backup = storageRom->has_needs_backup;
|
||||
storageUpdate.needs_backup = storageRom->needs_backup;
|
||||
}
|
||||
if (!storageUpdate.has_unfinished_backup) {
|
||||
storageUpdate.has_unfinished_backup = storageRom->has_unfinished_backup;
|
||||
storageUpdate.unfinished_backup = storageRom->unfinished_backup;
|
||||
}
|
||||
if (!storageUpdate.has_no_backup) {
|
||||
storageUpdate.has_no_backup = storageRom->has_no_backup;
|
||||
storageUpdate.no_backup = storageRom->no_backup;
|
||||
}
|
||||
if (!storageUpdate.has_flags) {
|
||||
storageUpdate.has_flags = storageRom->has_flags;
|
||||
storageUpdate.flags = storageRom->flags;
|
||||
}
|
||||
}
|
||||
|
||||
// backup meta
|
||||
uint32_t meta_backup[FLASH_META_DESC_LEN / sizeof(uint32_t)];
|
||||
memcpy(meta_backup, FLASH_PTR(FLASH_META_START), FLASH_META_DESC_LEN);
|
||||
|
||||
// erase storage
|
||||
svc_flash_erase_sector(FLASH_META_SECTOR_FIRST);
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
|
||||
// copy meta back
|
||||
uint32_t flash = FLASH_META_START;
|
||||
flash = storage_flash_words(flash, meta_backup, FLASH_META_DESC_LEN / sizeof(uint32_t));
|
||||
|
||||
// copy storage
|
||||
flash = storage_flash_words(flash, &storage_magic, sizeof(storage_magic) / sizeof(uint32_t));
|
||||
flash = storage_flash_words(flash, storage_uuid, sizeof(storage_uuid) / sizeof(uint32_t));
|
||||
|
||||
if (update) {
|
||||
flash = storage_flash_words(flash, (const uint32_t *)&storageUpdate, sizeof(storageUpdate) / sizeof(uint32_t));
|
||||
}
|
||||
storage_clear_update();
|
||||
|
||||
// fill remainder with zero for future extensions
|
||||
while (flash < FLASH_STORAGE_PINAREA) {
|
||||
flash_write32(flash, 0);
|
||||
flash += sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
|
||||
void storage_clear_update(void)
|
||||
{
|
||||
memzero(&storageUpdate, sizeof(storageUpdate));
|
||||
}
|
||||
|
||||
void storage_update(void)
|
||||
{
|
||||
svc_flash_unlock();
|
||||
storage_commit_locked(true);
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
}
|
||||
|
||||
static void storage_setNode(const HDNodeType *node) {
|
||||
storageUpdate.node.depth = node->depth;
|
||||
storageUpdate.node.fingerprint = node->fingerprint;
|
||||
storageUpdate.node.child_num = node->child_num;
|
||||
|
||||
storageUpdate.node.chain_code.size = 32;
|
||||
memcpy(storageUpdate.node.chain_code.bytes, node->chain_code.bytes, 32);
|
||||
|
||||
if (node->has_private_key) {
|
||||
storageUpdate.node.has_private_key = true;
|
||||
storageUpdate.node.private_key.size = 32;
|
||||
memcpy(storageUpdate.node.private_key.bytes, node->private_key.bytes, 32);
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_LINK
|
||||
void storage_dumpNode(HDNodeType *node) {
|
||||
node->depth = storageRom->node.depth;
|
||||
node->fingerprint = storageRom->node.fingerprint;
|
||||
node->child_num = storageRom->node.child_num;
|
||||
|
||||
node->chain_code.size = 32;
|
||||
memcpy(node->chain_code.bytes, storageRom->node.chain_code.bytes, 32);
|
||||
|
||||
if (storageRom->node.has_private_key) {
|
||||
node->has_private_key = true;
|
||||
node->private_key.size = 32;
|
||||
memcpy(node->private_key.bytes, storageRom->node.private_key.bytes, 32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void storage_loadDevice(const LoadDevice *msg)
|
||||
{
|
||||
session_clear(true);
|
||||
|
||||
storageUpdate.has_imported = true;
|
||||
storageUpdate.imported = true;
|
||||
|
||||
storage_setPin(msg->has_pin ? msg->pin : "");
|
||||
storage_setPassphraseProtection(msg->has_passphrase_protection && msg->passphrase_protection);
|
||||
|
||||
if (msg->has_node) {
|
||||
storageUpdate.has_node = true;
|
||||
storageUpdate.has_mnemonic = false;
|
||||
storage_setNode(&(msg->node));
|
||||
sessionSeedCached = false;
|
||||
memzero(&sessionSeed, sizeof(sessionSeed));
|
||||
} else if (msg->has_mnemonic) {
|
||||
storageUpdate.has_mnemonic = true;
|
||||
storageUpdate.has_node = false;
|
||||
strlcpy(storageUpdate.mnemonic, msg->mnemonic, sizeof(storageUpdate.mnemonic));
|
||||
sessionSeedCached = false;
|
||||
memzero(&sessionSeed, sizeof(sessionSeed));
|
||||
}
|
||||
|
||||
if (msg->has_language) {
|
||||
storageUpdate.has_language = true;
|
||||
strlcpy(storageUpdate.language, msg->language, sizeof(storageUpdate.language));
|
||||
}
|
||||
|
||||
storage_setLabel(msg->has_label ? msg->label : "");
|
||||
|
||||
if (msg->has_u2f_counter) {
|
||||
storageUpdate.has_u2f_counter = true;
|
||||
storageUpdate.u2f_counter = msg->u2f_counter - storage_u2f_offset;
|
||||
}
|
||||
|
||||
storage_update();
|
||||
}
|
||||
|
||||
void storage_setLabel(const char *label)
|
||||
{
|
||||
storageUpdate.has_label = true;
|
||||
if (!label) return;
|
||||
strlcpy(storageUpdate.label, label, sizeof(storageUpdate.label));
|
||||
}
|
||||
|
||||
void storage_setLanguage(const char *lang)
|
||||
{
|
||||
if (!lang) return;
|
||||
// sanity check
|
||||
if (strcmp(lang, "english") == 0) {
|
||||
storageUpdate.has_language = true;
|
||||
strlcpy(storageUpdate.language, lang, sizeof(storageUpdate.language));
|
||||
}
|
||||
}
|
||||
|
||||
void storage_setPassphraseProtection(bool passphrase_protection)
|
||||
{
|
||||
sessionSeedCached = false;
|
||||
sessionPassphraseCached = false;
|
||||
|
||||
storageUpdate.has_passphrase_protection = true;
|
||||
storageUpdate.passphrase_protection = passphrase_protection;
|
||||
}
|
||||
|
||||
bool storage_hasPassphraseProtection(void)
|
||||
{
|
||||
return storageRom->has_passphrase_protection && storageRom->passphrase_protection;
|
||||
}
|
||||
|
||||
void storage_setHomescreen(const uint8_t *data, uint32_t size)
|
||||
{
|
||||
storageUpdate.has_homescreen = true;
|
||||
if (data && size == 1024) {
|
||||
memcpy(storageUpdate.homescreen.bytes, data, size);
|
||||
storageUpdate.homescreen.size = size;
|
||||
} else {
|
||||
memzero(storageUpdate.homescreen.bytes, sizeof(storageUpdate.homescreen.bytes));
|
||||
storageUpdate.homescreen.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_root_node_callback(uint32_t iter, uint32_t total)
|
||||
{
|
||||
usbSleep(1);
|
||||
layoutProgress(_("Waking up"), 1000 * iter / total);
|
||||
}
|
||||
|
||||
const uint8_t *storage_getSeed(bool usePassphrase)
|
||||
{
|
||||
// root node is properly cached
|
||||
if (usePassphrase == sessionSeedUsesPassphrase
|
||||
&& sessionSeedCached) {
|
||||
return sessionSeed;
|
||||
}
|
||||
|
||||
// if storage has mnemonic, convert it to node and use it
|
||||
if (storageRom->has_mnemonic) {
|
||||
if (usePassphrase && !protectPassphrase()) {
|
||||
return NULL;
|
||||
}
|
||||
// if storage was not imported (i.e. it was properly generated or recovered)
|
||||
if (!storageRom->has_imported || !storageRom->imported) {
|
||||
// test whether mnemonic is a valid BIP-0039 mnemonic
|
||||
if (!mnemonic_check(storageRom->mnemonic)) {
|
||||
// and if not then halt the device
|
||||
storage_show_error();
|
||||
}
|
||||
}
|
||||
char oldTiny = usbTiny(1);
|
||||
mnemonic_to_seed(storageRom->mnemonic, usePassphrase ? sessionPassphrase : "", sessionSeed, get_root_node_callback); // BIP-0039
|
||||
usbTiny(oldTiny);
|
||||
sessionSeedCached = true;
|
||||
sessionSeedUsesPassphrase = usePassphrase;
|
||||
return sessionSeed;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool storage_loadNode(const StorageHDNode *node, const char *curve, HDNode *out) {
|
||||
return hdnode_from_xprv(node->depth, node->child_num, node->chain_code.bytes, node->private_key.bytes, curve, out);
|
||||
}
|
||||
|
||||
bool storage_getU2FRoot(HDNode *node)
|
||||
{
|
||||
return storageRom->has_u2froot && storage_loadNode(&storageRom->u2froot, NIST256P1_NAME, node);
|
||||
}
|
||||
|
||||
bool storage_getRootNode(HDNode *node, const char *curve, bool usePassphrase)
|
||||
{
|
||||
// if storage has node, decrypt and use it
|
||||
if (storageRom->has_node && strcmp(curve, SECP256K1_NAME) == 0) {
|
||||
if (!protectPassphrase()) {
|
||||
return false;
|
||||
}
|
||||
if (!storage_loadNode(&storageRom->node, curve, node)) {
|
||||
return false;
|
||||
}
|
||||
if (storageRom->has_passphrase_protection && storageRom->passphrase_protection && sessionPassphraseCached && strlen(sessionPassphrase) > 0) {
|
||||
// decrypt hd node
|
||||
uint8_t secret[64];
|
||||
PBKDF2_HMAC_SHA512_CTX pctx;
|
||||
char oldTiny = usbTiny(1);
|
||||
pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), (const uint8_t *)"TREZORHD", 8, 1);
|
||||
get_root_node_callback(0, BIP39_PBKDF2_ROUNDS);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8);
|
||||
get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8, BIP39_PBKDF2_ROUNDS);
|
||||
}
|
||||
pbkdf2_hmac_sha512_Final(&pctx, secret);
|
||||
usbTiny(oldTiny);
|
||||
aes_decrypt_ctx ctx;
|
||||
aes_decrypt_key256(secret, &ctx);
|
||||
aes_cbc_decrypt(node->chain_code, node->chain_code, 32, secret + 32, &ctx);
|
||||
aes_cbc_decrypt(node->private_key, node->private_key, 32, secret + 32, &ctx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t *seed = storage_getSeed(usePassphrase);
|
||||
if (seed == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hdnode_from_seed(seed, 64, curve, node);
|
||||
}
|
||||
|
||||
const char *storage_getLabel(void)
|
||||
{
|
||||
return storageRom->has_label ? storageRom->label : 0;
|
||||
}
|
||||
|
||||
const char *storage_getLanguage(void)
|
||||
{
|
||||
return storageRom->has_language ? storageRom->language : 0;
|
||||
}
|
||||
|
||||
const uint8_t *storage_getHomescreen(void)
|
||||
{
|
||||
return (storageRom->has_homescreen && storageRom->homescreen.size == 1024) ? storageRom->homescreen.bytes : 0;
|
||||
}
|
||||
|
||||
void storage_setMnemonic(const char *mnemonic)
|
||||
{
|
||||
storageUpdate.has_mnemonic = true;
|
||||
strlcpy(storageUpdate.mnemonic, mnemonic, sizeof(storageUpdate.mnemonic));
|
||||
}
|
||||
|
||||
bool storage_hasNode(void)
|
||||
{
|
||||
return storageRom->has_node;
|
||||
}
|
||||
|
||||
bool storage_hasMnemonic(void)
|
||||
{
|
||||
return storageRom->has_mnemonic;
|
||||
}
|
||||
|
||||
const char *storage_getMnemonic(void)
|
||||
{
|
||||
return storageUpdate.has_mnemonic ? storageUpdate.mnemonic
|
||||
: storageRom->has_mnemonic ? storageRom->mnemonic : 0;
|
||||
}
|
||||
|
||||
/* Check whether mnemonic matches storage. The mnemonic must be
|
||||
* a null-terminated string.
|
||||
*/
|
||||
bool storage_containsMnemonic(const char *mnemonic) {
|
||||
/* The execution time of the following code only depends on the
|
||||
* (public) input. This avoids timing attacks.
|
||||
*/
|
||||
char diff = 0;
|
||||
uint32_t i = 0;
|
||||
for (; mnemonic[i]; i++) {
|
||||
diff |= (storageRom->mnemonic[i] - mnemonic[i]);
|
||||
}
|
||||
diff |= storageRom->mnemonic[i];
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
/* Check whether pin matches storage. The pin must be
|
||||
* a null-terminated string with at most 9 characters.
|
||||
*/
|
||||
bool storage_containsPin(const char *pin)
|
||||
{
|
||||
/* The execution time of the following code only depends on the
|
||||
* (public) input. This avoids timing attacks.
|
||||
*/
|
||||
char diff = 0;
|
||||
uint32_t i = 0;
|
||||
while (pin[i]) {
|
||||
diff |= storageRom->pin[i] - pin[i];
|
||||
i++;
|
||||
}
|
||||
diff |= storageRom->pin[i];
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
bool storage_hasPin(void)
|
||||
{
|
||||
return storageRom->has_pin && storageRom->pin[0] != 0;
|
||||
}
|
||||
|
||||
void storage_setPin(const char *pin)
|
||||
{
|
||||
storageUpdate.has_pin = true;
|
||||
strlcpy(storageUpdate.pin, pin, sizeof(storageUpdate.pin));
|
||||
session_cachePin();
|
||||
}
|
||||
|
||||
const char *storage_getPin(void)
|
||||
{
|
||||
return storageRom->has_pin ? storageRom->pin : 0;
|
||||
}
|
||||
|
||||
void session_cachePassphrase(const char *passphrase)
|
||||
{
|
||||
strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase));
|
||||
sessionPassphraseCached = true;
|
||||
}
|
||||
|
||||
bool session_isPassphraseCached(void)
|
||||
{
|
||||
return sessionPassphraseCached;
|
||||
}
|
||||
|
||||
bool session_getState(const uint8_t *salt, uint8_t *state, const char *passphrase)
|
||||
{
|
||||
if (!passphrase && !sessionPassphraseCached) {
|
||||
return false;
|
||||
} else {
|
||||
passphrase = sessionPassphrase;
|
||||
}
|
||||
if (!salt) {
|
||||
// if salt is not provided fill the first half of the state with random data
|
||||
random_buffer(state, 32);
|
||||
} else {
|
||||
// if salt is provided fill the first half of the state with salt
|
||||
memcpy(state, salt, 32);
|
||||
}
|
||||
// state[0:32] = salt
|
||||
// state[32:64] = HMAC(passphrase, salt || device_id)
|
||||
HMAC_SHA256_CTX ctx;
|
||||
hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase));
|
||||
hmac_sha256_Update(&ctx, state, 32);
|
||||
hmac_sha256_Update(&ctx, (const uint8_t *)storage_uuid, sizeof(storage_uuid));
|
||||
hmac_sha256_Final(&ctx, state + 32);
|
||||
|
||||
memzero(&ctx, sizeof(ctx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void session_cachePin(void)
|
||||
{
|
||||
sessionPinCached = true;
|
||||
}
|
||||
|
||||
void session_uncachePin(void)
|
||||
{
|
||||
sessionPinCached = false;
|
||||
}
|
||||
|
||||
bool session_isPinCached(void)
|
||||
{
|
||||
return sessionPinCached;
|
||||
}
|
||||
|
||||
void storage_clearPinArea(void)
|
||||
{
|
||||
svc_flash_unlock();
|
||||
svc_flash_erase_sector(FLASH_META_SECTOR_LAST);
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
storage_u2f_offset = 0;
|
||||
}
|
||||
|
||||
// called when u2f area or pin area overflows
|
||||
static void storage_area_recycle(uint32_t new_pinfails)
|
||||
{
|
||||
// first clear storage marker. In case of a failure below it is better
|
||||
// to clear the storage than to allow restarting with zero PIN failures
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
flash_write32(FLASH_STORAGE_START, 0);
|
||||
if (*(const uint32_t *)FLASH_PTR(FLASH_STORAGE_START) != 0) {
|
||||
storage_show_error();
|
||||
}
|
||||
|
||||
// erase pinarea/u2f sector
|
||||
svc_flash_erase_sector(FLASH_META_SECTOR_LAST);
|
||||
flash_write32(FLASH_STORAGE_PINAREA, new_pinfails);
|
||||
if (*(const volatile uint32_t *)FLASH_PTR(FLASH_STORAGE_PINAREA) != new_pinfails) {
|
||||
storage_show_error();
|
||||
}
|
||||
|
||||
// restore storage sector
|
||||
storageUpdate.has_u2f_counter = true;
|
||||
storageUpdate.u2f_counter += storage_u2f_offset;
|
||||
storage_u2f_offset = 0;
|
||||
storage_commit_locked(true);
|
||||
}
|
||||
|
||||
void storage_resetPinFails(uint32_t flash_pinfails)
|
||||
{
|
||||
svc_flash_unlock();
|
||||
if (flash_pinfails + sizeof(uint32_t)
|
||||
>= FLASH_STORAGE_PINAREA + FLASH_STORAGE_PINAREA_LEN) {
|
||||
// recycle extra storage sector
|
||||
storage_area_recycle(0xffffffff);
|
||||
} else {
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
flash_write32(flash_pinfails, 0);
|
||||
}
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
}
|
||||
|
||||
bool storage_increasePinFails(uint32_t flash_pinfails)
|
||||
{
|
||||
uint32_t newctr = *(const uint32_t*)FLASH_PTR(flash_pinfails) << 1;
|
||||
// counter already at maximum, we do not increase it any more
|
||||
// return success so that a good pin is accepted
|
||||
if (!newctr)
|
||||
return true;
|
||||
|
||||
svc_flash_unlock();
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
flash_write32(flash_pinfails, newctr);
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
|
||||
return *(const uint32_t*)FLASH_PTR(flash_pinfails) == newctr;
|
||||
}
|
||||
|
||||
uint32_t storage_getPinWait(uint32_t flash_pinfails)
|
||||
{
|
||||
// The pin failure word is the inverted wait time in seconds.
|
||||
// It's inverted because flash allows changing 1 to 0 but not vice versa.
|
||||
return ~*(const uint32_t*)FLASH_PTR(flash_pinfails);
|
||||
}
|
||||
|
||||
uint32_t storage_getPinFailsOffset(void)
|
||||
{
|
||||
uint32_t flash_pinfails = FLASH_STORAGE_PINAREA;
|
||||
while (*(const uint32_t*)FLASH_PTR(flash_pinfails) == 0)
|
||||
flash_pinfails += sizeof(uint32_t);
|
||||
return flash_pinfails;
|
||||
}
|
||||
|
||||
bool storage_isInitialized(void)
|
||||
{
|
||||
return storageRom->has_node || storageRom->has_mnemonic;
|
||||
}
|
||||
|
||||
bool storage_isImported(void)
|
||||
{
|
||||
return storageRom->has_imported && storageRom->imported;
|
||||
}
|
||||
|
||||
void storage_setImported(bool imported)
|
||||
{
|
||||
storageUpdate.has_imported = true;
|
||||
storageUpdate.imported = imported;
|
||||
}
|
||||
|
||||
bool storage_needsBackup(void)
|
||||
{
|
||||
return storageUpdate.has_needs_backup ? storageUpdate.needs_backup
|
||||
: storageRom->has_needs_backup && storageRom->needs_backup;
|
||||
}
|
||||
|
||||
void storage_setNeedsBackup(bool needs_backup)
|
||||
{
|
||||
storageUpdate.has_needs_backup = true;
|
||||
storageUpdate.needs_backup = needs_backup;
|
||||
}
|
||||
|
||||
bool storage_unfinishedBackup(void)
|
||||
{
|
||||
return storageUpdate.has_unfinished_backup ? storageUpdate.unfinished_backup
|
||||
: storageRom->has_unfinished_backup && storageRom->unfinished_backup;
|
||||
}
|
||||
|
||||
void storage_setUnfinishedBackup(bool unfinished_backup)
|
||||
{
|
||||
storageUpdate.has_unfinished_backup = true;
|
||||
storageUpdate.unfinished_backup = unfinished_backup;
|
||||
}
|
||||
|
||||
bool storage_noBackup(void)
|
||||
{
|
||||
return storageUpdate.has_no_backup ? storageUpdate.no_backup
|
||||
: storageRom->has_no_backup && storageRom->no_backup;
|
||||
}
|
||||
|
||||
void storage_setNoBackup(void)
|
||||
{
|
||||
storageUpdate.has_no_backup = true;
|
||||
storageUpdate.no_backup = true;
|
||||
}
|
||||
|
||||
void storage_applyFlags(uint32_t flags)
|
||||
{
|
||||
if ((storageRom->flags | flags) == storageRom->flags) {
|
||||
return; // no new flags
|
||||
}
|
||||
storageUpdate.has_flags = true;
|
||||
storageUpdate.flags |= flags;
|
||||
storage_update();
|
||||
}
|
||||
|
||||
uint32_t storage_getFlags(void)
|
||||
{
|
||||
return storageRom->has_flags ? storageRom->flags : 0;
|
||||
}
|
||||
|
||||
uint32_t storage_nextU2FCounter(void)
|
||||
{
|
||||
uint32_t flash_u2f_offset = FLASH_STORAGE_U2FAREA +
|
||||
sizeof(uint32_t) * (storage_u2f_offset / 32);
|
||||
uint32_t newval = 0xfffffffe << (storage_u2f_offset & 31);
|
||||
|
||||
svc_flash_unlock();
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
flash_write32(flash_u2f_offset, newval);
|
||||
storage_u2f_offset++;
|
||||
if (storage_u2f_offset >= 8 * FLASH_STORAGE_U2FAREA_LEN) {
|
||||
storage_area_recycle(*(const uint32_t*)
|
||||
FLASH_PTR(storage_getPinFailsOffset()));
|
||||
}
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
return storageRom->u2f_counter + storage_u2f_offset;
|
||||
}
|
||||
|
||||
void storage_setU2FCounter(uint32_t u2fcounter)
|
||||
{
|
||||
storageUpdate.has_u2f_counter = true;
|
||||
storageUpdate.u2f_counter = u2fcounter - storage_u2f_offset;
|
||||
}
|
||||
|
||||
uint32_t storage_getAutoLockDelayMs()
|
||||
{
|
||||
const uint32_t default_delay_ms = 10 * 60 * 1000U; // 10 minutes
|
||||
return storageRom->has_auto_lock_delay_ms ? storageRom->auto_lock_delay_ms : default_delay_ms;
|
||||
}
|
||||
|
||||
void storage_setAutoLockDelayMs(uint32_t auto_lock_delay_ms)
|
||||
{
|
||||
const uint32_t min_delay_ms = 10 * 1000U; // 10 seconds
|
||||
auto_lock_delay_ms = MAX(auto_lock_delay_ms, min_delay_ms);
|
||||
storageUpdate.has_auto_lock_delay_ms = true;
|
||||
storageUpdate.auto_lock_delay_ms = auto_lock_delay_ms;
|
||||
}
|
||||
|
||||
void storage_wipe(void)
|
||||
{
|
||||
session_clear(true);
|
||||
storage_generate_uuid();
|
||||
|
||||
svc_flash_unlock();
|
||||
storage_commit_locked(false);
|
||||
storage_check_flash_errors(svc_flash_lock());
|
||||
|
||||
storage_clearPinArea();
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
#include "util.h"
|
||||
#include "usb.h"
|
||||
#include "setup.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "layout.h"
|
||||
#include "layout2.h"
|
||||
#include "rng.h"
|
||||
@ -76,7 +76,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();
|
||||
@ -108,13 +108,13 @@ int main(void)
|
||||
|
||||
#if DEBUG_LINK
|
||||
oledSetDebugLink(1);
|
||||
storage_wipe();
|
||||
config_wipe();
|
||||
#endif
|
||||
|
||||
oledDrawBitmap(40, 0, &bmp_logo64);
|
||||
oledRefresh();
|
||||
|
||||
storage_init();
|
||||
config_init();
|
||||
layoutHome();
|
||||
usbInit();
|
||||
for (;;) {
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <ecdsa.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "bip32.h"
|
||||
#include "layout2.h"
|
||||
#include "usb.h"
|
||||
@ -460,7 +460,7 @@ static void getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], const char **a
|
||||
static const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count)
|
||||
{
|
||||
static CONFIDENTIAL HDNode node;
|
||||
if (!storage_getU2FRoot(&node)) {
|
||||
if (!config_getU2FRoot(&node)) {
|
||||
layoutHome();
|
||||
debugLog(0, "", "ERR: Device not init");
|
||||
return 0;
|
||||
@ -546,7 +546,7 @@ void u2f_register(const APDU *a)
|
||||
static U2F_REGISTER_REQ last_req;
|
||||
const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data;
|
||||
|
||||
if (!storage_isInitialized()) {
|
||||
if (!config_isInitialized()) {
|
||||
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
@ -654,7 +654,7 @@ void u2f_authenticate(const APDU *a)
|
||||
const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data;
|
||||
static U2F_AUTHENTICATE_REQ last_req;
|
||||
|
||||
if (!storage_isInitialized()) {
|
||||
if (!config_isInitialized()) {
|
||||
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
@ -727,7 +727,7 @@ void u2f_authenticate(const APDU *a)
|
||||
U2F_AUTHENTICATE_RESP *resp =
|
||||
(U2F_AUTHENTICATE_RESP *)&buf;
|
||||
|
||||
const uint32_t ctr = storage_nextU2FCounter();
|
||||
const uint32_t ctr = config_nextU2FCounter();
|
||||
resp->flags = U2F_AUTH_FLAG_TUP;
|
||||
resp->ctr[0] = ctr >> 24 & 0xff;
|
||||
resp->ctr[1] = ctr >> 16 & 0xff;
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "debug.h"
|
||||
#include "messages.h"
|
||||
#include "u2f.h"
|
||||
#include "storage.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "timer.h"
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
#define USB_STRINGS \
|
||||
X(MANUFACTURER, "SatoshiLabs") \
|
||||
X(PRODUCT, "TREZOR") \
|
||||
X(SERIAL_NUMBER, storage_uuid_str) \
|
||||
X(SERIAL_NUMBER, config_uuid_str) \
|
||||
X(INTERFACE_MAIN, "TREZOR Interface") \
|
||||
X(INTERFACE_DEBUG, "TREZOR Debug Link Interface") \
|
||||
X(INTERFACE_U2F, "TREZOR U2F Interface") \
|
||||
|
137
flash.c
Normal file
137
flash.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "flash.h"
|
||||
#include "memory.h"
|
||||
#include "supervise.h"
|
||||
|
||||
static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = {
|
||||
[ 0] = 0x08000000, // - 0x08003FFF | 16 KiB
|
||||
[ 1] = 0x08004000, // - 0x08007FFF | 16 KiB
|
||||
[ 2] = 0x08008000, // - 0x0800BFFF | 16 KiB
|
||||
[ 3] = 0x0800C000, // - 0x0800FFFF | 16 KiB
|
||||
[ 4] = 0x08010000, // - 0x0801FFFF | 64 KiB
|
||||
[ 5] = 0x08020000, // - 0x0803FFFF | 128 KiB
|
||||
[ 6] = 0x08040000, // - 0x0805FFFF | 128 KiB
|
||||
[ 7] = 0x08060000, // - 0x0807FFFF | 128 KiB
|
||||
[ 8] = 0x08080000, // - 0x0809FFFF | 128 KiB
|
||||
[ 9] = 0x080A0000, // - 0x080BFFFF | 128 KiB
|
||||
[10] = 0x080C0000, // - 0x080DFFFF | 128 KiB
|
||||
[11] = 0x080E0000, // - 0x080FFFFF | 128 KiB
|
||||
[12] = 0x08100000, // last element - not a valid sector
|
||||
};
|
||||
|
||||
static secbool flash_check_success(uint32_t status)
|
||||
{
|
||||
return (status & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) ? secfalse : sectrue;
|
||||
}
|
||||
|
||||
void flash_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
secbool flash_unlock_write(void)
|
||||
{
|
||||
svc_flash_unlock();
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
secbool flash_lock_write(void)
|
||||
{
|
||||
return flash_check_success(svc_flash_lock());
|
||||
}
|
||||
|
||||
const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size)
|
||||
{
|
||||
if (sector >= FLASH_SECTOR_COUNT) {
|
||||
return NULL;
|
||||
}
|
||||
const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset;
|
||||
const uint32_t next = FLASH_SECTOR_TABLE[sector + 1];
|
||||
if (addr + size > next) {
|
||||
return NULL;
|
||||
}
|
||||
return (const void *)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 *)addr) != 0xFFFFFFFF) {
|
||||
return secfalse;
|
||||
}
|
||||
}
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
secbool flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data)
|
||||
{
|
||||
uint32_t address = (uint32_t)flash_get_address(sector, offset, 1);
|
||||
if (address == 0) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
if ((*((uint8_t*)address) & data) != data) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X8);
|
||||
flash_write8(address, data);
|
||||
|
||||
if (*((uint8_t*)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 == 0) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
if (offset % 4 != 0) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
if ((*((uint32_t*)address) & data) != data) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
svc_flash_program(FLASH_CR_PROGRAM_X32);
|
||||
flash_write32(address, data);
|
||||
|
||||
if (*((uint32_t*)address) != data) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
return sectrue;
|
||||
}
|
47
flash.h
Normal file
47
flash.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TREZORHAL_FLASH_H
|
||||
#define TREZORHAL_FLASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "secbool.h"
|
||||
|
||||
#define FLASH_SECTOR_COUNT 24
|
||||
|
||||
// note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427) (reference RM0090 section 3.7.5)
|
||||
#ifndef STM32F427xx
|
||||
#define FLASH_SR_RDERR 0
|
||||
#endif
|
||||
|
||||
#define FLASH_STATUS_ALL_FLAGS (FLASH_SR_RDERR | FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR | FLASH_SR_SOP | FLASH_SR_EOP)
|
||||
|
||||
void flash_init(void);
|
||||
|
||||
secbool __wur flash_unlock_write(void);
|
||||
secbool __wur flash_lock_write(void);
|
||||
|
||||
const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size);
|
||||
|
||||
secbool __wur flash_erase(uint8_t sector);
|
||||
secbool __wur flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data);
|
||||
secbool __wur flash_write_word(uint8_t sector, uint32_t offset, uint32_t data);
|
||||
|
||||
#endif // TREZORHAL_FLASH_H
|
39
norcow_config.h
Normal file
39
norcow_config.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __NORCOW_CONFIG_H__
|
||||
#define __NORCOW_CONFIG_H__
|
||||
|
||||
#include "flash.h"
|
||||
|
||||
#define NORCOW_SECTOR_COUNT 2
|
||||
#define NORCOW_SECTOR_SIZE (16*1024)
|
||||
#define NORCOW_SECTORS {2, 3}
|
||||
|
||||
/*
|
||||
* The length of the sector header in bytes. The header is preserved between sector erasures.
|
||||
*/
|
||||
#define NORCOW_HEADER_LEN (0x100)
|
||||
|
||||
/*
|
||||
* Current storage version.
|
||||
*/
|
||||
#define NORCOW_VERSION ((uint32_t)0x00000001)
|
||||
|
||||
#endif
|
33
secbool.h
Normal file
33
secbool.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TREZORHAL_SECBOOL_H
|
||||
#define TREZORHAL_SECBOOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t secbool;
|
||||
#define sectrue 0xAAAAAAAAU
|
||||
#define secfalse 0x00000000U
|
||||
|
||||
#ifndef __wur
|
||||
#define __wur __attribute__ ((warn_unused_result))
|
||||
#endif
|
||||
|
||||
#endif
|
1
vendor/trezor-storage
vendored
Submodule
1
vendor/trezor-storage
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 840f7461ee6f0c5cb0db75c46018615dbd5b0256
|
Loading…
Reference in New Issue
Block a user