Merge branch 'pin_fails'

This commit is contained in:
Pavol Rusnak 2017-12-07 15:29:47 +01:00
commit f88080b904
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
27 changed files with 978 additions and 345 deletions

View File

@ -11,6 +11,7 @@ SOURCE_MOD = []
SOURCE_MOD += [
'embed/extmod/modtrezorconfig/modtrezorconfig.c',
'embed/extmod/modtrezorconfig/norcow.c',
'embed/extmod/modtrezorconfig/storage.c',
]
# modtrezorcrypto

View File

@ -12,6 +12,7 @@ LIBS_MOD = []
SOURCE_MOD += [
'embed/extmod/modtrezorconfig/modtrezorconfig.c',
'embed/extmod/modtrezorconfig/norcow.c',
'embed/extmod/modtrezorconfig/storage.c',
]
# modtrezorcrypto
@ -215,6 +216,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']

View File

@ -1,54 +1,89 @@
/*
* 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"
#include "py/objstr.h"
#if MICROPY_PY_TREZORCONFIG
#include <stdint.h>
#include <string.h>
#include "norcow.h"
static bool initialized = 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();
if (!r) {
if (sectrue != storage_init()) {
mp_raise_msg(&mp_type_RuntimeError, "Could not initialize config module");
}
initialized = true;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_init_obj, mod_trezorconfig_init);
/// def unlock(pin: str) -> bool:
/// '''
/// 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) {
mp_buffer_info_t buf;
mp_get_buffer_raise(pin, &buf, MP_BUFFER_READ);
if (sectrue != storage_unlock(buf.buf, buf.len)) {
return mp_const_false;
}
return mp_const_true;
}
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 (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);
/// 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);
if (sectrue != storage_change_pin(pinbuf.buf, pinbuf.len, newbuf.buf, newbuf.len)) {
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).
/// '''
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");
}
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);
if (!r || len == 0) {
if (sectrue != storage_get(appkey, &val, &len) || 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);
@ -57,16 +92,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");
}
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);
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;
@ -78,11 +109,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();
if (!r) {
if (sectrue != storage_wipe()) {
mp_raise_msg(&mp_type_RuntimeError, "Could not wipe storage");
}
return mp_const_none;
@ -92,6 +119,9 @@ 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_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) },

View File

@ -1 +0,0 @@
../../../vendor/norcow/norcow.c

View File

@ -0,0 +1,262 @@
#include <string.h>
#include "norcow.h"
#include "../../trezorhal/flash.h"
#ifndef NORCOW_SECTORS
#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;
/*
* Erases sector
*/
static secbool norcow_erase(uint8_t sector)
{
if (sector >= NORCOW_SECTOR_COUNT) {
return secfalse;
}
return flash_erase_sectors(&norcow_sectors[sector], 1, NULL);
}
/*
* 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;
}
return flash_get_address(norcow_sectors[sector], offset, size);
}
/*
* Writes data to given sector, starting from offset
*/
static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len)
{
if (sector >= NORCOW_SECTOR_COUNT) {
return secfalse;
}
if (sectrue != flash_unlock()) {
return secfalse;
}
// write prefix
if (sectrue != flash_write_word_rel(norcow_sectors[sector], offset, prefix)) {
flash_lock();
return secfalse;
}
offset += sizeof(uint32_t);
// write data
for (uint16_t i = 0; i < len; i++, offset++) {
if (sectrue != flash_write_byte_rel(norcow_sectors[sector], offset, data[i])) {
flash_lock();
return secfalse;
}
}
// pad with zeroes
for (; offset % 4; offset++) {
if (sectrue != flash_write_byte_rel(norcow_sectors[sector], offset, 0x00)) {
flash_lock();
return secfalse;
}
}
flash_lock();
return sectrue;
}
#define ALIGN4(X) (X) = ((X) + 3) & ~3
/*
* Reads one item starting from offset
*/
static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, const void **val, uint16_t *len, uint32_t *pos)
{
*pos = offset;
const void *k = norcow_ptr(sector, *pos, 2);
if (k == NULL) return secfalse;
*pos += 2;
memcpy(key, k, sizeof(uint16_t));
if (*key == 0xFFFF) {
return secfalse;
}
const void *l = norcow_ptr(sector, *pos, 2);
if (l == NULL) return secfalse;
*pos += 2;
memcpy(len, l, sizeof(uint16_t));
*val = norcow_ptr(sector, *pos, *len);
if (*val == NULL) return secfalse;
*pos += *len;
ALIGN4(*pos);
return sectrue;
}
/*
* Writes one item starting from offset
*/
static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void *val, uint16_t len, uint32_t *pos)
{
uint32_t prefix = (len << 16) | key;
*pos = offset + sizeof(uint32_t) + len;
ALIGN4(*pos);
return norcow_write(sector, offset, prefix, val, len);
}
/*
* Finds item in given sector
*/
static secbool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len)
{
*val = 0;
*len = 0;
uint32_t offset = 0;
for (;;) {
uint16_t k, l;
const void *v;
uint32_t pos;
if (sectrue != read_item(sector, offset, &k, &v, &l, &pos)) {
break;
}
if (key == k) {
*val = v;
*len = l;
}
offset = pos;
}
return sectrue * (*val != NULL);
}
/*
* Finds first unused offset in given sector
*/
static uint32_t find_free_offset(uint8_t sector)
{
uint32_t offset = 0;
for (;;) {
uint16_t key, len;
const void *val;
uint32_t pos;
if (sectrue != read_item(sector, offset, &key, &val, &len, &pos)) {
break;
}
offset = pos;
}
return offset;
}
/*
* Compacts active sector and sets new active sector
*/
static void compact()
{
uint8_t norcow_next_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT;
uint32_t offset = 0, offsetw = 0;
for (;;) {
// read item
uint16_t k, l;
const void *v;
uint32_t pos;
secbool r = read_item(norcow_active_sector, offset, &k, &v, &l, &pos);
if (sectrue != r) break;
offset = pos;
// check if not already saved
const void *v2;
uint16_t l2;
r = find_item(norcow_next_sector, k, &v2, &l2);
if (sectrue == r) continue;
// scan for latest instance
uint32_t offsetr = offset;
for (;;) {
uint16_t k2;
uint32_t posr;
r = read_item(norcow_active_sector, offsetr, &k2, &v2, &l2, &posr);
if (sectrue != r) break;
if (k == k2) {
v = v2;
l = l2;
}
offsetr = posr;
}
// copy the last item
uint32_t posw;
r = write_item(norcow_next_sector, offsetw, k, v, l, &posw);
if (sectrue != 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
*/
secbool norcow_init(void)
{
// 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 sectrue;
}
/*
* Wipe the storage
*/
secbool norcow_wipe(void)
{
for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) {
if (sectrue != norcow_erase(i)) {
return secfalse;
}
}
norcow_active_sector = 0;
norcow_active_offset = 0;
return sectrue;
}
/*
* Looks for the given key, returns status of the operation
*/
secbool norcow_get(uint16_t key, const void **val, uint16_t *len)
{
return find_item(norcow_active_sector, key, val, len);
}
/*
* Sets the given key, returns status of the operation
*/
secbool norcow_set(uint16_t key, const void *val, uint16_t len)
{
// check whether there is enough free space
// and compact if full
if (norcow_active_offset + sizeof(uint32_t) + len > NORCOW_SECTOR_SIZE) {
compact();
}
// write item
uint32_t pos;
secbool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos);
if (sectrue == r) {
norcow_active_offset = pos;
}
return r;
}

View File

@ -1 +0,0 @@
../../../vendor/norcow/norcow.h

View File

@ -0,0 +1,34 @@
#ifndef __NORCOW_H__
#define __NORCOW_H__
#include <stdint.h>
#include "../../trezorhal/secbool.h"
/*
* Storage parameters:
*/
#define NORCOW_SECTOR_COUNT 2
#define NORCOW_SECTOR_SIZE (64*1024)
/*
* Initialize storage
*/
secbool norcow_init(void);
/*
* Wipe the storage
*/
secbool norcow_wipe(void);
/*
* Looks for the given key, returns status of the operation
*/
secbool norcow_get(uint16_t key, const void **val, uint16_t *len);
/*
* Sets the given key, returns status of the operation
*/
secbool norcow_set(uint16_t key, const void *val, uint16_t len);
#endif

View File

@ -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

View File

@ -0,0 +1,215 @@
/*
* Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs
*
* Licensed under TREZOR License
* see LICENSE file for details
*/
#include <string.h>
#include "common.h"
#include "norcow.h"
#include "../../trezorhal/flash.h"
// Byte-length of flash sector containing fail counters.
#define PIN_SECTOR_SIZE 0x4000
// Maximum number of failed unlock attempts.
#define PIN_MAX_TRIES 15
// 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;
}
if (sectrue != norcow_init()) {
return secfalse;
}
initialized = sectrue;
return sectrue;
}
static void pin_fails_reset(uint32_t ofs)
{
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.
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 secbool pin_fails_increase(uint32_t ofs)
{
uint32_t ctr = ~PIN_MAX_TRIES;
if (sectrue != flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &ctr)) {
return secfalse;
}
ctr = ctr << 1;
flash_unlock();
if (sectrue != flash_write_word_rel(FLASH_SECTOR_PIN_AREA, ofs, ctr)) {
flash_lock();
return secfalse;
}
flash_lock();
uint32_t check = 0;
if (sectrue != flash_read_word_rel(FLASH_SECTOR_PIN_AREA, ofs, &check)) {
return secfalse;
}
if (ctr != check) {
return secfalse;
}
return sectrue;
}
static void pin_fails_check_max(uint32_t ctr)
{
if (~ctr >= 1 << PIN_MAX_TRIES) {
for (;;) {
if (norcow_wipe()) {
break;
}
}
shutdown();
}
}
static secbool pin_fails_read(uint32_t *ofs, uint32_t *ctr)
{
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 secfalse;
}
if (c != 0) {
*ofs = o;
*ctr = c;
return sectrue;
}
}
return secfalse;
}
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 sectrue * (0 == diff);
}
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);
if (NULL != spin) {
return const_cmp(pin, pinlen, spin, spinlen);
} else {
return sectrue * (0 == pinlen);
}
}
static secbool pin_check(const uint8_t *pin, size_t len)
{
uint32_t ofs;
uint32_t ctr;
if (sectrue != pin_fails_read(&ofs, &ctr)) {
return secfalse;
}
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 (sectrue != pin_fails_increase(ofs)) {
return secfalse;
}
if (sectrue != pin_cmp(pin, len)) {
pin_fails_check_max(ctr << 1);
return secfalse;
}
pin_fails_reset(ofs);
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 || sectrue != unlocked || PIN_KEY == key) {
return secfalse;
}
return norcow_get(key, val, len);
}
secbool storage_set(uint16_t key, const void *val, uint16_t len)
{
if (sectrue != initialized || sectrue != unlocked || PIN_KEY == key) {
return secfalse;
}
return norcow_set(key, val, len);
}
secbool storage_has_pin(void)
{
if (sectrue != initialized) {
return secfalse;
}
const void *spin = NULL;
uint16_t spinlen = 0;
norcow_get(PIN_KEY, &spin, &spinlen);
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 || sectrue != unlocked || newlen > PIN_MAXLEN) {
return secfalse;
}
if (sectrue != pin_check(pin, len)) {
return secfalse;
}
return norcow_set(PIN_KEY, newpin, newlen);
}
secbool storage_wipe(void)
{
return norcow_wipe();
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs
*
* Licensed under TREZOR License
* see LICENSE file for details
*/
#include <stdint.h>
#include <stddef.h>
#include "../../trezorhal/secbool.h"
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);

