From 9f75d342a4872a9544b0ec36493bf3e9de5454fc Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 28 Dec 2018 16:54:25 +0100 Subject: [PATCH] Add trezor-storage submodule. Update modtrezorconfig to work with the new storage_get() interface. Update tests to expect None instead of bytes(), when the value is not found in the storage. --- .gitmodules | 3 + SConscript.firmware | 7 +- SConscript.unix | 7 +- .../extmod/modtrezorconfig/modtrezorconfig.c | 34 +- embed/extmod/modtrezorconfig/norcow.c | 305 ------------------ embed/extmod/modtrezorconfig/norcow.h | 58 ---- embed/extmod/modtrezorconfig/norcow_config.h | 43 --- embed/extmod/modtrezorconfig/storage.c | 238 -------------- embed/extmod/modtrezorconfig/storage.h | 38 --- src/apps/common/storage.py | 10 +- tests/test_trezor.config.py | 12 +- vendor/trezor-storage | 1 + 12 files changed, 55 insertions(+), 701 deletions(-) delete mode 100644 embed/extmod/modtrezorconfig/norcow.c delete mode 100644 embed/extmod/modtrezorconfig/norcow.h delete mode 100644 embed/extmod/modtrezorconfig/norcow_config.h delete mode 100644 embed/extmod/modtrezorconfig/storage.c delete mode 100644 embed/extmod/modtrezorconfig/storage.h create mode 160000 vendor/trezor-storage diff --git a/.gitmodules b/.gitmodules index eca08288a..67995f4c5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "vendor/nanopb"] path = vendor/nanopb url = https://github.com/nanopb/nanopb.git +[submodule "vendor/trezor-storage"] + path = vendor/trezor-storage + url = git@github.com:satoshilabs/trezor-storage.git diff --git a/SConscript.firmware b/SConscript.firmware index f75681b41..679992640 100644 --- a/SConscript.firmware +++ b/SConscript.firmware @@ -10,10 +10,13 @@ SOURCE_MOD = [] PYOPT = '1' # modtrezorconfig +CPPPATH_MOD += [ + 'vendor/trezor-storage/c', +] SOURCE_MOD += [ 'embed/extmod/modtrezorconfig/modtrezorconfig.c', - 'embed/extmod/modtrezorconfig/norcow.c', - 'embed/extmod/modtrezorconfig/storage.c', + 'vendor/trezor-storage/c/norcow.c', + 'vendor/trezor-storage/c/storage.c', ] # modtrezorcrypto diff --git a/SConscript.unix b/SConscript.unix index 7fadbb87e..9609c7d32 100644 --- a/SConscript.unix +++ b/SConscript.unix @@ -9,10 +9,13 @@ SOURCE_MOD = [] LIBS_MOD = [] # modtrezorconfig +CPPPATH_MOD += [ + 'vendor/trezor-storage/c', +] SOURCE_MOD += [ 'embed/extmod/modtrezorconfig/modtrezorconfig.c', - 'embed/extmod/modtrezorconfig/norcow.c', - 'embed/extmod/modtrezorconfig/storage.c', + 'vendor/trezor-storage/c/norcow.c', + 'vendor/trezor-storage/c/storage.c', ] # modtrezorcrypto diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index 8de2c5a7b..f492adb85 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -41,11 +41,12 @@ STATIC void wrapped_ui_wait_callback(uint32_t wait, uint32_t progress) { /// called from this module! /// ''' STATIC mp_obj_t mod_trezorconfig_init(size_t n_args, const mp_obj_t *args) { + // TODO: Add salt. if (n_args > 0) { ui_wait_callback = args[0]; - storage_init(wrapped_ui_wait_callback); + storage_init(wrapped_ui_wait_callback, (const uint8_t*)"", 0); } else { - storage_init(NULL); + storage_init(NULL, (const uint8_t*)"", 0); } return mp_const_none; } @@ -57,7 +58,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_init_obj, 0, 1, mod_ /// ''' STATIC mp_obj_t mod_trezorconfig_check_pin(mp_obj_t pin) { uint32_t pin_i = trezor_obj_get_uint(pin); - if (sectrue != storage_check_pin(pin_i)) { + if (sectrue != storage_unlock(pin_i)) { return mp_const_false; } return mp_const_true; @@ -90,6 +91,15 @@ STATIC mp_obj_t mod_trezorconfig_has_pin(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_has_pin_obj, mod_trezorconfig_has_pin); +/// def get_pin_rem() -> int: +/// ''' +/// Returns the number of remaining PIN entry attempts. +/// ''' +STATIC mp_obj_t mod_trezorconfig_get_pin_rem(void) { + return mp_obj_new_int_from_uint(storage_get_pin_rem()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_get_pin_rem_obj, mod_trezorconfig_get_pin_rem); + /// def change_pin(pin: int, newpin: int) -> bool: /// ''' /// Change PIN. Returns True on success, False on failure. @@ -106,7 +116,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_change_pin_obj, mod_trezorconf /// def get(app: int, key: int, public: bool=False) -> bytes: /// ''' -/// Gets a value of given key for given app (or empty bytes if not set). +/// Gets the value of the given key for the given app (or None if not set). +/// Raises a RuntimeError if decryption or authentication of the stored value fails. /// ''' STATIC mp_obj_t mod_trezorconfig_get(size_t n_args, const mp_obj_t *args) { uint8_t app = trezor_obj_get_uint8(args[0]) & 0x7F; @@ -116,11 +127,19 @@ STATIC mp_obj_t mod_trezorconfig_get(size_t n_args, const mp_obj_t *args) { } uint16_t appkey = (app << 8) | key; uint16_t len = 0; - const void *val; - if (sectrue != storage_get(appkey, &val, &len) || len == 0) { + if (sectrue != storage_get(appkey, NULL, 0, &len)) { + return mp_const_none; + } + if (len == 0) { return mp_const_empty_bytes; } - return mp_obj_new_bytes(val, len); + vstr_t vstr; + vstr_init_len(&vstr, len); + if (sectrue != storage_get(appkey, vstr.buf, vstr.len, &len)) { + vstr_clear(&vstr); + mp_raise_msg(&mp_type_RuntimeError, "Failed to get value from storage."); + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_get_obj, 2, 3, mod_trezorconfig_get); @@ -160,6 +179,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorconfig_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_check_pin), MP_ROM_PTR(&mod_trezorconfig_check_pin_obj) }, { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&mod_trezorconfig_unlock_obj) }, { MP_ROM_QSTR(MP_QSTR_has_pin), MP_ROM_PTR(&mod_trezorconfig_has_pin_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_pin_rem), MP_ROM_PTR(&mod_trezorconfig_get_pin_rem_obj) }, { MP_ROM_QSTR(MP_QSTR_change_pin), MP_ROM_PTR(&mod_trezorconfig_change_pin_obj) }, { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&mod_trezorconfig_get_obj) }, { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&mod_trezorconfig_set_obj) }, diff --git a/embed/extmod/modtrezorconfig/norcow.c b/embed/extmod/modtrezorconfig/norcow.c deleted file mode 100644 index ed54be3b2..000000000 --- a/embed/extmod/modtrezorconfig/norcow.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * This file is part of the TREZOR project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include "norcow.h" -#include "flash.h" -#include "common.h" - -// NRCW = 4e524357 -#define NORCOW_MAGIC ((uint32_t)0x5743524e) -#define NORCOW_MAGIC_LEN (sizeof(uint32_t)) - -static const uint8_t norcow_sectors[NORCOW_SECTOR_COUNT] = NORCOW_SECTORS; -static uint8_t norcow_active_sector = 0; -static uint32_t norcow_active_offset = NORCOW_MAGIC_LEN; - -/* - * Returns pointer to sector, starting with offset - * Fails when there is not enough space for data of given size - */ -static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) -{ - ensure(sectrue * (sector <= NORCOW_SECTOR_COUNT), "invalid sector"); - return flash_get_address(norcow_sectors[sector], offset, size); -} - -/* - * Writes data to given sector, starting from offset - */ -static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len) -{ - if (sector >= NORCOW_SECTOR_COUNT) { - return secfalse; - } - ensure(flash_unlock(), NULL); - - // write prefix - ensure(flash_write_word(norcow_sectors[sector], offset, prefix), NULL); - - if (len > 0) { - offset += sizeof(uint32_t); - // write data - for (uint16_t i = 0; i < len; i++, offset++) { - ensure(flash_write_byte(norcow_sectors[sector], offset, data[i]), NULL); - } - // pad with zeroes - for (; offset % 4; offset++) { - ensure(flash_write_byte(norcow_sectors[sector], offset, 0x00), NULL); - } - } - ensure(flash_lock(), NULL); - return sectrue; -} - -/* - * Erases sector (and sets a magic) - */ -static void norcow_erase(uint8_t sector, secbool set_magic) -{ - ensure(sectrue * (sector <= NORCOW_SECTOR_COUNT), "invalid sector"); - ensure(flash_erase_sector(norcow_sectors[sector]), "erase failed"); - if (sectrue == set_magic) { - ensure(norcow_write(sector, 0, NORCOW_MAGIC, NULL, 0), "set magic failed"); - } -} - -#define ALIGN4(X) (X) = ((X) + 3) & ~3 - -/* - * Reads one item starting from offset - */ -static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, const void **val, uint16_t *len, uint32_t *pos) -{ - *pos = offset; - - const void *k = norcow_ptr(sector, *pos, 2); - if (k == NULL) return secfalse; - *pos += 2; - memcpy(key, k, sizeof(uint16_t)); - if (*key == 0xFFFF) { - return secfalse; - } - - const void *l = norcow_ptr(sector, *pos, 2); - if (l == NULL) return secfalse; - *pos += 2; - memcpy(len, l, sizeof(uint16_t)); - - *val = norcow_ptr(sector, *pos, *len); - if (*val == NULL) return secfalse; - *pos += *len; - ALIGN4(*pos); - return sectrue; -} - -/* - * Writes one item starting from offset - */ -static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void *val, uint16_t len, uint32_t *pos) -{ - uint32_t prefix = (len << 16) | key; - *pos = offset + sizeof(uint32_t) + len; - ALIGN4(*pos); - return norcow_write(sector, offset, prefix, val, len); -} - -/* - * Finds item in given sector - */ -static secbool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len) -{ - *val = 0; - *len = 0; - uint32_t offset = NORCOW_MAGIC_LEN; - for (;;) { - uint16_t k, l; - const void *v; - uint32_t pos; - if (sectrue != read_item(sector, offset, &k, &v, &l, &pos)) { - break; - } - if (key == k) { - *val = v; - *len = l; - } - offset = pos; - } - return sectrue * (*val != NULL); -} - -/* - * Finds first unused offset in given sector - */ -static uint32_t find_free_offset(uint8_t sector) -{ - uint32_t offset = NORCOW_MAGIC_LEN; - for (;;) { - uint16_t key, len; - const void *val; - uint32_t pos; - if (sectrue != read_item(sector, offset, &key, &val, &len, &pos)) { - break; - } - offset = pos; - } - return offset; -} - -/* - * Compacts active sector and sets new active sector - */ -static void compact() -{ - uint8_t norcow_next_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; - norcow_erase(norcow_next_sector, sectrue); - - uint32_t offset = NORCOW_MAGIC_LEN, offsetw = NORCOW_MAGIC_LEN; - - for (;;) { - // read item - uint16_t k, l; - const void *v; - uint32_t pos; - secbool r = read_item(norcow_active_sector, offset, &k, &v, &l, &pos); - if (sectrue != r) { - break; - } - offset = pos; - - // check if not already saved - const void *v2; - uint16_t l2; - r = find_item(norcow_next_sector, k, &v2, &l2); - if (sectrue == r) { - continue; - } - - // scan for latest instance - uint32_t offsetr = offset; - for (;;) { - uint16_t k2; - uint32_t posr; - r = read_item(norcow_active_sector, offsetr, &k2, &v2, &l2, &posr); - if (sectrue != r) { - break; - } - if (k == k2) { - v = v2; - l = l2; - } - offsetr = posr; - } - - // copy the last item - uint32_t posw; - ensure(write_item(norcow_next_sector, offsetw, k, v, l, &posw), "compaction write failed"); - offsetw = posw; - } - - norcow_erase(norcow_active_sector, secfalse); - norcow_active_sector = norcow_next_sector; - norcow_active_offset = find_free_offset(norcow_active_sector); -} - -/* - * Initializes storage - */ -void norcow_init(void) -{ - flash_init(); - secbool found = secfalse; - // detect active sector - starts with magic - for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { - const uint32_t *magic = norcow_ptr(i, 0, NORCOW_MAGIC_LEN); - if (magic != NULL && *magic == NORCOW_MAGIC) { - found = sectrue; - norcow_active_sector = i; - break; - } - } - // no active sectors found - let's erase - if (sectrue == found) { - norcow_active_offset = find_free_offset(norcow_active_sector); - } else { - norcow_wipe(); - } -} - -/* - * Wipe the storage - */ -void norcow_wipe(void) -{ - norcow_erase(0, sectrue); - for (uint8_t i = 1; i < NORCOW_SECTOR_COUNT; i++) { - norcow_erase(i, secfalse); - } - norcow_active_sector = 0; - norcow_active_offset = NORCOW_MAGIC_LEN; -} - -/* - * Looks for the given key, returns status of the operation - */ -secbool norcow_get(uint16_t key, const void **val, uint16_t *len) -{ - return find_item(norcow_active_sector, key, val, len); -} - -/* - * Sets the given key, returns status of the operation - */ -secbool norcow_set(uint16_t key, const void *val, uint16_t len) -{ - // check whether there is enough free space - // and compact if full - if (norcow_active_offset + sizeof(uint32_t) + len > NORCOW_SECTOR_SIZE) { - compact(); - } - // write item - uint32_t pos; - secbool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos); - if (sectrue == r) { - norcow_active_offset = pos; - } - return r; -} - -/* - * Update a word in flash at the given pointer. The pointer must point - * into the NORCOW area. - */ -secbool norcow_update(uint16_t key, uint16_t offset, uint32_t value) -{ - const void *ptr; - uint16_t len; - if (sectrue != find_item(norcow_active_sector, key, &ptr, &len)) { - return secfalse; - } - if ((offset & 3) != 0 || offset >= len) { - return secfalse; - } - uint32_t sector_offset = (const uint8_t*) ptr - (const uint8_t *)norcow_ptr(norcow_active_sector, 0, NORCOW_SECTOR_SIZE) + offset; - ensure(flash_unlock(), NULL); - ensure(flash_write_word(norcow_sectors[norcow_active_sector], sector_offset, value), NULL); - ensure(flash_lock(), NULL); - return sectrue; -} diff --git a/embed/extmod/modtrezorconfig/norcow.h b/embed/extmod/modtrezorconfig/norcow.h deleted file mode 100644 index 00bab4d0a..000000000 --- a/embed/extmod/modtrezorconfig/norcow.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the TREZOR project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __NORCOW_H__ -#define __NORCOW_H__ - -#include -#include "secbool.h" - -/* - * Storage parameters - */ - -#include "norcow_config.h" - -/* - * Initialize storage - */ -void norcow_init(void); - -/* - * Wipe the storage - */ -void norcow_wipe(void); - -/* - * Looks for the given key, returns status of the operation - */ -secbool norcow_get(uint16_t key, const void **val, uint16_t *len); - -/* - * Sets the given key, returns status of the operation - */ -secbool norcow_set(uint16_t key, const void *val, uint16_t len); - -/* - * Update a word in flash in the given key at the given offset. - * Note that you can only change bits from 1 to 0. - */ -secbool norcow_update(uint16_t key, uint16_t offset, uint32_t value); - -#endif diff --git a/embed/extmod/modtrezorconfig/norcow_config.h b/embed/extmod/modtrezorconfig/norcow_config.h deleted file mode 100644 index d792776d5..000000000 --- a/embed/extmod/modtrezorconfig/norcow_config.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the TREZOR project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __NORCOW_CONFIG_H__ -#define __NORCOW_CONFIG_H__ - -#include "flash.h" - -#define NORCOW_SECTOR_COUNT 2 - -#if TREZOR_MODEL == T - -#define NORCOW_SECTOR_SIZE (64*1024) -#define NORCOW_SECTORS {FLASH_SECTOR_STORAGE_1, FLASH_SECTOR_STORAGE_2} - -#elif TREZOR_MODEL == 1 - -#define NORCOW_SECTOR_SIZE (16*1024) -#define NORCOW_SECTORS {2, 3} - -#else - -#error Unknown TREZOR Model - -#endif - -#endif diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c deleted file mode 100644 index 5e0f8343b..000000000 --- a/embed/extmod/modtrezorconfig/storage.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * This file is part of the TREZOR project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include "common.h" -#include "norcow.h" -#include "storage.h" - -// Norcow storage key of configured PIN. -#define PIN_KEY 0x0000 - -// Maximum PIN length. -#define PIN_MAXLEN 32 - -// Byte-length of flash section containing fail counters. -#define PIN_FAIL_KEY 0x0001 -#define PIN_FAIL_SECTOR_SIZE 32 - -// Maximum number of failed unlock attempts. -#define PIN_MAX_TRIES 15 - -static secbool initialized = secfalse; -static secbool unlocked = secfalse; -static PIN_UI_WAIT_CALLBACK ui_callback = NULL; - -void storage_init(PIN_UI_WAIT_CALLBACK callback) -{ - initialized = secfalse; - unlocked = secfalse; - norcow_init(); - initialized = sectrue; - ui_callback = callback; -} - -static secbool pin_fails_reset(uint16_t ofs) -{ - return norcow_update(PIN_FAIL_KEY, ofs, 0); -} - -static secbool pin_fails_increase(const uint32_t *ptr, uint16_t ofs) -{ - uint32_t ctr = *ptr; - ctr = ctr << 1; - - if (sectrue != norcow_update(PIN_FAIL_KEY, ofs, ctr)) { - return secfalse; - } - - uint32_t check = *ptr; - if (ctr != check) { - return secfalse; - } - return sectrue; -} - -static void pin_fails_check_max(uint32_t ctr) -{ - if (~ctr >= (1 << PIN_MAX_TRIES)) { - norcow_wipe(); - ensure(secfalse, "pin_fails_check_max"); - } -} - -static secbool pin_cmp(const uint32_t pin) -{ - const void *spin = NULL; - uint16_t spinlen = 0; - norcow_get(PIN_KEY, &spin, &spinlen); - if (NULL != spin && spinlen == sizeof(uint32_t)) { - return sectrue * (pin == *(const uint32_t*)spin); - } else { - return sectrue * (1 == pin); - } -} - -static secbool pin_get_fails(const uint32_t **pinfail, uint32_t *pofs) -{ - const void *vpinfail; - uint16_t pinfaillen; - unsigned int ofs; - // The PIN_FAIL_KEY points to an area of words, initialized to - // 0xffffffff (meaning no pin failures). The first non-zero word - // in this area is the current pin failure counter. If PIN_FAIL_KEY - // has no configuration or is empty, the pin failure counter is 0. - // We rely on the fact that flash allows to clear bits and we clear one - // bit to indicate pin failure. On success, the word is set to 0, - // indicating that the next word is the pin failure counter. - - // Find the current pin failure counter - if (secfalse != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { - *pinfail = vpinfail; - for (ofs = 0; ofs < pinfaillen / sizeof(uint32_t); ofs++) { - if (((const uint32_t *) vpinfail)[ofs]) { - *pinfail = vpinfail; - *pofs = ofs; - return sectrue; - } - } - } - - // No pin failure section, or all entries used -> create a new one. - uint32_t pinarea[PIN_FAIL_SECTOR_SIZE]; - memset(pinarea, 0xff, sizeof(pinarea)); - if (sectrue != norcow_set(PIN_FAIL_KEY, pinarea, sizeof(pinarea))) { - return secfalse; - } - if (sectrue != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { - return secfalse; - } - *pinfail = vpinfail; - *pofs = 0; - return sectrue; -} - -secbool storage_check_pin(const uint32_t pin) -{ - const uint32_t *pinfail = NULL; - uint32_t ofs; - uint32_t ctr; - - // Get the pin failure counter - if (pin_get_fails(&pinfail, &ofs) != sectrue) { - return secfalse; - } - - // Read current failure counter - ctr = pinfail[ofs]; - // Wipe storage if too many failures - pin_fails_check_max(ctr); - - // Sleep for ~ctr seconds before checking the PIN. - uint32_t progress; - for (uint32_t wait = ~ctr; wait > 0; wait--) { - for (int i = 0; i < 10; i++) { - if (ui_callback) { - if ((~ctr) > 1000000) { // precise enough - progress = (~ctr - wait) / ((~ctr) / 1000); - } else { - progress = ((~ctr - wait) * 10 + i) * 100 / (~ctr); - } - ui_callback(wait, progress); - } - hal_delay(100); - } - } - // Show last frame if we were waiting - if ((~ctr > 0) && ui_callback) { - ui_callback(0, 1000); - } - - // First, we increase PIN fail counter in storage, even before checking the - // PIN. If the PIN is correct, we reset the counter afterwards. If not, we - // check if this is the last allowed attempt. - if (sectrue != pin_fails_increase(pinfail + ofs, ofs * sizeof(uint32_t))) { - return secfalse; - } - if (sectrue != pin_cmp(pin)) { - // Wipe storage if too many failures - pin_fails_check_max(ctr << 1); - return secfalse; - } - // Finally set the counter to 0 to indicate success. - return pin_fails_reset(ofs * sizeof(uint32_t)); -} - -secbool storage_unlock(const uint32_t pin) -{ - unlocked = secfalse; - if (sectrue == initialized && sectrue == storage_check_pin(pin)) { - unlocked = sectrue; - } - return unlocked; -} - -secbool storage_get(const uint16_t key, const void **val, uint16_t *len) -{ - const uint8_t app = key >> 8; - // APP == 0 is reserved for PIN related values - if (sectrue != initialized || app == 0) { - return secfalse; - } - // top bit of APP set indicates the value can be read from unlocked device - if (sectrue != unlocked && ((app & 0x80) == 0)) { - return secfalse; - } - return norcow_get(key, val, len); -} - -secbool storage_set(const uint16_t key, const void *val, uint16_t len) -{ - const uint8_t app = key >> 8; - // APP == 0 is reserved for PIN related values - if (sectrue != initialized || sectrue != unlocked || app == 0) { - return secfalse; - } - return norcow_set(key, val, len); -} - -secbool storage_has_pin(void) -{ - if (sectrue != initialized) { - return secfalse; - } - return sectrue == pin_cmp(1) ? secfalse : sectrue; -} - -secbool storage_change_pin(const uint32_t oldpin, const uint32_t newpin) -{ - if (sectrue != initialized || sectrue != unlocked) { - return secfalse; - } - if (sectrue != storage_check_pin(oldpin)) { - return secfalse; - } - return norcow_set(PIN_KEY, &newpin, sizeof(uint32_t)); -} - -void storage_wipe(void) -{ - norcow_wipe(); -} diff --git a/embed/extmod/modtrezorconfig/storage.h b/embed/extmod/modtrezorconfig/storage.h deleted file mode 100644 index 797528175..000000000 --- a/embed/extmod/modtrezorconfig/storage.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of the TREZOR project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __STORAGE_H__ -#define __STORAGE_H__ - -#include -#include -#include "secbool.h" - -typedef void (*PIN_UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress); - -void storage_init(PIN_UI_WAIT_CALLBACK callback); -void storage_wipe(void); -secbool storage_check_pin(const uint32_t pin); -secbool storage_unlock(const uint32_t pin); -secbool storage_has_pin(void); -secbool storage_change_pin(const uint32_t oldpin, const uint32_t newpin); -secbool storage_get(const uint16_t key, const void **val, uint16_t *len); -secbool storage_set(const uint16_t key, const void *val, uint16_t len); - -#endif diff --git a/src/apps/common/storage.py b/src/apps/common/storage.py index d1be98c2a..b16d8be57 100644 --- a/src/apps/common/storage.py +++ b/src/apps/common/storage.py @@ -46,11 +46,17 @@ def is_initialized() -> bool: def get_label() -> str: - return config.get(_APP, _LABEL, True).decode() # public + label = config.get(_APP, _LABEL, True) # public + if label is None: + return None + return label.decode() def get_mnemonic() -> str: - return config.get(_APP, _MNEMONIC).decode() + mnemonic = config.get(_APP, _MNEMONIC) + if mnemonic is None: + return None + return mnemonic.decode() def has_passphrase() -> bool: diff --git a/tests/test_trezor.config.py b/tests/test_trezor.config.py index 68ee5ac60..4bba499da 100644 --- a/tests/test_trezor.config.py +++ b/tests/test_trezor.config.py @@ -37,8 +37,8 @@ class TestConfig(unittest.TestCase): config.wipe() v0 = config.get(1, 1) v1 = config.get(1, 2) - self.assertEqual(v0, bytes()) - self.assertEqual(v1, bytes()) + self.assertEqual(v0, None) + self.assertEqual(v1, None) def test_lock(self): for _ in range(128): @@ -49,7 +49,7 @@ class TestConfig(unittest.TestCase): value = random.bytes(16) config.set(appid, key, value) config.init() - self.assertEqual(config.get(appid, key), bytes()) + self.assertEqual(config.get(appid, key), None) with self.assertRaises(RuntimeError): config.set(appid, key, bytes()) config.init() @@ -79,7 +79,7 @@ class TestConfig(unittest.TestCase): v1 = config.get(appid, key) v2 = config.get(appid, key, True) self.assertNotEqual(v1, v2) - self.assertEqual(v1, bytes()) + self.assertEqual(v1, None) self.assertEqual(v2, value16) def test_change_pin(self): @@ -90,7 +90,7 @@ class TestConfig(unittest.TestCase): config.set(PINAPP, PINKEY, b'value') self.assertEqual(config.change_pin(pin_to_int('000'), pin_to_int('666')), False) self.assertEqual(config.change_pin(pin_to_int(''), pin_to_int('000')), True) - self.assertEqual(config.get(PINAPP, PINKEY), bytes()) + self.assertEqual(config.get(PINAPP, PINKEY), None) config.set(1, 1, b'value') config.init() self.assertEqual(config.unlock(pin_to_int('000')), True) @@ -129,7 +129,7 @@ class TestConfig(unittest.TestCase): for _ in range(128): appid, key = random_entry() value = config.get(appid, key) - self.assertEqual(value, bytes()) + self.assertEqual(value, None) if __name__ == '__main__': diff --git a/vendor/trezor-storage b/vendor/trezor-storage new file mode 160000 index 000000000..639bea4ea --- /dev/null +++ b/vendor/trezor-storage @@ -0,0 +1 @@ +Subproject commit 639bea4ea8a56e4a2f30b39e25a363c462a70e2d