From 4cabe402208155d3080b34f932f62909361107ff Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Mon, 16 Oct 2017 18:33:05 +0200 Subject: [PATCH 01/27] storage: pin checking in c proof of concept TODO: move out of modtrezorconfig TODO: flash unix mock --- .../extmod/modtrezorconfig/modtrezorconfig.c | 151 +++++++++++++++++- embed/trezorhal/flash.c | 22 +++ embed/trezorhal/flash.h | 3 + 3 files changed, 175 insertions(+), 1 deletion(-) diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index 9244918ab..b330333b5 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -1,19 +1,140 @@ /* - * Copyright (c) Pavol Rusnak, SatoshiLabs + * Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs * * Licensed under TREZOR License * see LICENSE file for details */ #include "py/runtime.h" +#include "py/mphal.h" #if MICROPY_PY_TREZORCONFIG #include #include #include "norcow.h" +#include "flash.h" + +#define MAX_WRONG_PINS 15 +#define FAIL_SECTOR_LEN 16 * 1024 +#define STORAGE_KEY_PIN 0x00 + +static void pin_fails_reset(uint32_t ofs) +{ + if (ofs + sizeof(uint32_t) >= FAIL_SECTOR_LEN) { + // ofs points to the last word of the PIN fails area. Because there is + // no space left, we recycle the sector (set all words to 0xffffffff). + // On next unlock attempt, we start counting from the the first word. + flash_erase_sectors(FLASH_SECTOR_PIN_AREA, FLASH_SECTOR_PIN_AREA, NULL); + } else { + // Mark this counter as exhausted. On next unlock attempt, pinfails_get + // seeks to the next word. + flash_unlock(); + flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, 0); + flash_lock(); + } +} + +static bool pin_fails_increase(uint32_t ofs) +{ + uint32_t ctr = ~MAX_WRONG_PINS; + if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &ctr)) { + return false; + } + ctr = ctr << 1; + + flash_unlock(); + if (!flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, ctr)) { + flash_lock(); + return false; + } + flash_lock(); + + uint32_t check = 0; + if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &check)) { + return false; + } + return ctr == check; +} + +static bool pin_fails_check_max(uint32_t ctr) +{ + if (~ctr >= 1 << MAX_WRONG_PINS) { + norcow_wipe(); + // TODO: shutdown + return false; + } + return true; +} + +static bool pin_fails_get(uint32_t *ofs, uint32_t *ctr) +{ + if (!ofs || !ctr) { + return false; + } + for (uint32_t o = 0; o < FAIL_SECTOR_LEN; o += sizeof(uint32_t)) { + uint32_t c = 0; + if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, o, &c)) { + return false; + } + if (c != 0) { + *ofs = o; + *ctr = c; + return true; + } + } + return false; +} + +static bool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, size_t seclen) +{ + size_t diff = seclen - publen; + for (size_t i = 0; i < publen; i++) { + diff |= pub[i] ^ sec[i]; + } + return diff == 0; +} + +static bool pin_check(const uint8_t *pin, size_t pinlen) +{ + const void *st_pin; + uint16_t st_pinlen; + if (!norcow_get(STORAGE_KEY_PIN, &st_pin, &st_pinlen)) { + return false; + } + return const_cmp(pin, pinlen, st_pin, (size_t)st_pinlen); +} + +static bool pin_unlock(const uint8_t *pin, size_t pinlen) +{ + uint32_t ofs; + uint32_t ctr; + if (!pin_fails_get(&ofs, &ctr)) { + return false; + } + pin_fails_check_max(ctr); + + // Sleep for ~ctr seconds before checking the PIN. + for (uint32_t wait = ~ctr; wait > 0; wait--) { + mp_hal_delay_ms(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 (!pin_fails_increase(ofs)) { + return false; + } + if (!pin_check(pin, pinlen)) { + pin_fails_check_max(ctr << 1); + return false; + } + pin_fails_reset(ofs); + return true; +} static bool initialized = false; +static bool unlocked = false; /// def init() -> None: /// ''' @@ -25,10 +146,29 @@ STATIC mp_obj_t mod_trezorconfig_init(void) { mp_raise_msg(&mp_type_RuntimeError, "Could not initialize config module"); } initialized = true; + unlocked = false; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_init_obj, mod_trezorconfig_init); +/// def unlock(pin: str) -> None: +/// ''' +/// Tries to unlock the storage with given PIN key. +/// ''' +STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin) { + if (!initialized) { + mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); + } + mp_buffer_info_t pinbuf; + mp_get_buffer_raise(pin, &pinbuf, MP_BUFFER_READ); + bool r = pin_unlock(pinbuf.buf, pinbuf.len); + if (!r) { + return mp_const_false; + } + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_unlock_obj, mod_trezorconfig_unlock); + /// def get(app: int, key: int) -> bytes: /// ''' /// Gets a value of given key for given app (or empty bytes if not set). @@ -37,6 +177,10 @@ STATIC mp_obj_t mod_trezorconfig_get(mp_obj_t app, mp_obj_t key) { if (!initialized) { mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); } + if (!unlocked) { + mp_raise_msg(&mp_type_RuntimeError, "Config module locked"); + // TODO: shutdown? + } uint8_t a = mp_obj_get_int(app); uint8_t k = mp_obj_get_int(key); uint16_t appkey = a << 8 | k, len; @@ -60,6 +204,10 @@ STATIC mp_obj_t mod_trezorconfig_set(mp_obj_t app, mp_obj_t key, mp_obj_t value) if (!initialized) { mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); } + if (!unlocked) { + mp_raise_msg(&mp_type_RuntimeError, "Config module locked"); + // TODO: shutdown? + } uint8_t a = mp_obj_get_int(app); uint8_t k = mp_obj_get_int(key); uint16_t appkey = a << 8 | k; @@ -92,6 +240,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_wipe_obj, mod_trezorconfig_wip STATIC const mp_rom_map_elem_t mp_module_trezorconfig_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorconfig) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mod_trezorconfig_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&mod_trezorconfig_unlock_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) }, { MP_ROM_QSTR(MP_QSTR_wipe), MP_ROM_PTR(&mod_trezorconfig_wipe_obj) }, diff --git a/embed/trezorhal/flash.c b/embed/trezorhal/flash.c index b425c60a0..ed312a67a 100644 --- a/embed/trezorhal/flash.c +++ b/embed/trezorhal/flash.c @@ -91,6 +91,28 @@ secbool flash_write_word(uint32_t address, uint32_t data) return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data)); } +bool flash_write_byte_rel(uint32_t sector, uint32_t offset, uint8_t data) +{ + return HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, SECTOR_TABLE[sector] + offset, data); +} + +bool flash_write_word_rel(uint32_t sector, uint32_t offset, uint32_t data) +{ + if (offset % 4 != 0) { + return false; + } + return HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SECTOR_TABLE[sector] + offset, data); +} + +bool flash_read_word_rel(uint32_t sector, uint32_t offset, uint32_t *data) +{ + if (offset % 4 != 0) { + return false; + } + *data = *((uint32_t *) SECTOR_TABLE[sector] + offset); + return true; +} + #define FLASH_OTP_LOCK_BASE 0x1FFF7A00U secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen) diff --git a/embed/trezorhal/flash.h b/embed/trezorhal/flash.h index f4a484d1e..d50d71557 100644 --- a/embed/trezorhal/flash.h +++ b/embed/trezorhal/flash.h @@ -51,6 +51,9 @@ secbool flash_lock(void); secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)); secbool flash_write_byte(uint32_t address, uint8_t data); secbool flash_write_word(uint32_t address, uint32_t data); +secbool flash_write_byte_rel(uint32_t sector, uint32_t offset, uint8_t data); +secbool flash_write_word_rel(uint32_t sector, uint32_t offset, uint32_t data); +secbool flash_read_word_rel(uint32_t sector, uint32_t offset, uint32_t *data); #define FLASH_OTP_NUM_BLOCKS 16 #define FLASH_OTP_BLOCK_SIZE 32 From 543e8c5007aced742cbf2a332f317db8631f662e Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 17 Oct 2017 15:11:27 +0200 Subject: [PATCH 02/27] flash: add unix/flash.c --- SConscript.unix | 1 + embed/extmod/modtrezorio/modtrezorio-flash.h | 8 +- embed/trezorhal/flash.c | 6 +- embed/trezorhal/flash.h | 6 +- embed/unix/flash.c | 178 +++++++++++++++++++ 5 files changed, 186 insertions(+), 13 deletions(-) create mode 100644 embed/unix/flash.c diff --git a/SConscript.unix b/SConscript.unix index 8b215430e..00a117757 100644 --- a/SConscript.unix +++ b/SConscript.unix @@ -215,6 +215,7 @@ SOURCE_UNIX = [ 'vendor/micropython/ports/unix/alloc.c', 'embed/unix/common.c', 'embed/unix/touch.c', + 'embed/unix/flash.c', ] SOURCE_EMIT_NATIVE = ['vendor/micropython/py/emitnative.c'] diff --git a/embed/extmod/modtrezorio/modtrezorio-flash.h b/embed/extmod/modtrezorio/modtrezorio-flash.h index 2003f450e..31c544957 100644 --- a/embed/extmod/modtrezorio/modtrezorio-flash.h +++ b/embed/extmod/modtrezorio/modtrezorio-flash.h @@ -5,13 +5,7 @@ * see LICENSE file for details */ -#if defined TREZOR_STM32 -#include "flash.h" -#elif defined TREZOR_UNIX -#include "unix-flash-mock.h" -#else -#error Unsupported TREZOR port. Only STM32 and UNIX ports are supported. -#endif +#include "../../trezorhal/flash.h" /// class FlashOTP: /// ''' diff --git a/embed/trezorhal/flash.c b/embed/trezorhal/flash.c index ed312a67a..08bccde09 100644 --- a/embed/trezorhal/flash.c +++ b/embed/trezorhal/flash.c @@ -91,12 +91,12 @@ secbool flash_write_word(uint32_t address, uint32_t data) return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data)); } -bool flash_write_byte_rel(uint32_t sector, uint32_t offset, uint8_t data) +bool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) { return HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, SECTOR_TABLE[sector] + offset, data); } -bool flash_write_word_rel(uint32_t sector, uint32_t offset, uint32_t data) +bool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) { if (offset % 4 != 0) { return false; @@ -104,7 +104,7 @@ bool flash_write_word_rel(uint32_t sector, uint32_t offset, uint32_t data) return HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SECTOR_TABLE[sector] + offset, data); } -bool flash_read_word_rel(uint32_t sector, uint32_t offset, uint32_t *data) +bool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) { if (offset % 4 != 0) { return false; diff --git a/embed/trezorhal/flash.h b/embed/trezorhal/flash.h index d50d71557..e62b6f101 100644 --- a/embed/trezorhal/flash.h +++ b/embed/trezorhal/flash.h @@ -51,9 +51,9 @@ secbool flash_lock(void); secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)); secbool flash_write_byte(uint32_t address, uint8_t data); secbool flash_write_word(uint32_t address, uint32_t data); -secbool flash_write_byte_rel(uint32_t sector, uint32_t offset, uint8_t data); -secbool flash_write_word_rel(uint32_t sector, uint32_t offset, uint32_t data); -secbool flash_read_word_rel(uint32_t sector, uint32_t offset, uint32_t *data); +secbool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data); +secbool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data); +secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data); #define FLASH_OTP_NUM_BLOCKS 16 #define FLASH_OTP_BLOCK_SIZE 32 diff --git a/embed/unix/flash.c b/embed/unix/flash.c new file mode 100644 index 000000000..b554ec73a --- /dev/null +++ b/embed/unix/flash.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) Jan Pochyla, SatoshiLabs + * + * Licensed under TREZOR License + * see LICENSE file for details + */ + +#include +#include + +#include "../trezorhal/flash.h" + +#define SECTOR_COUNT 24 + +static const uint32_t sector_table[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, // - 0x08103FFF | 16 KiB + [13] = 0x08104000, // - 0x08107FFF | 16 KiB + [14] = 0x08108000, // - 0x0810BFFF | 16 KiB + [15] = 0x0810C000, // - 0x0810FFFF | 16 KiB + [16] = 0x08110000, // - 0x0811FFFF | 64 KiB + [17] = 0x08120000, // - 0x0813FFFF | 128 KiB + [18] = 0x08140000, // - 0x0815FFFF | 128 KiB + [19] = 0x08160000, // - 0x0817FFFF | 128 KiB + [20] = 0x08180000, // - 0x0819FFFF | 128 KiB + [21] = 0x081A0000, // - 0x081BFFFF | 128 KiB + [22] = 0x081C0000, // - 0x081DFFFF | 128 KiB + [23] = 0x081E0000, // - 0x081FFFFF | 128 KiB + [24] = 0x08200000, // last element - not a valid sector +}; + +#define FLASH_FILE "tmp" + +static uint8_t flash_buffer[0x200000]; + +static void flash_sync(void) +{ + FILE *f = fopen(FLASH_FILE, "wb"); + if (f) { + fwrite(flash_buffer, sizeof(flash_buffer), 1, f); + fclose(f); + } +} + +static void flash_read(void) +{ + FILE *f = fopen(FLASH_FILE, "rb"); + if (f) { + fread(flash_buffer, sizeof(flash_buffer), 1, f); + fclose(f); + } +} + +static void *sector_ptr(uint8_t sector, uint32_t offset, uint32_t size) +{ + if (sector >= SECTOR_COUNT) { + return NULL; + } + uint32_t sector_size = sector_table[sector + 1] - sector_table[sector]; + if (offset + size > sector_size) { + return NULL; + } + uint32_t sector_offset = sector_table[sector] - sector_table[0]; + return flash_buffer + sector_offset + offset; +} + +int flash_init(void) +{ + flash_read(); + return 0; +} + +void flash_set_option_bytes(void) +{ +} + +bool flash_unlock(void) +{ + return true; +} + +bool flash_lock(void) +{ + return true; +} + +bool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) +{ + if (progress) { + progress(0, len); + } + for (int i = 0; i < len; i++) { + uint8_t sector = sectors[i]; + uint32_t offset = sector_table[sector] - sector_table[0]; + uint32_t size = sector_table[sector + 1] - sector_table[sector]; + memset(flash_buffer + offset, 0xFF, size); + if (progress) { + progress(i + 1, len); + } + flash_sync(); + } + return true; +} + +bool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) +{ + uint8_t *flash = sector_ptr(sector, offset, sizeof(data)); + if (!flash) { + return false; + } + if ((flash[0] & data) != data) { + return false; // we cannot change zeros to ones + } + flash[0] = data; + flash_sync(); + return true; +} + +bool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) +{ + if (offset % 4) { // we write only at 4-byte boundary + return false; + } + uint32_t *flash = sector_ptr(sector, offset, sizeof(data)); + if (!flash) { + return false; + } + if ((flash[0] & data) != data) { + return false; // we cannot change zeros to ones + } + flash[0] = data; + flash_sync(); + return true; +} + +bool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) +{ + if (offset % 4) { // we read only at 4-byte boundary + return false; + } + uint32_t *flash = sector_ptr(sector, offset, sizeof(data)); + if (!flash) { + return false; + } + data[0] = flash[0]; + return true; +} + +bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen) +{ + return false; +} + +bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, uint8_t datalen) +{ + return false; +} + +bool flash_otp_lock(uint8_t block) +{ + return false; +} + +bool flash_otp_is_locked(uint8_t block) +{ + return false; +} From fdc2f6a39d40d5ed1608548db6d3ffe473b397fb Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 17 Oct 2017 15:11:58 +0200 Subject: [PATCH 03/27] modtrezorconfig: use new flash API --- embed/extmod/modtrezorconfig/modtrezorconfig.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index b330333b5..cbd429948 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -13,10 +13,12 @@ #include #include #include "norcow.h" -#include "flash.h" +#include "../../trezorhal/flash.h" #define MAX_WRONG_PINS 15 -#define FAIL_SECTOR_LEN 16 * 1024 + +#define FAIL_SECTOR_LEN 0x4000 + #define STORAGE_KEY_PIN 0x00 static void pin_fails_reset(uint32_t ofs) @@ -25,7 +27,7 @@ static void pin_fails_reset(uint32_t ofs) // ofs points to the last word of the PIN fails area. Because there is // no space left, we recycle the sector (set all words to 0xffffffff). // On next unlock attempt, we start counting from the the first word. - flash_erase_sectors(FLASH_SECTOR_PIN_AREA, FLASH_SECTOR_PIN_AREA, NULL); + flash_erase_sectors((uint8_t[]) { FLASH_SECTOR_PIN_AREA }, 1, NULL); } else { // Mark this counter as exhausted. On next unlock attempt, pinfails_get // seeks to the next word. From f07b4dda0b6794d9c02bc72722f61af42179973d Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 17 Oct 2017 19:18:16 +0200 Subject: [PATCH 04/27] modtrezorconfig: extract pin & unlocking to storage.c --- SConscript.firmware | 1 + SConscript.unix | 1 + .../extmod/modtrezorconfig/modtrezorconfig.c | 180 ++--------------- embed/extmod/modtrezorconfig/storage.c | 188 ++++++++++++++++++ embed/extmod/modtrezorconfig/storage.h | 15 ++ 5 files changed, 222 insertions(+), 163 deletions(-) create mode 100644 embed/extmod/modtrezorconfig/storage.c create mode 100644 embed/extmod/modtrezorconfig/storage.h diff --git a/SConscript.firmware b/SConscript.firmware index 1bcfbfe72..b742700ee 100644 --- a/SConscript.firmware +++ b/SConscript.firmware @@ -11,6 +11,7 @@ SOURCE_MOD = [] SOURCE_MOD += [ 'embed/extmod/modtrezorconfig/modtrezorconfig.c', 'embed/extmod/modtrezorconfig/norcow.c', + 'embed/extmod/modtrezorconfig/storage.c', ] # modtrezorcrypto diff --git a/SConscript.unix b/SConscript.unix index 00a117757..f64f42e78 100644 --- a/SConscript.unix +++ b/SConscript.unix @@ -12,6 +12,7 @@ LIBS_MOD = [] SOURCE_MOD += [ 'embed/extmod/modtrezorconfig/modtrezorconfig.c', 'embed/extmod/modtrezorconfig/norcow.c', + 'embed/extmod/modtrezorconfig/storage.c', ] # modtrezorcrypto diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index cbd429948..46ae1f106 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -7,163 +7,36 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "py/objstr.h" #if MICROPY_PY_TREZORCONFIG -#include -#include #include "norcow.h" -#include "../../trezorhal/flash.h" - -#define MAX_WRONG_PINS 15 - -#define FAIL_SECTOR_LEN 0x4000 - -#define STORAGE_KEY_PIN 0x00 - -static void pin_fails_reset(uint32_t ofs) -{ - if (ofs + sizeof(uint32_t) >= FAIL_SECTOR_LEN) { - // ofs points to the last word of the PIN fails area. Because there is - // no space left, we recycle the sector (set all words to 0xffffffff). - // On next unlock attempt, we start counting from the the first word. - flash_erase_sectors((uint8_t[]) { FLASH_SECTOR_PIN_AREA }, 1, NULL); - } else { - // Mark this counter as exhausted. On next unlock attempt, pinfails_get - // seeks to the next word. - flash_unlock(); - flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, 0); - flash_lock(); - } -} - -static bool pin_fails_increase(uint32_t ofs) -{ - uint32_t ctr = ~MAX_WRONG_PINS; - if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &ctr)) { - return false; - } - ctr = ctr << 1; - - flash_unlock(); - if (!flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, ctr)) { - flash_lock(); - return false; - } - flash_lock(); - - uint32_t check = 0; - if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &check)) { - return false; - } - return ctr == check; -} - -static bool pin_fails_check_max(uint32_t ctr) -{ - if (~ctr >= 1 << MAX_WRONG_PINS) { - norcow_wipe(); - // TODO: shutdown - return false; - } - return true; -} - -static bool pin_fails_get(uint32_t *ofs, uint32_t *ctr) -{ - if (!ofs || !ctr) { - return false; - } - for (uint32_t o = 0; o < FAIL_SECTOR_LEN; o += sizeof(uint32_t)) { - uint32_t c = 0; - if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, o, &c)) { - return false; - } - if (c != 0) { - *ofs = o; - *ctr = c; - return true; - } - } - return false; -} - -static bool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, size_t seclen) -{ - size_t diff = seclen - publen; - for (size_t i = 0; i < publen; i++) { - diff |= pub[i] ^ sec[i]; - } - return diff == 0; -} - -static bool pin_check(const uint8_t *pin, size_t pinlen) -{ - const void *st_pin; - uint16_t st_pinlen; - if (!norcow_get(STORAGE_KEY_PIN, &st_pin, &st_pinlen)) { - return false; - } - return const_cmp(pin, pinlen, st_pin, (size_t)st_pinlen); -} - -static bool pin_unlock(const uint8_t *pin, size_t pinlen) -{ - uint32_t ofs; - uint32_t ctr; - if (!pin_fails_get(&ofs, &ctr)) { - return false; - } - pin_fails_check_max(ctr); - - // Sleep for ~ctr seconds before checking the PIN. - for (uint32_t wait = ~ctr; wait > 0; wait--) { - mp_hal_delay_ms(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 (!pin_fails_increase(ofs)) { - return false; - } - if (!pin_check(pin, pinlen)) { - pin_fails_check_max(ctr << 1); - return false; - } - pin_fails_reset(ofs); - return true; -} - -static bool initialized = false; -static bool unlocked = false; +#include "storage.h" /// def init() -> None: /// ''' -/// Initializes the storage. Must be called before any other method is called from this module! +/// Initializes the storage. Must be called before any other method is +/// called from this module! /// ''' STATIC mp_obj_t mod_trezorconfig_init(void) { - bool r = norcow_init(); + bool r = storage_init(); if (!r) { mp_raise_msg(&mp_type_RuntimeError, "Could not initialize config module"); } - initialized = true; - unlocked = false; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_init_obj, mod_trezorconfig_init); -/// def unlock(pin: str) -> None: +/// def unlock(pin: str) -> bool: /// ''' -/// Tries to unlock the storage with given PIN key. +/// Attempts to unlock the storage with given PIN. Returns True on +/// success, False on failure. /// ''' STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin) { - if (!initialized) { - mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); - } - mp_buffer_info_t pinbuf; - mp_get_buffer_raise(pin, &pinbuf, MP_BUFFER_READ); - bool r = pin_unlock(pinbuf.buf, pinbuf.len); + mp_buffer_info_t buf; + mp_get_buffer_raise(pin, &buf, MP_BUFFER_READ); + bool r = storage_unlock(buf.buf, buf.len); if (!r) { return mp_const_false; } @@ -176,25 +49,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_unlock_obj, mod_trezorconfig_u /// Gets a value of given key for given app (or empty bytes if not set). /// ''' STATIC mp_obj_t mod_trezorconfig_get(mp_obj_t app, mp_obj_t key) { - if (!initialized) { - mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); - } - if (!unlocked) { - mp_raise_msg(&mp_type_RuntimeError, "Config module locked"); - // TODO: shutdown? - } uint8_t a = mp_obj_get_int(app); uint8_t k = mp_obj_get_int(key); - uint16_t appkey = a << 8 | k, len; + uint16_t appkey = a << 8 | k; + uint16_t len = 0; const void *val; - bool r = norcow_get(appkey, &val, &len); + bool r = storage_get(appkey, &val, &len); if (!r || len == 0) { return mp_const_empty_bytes; } - vstr_t vstr; - vstr_init_len(&vstr, len); - memcpy(vstr.buf, val, len); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_str_of_type(&mp_type_bytes, val, len); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_get_obj, mod_trezorconfig_get); @@ -203,19 +67,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_get_obj, mod_trezorconfig_get) /// Sets a value of given key for given app. /// ''' STATIC mp_obj_t mod_trezorconfig_set(mp_obj_t app, mp_obj_t key, mp_obj_t value) { - if (!initialized) { - mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); - } - if (!unlocked) { - mp_raise_msg(&mp_type_RuntimeError, "Config module locked"); - // TODO: shutdown? - } uint8_t a = mp_obj_get_int(app); uint8_t k = mp_obj_get_int(key); uint16_t appkey = a << 8 | k; mp_buffer_info_t v; mp_get_buffer_raise(value, &v, MP_BUFFER_READ); - bool r = norcow_set(appkey, v.buf, v.len); + bool r = storage_set(appkey, v.buf, v.len); if (!r) { mp_raise_msg(&mp_type_RuntimeError, "Could not save value"); } @@ -228,10 +85,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorconfig_set_obj, mod_trezorconfig_set) /// Erases the whole config. Use with caution! /// ''' STATIC mp_obj_t mod_trezorconfig_wipe(void) { - if (!initialized) { - mp_raise_msg(&mp_type_RuntimeError, "Config module not initialized"); - } - bool r = norcow_wipe(); + bool r = storage_wipe(); if (!r) { mp_raise_msg(&mp_type_RuntimeError, "Could not wipe storage"); } diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c new file mode 100644 index 000000000..eb4f2fcc4 --- /dev/null +++ b/embed/extmod/modtrezorconfig/storage.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs + * + * Licensed under TREZOR License + * see LICENSE file for details + */ + +#include + +#include "norcow.h" +#include "../../trezorhal/flash.h" + +// Byte-length of flash sector containing fail counters. +#define PIN_AREA_LEN 0x4000 + +// Maximum number of failed unlock attempts. +#define PIN_MAX_TRIES 15 + +// Norcow storage key of configured PIN. +#define PIN_KEY 0x0000 + +static bool initialized = false; +static bool unlocked = false; + +bool storage_init(void) +{ + if (!norcow_init()) { + return false; + } + initialized = true; + unlocked = false; + return true; +} + +static void pin_fails_reset(uint32_t ofs) +{ + if (ofs + sizeof(uint32_t) >= PIN_AREA_LEN) { + // ofs points to the last word of the PIN fails area. Because there is + // no space left, we recycle the sector (set all words to 0xffffffff). + // On next unlock attempt, we start counting from the the first word. + flash_erase_sectors((uint8_t[]) { FLASH_SECTOR_PIN_AREA }, 1, NULL); + } else { + // Mark this counter as exhausted. On next unlock attempt, pinfails_get + // seeks to the next word. + flash_unlock(); + flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, 0); + flash_lock(); + } +} + +static bool pin_fails_increase(uint32_t ofs) +{ + uint32_t ctr = ~PIN_MAX_TRIES; + if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &ctr)) { + return false; + } + ctr = ctr << 1; + + flash_unlock(); + if (!flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, ctr)) { + flash_lock(); + return false; + } + flash_lock(); + + uint32_t check = 0; + if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &check)) { + return false; + } + return ctr == check; +} + +static void pin_fails_check_max(uint32_t ctr) +{ + if (~ctr >= 1 << PIN_MAX_TRIES) { + for (;;) { + if (norcow_wipe()) { + break; + } + } + // shutdown(); + } +} + +static bool pin_fails_read(uint32_t *ofs, uint32_t *ctr) +{ + if (!ofs || !ctr) { + return false; + } + for (uint32_t o = 0; o < PIN_AREA_LEN; o += sizeof(uint32_t)) { + uint32_t c = 0; + if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, o, &c)) { + return false; + } + if (c != 0) { + *ofs = o; + *ctr = c; + return true; + } + } + return false; +} + +static bool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, size_t seclen) +{ + size_t diff = seclen ^ publen; + for (size_t i = 0; i < publen; i++) { + diff |= pub[i] ^ sec[i]; + } + return diff == 0; +} + +static bool pin_check(const uint8_t *pin, size_t pinlen) +{ + const void *st_pin; + uint16_t st_pinlen; + if (!norcow_get(PIN_KEY, &st_pin, &st_pinlen)) { + return false; + } + return const_cmp(pin, pinlen, st_pin, (size_t)st_pinlen); +} + +bool storage_unlock(const uint8_t *pin, size_t len) +{ + if (!initialized) { + return false; + } + + uint32_t ofs; + uint32_t ctr; + if (!pin_fails_read(&ofs, &ctr)) { + return false; + } + pin_fails_check_max(ctr); + + // Sleep for ~ctr seconds before checking the PIN. + for (uint32_t wait = ~ctr; wait > 0; wait--) { + // hal_delay(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 (!pin_fails_increase(ofs)) { + return false; + } + if (!pin_check(pin, len)) { + pin_fails_check_max(ctr << 1); + return false; + } + pin_fails_reset(ofs); + return true; +} + +bool storage_get(uint16_t key, const void **val, uint16_t *len) +{ + if (!initialized) { + return false; + } + if (!unlocked) { + // shutdown(); + return false; + } + if (key == PIN_KEY) { + return false; + } + return norcow_get(key, val, len); +} + +bool storage_set(uint16_t key, const void *val, uint16_t len) +{ + if (!initialized) { + return false; + } + if (!unlocked) { + // shutdown(); + return false; + } + if (key == PIN_KEY) { + return false; + } + return norcow_set(key, val, len); +} + +bool storage_wipe(void) +{ + return norcow_wipe(); +} diff --git a/embed/extmod/modtrezorconfig/storage.h b/embed/extmod/modtrezorconfig/storage.h new file mode 100644 index 000000000..2bbe58c8d --- /dev/null +++ b/embed/extmod/modtrezorconfig/storage.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs + * + * Licensed under TREZOR License + * see LICENSE file for details + */ + +#include +#include + +bool storage_init(void); +bool storage_wipe(void); +bool storage_unlock(const uint8_t *pin, size_t len); +bool storage_get(uint16_t key, const void **val, uint16_t *len); +bool storage_set(uint16_t key, const void *val, uint16_t len); From c78babdc12fc18cc400585f19d53af2482100aa1 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 18 Oct 2017 18:30:53 +0200 Subject: [PATCH 05/27] flash: add flash_get_address --- embed/trezorhal/flash.c | 37 +++++++++++++++++++++++++++++-------- embed/trezorhal/flash.h | 2 ++ embed/unix/flash.c | 36 ++++++++++++++++++------------------ 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/embed/trezorhal/flash.c b/embed/trezorhal/flash.c index 08bccde09..99cdb1b09 100644 --- a/embed/trezorhal/flash.c +++ b/embed/trezorhal/flash.c @@ -1,6 +1,14 @@ +/* + * Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs + * + * Licensed under TREZOR License + * see LICENSE file for details + */ + #include STM32_HAL_H #include + #include "flash.h" // see docs/memory.md for more information @@ -46,6 +54,19 @@ secbool flash_lock(void) return sectrue; } +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) +{ + if (sector >= SECTOR_COUNT) { + return NULL; + } + uint32_t addr = SECTOR_TABLE[sector]; + uint32_t next = SECTOR_TABLE[sector + 1]; + if (offset + size > next - addr) { + return NULL; + } + return (const uint8_t *)addr + offset; +} + secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) { if (sectrue != flash_unlock()) { @@ -91,26 +112,26 @@ secbool flash_write_word(uint32_t address, uint32_t data) return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data)); } -bool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) +secbool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) { - return HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, SECTOR_TABLE[sector] + offset, data); + return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, SECTOR_TABLE[sector] + offset, data)); } -bool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) +secbool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) { if (offset % 4 != 0) { - return false; + return secfalse; } - return HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SECTOR_TABLE[sector] + offset, data); + return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SECTOR_TABLE[sector] + offset, data)); } -bool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) +secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) { if (offset % 4 != 0) { - return false; + return secfalse; } *data = *((uint32_t *) SECTOR_TABLE[sector] + offset); - return true; + return sectrue; } #define FLASH_OTP_LOCK_BASE 0x1FFF7A00U diff --git a/embed/trezorhal/flash.h b/embed/trezorhal/flash.h index e62b6f101..fea66b296 100644 --- a/embed/trezorhal/flash.h +++ b/embed/trezorhal/flash.h @@ -48,6 +48,8 @@ extern const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1]; secbool flash_unlock(void); secbool flash_lock(void); +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size); + secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)); secbool flash_write_byte(uint32_t address, uint8_t data); secbool flash_write_word(uint32_t address, uint32_t data); diff --git a/embed/unix/flash.c b/embed/unix/flash.c index b554ec73a..f4a4d9116 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -62,19 +62,6 @@ static void flash_read(void) } } -static void *sector_ptr(uint8_t sector, uint32_t offset, uint32_t size) -{ - if (sector >= SECTOR_COUNT) { - return NULL; - } - uint32_t sector_size = sector_table[sector + 1] - sector_table[sector]; - if (offset + size > sector_size) { - return NULL; - } - uint32_t sector_offset = sector_table[sector] - sector_table[0]; - return flash_buffer + sector_offset + offset; -} - int flash_init(void) { flash_read(); @@ -95,6 +82,19 @@ bool flash_lock(void) return true; } +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) +{ + if (sector >= SECTOR_COUNT) { + return NULL; + } + uint32_t sector_size = sector_table[sector + 1] - sector_table[sector]; + if (offset + size > sector_size) { + return NULL; + } + uint32_t sector_offset = sector_table[sector] - sector_table[0]; + return flash_buffer + sector_offset + offset; +} + bool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) { if (progress) { @@ -115,12 +115,12 @@ bool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int p bool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) { - uint8_t *flash = sector_ptr(sector, offset, sizeof(data)); + uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { return false; } if ((flash[0] & data) != data) { - return false; // we cannot change zeros to ones + return false; // we cannot change zeroes to ones } flash[0] = data; flash_sync(); @@ -132,12 +132,12 @@ bool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) if (offset % 4) { // we write only at 4-byte boundary return false; } - uint32_t *flash = sector_ptr(sector, offset, sizeof(data)); + uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { return false; } if ((flash[0] & data) != data) { - return false; // we cannot change zeros to ones + return false; // we cannot change zeroes to ones } flash[0] = data; flash_sync(); @@ -149,7 +149,7 @@ bool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) if (offset % 4) { // we read only at 4-byte boundary return false; } - uint32_t *flash = sector_ptr(sector, offset, sizeof(data)); + uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { return false; } From 12708b49873ce38ccec003364a6e3779ff83d18f Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 18 Oct 2017 18:31:19 +0200 Subject: [PATCH 06/27] unix/flash: default FLASH_FILE --- embed/unix/flash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embed/unix/flash.c b/embed/unix/flash.c index f4a4d9116..3daa2e39b 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -10,6 +10,10 @@ #include "../trezorhal/flash.h" +#ifndef FLASH_FILE +#define FLASH_FILE "/var/tmp/trezor.config" +#endif + #define SECTOR_COUNT 24 static const uint32_t sector_table[SECTOR_COUNT + 1] = { @@ -40,8 +44,6 @@ static const uint32_t sector_table[SECTOR_COUNT + 1] = { [24] = 0x08200000, // last element - not a valid sector }; -#define FLASH_FILE "tmp" - static uint8_t flash_buffer[0x200000]; static void flash_sync(void) From d4443a4f7f23985263c44c715b2ea46e019ff7ab Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 18 Oct 2017 18:31:37 +0200 Subject: [PATCH 07/27] unix/flash: start with 0xff --- embed/unix/flash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 3daa2e39b..2790660d6 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -66,6 +66,7 @@ static void flash_read(void) int flash_init(void) { + memset(flash_buffer, 0xFF, sizeof(flash_buffer)); flash_read(); return 0; } From 27bfcfe1af1b16b24448c3f108cbb9236cec90ca Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 18 Oct 2017 18:36:26 +0200 Subject: [PATCH 08/27] norcow: import into tree --- embed/extmod/modtrezorconfig/norcow.c | 339 +++++++++++++++++++++++++- embed/extmod/modtrezorconfig/norcow.h | 35 ++- vendor/norcow | 1 - 3 files changed, 372 insertions(+), 3 deletions(-) mode change 120000 => 100644 embed/extmod/modtrezorconfig/norcow.c mode change 120000 => 100644 embed/extmod/modtrezorconfig/norcow.h delete mode 160000 vendor/norcow diff --git a/embed/extmod/modtrezorconfig/norcow.c b/embed/extmod/modtrezorconfig/norcow.c deleted file mode 120000 index 0c15ca2f9..000000000 --- a/embed/extmod/modtrezorconfig/norcow.c +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/norcow/norcow.c \ No newline at end of file diff --git a/embed/extmod/modtrezorconfig/norcow.c b/embed/extmod/modtrezorconfig/norcow.c new file mode 100644 index 000000000..33edde558 --- /dev/null +++ b/embed/extmod/modtrezorconfig/norcow.c @@ -0,0 +1,338 @@ +#include + +#include "norcow.h" +#include "norcow_config.h" + +#ifdef NORCOW_UNIX +#ifndef NORCOW_FILE +#error Undefined NORCOW_FILE +#endif +#include +uint8_t norcow_buffer[NORCOW_SECTOR_COUNT * NORCOW_SECTOR_SIZE]; +#endif + +#ifdef NORCOW_STM32 +#ifndef NORCOW_SECTORS +#error Undefined NORCOW_SECTORS +#endif +#ifndef NORCOW_ADDRESSES +#error Undefined NORCOW_ADDRESSES +#endif +static uint32_t norcow_sectors[NORCOW_SECTOR_COUNT] = NORCOW_SECTORS; +static uint32_t norcow_addresses[NORCOW_SECTOR_COUNT] = NORCOW_ADDRESSES; +#include STM32_HAL_H +#endif + +static uint8_t norcow_active_sector = 0; +static uint32_t norcow_active_offset = 0; + +/* + * Synchronizes in-memory storage with file on disk (UNIX only) + */ +#ifdef NORCOW_UNIX +static void norcow_sync(void) +{ + FILE *f = fopen(NORCOW_FILE, "wb"); + if (f) { + fwrite(norcow_buffer, sizeof(norcow_buffer), 1, f); + fclose(f); + } +} +#endif + +/* + * Erases sector + */ +static bool norcow_erase(uint8_t sector) +{ + if (sector >= NORCOW_SECTOR_COUNT) { + return false; + } +#ifdef NORCOW_UNIX + memset(norcow_buffer + sector * NORCOW_SECTOR_SIZE, 0xFF, NORCOW_SECTOR_SIZE); + norcow_sync(); + return true; +#endif +#ifdef NORCOW_STM32 + HAL_FLASH_Unlock(); + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); + FLASH_EraseInitTypeDef EraseInitStruct; + EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; + EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; + EraseInitStruct.NbSectors = 1; + EraseInitStruct.Sector = norcow_sectors[sector]; + HAL_StatusTypeDef r; + uint32_t SectorError = 0; + r = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError); + HAL_FLASH_Lock(); + return r == HAL_OK; +#endif + return false; +} + +/* + * 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) +{ + if (sector >= NORCOW_SECTOR_COUNT) { + return NULL; + } + if (offset + size > NORCOW_SECTOR_SIZE) { + return NULL; + } +#ifdef NORCOW_UNIX + return (const void *)(norcow_buffer + sector * NORCOW_SECTOR_SIZE + offset); +#endif +#ifdef NORCOW_STM32 + return (const void *)(norcow_addresses[sector] + offset); +#endif +} + +/* + * Writes data to given sector, starting from offset + */ +static bool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len) +{ + if (offset % 4) { // we write only at 4-byte boundary + return false; + } + const uint8_t *ptr = (const uint8_t *)norcow_ptr(sector, offset, sizeof(uint32_t) + len); + if (!ptr) { + return false; + } +#ifdef NORCOW_UNIX + // check whether we are about just change 1s to 0s + // and bailout if not + if ((*(uint32_t *)ptr & prefix) != prefix) { + return false; + } + for (size_t i = 0; i < len; i++) { + if ((ptr[sizeof(uint32_t) + i] & data[i]) != data[i]) { + return false; + } + } + memcpy((void *)ptr, &prefix, sizeof(uint32_t)); + memcpy((void *)(ptr + sizeof(uint32_t)), data, len); + norcow_sync(); + return true; +#endif +#ifdef NORCOW_STM32 + HAL_FLASH_Unlock(); + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); + uint32_t addr = (uint32_t)ptr; + HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, prefix); + addr += 4; + for (size_t i = 0; i < (len + 3) / sizeof(uint32_t); i++) { + const uint32_t *d = (const uint32_t *)(data + i * sizeof(uint32_t)); + HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *d); + addr += 4; + } + HAL_FLASH_Lock(); + return true; +#endif + return false; +} + +#define ALIGN4(X) (X) = ((X) + 3) & ~3 + +/* + * Reads one item starting from offset + */ +static bool 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 false; + *pos += 2; + memcpy(key, k, sizeof(uint16_t)); + if (*key == 0xFFFF) { + return false; + } + + const void *l = norcow_ptr(sector, *pos, 2); + if (l == NULL) return false; + *pos += 2; + memcpy(len, l, sizeof(uint16_t)); + + *val = norcow_ptr(sector, *pos, *len); + if (*val == NULL) return false; + *pos += *len; + ALIGN4(*pos); + return true; +} + +/* + * Writes one item starting from offset + */ +static bool 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 bool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len) +{ + *val = 0; + *len = 0; + uint32_t offset = 0; + for (;;) { + uint16_t k, l; + const void *v; + uint32_t pos; + bool r = read_item(sector, offset, &k, &v, &l, &pos); + if (!r) break; + if (key == k) { + *val = v; + *len = l; + } + offset = pos; + } + return (*val); +} + +/* + * Finds first unused offset in given sector + */ +static uint32_t find_free_offset(uint8_t sector) +{ + uint32_t offset = 0; + for (;;) { + uint16_t key, len; + const void *val; + uint32_t pos; + bool r = read_item(sector, offset, &key, &val, &len, &pos); + if (!r) 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; + + uint32_t offset = 0, offsetw = 0; + + for (;;) { + // read item + uint16_t k, l; + const void *v; + uint32_t pos; + bool r = read_item(norcow_active_sector, offset, &k, &v, &l, &pos); + if (!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 (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 (!r) break; + if (k == k2) { + v = v2; + l = l2; + } + offsetr = posr; + } + + // copy the last item + uint32_t posw; + r = write_item(norcow_next_sector, offsetw, k, v, l, &posw); + if (!r) { } // TODO: error + offsetw = posw; + } + + norcow_erase(norcow_active_sector); + norcow_active_sector = norcow_next_sector; + norcow_active_offset = find_free_offset(norcow_active_sector); +} + +/* + * Initializes storage + */ +bool norcow_init(void) +{ +#ifdef NORCOW_UNIX + memset(norcow_buffer, 0xFF, sizeof(norcow_buffer)); + FILE *f = fopen(NORCOW_FILE, "rb"); + if (f) { + size_t r = fread(norcow_buffer, sizeof(norcow_buffer), 1, f); + fclose(f); + if (r != 1) { + memset(norcow_buffer, 0xFF, sizeof(norcow_buffer)); + } + } +#endif + // detect active sector (inactive sectors are empty = start with 0xFF) + for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { + const uint8_t *b = norcow_ptr(i, 0, 1); + if (b != NULL && *b != 0xFF) { + norcow_active_sector = i; + break; + } + } + norcow_active_offset = find_free_offset(norcow_active_sector); + return true; +} + +/* + * Wipe the storage + */ +bool norcow_wipe(void) +{ + for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { + if (!norcow_erase(i)) { + return false; + } + } + norcow_active_sector = 0; + norcow_active_offset = 0; + return true; +} + +/* + * Looks for the given key, returns status of the operation + */ +bool 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 + */ +bool 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; + bool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos); + if (r) { + norcow_active_offset = pos; + } + return r; +} diff --git a/embed/extmod/modtrezorconfig/norcow.h b/embed/extmod/modtrezorconfig/norcow.h deleted file mode 120000 index f33ecb157..000000000 --- a/embed/extmod/modtrezorconfig/norcow.h +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/norcow/norcow.h \ No newline at end of file diff --git a/embed/extmod/modtrezorconfig/norcow.h b/embed/extmod/modtrezorconfig/norcow.h new file mode 100644 index 000000000..877df8804 --- /dev/null +++ b/embed/extmod/modtrezorconfig/norcow.h @@ -0,0 +1,34 @@ +#ifndef __NORCOW_H__ +#define __NORCOW_H__ + +#include +#include + +/* + * Storage parameters: + */ + +#define NORCOW_SECTOR_COUNT 2 +#define NORCOW_SECTOR_SIZE (64*1024) + +/* + * Initialize storage + */ +bool norcow_init(void); + +/* + * Wipe the storage + */ +bool norcow_wipe(void); + +/* + * Looks for the given key, returns status of the operation + */ +bool norcow_get(uint16_t key, const void **val, uint16_t *len); + +/* + * Sets the given key, returns status of the operation + */ +bool norcow_set(uint16_t key, const void *val, uint16_t len); + +#endif diff --git a/vendor/norcow b/vendor/norcow deleted file mode 160000 index 56f11a3d6..000000000 --- a/vendor/norcow +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 56f11a3d6c8c77d4ecb82e1a55d3003263ef2a72 From b6ba95625c94dd045dac3b44ff1cf81be08f0208 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 18 Oct 2017 18:37:09 +0200 Subject: [PATCH 09/27] norcow: port on top of flash.c, remove config --- embed/extmod/modtrezorconfig/norcow.c | 120 ++++--------------- embed/extmod/modtrezorconfig/norcow_config.h | 18 --- 2 files changed, 22 insertions(+), 116 deletions(-) delete mode 100644 embed/extmod/modtrezorconfig/norcow_config.h diff --git a/embed/extmod/modtrezorconfig/norcow.c b/embed/extmod/modtrezorconfig/norcow.c index 33edde558..57655437b 100644 --- a/embed/extmod/modtrezorconfig/norcow.c +++ b/embed/extmod/modtrezorconfig/norcow.c @@ -1,45 +1,17 @@ #include #include "norcow.h" -#include "norcow_config.h" -#ifdef NORCOW_UNIX -#ifndef NORCOW_FILE -#error Undefined NORCOW_FILE -#endif -#include -uint8_t norcow_buffer[NORCOW_SECTOR_COUNT * NORCOW_SECTOR_SIZE]; -#endif +#include "../../trezorhal/flash.h" -#ifdef NORCOW_STM32 #ifndef NORCOW_SECTORS -#error Undefined NORCOW_SECTORS -#endif -#ifndef NORCOW_ADDRESSES -#error Undefined NORCOW_ADDRESSES -#endif -static uint32_t norcow_sectors[NORCOW_SECTOR_COUNT] = NORCOW_SECTORS; -static uint32_t norcow_addresses[NORCOW_SECTOR_COUNT] = NORCOW_ADDRESSES; -#include STM32_HAL_H +#define NORCOW_SECTORS {4, 16} #endif +static uint8_t norcow_sectors[NORCOW_SECTOR_COUNT] = NORCOW_SECTORS; static uint8_t norcow_active_sector = 0; static uint32_t norcow_active_offset = 0; -/* - * Synchronizes in-memory storage with file on disk (UNIX only) - */ -#ifdef NORCOW_UNIX -static void norcow_sync(void) -{ - FILE *f = fopen(NORCOW_FILE, "wb"); - if (f) { - fwrite(norcow_buffer, sizeof(norcow_buffer), 1, f); - fclose(f); - } -} -#endif - /* * Erases sector */ @@ -48,26 +20,7 @@ static bool norcow_erase(uint8_t sector) if (sector >= NORCOW_SECTOR_COUNT) { return false; } -#ifdef NORCOW_UNIX - memset(norcow_buffer + sector * NORCOW_SECTOR_SIZE, 0xFF, NORCOW_SECTOR_SIZE); - norcow_sync(); - return true; -#endif -#ifdef NORCOW_STM32 - HAL_FLASH_Unlock(); - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - FLASH_EraseInitTypeDef EraseInitStruct; - EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; - EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; - EraseInitStruct.NbSectors = 1; - EraseInitStruct.Sector = norcow_sectors[sector]; - HAL_StatusTypeDef r; - uint32_t SectorError = 0; - r = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError); - HAL_FLASH_Lock(); - return r == HAL_OK; -#endif - return false; + return flash_erase_sectors(&norcow_sectors[sector], 1, NULL); } /* @@ -79,15 +32,7 @@ static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) if (sector >= NORCOW_SECTOR_COUNT) { return NULL; } - if (offset + size > NORCOW_SECTOR_SIZE) { - return NULL; - } -#ifdef NORCOW_UNIX - return (const void *)(norcow_buffer + sector * NORCOW_SECTOR_SIZE + offset); -#endif -#ifdef NORCOW_STM32 - return (const void *)(norcow_addresses[sector] + offset); -#endif + return flash_get_address(norcow_sectors[sector], offset, size); } /* @@ -95,44 +40,34 @@ static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) */ static bool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len) { - if (offset % 4) { // we write only at 4-byte boundary + if (sector >= NORCOW_SECTOR_COUNT) { return false; } - const uint8_t *ptr = (const uint8_t *)norcow_ptr(sector, offset, sizeof(uint32_t) + len); - if (!ptr) { + if (!flash_unlock()) { return false; } -#ifdef NORCOW_UNIX - // check whether we are about just change 1s to 0s - // and bailout if not - if ((*(uint32_t *)ptr & prefix) != prefix) { + // write prefix + if (!flash_write_word_rel(norcow_sectors[sector], offset, prefix)) { + flash_lock(); return false; } - for (size_t i = 0; i < len; i++) { - if ((ptr[sizeof(uint32_t) + i] & data[i]) != data[i]) { + offset += sizeof(uint32_t); + // write data + for (uint16_t i = 0; i < len; i++, offset++) { + if (!flash_write_byte_rel(norcow_sectors[sector], offset, data[i])) { + flash_lock(); return false; } } - memcpy((void *)ptr, &prefix, sizeof(uint32_t)); - memcpy((void *)(ptr + sizeof(uint32_t)), data, len); - norcow_sync(); - return true; -#endif -#ifdef NORCOW_STM32 - HAL_FLASH_Unlock(); - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - uint32_t addr = (uint32_t)ptr; - HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, prefix); - addr += 4; - for (size_t i = 0; i < (len + 3) / sizeof(uint32_t); i++) { - const uint32_t *d = (const uint32_t *)(data + i * sizeof(uint32_t)); - HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *d); - addr += 4; + // pad with zeroes + for (; offset % 4; offset++) { + if (!flash_write_byte_rel(norcow_sectors[sector], offset, 0x00)) { + flash_lock(); + return false; + } } - HAL_FLASH_Lock(); + flash_lock(); return true; -#endif - return false; } #define ALIGN4(X) (X) = ((X) + 3) & ~3 @@ -272,17 +207,6 @@ static void compact() */ bool norcow_init(void) { -#ifdef NORCOW_UNIX - memset(norcow_buffer, 0xFF, sizeof(norcow_buffer)); - FILE *f = fopen(NORCOW_FILE, "rb"); - if (f) { - size_t r = fread(norcow_buffer, sizeof(norcow_buffer), 1, f); - fclose(f); - if (r != 1) { - memset(norcow_buffer, 0xFF, sizeof(norcow_buffer)); - } - } -#endif // detect active sector (inactive sectors are empty = start with 0xFF) for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { const uint8_t *b = norcow_ptr(i, 0, 1); diff --git a/embed/extmod/modtrezorconfig/norcow_config.h b/embed/extmod/modtrezorconfig/norcow_config.h deleted file mode 100644 index dfa38e0dc..000000000 --- a/embed/extmod/modtrezorconfig/norcow_config.h +++ /dev/null @@ -1,18 +0,0 @@ -#if defined TREZOR_STM32 - -#define NORCOW_STM32 1 - -#define NORCOW_SECTORS {4, 16} -#define NORCOW_ADDRESSES {0x08010000, 0x08110000} - -#elif defined TREZOR_UNIX - -#define NORCOW_UNIX 1 - -#define NORCOW_FILE "/var/tmp/trezor.config" - -#else - -#error Unsupported TREZOR port. Only STM32 and UNIX ports are supported. - -#endif From 353883e0f303713e041780ba1cfba426262aacc0 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 18 Oct 2017 18:37:22 +0200 Subject: [PATCH 10/27] storage: rename constant --- embed/extmod/modtrezorconfig/storage.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index eb4f2fcc4..11a572e85 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -11,7 +11,7 @@ #include "../../trezorhal/flash.h" // Byte-length of flash sector containing fail counters. -#define PIN_AREA_LEN 0x4000 +#define PIN_SECTOR_SIZE 0x4000 // Maximum number of failed unlock attempts. #define PIN_MAX_TRIES 15 @@ -34,7 +34,7 @@ bool storage_init(void) static void pin_fails_reset(uint32_t ofs) { - if (ofs + sizeof(uint32_t) >= PIN_AREA_LEN) { + if (ofs + sizeof(uint32_t) >= PIN_SECTOR_SIZE) { // ofs points to the last word of the PIN fails area. Because there is // no space left, we recycle the sector (set all words to 0xffffffff). // On next unlock attempt, we start counting from the the first word. @@ -87,7 +87,7 @@ static bool pin_fails_read(uint32_t *ofs, uint32_t *ctr) if (!ofs || !ctr) { return false; } - for (uint32_t o = 0; o < PIN_AREA_LEN; o += sizeof(uint32_t)) { + for (uint32_t o = 0; o < PIN_SECTOR_SIZE; o += sizeof(uint32_t)) { uint32_t c = 0; if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, o, &c)) { return false; From c2ec40bebfa334a93eaa866367d0ebd36f870be0 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 24 Oct 2017 13:54:21 +0200 Subject: [PATCH 11/27] flash: flash_init returns bool --- embed/trezorhal/flash.c | 5 +++++ embed/trezorhal/flash.h | 4 ++++ embed/unix/flash.c | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/embed/trezorhal/flash.c b/embed/trezorhal/flash.c index 99cdb1b09..60ac1abee 100644 --- a/embed/trezorhal/flash.c +++ b/embed/trezorhal/flash.c @@ -41,6 +41,11 @@ const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { [24] = 0x08200000, // last element - not a valid sector }; +secbool flash_init(void) +{ + return sectrue; +} + secbool flash_unlock(void) { HAL_FLASH_Unlock(); diff --git a/embed/trezorhal/flash.h b/embed/trezorhal/flash.h index fea66b296..dbd8bb30f 100644 --- a/embed/trezorhal/flash.h +++ b/embed/trezorhal/flash.h @@ -45,6 +45,10 @@ extern const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1]; +secbool flash_init(void); + +void flash_set_option_bytes(void); + secbool flash_unlock(void); secbool flash_lock(void); diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 2790660d6..45be5c99b 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -64,11 +64,11 @@ static void flash_read(void) } } -int flash_init(void) +bool flash_init(void) { memset(flash_buffer, 0xFF, sizeof(flash_buffer)); flash_read(); - return 0; + return true; } void flash_set_option_bytes(void) From 418f035811d2d649731bd869b27770254ba76a8f Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 24 Oct 2017 13:55:29 +0200 Subject: [PATCH 12/27] storage: add has_pin and change_pin TODO: finish change_pin --- .../extmod/modtrezorconfig/modtrezorconfig.c | 32 +++++++++++++++ embed/extmod/modtrezorconfig/storage.c | 40 ++++++++++++++++--- embed/extmod/modtrezorconfig/storage.h | 2 + 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index 46ae1f106..4228fb2f8 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -44,6 +44,36 @@ STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_unlock_obj, mod_trezorconfig_unlock); +/// def has_pin() -> bool: +/// ''' +/// Returns True if storage has a configured PIN, False otherwise. +/// ''' +STATIC mp_obj_t mod_trezorconfig_has_pin(void) { + if (storage_has_pin()) { + return mp_const_true; + } else { + return mp_const_false; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_has_pin_obj, mod_trezorconfig_has_pin); + +/// def change_pin(pin: str, newpin: str) -> bool: +/// ''' +/// Change PIN. Returns True on success, False on failure. +/// ''' +STATIC mp_obj_t mod_trezorconfig_change_pin(mp_obj_t pin, mp_obj_t newpin) { + mp_buffer_info_t pinbuf; + mp_get_buffer_raise(pin, &pinbuf, MP_BUFFER_READ); + mp_buffer_info_t newbuf; + mp_get_buffer_raise(newpin, &newbuf, MP_BUFFER_READ); + bool r = storage_change_pin(pinbuf.buf, pinbuf.len, newbuf.buf, newbuf.len); + if (!r) { + return mp_const_false; + } + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_change_pin_obj, mod_trezorconfig_change_pin); + /// def get(app: int, key: int) -> bytes: /// ''' /// Gets a value of given key for given app (or empty bytes if not set). @@ -97,6 +127,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorconfig_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorconfig) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mod_trezorconfig_init_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_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) }, { MP_ROM_QSTR(MP_QSTR_wipe), MP_ROM_PTR(&mod_trezorconfig_wipe_obj) }, diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index 11a572e85..e5cb1db0c 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -24,6 +24,9 @@ static bool unlocked = false; bool storage_init(void) { + if (!flash_init()) { + return false; + } if (!norcow_init()) { return false; } @@ -112,12 +115,10 @@ static bool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, siz static bool pin_check(const uint8_t *pin, size_t pinlen) { - const void *st_pin; - uint16_t st_pinlen; - if (!norcow_get(PIN_KEY, &st_pin, &st_pinlen)) { - return false; - } - return const_cmp(pin, pinlen, st_pin, (size_t)st_pinlen); + const void *spin = NULL; + uint16_t spinlen = 0; + norcow_get(PIN_KEY, &spin, &spinlen); + return const_cmp(pin, pinlen, spin, (size_t)spinlen); } bool storage_unlock(const uint8_t *pin, size_t len) @@ -182,6 +183,33 @@ bool storage_set(uint16_t key, const void *val, uint16_t len) return norcow_set(key, val, len); } +bool storage_has_pin(void) +{ + if (!initialized) { + return false; + } + const void *spin = NULL; + uint16_t spinlen = 0; + norcow_get(PIN_KEY, &spin, &spinlen); + return spinlen != 0; +} + +bool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen) +{ + if (!initialized) { + return false; + } + if (!unlocked) { + // shutdown(); + return false; + } + if (!pin_check(pin, len)) { + return false; + } + // TODO + return true; +} + bool storage_wipe(void) { return norcow_wipe(); diff --git a/embed/extmod/modtrezorconfig/storage.h b/embed/extmod/modtrezorconfig/storage.h index 2bbe58c8d..3ad3e8e56 100644 --- a/embed/extmod/modtrezorconfig/storage.h +++ b/embed/extmod/modtrezorconfig/storage.h @@ -11,5 +11,7 @@ bool storage_init(void); bool storage_wipe(void); bool storage_unlock(const uint8_t *pin, size_t len); +bool storage_has_pin(void); +bool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen); bool storage_get(uint16_t key, const void **val, uint16_t *len); bool storage_set(uint16_t key, const void *val, uint16_t len); From 662cfc1b2c2a48bade370ae296e1c30be94f930d Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Fri, 27 Oct 2017 17:34:56 +0200 Subject: [PATCH 13/27] unix/flash: use secbool --- embed/unix/flash.c | 58 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 45be5c99b..44cad6dfb 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -64,25 +64,25 @@ static void flash_read(void) } } -bool flash_init(void) +secbool flash_init(void) { memset(flash_buffer, 0xFF, sizeof(flash_buffer)); flash_read(); - return true; + return sectrue; } void flash_set_option_bytes(void) { } -bool flash_unlock(void) +secbool flash_unlock(void) { - return true; + return sectrue; } -bool flash_lock(void) +secbool flash_lock(void) { - return true; + return sectrue; } const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) @@ -98,7 +98,7 @@ const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) return flash_buffer + sector_offset + offset; } -bool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) +secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) { if (progress) { progress(0, len); @@ -113,69 +113,69 @@ bool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int p } flash_sync(); } - return true; + return sectrue; } -bool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) +secbool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) { uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { - return false; + return secfalse; } if ((flash[0] & data) != data) { - return false; // we cannot change zeroes to ones + return secfalse; // we cannot change zeroes to ones } flash[0] = data; flash_sync(); - return true; + return sectrue; } -bool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) +secbool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) { if (offset % 4) { // we write only at 4-byte boundary - return false; + return secfalse; } uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { - return false; + return secfalse; } if ((flash[0] & data) != data) { - return false; // we cannot change zeroes to ones + return secfalse; // we cannot change zeroes to ones } flash[0] = data; flash_sync(); - return true; + return sectrue; } -bool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) +secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) { if (offset % 4) { // we read only at 4-byte boundary - return false; + return secfalse; } uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { - return false; + return secfalse; } data[0] = flash[0]; - return true; + return sectrue; } -bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen) +secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen) { - return false; + return secfalse; } -bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, uint8_t datalen) +secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, uint8_t datalen) { - return false; + return secfalse; } -bool flash_otp_lock(uint8_t block) +secbool flash_otp_lock(uint8_t block) { - return false; + return secfalse; } -bool flash_otp_is_locked(uint8_t block) +secbool flash_otp_is_locked(uint8_t block) { - return false; + return secfalse; } From 0376ff7f48a47bc3c851c6b61774039564aaff0b Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Fri, 27 Oct 2017 17:49:30 +0200 Subject: [PATCH 14/27] storage, norcow: use secbool --- embed/extmod/modtrezorconfig/norcow.c | 86 ++++++++--------- embed/extmod/modtrezorconfig/norcow.h | 9 +- embed/extmod/modtrezorconfig/storage.c | 129 +++++++++++++------------ embed/extmod/modtrezorconfig/storage.h | 15 +-- embed/trezorhal/flash.c | 12 +-- 5 files changed, 128 insertions(+), 123 deletions(-) diff --git a/embed/extmod/modtrezorconfig/norcow.c b/embed/extmod/modtrezorconfig/norcow.c index 57655437b..b8917b308 100644 --- a/embed/extmod/modtrezorconfig/norcow.c +++ b/embed/extmod/modtrezorconfig/norcow.c @@ -15,10 +15,10 @@ static uint32_t norcow_active_offset = 0; /* * Erases sector */ -static bool norcow_erase(uint8_t sector) +static secbool norcow_erase(uint8_t sector) { if (sector >= NORCOW_SECTOR_COUNT) { - return false; + return secfalse; } return flash_erase_sectors(&norcow_sectors[sector], 1, NULL); } @@ -38,36 +38,36 @@ static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) /* * Writes data to given sector, starting from offset */ -static bool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len) +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 false; + return secfalse; } - if (!flash_unlock()) { - return false; + if (sectrue != flash_unlock()) { + return secfalse; } // write prefix - if (!flash_write_word_rel(norcow_sectors[sector], offset, prefix)) { + if (sectrue != flash_write_word_rel(norcow_sectors[sector], offset, prefix)) { flash_lock(); - return false; + return secfalse; } offset += sizeof(uint32_t); // write data for (uint16_t i = 0; i < len; i++, offset++) { - if (!flash_write_byte_rel(norcow_sectors[sector], offset, data[i])) { + if (sectrue != flash_write_byte_rel(norcow_sectors[sector], offset, data[i])) { flash_lock(); - return false; + return secfalse; } } // pad with zeroes for (; offset % 4; offset++) { - if (!flash_write_byte_rel(norcow_sectors[sector], offset, 0x00)) { + if (sectrue != flash_write_byte_rel(norcow_sectors[sector], offset, 0x00)) { flash_lock(); - return false; + return secfalse; } } flash_lock(); - return true; + return sectrue; } #define ALIGN4(X) (X) = ((X) + 3) & ~3 @@ -75,34 +75,34 @@ static bool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const /* * Reads one item starting from offset */ -static bool read_item(uint8_t sector, uint32_t offset, uint16_t *key, const void **val, uint16_t *len, uint32_t *pos) +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 false; + if (k == NULL) return secfalse; *pos += 2; memcpy(key, k, sizeof(uint16_t)); if (*key == 0xFFFF) { - return false; + return secfalse; } const void *l = norcow_ptr(sector, *pos, 2); - if (l == NULL) return false; + if (l == NULL) return secfalse; *pos += 2; memcpy(len, l, sizeof(uint16_t)); *val = norcow_ptr(sector, *pos, *len); - if (*val == NULL) return false; + if (*val == NULL) return secfalse; *pos += *len; ALIGN4(*pos); - return true; + return sectrue; } /* * Writes one item starting from offset */ -static bool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void *val, uint16_t len, uint32_t *pos) +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; @@ -113,7 +113,7 @@ static bool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void /* * Finds item in given sector */ -static bool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len) +static secbool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len) { *val = 0; *len = 0; @@ -122,15 +122,16 @@ static bool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t * uint16_t k, l; const void *v; uint32_t pos; - bool r = read_item(sector, offset, &k, &v, &l, &pos); - if (!r) break; + if (sectrue != read_item(sector, offset, &k, &v, &l, &pos)) { + break; + } if (key == k) { *val = v; *len = l; } offset = pos; } - return (*val); + return sectrue * (*val != NULL); } /* @@ -143,8 +144,9 @@ static uint32_t find_free_offset(uint8_t sector) uint16_t key, len; const void *val; uint32_t pos; - bool r = read_item(sector, offset, &key, &val, &len, &pos); - if (!r) break; + if (sectrue != read_item(sector, offset, &key, &val, &len, &pos)) { + break; + } offset = pos; } return offset; @@ -164,17 +166,15 @@ static void compact() uint16_t k, l; const void *v; uint32_t pos; - bool r = read_item(norcow_active_sector, offset, &k, &v, &l, &pos); - if (!r) break; + 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 (r) { - continue; - } + if (sectrue == r) continue; // scan for latest instance uint32_t offsetr = offset; @@ -182,7 +182,7 @@ static void compact() uint16_t k2; uint32_t posr; r = read_item(norcow_active_sector, offsetr, &k2, &v2, &l2, &posr); - if (!r) break; + if (sectrue != r) break; if (k == k2) { v = v2; l = l2; @@ -193,7 +193,7 @@ static void compact() // copy the last item uint32_t posw; r = write_item(norcow_next_sector, offsetw, k, v, l, &posw); - if (!r) { } // TODO: error + if (sectrue != r) { } // TODO: error offsetw = posw; } @@ -205,7 +205,7 @@ static void compact() /* * Initializes storage */ -bool norcow_init(void) +secbool norcow_init(void) { // detect active sector (inactive sectors are empty = start with 0xFF) for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { @@ -216,28 +216,28 @@ bool norcow_init(void) } } norcow_active_offset = find_free_offset(norcow_active_sector); - return true; + return sectrue; } /* * Wipe the storage */ -bool norcow_wipe(void) +secbool norcow_wipe(void) { for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { - if (!norcow_erase(i)) { - return false; + if (sectrue != norcow_erase(i)) { + return secfalse; } } norcow_active_sector = 0; norcow_active_offset = 0; - return true; + return sectrue; } /* * Looks for the given key, returns status of the operation */ -bool norcow_get(uint16_t key, const void **val, uint16_t *len) +secbool norcow_get(uint16_t key, const void **val, uint16_t *len) { return find_item(norcow_active_sector, key, val, len); } @@ -245,7 +245,7 @@ bool norcow_get(uint16_t key, const void **val, uint16_t *len) /* * Sets the given key, returns status of the operation */ -bool norcow_set(uint16_t key, const void *val, uint16_t len) +secbool norcow_set(uint16_t key, const void *val, uint16_t len) { // check whether there is enough free space // and compact if full @@ -254,8 +254,8 @@ bool norcow_set(uint16_t key, const void *val, uint16_t len) } // write item uint32_t pos; - bool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos); - if (r) { + secbool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos); + if (sectrue == r) { norcow_active_offset = pos; } return r; diff --git a/embed/extmod/modtrezorconfig/norcow.h b/embed/extmod/modtrezorconfig/norcow.h index 877df8804..4f4bf413a 100644 --- a/embed/extmod/modtrezorconfig/norcow.h +++ b/embed/extmod/modtrezorconfig/norcow.h @@ -3,6 +3,7 @@ #include #include +#include "../../trezorhal/secbool.h" /* * Storage parameters: @@ -14,21 +15,21 @@ /* * Initialize storage */ -bool norcow_init(void); +secbool norcow_init(void); /* * Wipe the storage */ -bool norcow_wipe(void); +secbool norcow_wipe(void); /* * Looks for the given key, returns status of the operation */ -bool norcow_get(uint16_t key, const void **val, uint16_t *len); +secbool norcow_get(uint16_t key, const void **val, uint16_t *len); /* * Sets the given key, returns status of the operation */ -bool norcow_set(uint16_t key, const void *val, uint16_t len); +secbool norcow_set(uint16_t key, const void *val, uint16_t len); #endif diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index e5cb1db0c..33c626645 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -19,20 +19,20 @@ // Norcow storage key of configured PIN. #define PIN_KEY 0x0000 -static bool initialized = false; -static bool unlocked = false; +static secbool initialized = secfalse; +static secbool unlocked = secfalse; -bool storage_init(void) +secbool storage_init(void) { - if (!flash_init()) { - return false; + if (sectrue != flash_init()) { + return secfalse; } - if (!norcow_init()) { - return false; + if (sectrue != norcow_init()) { + return secfalse; } - initialized = true; - unlocked = false; - return true; + initialized = sectrue; + unlocked = secfalse; + return sectrue; } static void pin_fails_reset(uint32_t ofs) @@ -51,26 +51,29 @@ static void pin_fails_reset(uint32_t ofs) } } -static bool pin_fails_increase(uint32_t ofs) +static secbool pin_fails_increase(uint32_t ofs) { uint32_t ctr = ~PIN_MAX_TRIES; - if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &ctr)) { - return false; + if (sectrue != flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &ctr)) { + return secfalse; } ctr = ctr << 1; flash_unlock(); - if (!flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, ctr)) { + if (sectrue != flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, ctr)) { flash_lock(); - return false; + return secfalse; } flash_lock(); uint32_t check = 0; - if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &check)) { - return false; + if (sectrue != flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &check)) { + return secfalse; } - return ctr == check; + if (ctr != check) { + return secfalse; + } + return sectrue; } static void pin_fails_check_max(uint32_t ctr) @@ -85,35 +88,35 @@ static void pin_fails_check_max(uint32_t ctr) } } -static bool pin_fails_read(uint32_t *ofs, uint32_t *ctr) +static secbool pin_fails_read(uint32_t *ofs, uint32_t *ctr) { - if (!ofs || !ctr) { - return false; + if (NULL == ofs || NULL == ctr) { + return secfalse; } for (uint32_t o = 0; o < PIN_SECTOR_SIZE; o += sizeof(uint32_t)) { uint32_t c = 0; if (!flash_read_word_rel(FLASH_SECTOR_PIN_AREA, o, &c)) { - return false; + return secfalse; } if (c != 0) { *ofs = o; *ctr = c; - return true; + return sectrue; } } - return false; + return secfalse; } -static bool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, size_t seclen) +static secbool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, size_t seclen) { size_t diff = seclen ^ publen; for (size_t i = 0; i < publen; i++) { diff |= pub[i] ^ sec[i]; } - return diff == 0; + return sectrue * (diff == 0); } -static bool pin_check(const uint8_t *pin, size_t pinlen) +static secbool pin_check(const uint8_t *pin, size_t pinlen) { const void *spin = NULL; uint16_t spinlen = 0; @@ -121,16 +124,16 @@ static bool pin_check(const uint8_t *pin, size_t pinlen) return const_cmp(pin, pinlen, spin, (size_t)spinlen); } -bool storage_unlock(const uint8_t *pin, size_t len) +secbool storage_unlock(const uint8_t *pin, size_t len) { - if (!initialized) { - return false; + if (sectrue != initialized) { + return secfalse; } uint32_t ofs; uint32_t ctr; - if (!pin_fails_read(&ofs, &ctr)) { - return false; + if (sectrue != pin_fails_read(&ofs, &ctr)) { + return secfalse; } pin_fails_check_max(ctr); @@ -142,75 +145,75 @@ bool storage_unlock(const uint8_t *pin, size_t len) // 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 (!pin_fails_increase(ofs)) { - return false; + if (sectrue != pin_fails_increase(ofs)) { + return secfalse; } - if (!pin_check(pin, len)) { + if (sectrue != pin_check(pin, len)) { pin_fails_check_max(ctr << 1); - return false; + return secfalse; } pin_fails_reset(ofs); - return true; + return sectrue; } -bool storage_get(uint16_t key, const void **val, uint16_t *len) +secbool storage_get(uint16_t key, const void **val, uint16_t *len) { - if (!initialized) { - return false; + if (sectrue != initialized) { + return secfalse; } - if (!unlocked) { + if (sectrue != unlocked) { // shutdown(); - return false; + return secfalse; } if (key == PIN_KEY) { - return false; + return secfalse; } return norcow_get(key, val, len); } -bool storage_set(uint16_t key, const void *val, uint16_t len) +secbool storage_set(uint16_t key, const void *val, uint16_t len) { - if (!initialized) { - return false; + if (sectrue != initialized) { + return secfalse; } - if (!unlocked) { + if (sectrue != unlocked) { // shutdown(); - return false; + return secfalse; } if (key == PIN_KEY) { - return false; + return secfalse; } return norcow_set(key, val, len); } -bool storage_has_pin(void) +secbool storage_has_pin(void) { - if (!initialized) { - return false; + if (sectrue != initialized) { + return secfalse; } const void *spin = NULL; uint16_t spinlen = 0; norcow_get(PIN_KEY, &spin, &spinlen); - return spinlen != 0; + return sectrue * (spinlen != 0); } -bool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen) +secbool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen) { - if (!initialized) { - return false; + if (sectrue != initialized) { + return secfalse; } - if (!unlocked) { + if (sectrue != unlocked) { // shutdown(); - return false; + return secfalse; } - if (!pin_check(pin, len)) { - return false; + if (sectrue != pin_check(pin, len)) { + return secfalse; } - // TODO - return true; + // TODO: change pin in storage + return sectrue; } -bool storage_wipe(void) +secbool storage_wipe(void) { return norcow_wipe(); } diff --git a/embed/extmod/modtrezorconfig/storage.h b/embed/extmod/modtrezorconfig/storage.h index 3ad3e8e56..403447bff 100644 --- a/embed/extmod/modtrezorconfig/storage.h +++ b/embed/extmod/modtrezorconfig/storage.h @@ -7,11 +7,12 @@ #include #include +#include "../../trezorhal/secbool.h" -bool storage_init(void); -bool storage_wipe(void); -bool storage_unlock(const uint8_t *pin, size_t len); -bool storage_has_pin(void); -bool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen); -bool storage_get(uint16_t key, const void **val, uint16_t *len); -bool storage_set(uint16_t key, const void *val, uint16_t len); +secbool storage_init(void); +secbool storage_wipe(void); +secbool storage_unlock(const uint8_t *pin, size_t len); +secbool storage_has_pin(void); +secbool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen); +secbool storage_get(uint16_t key, const void **val, uint16_t *len); +secbool storage_set(uint16_t key, const void *val, uint16_t len); diff --git a/embed/trezorhal/flash.c b/embed/trezorhal/flash.c index 60ac1abee..96439cc69 100644 --- a/embed/trezorhal/flash.c +++ b/embed/trezorhal/flash.c @@ -61,11 +61,11 @@ secbool flash_lock(void) const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) { - if (sector >= SECTOR_COUNT) { + if (sector >= FLASH_SECTOR_COUNT) { return NULL; } - uint32_t addr = SECTOR_TABLE[sector]; - uint32_t next = SECTOR_TABLE[sector + 1]; + uint32_t addr = FLASH_SECTOR_TABLE[sector]; + uint32_t next = FLASH_SECTOR_TABLE[sector + 1]; if (offset + size > next - addr) { return NULL; } @@ -119,7 +119,7 @@ secbool flash_write_word(uint32_t address, uint32_t data) secbool flash_write_byte_rel(uint8_t sector, uint32_t offset, uint8_t data) { - return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, SECTOR_TABLE[sector] + offset, data)); + return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, FLASH_SECTOR_TABLE[sector] + offset, data)); } secbool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) @@ -127,7 +127,7 @@ secbool flash_write_word_rel(uint8_t sector, uint32_t offset, uint32_t data) if (offset % 4 != 0) { return secfalse; } - return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SECTOR_TABLE[sector] + offset, data)); + return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_SECTOR_TABLE[sector] + offset, data)); } secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) @@ -135,7 +135,7 @@ secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) if (offset % 4 != 0) { return secfalse; } - *data = *((uint32_t *) SECTOR_TABLE[sector] + offset); + *data = *((uint32_t *) FLASH_SECTOR_TABLE[sector] + offset); return sectrue; } From 1f6cc70480abc7a79bf601eaa330acc086463962 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Sun, 5 Nov 2017 21:00:38 +0100 Subject: [PATCH 15/27] flash: remove flash_set_option_bytes --- embed/trezorhal/flash.h | 2 -- embed/unix/flash.c | 4 ---- 2 files changed, 6 deletions(-) diff --git a/embed/trezorhal/flash.h b/embed/trezorhal/flash.h index dbd8bb30f..33eba804b 100644 --- a/embed/trezorhal/flash.h +++ b/embed/trezorhal/flash.h @@ -47,8 +47,6 @@ extern const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1]; secbool flash_init(void); -void flash_set_option_bytes(void); - secbool flash_unlock(void); secbool flash_lock(void); diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 44cad6dfb..26a5ea471 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -71,10 +71,6 @@ secbool flash_init(void) return sectrue; } -void flash_set_option_bytes(void) -{ -} - secbool flash_unlock(void) { return sectrue; From 8288255048f4c89fac6c8a28225362a087995c3d Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 24 Oct 2017 13:58:40 +0200 Subject: [PATCH 16/27] storage: use new api, remove pin --- src/apps/common/request_passphrase.py | 2 +- src/apps/common/request_pin.py | 123 +++---------------- src/apps/common/seed.py | 5 - src/apps/common/storage.py | 167 ++++---------------------- src/apps/homescreen/__init__.py | 10 +- src/apps/management/apply_settings.py | 6 +- src/apps/management/load_device.py | 6 +- 7 files changed, 49 insertions(+), 270 deletions(-) diff --git a/src/apps/common/request_passphrase.py b/src/apps/common/request_passphrase.py index 6aacf7abb..888f89d06 100644 --- a/src/apps/common/request_passphrase.py +++ b/src/apps/common/request_passphrase.py @@ -22,7 +22,7 @@ async def request_passphrase(ctx): async def protect_by_passphrase(ctx): from apps.common import storage - if storage.is_protected_by_passphrase(): + if storage.has_passphrase(): return await request_passphrase(ctx) else: return '' diff --git a/src/apps/common/request_pin.py b/src/apps/common/request_pin.py index 801cbb664..52672138b 100644 --- a/src/apps/common/request_pin.py +++ b/src/apps/common/request_pin.py @@ -1,37 +1,24 @@ -from trezor import ui, res -from trezor import wire -from trezor.utils import unimport +from trezor import res +from trezor import ui -if __debug__: - matrix = None -DEFAULT_CANCEL = res.load(ui.ICON_CLEAR) -DEFAULT_LOCK = res.load(ui.ICON_LOCK) +class PinCancelled(Exception): + pass @ui.layout -async def request_pin_on_display(ctx: wire.Context, code: int=None) -> str: - from trezor.messages.ButtonRequest import ButtonRequest - from trezor.messages.ButtonRequestType import ProtectCall - from trezor.messages.FailureType import PinCancelled - from trezor.messages.wire_types import ButtonAck +async def request_pin(code: int = None) -> str: from trezor.ui.confirm import ConfirmDialog, CONFIRMED from trezor.ui.pin import PinMatrix - if __debug__: - global matrix - - _, label = _get_code_and_label(code) - - await ctx.call(ButtonRequest(code=ProtectCall), - ButtonAck) + label = _get_label(code) def onchange(): c = dialog.cancel if matrix.pin: - c.content = DEFAULT_CANCEL + c.content = res.load(ui.ICON_CLEAR) else: - c.content = DEFAULT_LOCK + c.content = res.load(ui.ICON_LOCK) c.taint() c.render() @@ -44,94 +31,18 @@ async def request_pin_on_display(ctx: wire.Context, code: int=None) -> str: matrix.onchange() while True: - res = await dialog - pin = matrix.pin + result = await dialog - if res == CONFIRMED: - matrix = None - return pin - elif res != CONFIRMED and pin: + if result == CONFIRMED: + return matrix.pin + elif result != CONFIRMED and matrix.pin: matrix.change('') continue else: - matrix = None - raise wire.FailureError(PinCancelled, 'PIN cancelled') - - -@ui.layout -@unimport -async def request_pin_on_client(ctx: wire.Context, code: int=None) -> str: - from trezor.messages.FailureType import PinCancelled - from trezor.messages.PinMatrixRequest import PinMatrixRequest - from trezor.messages.wire_types import PinMatrixAck, Cancel - from trezor.ui.pin import PinMatrix - - if __debug__: - global matrix - - code, label = _get_code_and_label(code) - - ui.display.clear() - matrix = PinMatrix(label) - matrix.render() - - ack = await ctx.call(PinMatrixRequest(type=code), - PinMatrixAck, Cancel) - digits = matrix.digits - matrix = None - - if ack.MESSAGE_WIRE_TYPE == Cancel: - raise wire.FailureError(PinCancelled, 'PIN cancelled') - return _decode_pin(ack.pin, digits) - - -request_pin = request_pin_on_client - - -@unimport -async def request_pin_twice(ctx: wire.Context) -> str: - from trezor.messages.FailureType import ActionCancelled - from trezor.messages import PinMatrixRequestType - - pin_first = await request_pin(ctx, PinMatrixRequestType.NewFirst) - pin_again = await request_pin(ctx, PinMatrixRequestType.NewSecond) - if pin_first != pin_again: - # changed message due to consistency with T1 msgs - raise wire.FailureError(ActionCancelled, 'PIN change failed') - - return pin_first - - -async def protect_by_pin_repeatedly(ctx: wire.Context, at_least_once: bool=False): - from . import storage - - locked = storage.is_locked() or at_least_once - while locked: - pin = await request_pin(ctx) - locked = not storage.unlock(pin, _render_pin_failure) - - -async def protect_by_pin_or_fail(ctx: wire.Context, at_least_once: bool=False): - from trezor.messages.FailureType import PinInvalid - from . import storage + raise PinCancelled() - locked = storage.is_locked() or at_least_once - if locked: - pin = await request_pin(ctx) - if not storage.unlock(pin, _render_pin_failure): - raise wire.FailureError(PinInvalid, 'PIN invalid') - -protect_by_pin = protect_by_pin_or_fail - - -def _render_pin_failure(sleep_ms: int): - ui.display.clear() - ui.display.text_center(240, 240, 'Sleeping for %d seconds' % (sleep_ms / 1000), - ui.BOLD, ui.RED, ui.BG) - - -def _get_code_and_label(code: int): +def _get_label(code: int): from trezor.messages import PinMatrixRequestType if code is None: code = PinMatrixRequestType.Current @@ -141,8 +52,4 @@ def _get_code_and_label(code: int): label = 'Enter PIN again' else: # PinMatrixRequestType.Current label = 'Enter PIN' - return code, label - - -def _decode_pin(pin: str, secret: list) -> str: - return ''.join([str(secret[int(d) - 1]) for d in pin]) + return label diff --git a/src/apps/common/seed.py b/src/apps/common/seed.py index af8a15ddd..8acf953e1 100644 --- a/src/apps/common/seed.py +++ b/src/apps/common/seed.py @@ -21,14 +21,11 @@ async def get_seed(ctx: wire.Context) -> bytes: async def compute_seed(ctx: wire.Context) -> bytes: from trezor.messages.FailureType import ProcessError from .request_passphrase import protect_by_passphrase - from .request_pin import protect_by_pin from . import storage if not storage.is_initialized(): raise wire.FailureError(ProcessError, 'Device is not initialized') - await protect_by_pin(ctx) - passphrase = await protect_by_passphrase(ctx) return bip39.seed(storage.get_mnemonic(), passphrase) @@ -37,8 +34,6 @@ def get_root_without_passphrase(curve_name=_DEFAULT_CURVE): from . import storage if not storage.is_initialized(): raise Exception('Device is not initialized') - if storage.is_locked(): - raise Exception('Unlock first') seed = bip39.seed(storage.get_mnemonic(), '') root = bip32.from_seed(seed, curve_name) return root diff --git a/src/apps/common/storage.py b/src/apps/common/storage.py index cc911306e..2e0836b5a 100644 --- a/src/apps/common/storage.py +++ b/src/apps/common/storage.py @@ -1,152 +1,62 @@ from micropython import const -import ustruct -import utime - from trezor import config -from trezor import utils - -_APP = const(1) - -DEVICE_ID = const(0) # str -VERSION = const(1) # varint -MNEMONIC = const(2) # str -LANGUAGE = const(3) # str -LABEL = const(4) # str -PIN = const(5) # bytes -PIN_FAILS = const(6) # varint -PASSPHRASE_PROTECTION = const(7) # varint - - -# pin lock -# === - -_locked = True - - -def is_locked() -> bool: - return is_protected_by_pin() and _locked - - -def unlock(user_pin: str, failure_callback=None) -> bool: - global _locked - - if not is_protected_by_pin(): - return True - # increment the pin fail counter before checking the pin - fails = bytes_to_int(config_get(PIN_FAILS)) + 1 - config_set_checked(PIN_FAILS, int_to_bytes(fails)) +_STORAGE_VERSION = b'\x01' - if const_equal(config_get(PIN), user_pin.encode()): - # unlock and reset the counter - _locked = False - config_set(PIN_FAILS, int_to_bytes(0)) - return True - - else: - # lock, run the callback (ie for ui) and sleep for a quadratic delay - _locked = True - delay_ms = fails * fails * 1000 - try: - if failure_callback: - failure_callback(delay_ms) - finally: - utime.sleep_ms(delay_ms) - return False - - -def lock(): - global _locked - _locked = True - - -def const_equal(a: bytes, b: bytes) -> bool: - return a == b # TODO: proper const equal - - -# settings -# === +_APP = const(0x0001) # app namespace +_DEVICE_ID = const(0x0000) # bytes +_VERSION = const(0x0001) # int +_MNEMONIC = const(0x0002) # str +_LANGUAGE = const(0x0003) # str +_LABEL = const(0x0004) # str +_USE_PASSPHRASE = const(0x0005) # 0x01 or empty def get_device_id() -> str: - dev_id = config_get(DEVICE_ID).decode() + dev_id = config.get(_APP, _DEVICE_ID).decode() if not dev_id: dev_id = new_device_id() - config_set(DEVICE_ID, dev_id.encode()) + config.set(_APP, _DEVICE_ID, dev_id.encode()) return dev_id def is_initialized() -> bool: - return bool(config_get(VERSION)) - - -def is_protected_by_pin() -> bool: - return bool(config_get(PIN)) - - -def is_protected_by_passphrase() -> bool: - return bool(bytes_to_int(config_get(PASSPHRASE_PROTECTION))) - - -def get_pin() -> str: - return config_get(PIN).decode() + return bool(config.get(_APP, _VERSION)) def get_label() -> str: - return config_get(LABEL).decode() - - -def get_language() -> str: - return config_get(LANGUAGE).decode() or _DEFAULT_LANGUAGE + return config.get(_APP, _LABEL).decode() def get_mnemonic() -> str: - utils.ensure(is_initialized()) - utils.ensure(not is_locked()) - - return config_get(MNEMONIC).decode() + return config.get(_APP, _MNEMONIC).decode() -# settings configuration -# === +def has_passphrase() -> bool: + return bool(config.get(_APP, _USE_PASSPHRASE)) def load_mnemonic(mnemonic: str): - utils.ensure(not is_initialized()) + config.set(_APP, _VERSION, _STORAGE_VERSION) + config.set(_APP, _MNEMONIC, mnemonic.encode()) - config_set(VERSION, int_to_bytes(1)) - config_set(MNEMONIC, mnemonic.encode()) - -_ALLOWED_LANGUAGES = ('english') -_DEFAULT_LANGUAGE = 'english' +def load_settings(label: str = None, use_passphrase: bool = None): + if label is not None: + config.set(_APP, _LABEL, label.encode()) + if use_passphrase is True: + config.set(_APP, _USE_PASSPHRASE, b'\x01') + if use_passphrase is False: + config.set(_APP, _USE_PASSPHRASE, b'') -def load_settings(language: str=None, - label: str=None, - pin: str=None, - passphrase_protection: bool=None): - utils.ensure(is_initialized()) - utils.ensure(not is_locked()) - - if language is not None and language in _ALLOWED_LANGUAGES: - if language is _DEFAULT_LANGUAGE: - config_set(LANGUAGE, b'') - else: - config_set(LANGUAGE, language.encode()) - if label is not None: - config_set(LABEL, label.encode()) - if pin is not None: - config_set(PIN, pin.encode()) - if passphrase_protection is not None: - config_set(PASSPHRASE_PROTECTION, - int_to_bytes(passphrase_protection)) +def change_pin(pin: str, newpin: str): + return config.change_pin(pin, newpin) def wipe(): from . import cache - lock() config.wipe() cache.clear() @@ -155,28 +65,3 @@ def new_device_id() -> str: from ubinascii import hexlify from trezor.crypto import random return hexlify(random.bytes(12)).decode('ascii').upper() - - -def config_get(key: int) -> bytes: - return config.get(_APP, key) - - -def config_set(key: int, value: bytes): - config.set(_APP, key, value) - - -def config_set_checked(key, value: bytes): - config_set(key, value) - check = config_get(key) - if check != value: - utils.halt('config.set failed') - - -# TODO: store ints as varints - -def int_to_bytes(i: int) -> bytes: - return ustruct.pack('>L', i) if i else bytes() - - -def bytes_to_int(b: bytes) -> int: - return ustruct.unpack('>L', b)[0] if b else 0 diff --git a/src/apps/homescreen/__init__.py b/src/apps/homescreen/__init__.py index b5afd96a7..6cd620548 100644 --- a/src/apps/homescreen/__init__.py +++ b/src/apps/homescreen/__init__.py @@ -19,10 +19,10 @@ async def respond_Features(ctx, msg): f.device_id = storage.get_device_id() f.label = storage.get_label() - f.language = storage.get_language() f.initialized = storage.is_initialized() - f.pin_protection = storage.is_protected_by_pin() - f.passphrase_protection = storage.is_protected_by_passphrase() + f.passphrase_protection = storage.has_passphrase() + f.pin_protection = False + f.language = 'english' return f @@ -41,10 +41,6 @@ async def respond_Pong(ctx, msg): from trezor import ui await require_confirm(ctx, Text('Confirm', ui.ICON_RESET), ProtectCall) - if msg.pin_protection: - from apps.common.request_pin import protect_by_pin - await protect_by_pin(ctx) - if msg.passphrase_protection: from apps.common.request_passphrase import protect_by_passphrase await protect_by_passphrase(ctx) diff --git a/src/apps/management/apply_settings.py b/src/apps/management/apply_settings.py index 815f10592..8718eeaf0 100644 --- a/src/apps/management/apply_settings.py +++ b/src/apps/management/apply_settings.py @@ -8,11 +8,8 @@ async def layout_apply_settings(ctx, msg): from trezor.messages.FailureType import ProcessError from trezor.ui.text import Text from ..common.confirm import require_confirm - from ..common.request_pin import protect_by_pin from ..common import storage - await protect_by_pin(ctx) - if msg.homescreen is not None: raise wire.FailureError( ProcessError, 'ApplySettings.homescreen is not supported') @@ -42,7 +39,6 @@ async def layout_apply_settings(ctx, msg): 'encryption?')) storage.load_settings(label=msg.label, - language=msg.language, - passphrase_protection=msg.use_passphrase) + use_passphrase=msg.use_passphrase) return Success(message='Settings applied') diff --git a/src/apps/management/load_device.py b/src/apps/management/load_device.py index 89c850cca..e851d7be4 100644 --- a/src/apps/management/load_device.py +++ b/src/apps/management/load_device.py @@ -26,9 +26,9 @@ async def layout_load_device(ctx, msg): ui.NORMAL, 'Continue only if you', 'know what you are doing!')) storage.load_mnemonic(msg.mnemonic) - storage.load_settings(pin=msg.pin, - passphrase_protection=msg.passphrase_protection, - language=msg.language, + storage.load_settings(use_passphrase=msg.passphrase_protection, label=msg.label) + if msg.pin: + storage.change_pin('', msg.pin) return Success(message='Device loaded') From a255938903ce633e4401b0c249efefd7620a50e2 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 24 Oct 2017 13:59:09 +0200 Subject: [PATCH 17/27] main: unlock storage after boot --- src/boot.py | 27 +++++++++++++++++++++++++++ src/main.py | 6 ++---- src/trezor/loop.py | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/boot.py diff --git a/src/boot.py b/src/boot.py new file mode 100644 index 000000000..d3afefc17 --- /dev/null +++ b/src/boot.py @@ -0,0 +1,27 @@ +from trezor import config +from trezor import loop +from trezor import ui + +from apps.common.request_pin import request_pin + + +async def unlock_layout(): + while True: + if config.has_pin(): + pin = await request_pin() + else: + pin = '' + if config.unlock(pin): + return + else: + await unlock_failed() + + +async def unlock_failed(): + pass + + +config.init() +ui.display.backlight(ui.BACKLIGHT_DIM) +loop.schedule(unlock_layout()) +loop.run() diff --git a/src/main.py b/src/main.py index c9dcc8549..b3ebac8aa 100644 --- a/src/main.py +++ b/src/main.py @@ -1,12 +1,11 @@ -from trezor import config +import boot + from trezor import io from trezor import log from trezor import loop from trezor import wire from trezor import workflow -config.init() - log.level = log.DEBUG # initialize the USB stack @@ -102,7 +101,6 @@ usb.add(usb_vcp) usb.add(usb_u2f) # load applications -from apps.common import storage if __debug__: from apps import debug from apps import homescreen diff --git a/src/trezor/loop.py b/src/trezor/loop.py index 714e018aa..83e339364 100644 --- a/src/trezor/loop.py +++ b/src/trezor/loop.py @@ -86,7 +86,7 @@ def run(): task_entry = [0, 0, 0] # deadline, task, value msg_entry = [0, 0] # iface | flags, value - while True: + while _queue or _paused: # compute the maximum amount of time we can wait for a message if _queue: delay = utime.ticks_diff(_queue.peektime(), utime.ticks_us()) From 30363305f41a48e00ef0b0ff3e57d11331c70538 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sun, 5 Nov 2017 23:32:32 +0100 Subject: [PATCH 18/27] modtrezorconfig: use secbool, not bool --- embed/extmod/modtrezorconfig/modtrezorconfig.c | 18 ++++++------------ embed/extmod/modtrezorconfig/norcow.h | 1 - 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index 4228fb2f8..9f2567252 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -20,8 +20,7 @@ /// called from this module! /// ''' STATIC mp_obj_t mod_trezorconfig_init(void) { - bool r = storage_init(); - if (!r) { + if (sectrue != storage_init()) { mp_raise_msg(&mp_type_RuntimeError, "Could not initialize config module"); } return mp_const_none; @@ -36,8 +35,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_init_obj, mod_trezorconfig_ini STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin) { mp_buffer_info_t buf; mp_get_buffer_raise(pin, &buf, MP_BUFFER_READ); - bool r = storage_unlock(buf.buf, buf.len); - if (!r) { + if (sectrue != storage_unlock(buf.buf, buf.len)) { return mp_const_false; } return mp_const_true; @@ -66,8 +64,7 @@ STATIC mp_obj_t mod_trezorconfig_change_pin(mp_obj_t pin, mp_obj_t newpin) { mp_get_buffer_raise(pin, &pinbuf, MP_BUFFER_READ); mp_buffer_info_t newbuf; mp_get_buffer_raise(newpin, &newbuf, MP_BUFFER_READ); - bool r = storage_change_pin(pinbuf.buf, pinbuf.len, newbuf.buf, newbuf.len); - if (!r) { + if (sectrue != storage_change_pin(pinbuf.buf, pinbuf.len, newbuf.buf, newbuf.len)) { return mp_const_false; } return mp_const_true; @@ -84,8 +81,7 @@ STATIC mp_obj_t mod_trezorconfig_get(mp_obj_t app, mp_obj_t key) { uint16_t appkey = a << 8 | k; uint16_t len = 0; const void *val; - bool r = storage_get(appkey, &val, &len); - if (!r || len == 0) { + if (sectrue != storage_get(appkey, &val, &len) || len == 0) { return mp_const_empty_bytes; } return mp_obj_new_str_of_type(&mp_type_bytes, val, len); @@ -102,8 +98,7 @@ STATIC mp_obj_t mod_trezorconfig_set(mp_obj_t app, mp_obj_t key, mp_obj_t value) uint16_t appkey = a << 8 | k; mp_buffer_info_t v; mp_get_buffer_raise(value, &v, MP_BUFFER_READ); - bool r = storage_set(appkey, v.buf, v.len); - if (!r) { + if (sectrue != storage_set(appkey, v.buf, v.len)) { mp_raise_msg(&mp_type_RuntimeError, "Could not save value"); } return mp_const_none; @@ -115,8 +110,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorconfig_set_obj, mod_trezorconfig_set) /// Erases the whole config. Use with caution! /// ''' STATIC mp_obj_t mod_trezorconfig_wipe(void) { - bool r = storage_wipe(); - if (!r) { + if (sectrue != storage_wipe()) { mp_raise_msg(&mp_type_RuntimeError, "Could not wipe storage"); } return mp_const_none; diff --git a/embed/extmod/modtrezorconfig/norcow.h b/embed/extmod/modtrezorconfig/norcow.h index 4f4bf413a..a96816001 100644 --- a/embed/extmod/modtrezorconfig/norcow.h +++ b/embed/extmod/modtrezorconfig/norcow.h @@ -2,7 +2,6 @@ #define __NORCOW_H__ #include -#include #include "../../trezorhal/secbool.h" /* From ee7d260d2fb1f2629dd44eade6ac9555ef177a26 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sun, 5 Nov 2017 23:51:10 +0100 Subject: [PATCH 19/27] trezorhal: use const where possible for flash --- embed/trezorhal/flash.c | 8 ++++---- embed/trezorhal/flash.h | 2 -- embed/trezorhal/image.c | 10 ++++++++-- embed/unix/flash.c | 12 ++++++------ 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/embed/trezorhal/flash.c b/embed/trezorhal/flash.c index 96439cc69..d13ea3ae3 100644 --- a/embed/trezorhal/flash.c +++ b/embed/trezorhal/flash.c @@ -13,7 +13,7 @@ // see docs/memory.md for more information -const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { +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 @@ -66,7 +66,7 @@ const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) } uint32_t addr = FLASH_SECTOR_TABLE[sector]; uint32_t next = FLASH_SECTOR_TABLE[sector + 1]; - if (offset + size > next - addr) { + if (addr + offset + size > next) { return NULL; } return (const uint8_t *)addr + offset; @@ -92,7 +92,7 @@ secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(in return secfalse; } // check whether the sector was really deleted (contains only 0xFF) - uint32_t addr_start = FLASH_SECTOR_TABLE[sectors[i]], addr_end = FLASH_SECTOR_TABLE[sectors[i] + 1]; + const uint32_t addr_start = FLASH_SECTOR_TABLE[sectors[i]], addr_end = FLASH_SECTOR_TABLE[sectors[i] + 1]; for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { if (*((const uint32_t *)addr) != 0xFFFFFFFF) { flash_lock(); @@ -135,7 +135,7 @@ secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) if (offset % 4 != 0) { return secfalse; } - *data = *((uint32_t *) FLASH_SECTOR_TABLE[sector] + offset); + *data = *((const uint32_t *)FLASH_SECTOR_TABLE[sector] + offset); return sectrue; } diff --git a/embed/trezorhal/flash.h b/embed/trezorhal/flash.h index 33eba804b..e4b1b766c 100644 --- a/embed/trezorhal/flash.h +++ b/embed/trezorhal/flash.h @@ -43,8 +43,6 @@ // note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427) (reference RM0090 section 3.7.5) #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) -extern const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1]; - secbool flash_init(void); secbool flash_unlock(void); diff --git a/embed/trezorhal/image.c b/embed/trezorhal/image.c index c20e0ebaa..9945b1950 100644 --- a/embed/trezorhal/image.c +++ b/embed/trezorhal/image.c @@ -144,7 +144,10 @@ secbool check_image_contents(const image_header * const hdr, uint32_t firstskip, if (0 == sectors || blocks < 1) { return secfalse; } - const void *data = (const void *)(FLASH_SECTOR_TABLE[sectors[0]] + firstskip); + const void *data = flash_get_address(sectors[0], firstskip, IMAGE_CHUNK_SIZE - firstskip); + if (!data) { + return secfalse; + } int remaining = hdr->codelen; if (sectrue != check_single_hash(hdr->hashes, data, MIN(remaining, IMAGE_CHUNK_SIZE - firstskip))) { return secfalse; @@ -155,7 +158,10 @@ secbool check_image_contents(const image_header * const hdr, uint32_t firstskip, if (block >= blocks) { return secfalse; } - data = (const void *)FLASH_SECTOR_TABLE[sectors[block]]; + data = flash_get_address(sectors[block], 0, IMAGE_CHUNK_SIZE); + if (!data) { + return secfalse; + } if (sectrue != check_single_hash(hdr->hashes + block * 32, data, MIN(remaining, IMAGE_CHUNK_SIZE))) { return secfalse; } diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 26a5ea471..11595fb1f 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -86,11 +86,11 @@ const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) if (sector >= SECTOR_COUNT) { return NULL; } - uint32_t sector_size = sector_table[sector + 1] - sector_table[sector]; + const uint32_t sector_size = sector_table[sector + 1] - sector_table[sector]; if (offset + size > sector_size) { return NULL; } - uint32_t sector_offset = sector_table[sector] - sector_table[0]; + const uint32_t sector_offset = sector_table[sector] - sector_table[0]; return flash_buffer + sector_offset + offset; } @@ -100,9 +100,9 @@ secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(in progress(0, len); } for (int i = 0; i < len; i++) { - uint8_t sector = sectors[i]; - uint32_t offset = sector_table[sector] - sector_table[0]; - uint32_t size = sector_table[sector + 1] - sector_table[sector]; + const uint8_t sector = sectors[i]; + const uint32_t offset = sector_table[sector] - sector_table[0]; + const uint32_t size = sector_table[sector + 1] - sector_table[sector]; memset(flash_buffer + offset, 0xFF, size); if (progress) { progress(i + 1, len); @@ -148,7 +148,7 @@ secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data) if (offset % 4) { // we read only at 4-byte boundary return secfalse; } - uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); + const uint32_t *flash = (const uint32_t *)flash_get_address(sector, offset, sizeof(data)); if (!flash) { return secfalse; } From 2e3b8336c2b51d1e9d9d01669eeeb2aa9f50b9a1 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 6 Nov 2017 11:12:43 +0100 Subject: [PATCH 20/27] unix: fix unused-result warning --- embed/unix/flash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 11595fb1f..578e49040 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -59,7 +59,8 @@ static void flash_read(void) { FILE *f = fopen(FLASH_FILE, "rb"); if (f) { - fread(flash_buffer, sizeof(flash_buffer), 1, f); + size_t r = fread(flash_buffer, sizeof(flash_buffer), 1, f); + (void)r; fclose(f); } } From 5444410e04ad9b6c59c234e81b6ec6b9e7caf08e Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 6 Nov 2017 15:44:35 +0100 Subject: [PATCH 21/27] unix: use return value from fread properly --- embed/unix/flash.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/embed/unix/flash.c b/embed/unix/flash.c index 578e49040..1a9a74917 100644 --- a/embed/unix/flash.c +++ b/embed/unix/flash.c @@ -55,20 +55,17 @@ static void flash_sync(void) } } -static void flash_read(void) +secbool flash_init(void) { FILE *f = fopen(FLASH_FILE, "rb"); + size_t r = 0; if (f) { - size_t r = fread(flash_buffer, sizeof(flash_buffer), 1, f); - (void)r; + r = fread(flash_buffer, sizeof(flash_buffer), 1, f); fclose(f); } -} - -secbool flash_init(void) -{ - memset(flash_buffer, 0xFF, sizeof(flash_buffer)); - flash_read(); + if (r != 1) { + memset(flash_buffer, 0xFF, sizeof(flash_buffer)); + } return sectrue; } From df77e749888c89109ef43e6832c7a4f136e97215 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Mon, 6 Nov 2017 17:26:13 +0100 Subject: [PATCH 22/27] storage: fix unlock --- embed/extmod/modtrezorconfig/storage.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index 33c626645..ae6901f2f 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -130,6 +130,8 @@ secbool storage_unlock(const uint8_t *pin, size_t len) return secfalse; } + unlocked = secfalse; + uint32_t ofs; uint32_t ctr; if (sectrue != pin_fails_read(&ofs, &ctr)) { @@ -153,7 +155,10 @@ secbool storage_unlock(const uint8_t *pin, size_t len) return secfalse; } pin_fails_reset(ofs); - return sectrue; + + unlocked = sectrue; + + return unlocked; } secbool storage_get(uint16_t key, const void **val, uint16_t *len) From 5e643e91952461bef979a7d36bc70218cebc774b Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Mon, 6 Nov 2017 17:26:25 +0100 Subject: [PATCH 23/27] modtrezorconfig: fix has_pin --- embed/extmod/modtrezorconfig/modtrezorconfig.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index 9f2567252..5103ff99d 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -47,11 +47,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_unlock_obj, mod_trezorconfig_u /// Returns True if storage has a configured PIN, False otherwise. /// ''' STATIC mp_obj_t mod_trezorconfig_has_pin(void) { - if (storage_has_pin()) { - return mp_const_true; - } else { + if (sectrue != storage_has_pin()) { return mp_const_false; } + return mp_const_true; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_has_pin_obj, mod_trezorconfig_has_pin); From 237740de9cc81023d3b05d5223f82241648ae2ac Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Mon, 6 Nov 2017 17:47:05 +0100 Subject: [PATCH 24/27] storage: implement change_pin --- embed/extmod/modtrezorconfig/storage.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index ae6901f2f..9e4d95098 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -211,10 +211,12 @@ secbool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin // shutdown(); return secfalse; } + // TODO: check for max length + // TODO: check with fail handling if (sectrue != pin_check(pin, len)) { return secfalse; } - // TODO: change pin in storage + norcow_set(PIN_KEY, (const void *)newpin, (uint16_t)newlen); return sectrue; } From cf14717d435b38c0b8270a89de404b4447297f64 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 8 Nov 2017 18:07:33 +0100 Subject: [PATCH 25/27] unix: add shutdown() As sys/socket.h defines shutdown also, we can't include common.h in unix mocks. --- embed/extmod/modtrezorio/unix-msg-mock.h | 5 ++++- embed/unix/common.c | 5 +++++ embed/unix/common.h | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/embed/extmod/modtrezorio/unix-msg-mock.h b/embed/extmod/modtrezorio/unix-msg-mock.h index 31adc5b66..5bf7a38fe 100644 --- a/embed/extmod/modtrezorio/unix-msg-mock.h +++ b/embed/extmod/modtrezorio/unix-msg-mock.h @@ -13,10 +13,13 @@ #include #include -#include "../../unix/common.h" #include "../../trezorhal/usb.h" #include "../../trezorhal/touch.h" +void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, const char *file, int line, const char *func); + +#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__)) + #define TREZOR_UDP_IFACE 0 #define TREZOR_UDP_PORT 21324 diff --git a/embed/unix/common.c b/embed/unix/common.c index d40858102..1d848b2da 100644 --- a/embed/unix/common.c +++ b/embed/unix/common.c @@ -31,3 +31,8 @@ void hal_delay(uint32_t ms) { usleep(1000 * ms); } + +void shutdown(void) +{ + exit(1); +} diff --git a/embed/unix/common.h b/embed/unix/common.h index fde45ec82..d1daf8a1d 100644 --- a/embed/unix/common.h +++ b/embed/unix/common.h @@ -9,4 +9,6 @@ void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, void hal_delay(uint32_t ms); +void shutdown(void); + #endif From 5bd39c70b034847a2ac4334403cf92f273c68c3b Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 8 Nov 2017 18:08:19 +0100 Subject: [PATCH 26/27] storage: fix change_pin and small details --- embed/extmod/modtrezorconfig/storage.c | 71 +++++++++++--------------- vendor/norcow | 1 + 2 files changed, 31 insertions(+), 41 deletions(-) create mode 160000 vendor/norcow diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index 9e4d95098..39b954899 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -7,6 +7,7 @@ #include +#include "common.h" #include "norcow.h" #include "../../trezorhal/flash.h" @@ -19,11 +20,16 @@ // Norcow storage key of configured PIN. #define PIN_KEY 0x0000 +// Maximum PIN length. +#define PIN_MAXLEN 32 + static secbool initialized = secfalse; static secbool unlocked = secfalse; secbool storage_init(void) { + initialized = secfalse; + unlocked = secfalse; if (sectrue != flash_init()) { return secfalse; } @@ -31,7 +37,6 @@ secbool storage_init(void) return secfalse; } initialized = sectrue; - unlocked = secfalse; return sectrue; } @@ -84,7 +89,7 @@ static void pin_fails_check_max(uint32_t ctr) break; } } - // shutdown(); + shutdown(); } } @@ -113,25 +118,23 @@ static secbool const_cmp(const uint8_t *pub, size_t publen, const uint8_t *sec, for (size_t i = 0; i < publen; i++) { diff |= pub[i] ^ sec[i]; } - return sectrue * (diff == 0); + return sectrue * (0 == diff); } -static secbool pin_check(const uint8_t *pin, size_t pinlen) +static secbool pin_cmp(const uint8_t *pin, size_t pinlen) { const void *spin = NULL; uint16_t spinlen = 0; norcow_get(PIN_KEY, &spin, &spinlen); - return const_cmp(pin, pinlen, spin, (size_t)spinlen); + if (NULL != spin) { + return const_cmp(pin, pinlen, spin, spinlen); + } else { + return sectrue * (0 == pinlen); + } } -secbool storage_unlock(const uint8_t *pin, size_t len) +static secbool pin_check(const uint8_t *pin, size_t len) { - if (sectrue != initialized) { - return secfalse; - } - - unlocked = secfalse; - uint32_t ofs; uint32_t ctr; if (sectrue != pin_fails_read(&ofs, &ctr)) { @@ -141,7 +144,7 @@ secbool storage_unlock(const uint8_t *pin, size_t len) // Sleep for ~ctr seconds before checking the PIN. for (uint32_t wait = ~ctr; wait > 0; wait--) { - // hal_delay(1000); + hal_delay(1000); } // First, we increase PIN fail counter in storage, even before checking the @@ -150,27 +153,27 @@ secbool storage_unlock(const uint8_t *pin, size_t len) if (sectrue != pin_fails_increase(ofs)) { return secfalse; } - if (sectrue != pin_check(pin, len)) { + if (sectrue != pin_cmp(pin, len)) { pin_fails_check_max(ctr << 1); return secfalse; } pin_fails_reset(ofs); - unlocked = sectrue; + return sectrue; +} +secbool storage_unlock(const uint8_t *pin, size_t len) +{ + unlocked = secfalse; + if (sectrue == initialized && sectrue == pin_check(pin, len)) { + unlocked = sectrue; + } return unlocked; } secbool storage_get(uint16_t key, const void **val, uint16_t *len) { - if (sectrue != initialized) { - return secfalse; - } - if (sectrue != unlocked) { - // shutdown(); - return secfalse; - } - if (key == PIN_KEY) { + if (sectrue != initialized || sectrue != unlocked || PIN_KEY == key) { return secfalse; } return norcow_get(key, val, len); @@ -178,14 +181,7 @@ secbool storage_get(uint16_t key, const void **val, uint16_t *len) secbool storage_set(uint16_t key, const void *val, uint16_t len) { - if (sectrue != initialized) { - return secfalse; - } - if (sectrue != unlocked) { - // shutdown(); - return secfalse; - } - if (key == PIN_KEY) { + if (sectrue != initialized || sectrue != unlocked || PIN_KEY == key) { return secfalse; } return norcow_set(key, val, len); @@ -199,25 +195,18 @@ secbool storage_has_pin(void) const void *spin = NULL; uint16_t spinlen = 0; norcow_get(PIN_KEY, &spin, &spinlen); - return sectrue * (spinlen != 0); + return sectrue * (0 != spinlen); } secbool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen) { - if (sectrue != initialized) { - return secfalse; - } - if (sectrue != unlocked) { - // shutdown(); + if (sectrue != initialized || sectrue != unlocked || newlen > PIN_MAXLEN) { return secfalse; } - // TODO: check for max length - // TODO: check with fail handling if (sectrue != pin_check(pin, len)) { return secfalse; } - norcow_set(PIN_KEY, (const void *)newpin, (uint16_t)newlen); - return sectrue; + return norcow_set(PIN_KEY, newpin, newlen); } secbool storage_wipe(void) diff --git a/vendor/norcow b/vendor/norcow new file mode 160000 index 000000000..56f11a3d6 --- /dev/null +++ b/vendor/norcow @@ -0,0 +1 @@ +Subproject commit 56f11a3d6c8c77d4ecb82e1a55d3003263ef2a72 From 40a9ac2e29e8bc0eb291849b54893a7e65173da9 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Fri, 10 Nov 2017 15:18:43 +0100 Subject: [PATCH 27/27] tests: fix trezor.config test --- tests/test_trezor.config.py | 63 ++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/tests/test_trezor.config.py b/tests/test_trezor.config.py index 57639e634..b47523d4d 100644 --- a/tests/test_trezor.config.py +++ b/tests/test_trezor.config.py @@ -4,6 +4,18 @@ from trezor.crypto import random from trezor import config +PINAPP = 0x00 +PINKEY = 0x00 + + +def random_entry(): + while True: + appid, key = random.uniform(256), random.uniform(256) + if appid != PINAPP or key != PINKEY: + break + return appid, key + + class TestConfig(unittest.TestCase): def test_init(self): @@ -14,23 +26,59 @@ class TestConfig(unittest.TestCase): def test_wipe(self): config.init() config.wipe() - config.set(0, 0, b'hello') + self.assertEqual(config.unlock(''), True) + config.set(0, 1, b'hello') config.set(1, 1, b'world') - v0 = config.get(0, 0) + v0 = config.get(0, 1) v1 = config.get(1, 1) self.assertEqual(v0, b'hello') self.assertEqual(v1, b'world') config.wipe() - v0 = config.get(0, 0) + v0 = config.get(0, 1) v1 = config.get(1, 1) self.assertEqual(v0, bytes()) self.assertEqual(v1, bytes()) + def test_lock(self): + for _ in range(128): + config.init() + config.wipe() + self.assertEqual(config.unlock(''), True) + appid, key = random_entry() + value = random.bytes(16) + config.set(appid, key, value) + config.init() + self.assertEqual(config.get(appid, key), bytes()) + with self.assertRaises(RuntimeError): + config.set(appid, key, bytes()) + config.init() + config.wipe() + self.assertEqual(config.change_pin('', 'xxx'), False) + + def test_change_pin(self): + config.init() + config.wipe() + self.assertEqual(config.unlock(''), True) + with self.assertRaises(RuntimeError): + config.set(PINAPP, PINKEY, 'xxx') + self.assertEqual(config.change_pin('xxx', 'yyy'), False) + self.assertEqual(config.change_pin('', 'xxx'), True) + self.assertEqual(config.get(PINAPP, PINKEY), bytes()) + config.set(1, 1, b'value') + config.init() + self.assertEqual(config.unlock('xxx'), True) + config.change_pin('xxx', '') + config.init() + self.assertEqual(config.unlock('xxx'), False) + self.assertEqual(config.unlock(''), True) + self.assertEqual(config.get(1, 1), b'value') + def test_set_get(self): config.init() config.wipe() - for _ in range(64): - appid, key = random.uniform(256), random.uniform(256) + self.assertEqual(config.unlock(''), True) + for _ in range(32): + appid, key = random_entry() value = random.bytes(128) config.set(appid, key, value) value2 = config.get(appid, key) @@ -39,8 +87,9 @@ class TestConfig(unittest.TestCase): def test_get_default(self): config.init() config.wipe() - for _ in range(64): - appid, key = random.uniform(256), random.uniform(256) + self.assertEqual(config.unlock(''), True) + for _ in range(128): + appid, key = random_entry() value = config.get(appid, key) self.assertEqual(value, bytes())