View File

@ -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:
/// '''

View File

@ -13,10 +13,13 @@
#include <stdlib.h>
#include <string.h>
#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

View File

@ -1,11 +1,19 @@
/*
* Copyright (c) Pavol Rusnak, Jan Pochyla, SatoshiLabs
*
* Licensed under TREZOR License
* see LICENSE file for details
*/
#include STM32_HAL_H
#include <string.h>
#include "flash.h"
// 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
@ -33,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();
@ -46,6 +59,19 @@ secbool flash_lock(void)
return sectrue;
}
const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size)
{
if (sector >= FLASH_SECTOR_COUNT) {
return NULL;
}
uint32_t addr = FLASH_SECTOR_TABLE[sector];
uint32_t next = FLASH_SECTOR_TABLE[sector + 1];
if (addr + offset + size > next) {
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()) {
@ -66,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();
@ -91,6 +117,28 @@ secbool flash_write_word(uint32_t address, uint32_t data)
return sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, 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, FLASH_SECTOR_TABLE[sector] + offset, data));
}
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, FLASH_SECTOR_TABLE[sector] + offset, data));
}
secbool flash_read_word_rel(uint8_t sector, uint32_t offset, uint32_t *data)
{
if (offset % 4 != 0) {
return secfalse;
}
*data = *((const uint32_t *)FLASH_SECTOR_TABLE[sector] + offset);
return sectrue;
}
#define FLASH_OTP_LOCK_BASE 0x1FFF7A00U
secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen)

