diff --git a/embed/extmod/modtrezorconfig/modtrezorconfig.c b/embed/extmod/modtrezorconfig/modtrezorconfig.c index 5fb0c03ca2..2f082c7db6 100644 --- a/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -25,20 +25,20 @@ STATIC mp_obj_t mod_trezorconfig_init(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_init_obj, mod_trezorconfig_init); -/// def unlock(pin: str) -> bool: +/// def unlock(pin: str, waitcallback: (int -> None)) -> 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) { +STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin, mp_obj_t waitcallback) { mp_buffer_info_t buf; mp_get_buffer_raise(pin, &buf, MP_BUFFER_READ); - if (sectrue != storage_unlock(buf.buf, buf.len)) { + if (sectrue != storage_unlock(buf.buf, buf.len, waitcallback)) { return mp_const_false; } return mp_const_true; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_unlock_obj, mod_trezorconfig_unlock); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_unlock_obj, mod_trezorconfig_unlock); /// def has_pin() -> bool: /// ''' @@ -52,21 +52,21 @@ STATIC mp_obj_t mod_trezorconfig_has_pin(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_has_pin_obj, mod_trezorconfig_has_pin); -/// def change_pin(pin: str, newpin: str) -> bool: +/// def change_pin(pin: str, newpin: str, waitcallback: (int -> None)) -> 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) { +STATIC mp_obj_t mod_trezorconfig_change_pin(mp_obj_t pin, mp_obj_t newpin, mp_obj_t waitcallback) { 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)) { + if (sectrue != storage_change_pin(pinbuf.buf, pinbuf.len, newbuf.buf, newbuf.len, waitcallback)) { return mp_const_false; } return mp_const_true; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_change_pin_obj, mod_trezorconfig_change_pin); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorconfig_change_pin_obj, mod_trezorconfig_change_pin); /// def get(app: int, key: int) -> bytes: /// ''' diff --git a/embed/extmod/modtrezorconfig/storage.c b/embed/extmod/modtrezorconfig/storage.c index e1dda88541..25a2456d3d 100644 --- a/embed/extmod/modtrezorconfig/storage.c +++ b/embed/extmod/modtrezorconfig/storage.c @@ -10,6 +10,8 @@ #include "common.h" #include "norcow.h" #include "../../trezorhal/flash.h" +#include "py/runtime.h" +#include "py/obj.h" // Norcow storage key of configured PIN. #define PIN_KEY 0x0000 @@ -89,14 +91,11 @@ static secbool pin_cmp(const uint8_t *pin, size_t pinlen) } } -static secbool pin_check(const uint8_t *pin, size_t len) +static secbool pin_get_fails(const uint32_t **pinfail, uint32_t *pofs) { - uint32_t ofs = 0; - uint32_t ctr; const void *vpinfail; - const uint32_t *pinfail = NULL; uint16_t pinfaillen; - + unsigned int ofs; // The PIN_FAIL_KEY points to an area of words, initialized to // 0xffffffff (meaning no pin failures). The first non-zero word // in this area is the current pin failure counter. If PIN_FAIL_KEY @@ -106,36 +105,51 @@ static secbool pin_check(const uint8_t *pin, size_t len) // indicating that the next word is the pin failure counter. // Find the current pin failure counter - secbool found = secfalse; if (secfalse != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { - pinfail = vpinfail; + *pinfail = vpinfail; for (ofs = 0; ofs < pinfaillen / sizeof(uint32_t); ofs++) { - if (pinfail[ofs]) { - found = sectrue; - break; + if (((const uint32_t *) vpinfail)[ofs]) { + *pinfail = vpinfail; + *pofs = ofs; + return sectrue; } } } - if (found == secfalse) { - // No pin failure section, or all entries used -> create a new one. - uint32_t pinarea[PIN_FAIL_SECTOR_SIZE]; - memset(pinarea, 0xff, sizeof(pinarea)); - if (sectrue != norcow_set(PIN_FAIL_KEY, pinarea, sizeof(pinarea))) { - return secfalse; - } - if (sectrue != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { - return secfalse; - } - pinfail = vpinfail; - ofs = 0; + + // No pin failure section, or all entries used -> create a new one. + uint32_t pinarea[PIN_FAIL_SECTOR_SIZE]; + memset(pinarea, 0xff, sizeof(pinarea)); + if (sectrue != norcow_set(PIN_FAIL_KEY, pinarea, sizeof(pinarea))) { + return secfalse; + } + if (sectrue != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { + return secfalse; + } + *pinfail = vpinfail; + *pofs = 0; + return sectrue; +} + +static secbool pin_check(const uint8_t *pin, size_t len, mp_obj_t callback) +{ + const uint32_t *pinfail = NULL; + uint32_t ofs; + uint32_t ctr; + + // Get the pin failure counter + if (pin_get_fails(&pinfail, &ofs) != sectrue) { + return secfalse; } // Read current failure counter ctr = pinfail[ofs]; + // Wipe storage if too many failures pin_fails_check_max(ctr); // Sleep for ~ctr seconds before checking the PIN. for (uint32_t wait = ~ctr; wait > 0; wait--) { + mp_obj_t waitobj = mp_obj_new_int(wait); + mp_call_function_1(callback, waitobj); hal_delay(1000); } @@ -146,6 +160,7 @@ static secbool pin_check(const uint8_t *pin, size_t len) return secfalse; } if (sectrue != pin_cmp(pin, len)) { + // Wipe storage if too many failures pin_fails_check_max(ctr << 1); return secfalse; } @@ -155,10 +170,10 @@ static secbool pin_check(const uint8_t *pin, size_t len) return sectrue; } -secbool storage_unlock(const uint8_t *pin, size_t len) +secbool storage_unlock(const uint8_t *pin, size_t len, mp_obj_t callback) { unlocked = secfalse; - if (sectrue == initialized && sectrue == pin_check(pin, len)) { + if (sectrue == initialized && sectrue == pin_check(pin, len, callback)) { unlocked = sectrue; } return unlocked; @@ -191,12 +206,12 @@ secbool storage_has_pin(void) return sectrue * (0 != spinlen); } -secbool 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, mp_obj_t callback) { if (sectrue != initialized || sectrue != unlocked || newlen > PIN_MAXLEN) { return secfalse; } - if (sectrue != pin_check(pin, len)) { + if (sectrue != pin_check(pin, len, callback)) { return secfalse; } return norcow_set(PIN_KEY, newpin, newlen); diff --git a/embed/extmod/modtrezorconfig/storage.h b/embed/extmod/modtrezorconfig/storage.h index 7fc9eef4c1..709d4d6f66 100644 --- a/embed/extmod/modtrezorconfig/storage.h +++ b/embed/extmod/modtrezorconfig/storage.h @@ -8,11 +8,13 @@ #include #include #include "../../trezorhal/secbool.h" +#include "py/obj.h" void storage_init(void); void storage_wipe(void); -secbool storage_unlock(const uint8_t *pin, size_t len); +secbool storage_unlock(const uint8_t *pin, size_t len, mp_obj_t callback); secbool storage_has_pin(void); -secbool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen); +uint32_t storage_pin_wait_time(void); +secbool storage_change_pin(const uint8_t *pin, size_t len, const uint8_t *newpin, size_t newlen, mp_obj_t callback); 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/src/apps/management/change_pin.py b/src/apps/management/change_pin.py index f1d4cc27c2..e5d90f5785 100644 --- a/src/apps/management/change_pin.py +++ b/src/apps/management/change_pin.py @@ -62,7 +62,11 @@ async def layout_change_pin(ctx, msg): else: new_pin = await request_pin_confirm(ctx) - config.change_pin(curr_pin, new_pin) + def show_timeout(wait): + # TODO + return + + config.change_pin(curr_pin, new_pin, show_timeout) if new_pin: return Success(message='PIN changed') diff --git a/src/boot.py b/src/boot.py index d3afefc176..0086e80869 100644 --- a/src/boot.py +++ b/src/boot.py @@ -5,13 +5,19 @@ from trezor import ui from apps.common.request_pin import request_pin +def show_timeout(wait): + # TODO + from trezor import log + log.debug('PIN', 'waiting %d seconds', wait) + + async def unlock_layout(): while True: if config.has_pin(): pin = await request_pin() else: pin = '' - if config.unlock(pin): + if config.unlock(pin, show_timeout): return else: await unlock_failed()