diff --git a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c index c72cc0d1c..e4a6900c1 100644 --- a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -173,6 +173,36 @@ STATIC mp_obj_t mod_trezorconfig_change_pin(size_t n_args, STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_change_pin_obj, 4, 4, mod_trezorconfig_change_pin); +/// def change_wipe_code( +/// pin: int, +/// ext_salt: Optional[bytes], +/// wipe_code: int, +/// ) -> bool: +/// """ +/// Change wipe code. Returns True on success, False on failure. +/// """ +STATIC mp_obj_t mod_trezorconfig_change_wipe_code(size_t n_args, + const mp_obj_t *args) { + uint32_t pin = trezor_obj_get_uint(args[0]); + uint32_t wipe_code = trezor_obj_get_uint(args[2]); + mp_buffer_info_t ext_salt_b; + const uint8_t *ext_salt = NULL; + if (args[1] != mp_const_none) { + mp_get_buffer_raise(args[1], &ext_salt_b, MP_BUFFER_READ); + if (ext_salt_b.len != EXTERNAL_SALT_SIZE) + mp_raise_msg(&mp_type_ValueError, "Invalid length of external salt."); + ext_salt = ext_salt_b.buf; + } + + if (sectrue != storage_change_wipe_code(pin, ext_salt, wipe_code)) { + return mp_const_false; + } + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + mod_trezorconfig_change_wipe_code_obj, 3, 3, + mod_trezorconfig_change_wipe_code); + /// def get(app: int, key: int, public: bool = False) -> Optional[bytes]: /// """ /// Gets the value of the given key for the given app (or None if not set). @@ -324,6 +354,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorconfig_globals_table[] = { MP_ROM_PTR(&mod_trezorconfig_get_pin_rem_obj)}, {MP_ROM_QSTR(MP_QSTR_change_pin), MP_ROM_PTR(&mod_trezorconfig_change_pin_obj)}, + {MP_ROM_QSTR(MP_QSTR_change_wipe_code), + MP_ROM_PTR(&mod_trezorconfig_change_wipe_code_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_delete), MP_ROM_PTR(&mod_trezorconfig_delete_obj)}, diff --git a/core/embed/extmod/modtrezorconfig/norcow_config.h b/core/embed/extmod/modtrezorconfig/norcow_config.h index b27ce58c6..405d1441a 100644 --- a/core/embed/extmod/modtrezorconfig/norcow_config.h +++ b/core/embed/extmod/modtrezorconfig/norcow_config.h @@ -46,6 +46,6 @@ /* * Current storage version. */ -#define NORCOW_VERSION ((uint32_t)0x00000001) +#define NORCOW_VERSION ((uint32_t)0x00000002) #endif diff --git a/core/mocks/generated/trezorconfig.pyi b/core/mocks/generated/trezorconfig.pyi index 5b8645b9b..146835997 100644 --- a/core/mocks/generated/trezorconfig.pyi +++ b/core/mocks/generated/trezorconfig.pyi @@ -60,6 +60,17 @@ def change_pin( """ +# extmod/modtrezorconfig/modtrezorconfig.c +def change_wipe_code( + pin: int, + ext_salt: Optional[bytes], + wipe_code: int, +) -> bool: + """ + Change wipe code. Returns True on success, False on failure. + """ + + # extmod/modtrezorconfig/modtrezorconfig.c def get(app: int, key: int, public: bool = False) -> Optional[bytes]: """ diff --git a/core/src/apps/common/request_pin.py b/core/src/apps/common/request_pin.py index 3c0690b1c..36cc7eaa4 100644 --- a/core/src/apps/common/request_pin.py +++ b/core/src/apps/common/request_pin.py @@ -117,3 +117,11 @@ async def show_pin_invalid(ctx: wire.Context) -> None: text = Text("Wrong PIN", ui.ICON_WRONG, ui.RED) text.normal("The PIN you entered is", "invalid.") await confirm(ctx, text, confirm=None, cancel="Close") + + +async def show_pin_matches_wipe_code(ctx: wire.Context) -> None: + from apps.common.confirm import confirm + + text = Text("Invalid PIN", ui.ICON_WRONG, ui.RED) + text.normal("The new PIN must be", "different from your", "wipe code.") + await confirm(ctx, text, confirm=None, cancel="Close") diff --git a/core/src/apps/management/__init__.py b/core/src/apps/management/__init__.py index d6781fb59..255bea63a 100644 --- a/core/src/apps/management/__init__.py +++ b/core/src/apps/management/__init__.py @@ -13,3 +13,4 @@ def boot() -> None: wire.add(MessageType.SetU2FCounter, __name__, "set_u2f_counter") wire.add(MessageType.GetNextU2FCounter, __name__, "get_next_u2f_counter") wire.add(MessageType.SdProtect, __name__, "sd_protect") + wire.add(MessageType.ChangeWipeCode, __name__, "change_wipe_code") diff --git a/core/src/apps/management/change_pin.py b/core/src/apps/management/change_pin.py index 991f901c5..ce99e2b29 100644 --- a/core/src/apps/management/change_pin.py +++ b/core/src/apps/management/change_pin.py @@ -10,6 +10,7 @@ from apps.common.request_pin import ( request_pin_and_sd_salt, request_pin_confirm, show_pin_invalid, + show_pin_matches_wipe_code, ) if False: @@ -40,7 +41,10 @@ async def change_pin(ctx: wire.Context, msg: ChangePin) -> Success: # write into storage if not config.change_pin(pin_to_int(curpin), pin_to_int(newpin), salt, salt): - await show_pin_invalid(ctx) + if newpin: + await show_pin_matches_wipe_code(ctx) + else: + await show_pin_invalid(ctx) raise wire.PinInvalid("PIN invalid") if newpin: diff --git a/core/src/apps/management/change_wipe_code.py b/core/src/apps/management/change_wipe_code.py new file mode 100644 index 000000000..12e927baa --- /dev/null +++ b/core/src/apps/management/change_wipe_code.py @@ -0,0 +1,99 @@ +from storage import is_initialized +from trezor import config, ui, wire +from trezor.messages.Success import Success +from trezor.pin import pin_to_int +from trezor.ui.popup import Popup +from trezor.ui.text import Text + +from apps.common.confirm import require_confirm +from apps.common.layout import show_success +from apps.common.request_pin import ( + request_pin_ack, + request_pin_and_sd_salt, + show_pin_invalid, +) + +if False: + from trezor.messages.ChangeWipeCode import ChangeWipeCode + + +async def change_wipe_code(ctx: wire.Context, msg: ChangeWipeCode) -> Success: + if not is_initialized(): + raise wire.NotInitialized("Device is not initialized") + + # Confirm that user wants to set or remove the wipe code. + await _require_confirm_action(ctx, msg) + + # Get the unlocking PIN. + pin, salt = await request_pin_and_sd_salt(ctx) + + if not msg.remove: + # Pre-check the entered PIN. + if config.has_pin() and not config.check_pin(pin_to_int(pin), salt): + await show_pin_invalid(ctx) + raise wire.PinInvalid("PIN invalid") + + # Get new wipe code. + wipe_code = await _request_wipe_code_confirm(ctx, pin) + else: + wipe_code = "" + + # Write into storage. + if not config.change_wipe_code(pin_to_int(pin), salt, pin_to_int(wipe_code)): + await show_pin_invalid(ctx) + raise wire.PinInvalid("PIN invalid") + + if wipe_code: + msg_screen = "set the wipe code." + msg_wire = "Wipe code set" + else: + msg_screen = "disabled the wipe code." + msg_wire = "Wipe code removed" + + await show_success(ctx, ("You have successfully", msg_screen)) + return Success(message=msg_wire) + + +def _require_confirm_action(ctx: wire.Context, msg: ChangeWipeCode) -> None: + if msg.remove: + text = Text("Disable wipe code", ui.ICON_CONFIG) + text.normal("Do you really want to") + text.bold("disable wipe code") + text.bold("protection?") + return require_confirm(ctx, text) + else: + text = Text("Set wipe code", ui.ICON_CONFIG) + text.normal("Do you really want to") + text.bold("set the wipe code?") + return require_confirm(ctx, text) + + +async def _request_wipe_code_confirm(ctx: wire.Context, pin: str) -> str: + while True: + code1 = await request_pin_ack(ctx, "Enter new wipe code") + if code1 == pin: + await _wipe_code_invalid() + continue + + code2 = await request_pin_ack(ctx, "Re-enter new wipe code") + if code1 == code2: + return code1 + await _wipe_code_mismatch() + + +async def _wipe_code_invalid() -> None: + text = Text("Invalid wipe code", ui.ICON_WRONG, ui.RED) + text.normal("The wipe code must be", "different from your PIN.") + text.normal("") + text.normal("Please try again.") + popup = Popup(text, 3000) # show for 3 seconds + await popup + + +async def _wipe_code_mismatch() -> None: + text = Text("Code mismatch", ui.ICON_WRONG, ui.RED) + text.normal("The wipe codes you", "entered do not match.") + text.normal("") + text.normal("Please try again.") + popup = Popup(text, 3000) # show for 3 seconds + await popup