View File

@ -43,14 +43,19 @@
// 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);
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);
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

View File

@ -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;
}

View File

@ -31,3 +31,8 @@ void hal_delay(uint32_t ms)
{
usleep(1000 * ms);
}
void shutdown(void)
{
exit(1);
}

View File

@ -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

175
embed/unix/flash.c Normal file
View File

@ -0,0 +1,175 @@
/*
* Copyright (c) Jan Pochyla, SatoshiLabs
*
* Licensed under TREZOR License
* see LICENSE file for details
*/
#include <string.h>
#include <stdio.h>
#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] = {
[ 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
};
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);
}
}
secbool flash_init(void)
{
FILE *f = fopen(FLASH_FILE, "rb");
size_t r = 0;
if (f) {
r = fread(flash_buffer, sizeof(flash_buffer), 1, f);
fclose(f);
}
if (r != 1) {
memset(flash_buffer, 0xFF, sizeof(flash_buffer));
}
return sectrue;
}
secbool flash_unlock(void)
{
return sectrue;
}
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;
}
const uint32_t sector_size = sector_table[sector + 1] - sector_table[sector];
if (offset + size > sector_size) {
return NULL;
}
const uint32_t sector_offset = sector_table[sector] - sector_table[0];
return flash_buffer + sector_offset + offset;
}
secbool 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++) {
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);
}
flash_sync();
}
return sectrue;
}
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 secfalse;
}
if ((flash[0] & data) != data) {
return secfalse; // we cannot change zeroes to ones
}
flash[0] = data;
flash_sync();
return sectrue;
}
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 secfalse;
}
uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data));
if (!flash) {
return secfalse;
}
if ((flash[0] & data) != data) {
return secfalse; // we cannot change zeroes to ones
}
flash[0] = data;
flash_sync();
return sectrue;
}
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;
}
const uint32_t *flash = (const uint32_t *)flash_get_address(sector, offset, sizeof(data));
if (!flash) {
return secfalse;
}
data[0] = flash[0];
return sectrue;
}
secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen)
{
return secfalse;
}
secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, uint8_t datalen)
{
return secfalse;
}
secbool flash_otp_lock(uint8_t block)
{
return secfalse;
}
secbool flash_otp_is_locked(uint8_t block)
{
return secfalse;
}

View File

@ -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 ''

View File

@ -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')
raise PinCancelled()
@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
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

View File

@ -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

View File

@ -1,152 +1,62 @@
from micropython import const
import ustruct
import utime
from trezor import config
from trezor import utils
_APP = const(1)
_STORAGE_VERSION = b'\x01'
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))
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(VERSION, int_to_bytes(1))
config_set(MNEMONIC, mnemonic.encode())
config.set(_APP, _VERSION, _STORAGE_VERSION)
config.set(_APP, _MNEMONIC, mnemonic.encode())
_ALLOWED_LANGUAGES = ('english')
_DEFAULT_LANGUAGE = 'english'
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())
def load_settings(label: str = None, use_passphrase: bool = None):
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))
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 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

View File

@ -20,10 +20,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
@ -42,10 +42,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)

View File

@ -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')

View File

@ -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')

27
src/boot.py Normal file
View File

@ -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()

View File

@ -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

View File

@ -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())

View File

@ -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())