diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index 4f81bdc8a..18f2dfa71 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -3,11 +3,10 @@ from trezor import ui, wire from trezor.messages import ButtonRequestType, SafetyCheckLevel from trezor.messages.Success import Success from trezor.strings import format_duration_ms -from trezor.ui.components.tt.text import Text +from trezor.ui.layouts import confirm_action, require from apps.base import reload_settings_from_storage from apps.common import safety_checks -from apps.common.confirm import require_confirm, require_hold_to_confirm if False: from trezor.messages.ApplySettings import ApplySettings, EnumTypeSafetyCheckLevel @@ -101,37 +100,62 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings): async def require_confirm_change_homescreen(ctx): - text = Text("Set homescreen", ui.ICON_CONFIG) - text.normal("Do you really want to", "change the homescreen", "image?") - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + await require( + confirm_action( + ctx, + "set_homescreen", + "Set homescreen", + description="Do you really want to change the homescreen image?", + br_code=ButtonRequestType.ProtectCall, + ) + ) async def require_confirm_change_label(ctx, label): - text = Text("Change label", ui.ICON_CONFIG) - text.normal("Do you really want to", "change the label to") - text.bold("%s?" % label) - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + await require( + confirm_action( + ctx, + "set_label", + "Change label", + description="Do you really want to change the label to {}?", + description_param=label, + br_code=ButtonRequestType.ProtectCall, + ) + ) async def require_confirm_change_passphrase(ctx, use): - text = Text("Enable passphrase" if use else "Disable passphrase", ui.ICON_CONFIG) - text.normal("Do you really want to") - text.normal("enable passphrase" if use else "disable passphrase") - text.normal("encryption?") - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + if use: + description = "Do you really want to enable passphrase encryption?" + else: + description = "Do you really want to disable passphrase encryption?" + await require( + confirm_action( + ctx, + "set_passphrase", + "Enable passphrase" if use else "Disable passphrase", + description=description, + br_code=ButtonRequestType.ProtectCall, + ) + ) async def require_confirm_change_passphrase_source( ctx, passphrase_always_on_device: bool ): - text = Text("Passphrase source", ui.ICON_CONFIG) if passphrase_always_on_device: - text.normal( - "Do you really want to", "enter passphrase always", "on the device?" - ) + description = "Do you really want to enter passphrase always on the device?" else: - text.normal("Do you want to revoke", "the passphrase on device", "setting?") - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + description = "Do you want to revoke the passphrase on device setting?" + await require( + confirm_action( + ctx, + "set_passphrase_source", + "Passphrase source", + description=description, + br_code=ButtonRequestType.ProtectCall, + ) + ) async def require_confirm_change_display_rotation(ctx, rotation): @@ -145,55 +169,85 @@ async def require_confirm_change_display_rotation(ctx, rotation): label = "west" else: raise wire.DataError("Unsupported display rotation") - text = Text("Change rotation", ui.ICON_CONFIG, new_lines=False) - text.normal("Do you really want to", "change display rotation") - text.normal("to") - text.bold("%s?" % label) - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + await require( + confirm_action( + ctx, + "set_rotation", + "Change rotation", + description="Do you really want to change display rotation to {}?", + description_param=label, + br_code=ButtonRequestType.ProtectCall, + ) + ) async def require_confirm_change_autolock_delay(ctx, delay_ms): - text = Text("Auto-lock delay", ui.ICON_CONFIG, new_lines=False) - text.normal("Do you really want to", "auto-lock your device", "after") - text.bold("{}?".format(format_duration_ms(delay_ms))) - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + await require( + confirm_action( + ctx, + "set_autolock_delay", + "Auto-lock delay", + description="Do you really want to auto-lock your device after {}?", + description_param=format_duration_ms(delay_ms), + br_code=ButtonRequestType.ProtectCall, + ) + ) async def require_confirm_safety_checks(ctx, level: EnumTypeSafetyCheckLevel) -> None: if level == SafetyCheckLevel.PromptAlways: - text = Text("Safety override", ui.ICON_CONFIG) - text.normal( - "Trezor will allow you to", - "approve some actions", - "which might be unsafe.", + await require( + confirm_action( + ctx, + "set_safety_checks", + "Safety override", + hold=True, + verb="Hold to confirm", + description="Trezor will allow you to approve some actions which might be unsafe.", + action="Are you sure?", + reverse=True, + larger_vspace=True, + br_code=ButtonRequestType.ProtectCall, + ) ) - text.br_half() - text.bold("Are you sure?") - await require_hold_to_confirm(ctx, text, ButtonRequestType.ProtectCall) elif level == SafetyCheckLevel.PromptTemporarily: - text = Text("Safety override", ui.ICON_CONFIG) - text.normal( - "Trezor will temporarily", - "allow you to approve", - "some actions which", - "might be unsafe.", + await require( + confirm_action( + ctx, + "set_safety_checks", + "Safety override", + hold=True, + verb="Hold to confirm", + description="Trezor will temporarily allow you to approve some actions which might be unsafe.", + action="Are you sure?", + reverse=True, + br_code=ButtonRequestType.ProtectCall, + ) ) - text.bold("Are you sure?") - await require_hold_to_confirm(ctx, text, ButtonRequestType.ProtectCall) elif level == SafetyCheckLevel.Strict: - text = Text("Safety checks", ui.ICON_CONFIG) - text.normal( - "Do you really want to", "enforce strict safety", "checks (recommended)?" + await require( + confirm_action( + ctx, + "set_safety_checks", + "Safety checks", + description="Do you really want to enforce strict safety checks (recommended)?", + br_code=ButtonRequestType.ProtectCall, + ) ) - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) else: raise ValueError # enum value out of range async def require_confirm_experimental_features(ctx, enable: bool) -> None: if enable: - text = Text("Experimental mode", ui.ICON_CONFIG) - text.normal("Enable experimental", "features?") - text.br_half() - text.bold("Only for development", "and beta testing!") - await require_confirm(ctx, text, ButtonRequestType.ProtectCall) + await require( + confirm_action( + ctx, + "set_experimental_features", + "Experimental mode", + description="Enable experimental features?", + action="Only for development and beta testing!", + reverse=True, + br_code=ButtonRequestType.ProtectCall, + ) + ) diff --git a/core/src/apps/management/change_pin.py b/core/src/apps/management/change_pin.py index 3d52d86bf..c21416e26 100644 --- a/core/src/apps/management/change_pin.py +++ b/core/src/apps/management/change_pin.py @@ -1,10 +1,8 @@ from storage.device import is_initialized -from trezor import config, ui, wire +from trezor import config, wire from trezor.messages.Success import Success -from trezor.ui.components.tt.text import Text -from trezor.ui.layouts import require, show_success +from trezor.ui.layouts import confirm_action, require, show_success -from apps.common.confirm import require_confirm from apps.common.request_pin import ( error_pin_invalid, error_pin_matches_wipe_code, @@ -63,22 +61,40 @@ def require_confirm_change_pin(ctx: wire.Context, msg: ChangePin) -> None: has_pin = config.has_pin() if msg.remove and has_pin: # removing pin - text = Text("Remove PIN", ui.ICON_CONFIG) - text.normal("Do you really want to") - text.bold("disable PIN protection?") - return require_confirm(ctx, text) + return require( + confirm_action( + ctx, + "set_pin", + "Remove PIN", + description="Do you really want to", + action="disable PIN protection?", + reverse=True, + ) + ) if not msg.remove and has_pin: # changing pin - text = Text("Change PIN", ui.ICON_CONFIG) - text.normal("Do you really want to") - text.bold("change your PIN?") - return require_confirm(ctx, text) + return require( + confirm_action( + ctx, + "set_pin", + "Change PIN", + description="Do you really want to", + action="change your PIN?", + reverse=True, + ) + ) if not msg.remove and not has_pin: # setting new pin - text = Text("Enable PIN", ui.ICON_CONFIG) - text.normal("Do you really want to") - text.bold("enable PIN protection?") - return require_confirm(ctx, text) + return require( + confirm_action( + ctx, + "set_pin", + "Enable PIN", + description="Do you really want to", + action="enable PIN protection?", + reverse=True, + ) + ) # removing non-existing PIN raise wire.ProcessError("PIN protection already disabled") diff --git a/core/src/apps/management/recovery_device/layout.py b/core/src/apps/management/recovery_device/layout.py index d8f6d91c6..48b7054c5 100644 --- a/core/src/apps/management/recovery_device/layout.py +++ b/core/src/apps/management/recovery_device/layout.py @@ -5,7 +5,7 @@ from trezor.messages import ButtonRequestType from trezor.ui.components.tt.scroll import Paginated from trezor.ui.components.tt.text import Text from trezor.ui.components.tt.word_select import WordSelector -from trezor.ui.layouts import require, show_success, show_warning +from trezor.ui.layouts import confirm_action, require, show_success, show_warning from apps.common import button_request from apps.common.confirm import confirm, info_confirm, require_confirm @@ -23,13 +23,25 @@ if False: async def confirm_abort(ctx: wire.GenericContext, dry_run: bool = False) -> bool: if dry_run: - text = Text("Abort seed check", ui.ICON_WIPE) - text.normal("Do you really want to", "abort the seed check?") + return await confirm_action( + ctx, + "abort_recovery", + "Abort seed check", + description="Do you really want to abort the seed check?", + icon=ui.ICON_WIPE, + br_code=ButtonRequestType.ProtectCall, + ) else: - text = Text("Abort recovery", ui.ICON_WIPE) - text.normal("Do you really want to", "abort the recovery", "process?") - text.bold("All progress will be lost.") - return await confirm(ctx, text, code=ButtonRequestType.ProtectCall) + return await confirm_action( + ctx, + "abort_recovery", + "Abort recovery", + description="Do you really want to abort the recovery process?", + action="All progress will be lost.", + reverse=True, + icon=ui.ICON_WIPE, + br_code=ButtonRequestType.ProtectCall, + ) async def request_word_count(ctx: wire.GenericContext, dry_run: bool) -> int: diff --git a/core/src/apps/management/reset_device/layout.py b/core/src/apps/management/reset_device/layout.py index 0cb1e5427..166c3696a 100644 --- a/core/src/apps/management/reset_device/layout.py +++ b/core/src/apps/management/reset_device/layout.py @@ -9,9 +9,15 @@ from trezor.ui.components.tt.info import InfoConfirm from trezor.ui.components.tt.num_input import NumInput from trezor.ui.components.tt.scroll import Paginated from trezor.ui.components.tt.text import Text -from trezor.ui.layouts import require, show_success +from trezor.ui.layouts import ( + confirm_action, + confirm_hex, + require, + show_success, + show_warning, +) -from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm +from apps.common.confirm import confirm, require_hold_to_confirm if False: from trezor import loop @@ -22,11 +28,18 @@ if __debug__: async def show_internal_entropy(ctx, entropy: bytes): - entropy_str = ubinascii.hexlify(entropy).decode() - lines = utils.chunks(entropy_str, 16) - text = Text("Internal entropy", ui.ICON_RESET) - text.mono(*lines) - await require_confirm(ctx, text, ButtonRequestType.ResetDevice) + await require( + confirm_hex( + ctx, + "entropy", + "Internal entropy", + data=ubinascii.hexlify(entropy).decode(), + icon=ui.ICON_RESET, + icon_color=ui.ORANGE_ICON, + width=16, + br_code=ButtonRequestType.ResetDevice, + ) + ) async def _show_share_words(ctx, share_words, share_index=None, group_index=None): @@ -186,34 +199,38 @@ async def _show_confirmation_success( async def _show_confirmation_failure(ctx, share_index): if share_index is None: - text = Text("Recovery seed", ui.ICON_WRONG, ui.RED) + header = "Recovery seed" else: - text = Text("Recovery share #%s" % (share_index + 1), ui.ICON_WRONG, ui.RED) - text.bold("That is the wrong word.") - text.normal("Please check again.") - await require_confirm( - ctx, text, ButtonRequestType.ResetDevice, confirm="Check again", cancel=None + header = "Recovery share #%s" % (share_index + 1) + await require( + show_warning( + ctx, + "warning_backup_check", + header=header, + subheader="That is the wrong word.", + content="Please check again.", + button="Check again", + br_code=ButtonRequestType.ResetDevice, + ) ) async def show_backup_warning(ctx, slip39=False): - text = Text("Caution", ui.ICON_NOCOPY) if slip39: - text.normal( - "Never make a digital", - "copy of your recovery", - "shares and never upload", - "them online!", - ) + description = "Never make a digital copy of your recovery shares and never upload them online!" else: - text.normal( - "Never make a digital", - "copy of your recovery", - "seed and never upload", - "it online!", + description = "Never make a digital copy of your recovery seed and never upload\nit online!" + await require( + confirm_action( + ctx, + "backup_warning", + "Caution", + description=description, + verb="I understand", + verb_cancel=None, + icon=ui.ICON_NOCOPY, + br_code=ButtonRequestType.ResetDevice, ) - await require_confirm( - ctx, text, ButtonRequestType.ResetDevice, "I understand", cancel=None ) diff --git a/core/src/trezor/ui/layouts/tt.py b/core/src/trezor/ui/layouts/tt.py index 019ef8830..d013b933f 100644 --- a/core/src/trezor/ui/layouts/tt.py +++ b/core/src/trezor/ui/layouts/tt.py @@ -62,6 +62,7 @@ async def confirm_action( title: str, action: str = None, description: str = None, + description_param: str = None, verb: Union[str, bytes, None] = Confirm.DEFAULT_CONFIRM, verb_cancel: Union[str, bytes, None] = Confirm.DEFAULT_CANCEL, hold: bool = False, @@ -80,7 +81,9 @@ async def confirm_action( ) if reverse and description is not None: - text.normal(description) + text.format_parametrized( + description, description_param if description_param is not None else "" + ) elif action is not None: text.bold(action) @@ -92,7 +95,9 @@ async def confirm_action( if reverse and action is not None: text.bold(action) elif description is not None: - text.normal(description) + text.format_parametrized( + description, description_param if description_param is not None else "" + ) cls = HoldToConfirm if hold else Confirm return is_confirmed( @@ -203,10 +208,12 @@ def _split_address(address: str) -> Iterator[str]: return chunks(address, MONO_CHARS_PER_LINE) -def _hex_lines(hex_data: str, lines: int = TEXT_MAX_LINES) -> Iterator[str]: - if len(hex_data) >= MONO_HEX_PER_LINE * lines: - hex_data = hex_data[: (MONO_HEX_PER_LINE * lines - 3)] + "..." - return chunks(hex_data, MONO_HEX_PER_LINE) +def _hex_lines( + hex_data: str, lines: int = TEXT_MAX_LINES, width: int = MONO_HEX_PER_LINE +) -> Iterator[str]: + if len(hex_data) >= width * lines: + hex_data = hex_data[: (width * lines - 3)] + "..." + return chunks(hex_data, width) def _show_address( @@ -370,14 +377,16 @@ def show_warning( ctx: wire.GenericContext, br_type: str, content: str, + header: str = "Warning", subheader: Optional[str] = None, button: str = "Try again", + br_code: EnumTypeButtonRequestType = ButtonRequestType.Warning, ) -> Awaitable[bool]: return _show_modal( ctx, br_type=br_type, - br_code=ButtonRequestType.Warning, - header="Warning", + br_code=br_code, + header=header, subheader=subheader, content=content, button_confirm=button, @@ -450,9 +459,10 @@ async def confirm_hex( br_code: EnumTypeButtonRequestType = ButtonRequestType.Other, icon: str = ui.ICON_SEND, # TODO cleanup @ redesign icon_color: int = ui.GREEN, # TODO cleanup @ redesign + width: int = MONO_HEX_PER_LINE, ) -> bool: text = Text(title, icon, icon_color) - text.mono(*_hex_lines(data)) + text.mono(*_hex_lines(data, width=width)) return is_confirmed(await interact(ctx, Confirm(text), br_type, br_code)) diff --git a/tests/common.py b/tests/common.py index 120726917..ada85e6f2 100644 --- a/tests/common.py +++ b/tests/common.py @@ -175,7 +175,7 @@ def click_through(debug, screens, code=None): debug.press_yes() -def read_and_confirm_mnemonic(debug, words): +def read_and_confirm_mnemonic(debug, words, choose_wrong=False): """Read a given number of mnemonic words from Trezor T screen and correctly answer confirmation questions. Return the full mnemonic. @@ -201,7 +201,11 @@ def read_and_confirm_mnemonic(debug, words): # check share for _ in range(3): index = debug.read_reset_word_pos() - debug.input(mnemonic[index]) + if choose_wrong: + debug.input(mnemonic[(index + 1) % len(mnemonic)]) + return None + else: + debug.input(mnemonic[index]) return " ".join(mnemonic) diff --git a/tests/device_tests/test_msg_resetdevice_bip39_t2.py b/tests/device_tests/test_msg_resetdevice_bip39_t2.py index d79c81212..12b60f187 100644 --- a/tests/device_tests/test_msg_resetdevice_bip39_t2.py +++ b/tests/device_tests/test_msg_resetdevice_bip39_t2.py @@ -216,6 +216,91 @@ class TestMsgResetDeviceT2: assert resp.pin_protection is True assert resp.passphrase_protection is True + @pytest.mark.setup_client(uninitialized=True) + def test_reset_failed_check(self, client): + mnemonic = None + strength = 256 # 24 words + + def input_flow(): + nonlocal mnemonic + # 1. Confirm Reset + # 2. Backup your seed + # 3. Confirm warning + yield from click_through(client.debug, screens=3, code=B.ResetDevice) + + # mnemonic phrases, wrong answer + btn_code = yield + assert btn_code == B.ResetDevice + mnemonic = read_and_confirm_mnemonic( + client.debug, words=24, choose_wrong=True + ) + + # warning screen + btn_code = yield + assert btn_code == B.ResetDevice + client.debug.press_yes() + + # mnemonic phrases + btn_code = yield + assert btn_code == B.ResetDevice + mnemonic = read_and_confirm_mnemonic(client.debug, words=24) + + # confirm recovery seed check + btn_code = yield + assert btn_code == B.Success + client.debug.press_yes() + + # confirm success + btn_code = yield + assert btn_code == B.Success + client.debug.press_yes() + + os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY) + with mock.patch("os.urandom", os_urandom), client: + client.set_expected_responses( + [ + proto.ButtonRequest(code=B.ResetDevice), + proto.EntropyRequest(), + proto.ButtonRequest(code=B.ResetDevice), + proto.ButtonRequest(code=B.ResetDevice), + proto.ButtonRequest(code=B.ResetDevice), + proto.ButtonRequest(code=B.ResetDevice), + proto.ButtonRequest(code=B.ResetDevice), + proto.ButtonRequest(code=B.Success), + proto.ButtonRequest(code=B.Success), + proto.Success, + proto.Features, + ] + ) + client.set_input_flow(input_flow) + + # PIN, passphrase, display random + device.reset( + client, + display_random=False, + strength=strength, + passphrase_protection=False, + pin_protection=False, + label="test", + language="en-US", + ) + + # generate mnemonic locally + internal_entropy = client.debug.state().reset_entropy + entropy = generate_entropy(strength, internal_entropy, EXTERNAL_ENTROPY) + expected_mnemonic = Mnemonic("english").to_mnemonic(entropy) + + # Compare that device generated proper mnemonic for given entropies + assert mnemonic == expected_mnemonic + + # Check if device is properly initialized + resp = client.call_raw(proto.Initialize()) + assert resp.initialized is True + assert resp.needs_backup is False + assert resp.pin_protection is False + assert resp.passphrase_protection is False + assert resp.backup_type is proto.BackupType.Bip39 + @pytest.mark.setup_client(uninitialized=True) def test_failed_pin(self, client): # external_entropy = b'zlutoucky kun upel divoke ody' * 2 diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 3c2799c60..4112102db 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -67,20 +67,20 @@ "cardano-test_sign_tx.py::test_cardano_sign_tx_failed[withdrawal_amount_is_too_large]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "cardano-test_sign_tx.py::test_cardano_sign_tx_failed[withdrawal_has_non_staking_path]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "cardano-test_sign_tx.py::test_cardano_sign_tx_with_multiple_chunks[large_transaction_to_be_-377cb9ff": "4a46dd8ac42295d853646a55ca1b85023b2235af6155be663b1de10a6c98def2", -"test_autolock.py::test_apply_auto_lock_delay": "38c720e0d29b7487060f2a0f8d256a5e5b4f735511e049c43f6ea62e560603ae", +"test_autolock.py::test_apply_auto_lock_delay": "de8ddc62320c82938920fa6b7d95cb571bd7fa766b192549971a975e24cc0b34", "test_autolock.py::test_apply_auto_lock_delay_out_of_range[0]": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", "test_autolock.py::test_apply_auto_lock_delay_out_of_range[1]": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", "test_autolock.py::test_apply_auto_lock_delay_out_of_range[4194304]": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", "test_autolock.py::test_apply_auto_lock_delay_out_of_range[536871]": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", "test_autolock.py::test_apply_auto_lock_delay_out_of_range[9]": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", -"test_autolock.py::test_apply_auto_lock_delay_valid[10]": "a751228f82166c107a8e8872919e2b010ef3079763adc473066e7a3ada36f864", -"test_autolock.py::test_apply_auto_lock_delay_valid[123]": "caf130bf5fa66fa5ac17432689046c1b6cd8b6a495bac3abef3c414d89b81e3f", -"test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "b2a9a7f3e50afb04fb174627a07b939284aa0acc8b3b53af56f75a35ff1b32c9", -"test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "ca2b4707227cc15089f4d6ba710706d2d5c270f19a4668c09f04f175143b202e", -"test_autolock.py::test_apply_auto_lock_delay_valid[60]": "af8d06c92fc5f9aad5685bf56e59b26ec44418a6174ff73db69803f23785802a", -"test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "437cc6b0780d482a835c23f0935683c40177ae4b0ff31da4fc99eba603545ffe", -"test_autolock.py::test_autolock_cancels_ui": "bb4776bfea145528544554b11bdf13ae99f63a371e8eb0885b0a9bd5b608e027", -"test_autolock.py::test_autolock_default_value": "b9f4cd94638f5f8f4c02026b0ccaee89b42406ab63ce7fcef5c9164467de939b", +"test_autolock.py::test_apply_auto_lock_delay_valid[10]": "1c4d26c24483449997b79f385e2a6eab000dfca98bfe8f3c08e7f007370fca1c", +"test_autolock.py::test_apply_auto_lock_delay_valid[123]": "8c9f07104463543083dfb461810a5fc8727e3a2c09644b4b3e423a43970a72c7", +"test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "941179a4f674b1c3d7004cc7073a29b327c70246eb853aa29f31e2e261f329e6", +"test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "dd430048994310dee37453ae03dfe9c1e15c2fc2ce952dd755bf5df27730907f", +"test_autolock.py::test_apply_auto_lock_delay_valid[60]": "a0e602c8c04eae2dc6dd0768f0a627bcc0327b69d6a9645ecbc3e02f0e4c7908", +"test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "f9e78f3bf23f4808d105da5ae1abc4f25ee550b7590aec0c15fcfb041eeb4413", +"test_autolock.py::test_autolock_cancels_ui": "a63bbf50bb612a87c459b27fe87b38f7b3e984b46c9d68b7604e40667072adc6", +"test_autolock.py::test_autolock_default_value": "e3e6a55dae2c8b152477080002d8b734e1bafe00f26e138294969e2dfd9c596e", "test_basic.py-test_device_id_different": "bc6acd0386b9d009e6550519917d6e08632b3badde0b0cf04c95abe5f773038a", "test_basic.py-test_device_id_same": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", "test_basic.py-test_features": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", @@ -106,11 +106,11 @@ "test_msg_applysettings.py-test_apply_homescreen_toif_fail[TOIf\\x80\\x00\\x80\\x00~\\x00\\x00\\x00-fefdb3aa": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", "test_msg_applysettings.py-test_apply_homescreen_toif_fail[TOIf\\x90\\x00\\x90\\x00~XXXf\\x90\\x00-4f4d817c": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", "test_msg_applysettings.py-test_apply_homescreen_toif_fail[TOIg\\x90\\x00\\x90\\x00~\\x00\\x00\\x00-63ffc926": "6badfcdd682ecaf16311749ef7a6c07c6a4d0df402427c8dd5a48d476751ed77", -"test_msg_applysettings.py-test_apply_settings": "8f9f6013bb8a44fda279e9c7d091328fd7ccb39222a02bee701918528355083a", +"test_msg_applysettings.py-test_apply_settings": "9394ea770233a5679c6292c7afe75f4d3528f1b1576f47e6bef8ede6276eec8b", "test_msg_applysettings.py-test_apply_settings_passphrase": "40de0143b32b5d06ece43d47be27bb91499f0c2417754ddb8e9e03ff41a7f6d4", "test_msg_applysettings.py-test_apply_settings_passphrase_on_device": "3e6527e227bdde54f51bc9c417b176d0d87fdb6c40c4761368f50eb201b4beed", -"test_msg_applysettings.py-test_apply_settings_rotation": "6f0fa323dd2c82d01994273c023d3ed5e43d43c9c562664a10266f4a7f7ba4cc", -"test_msg_applysettings.py-test_experimental_features": "3127d41bd8615097295b917110ece9dd364986809288c7f958ff71d52106e346", +"test_msg_applysettings.py-test_apply_settings_rotation": "5789f4a9274b8a37af5b279296cf13a95be6e528738474a1c64d36bb0692e216", +"test_msg_applysettings.py-test_experimental_features": "388d5f0ee2e8c521fd82bf8687bd5bdba26ed34695d2a1aecb28f6a72df8df4c", "test_msg_applysettings.py-test_label_too_long": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", "test_msg_applysettings.py-test_safety_checks": "4d37de3654678b7f16643cf4ef912a0bced545e0e0526e41ea0c9b820560344e", "test_msg_authorize_coinjoin.py::test_cancel_authorization": "d8a608beb6165f5667cc44dcff6bdc17ebb4638ddd3bd09e7f0e1e75d1e21135", @@ -365,6 +365,7 @@ "test_msg_resetdevice_bip39_t2.py-test_reset_device": "5f1b6cdc46e416430df1afd114bceda57fb644108d594ce1f466460ba4917b41", "test_msg_resetdevice_bip39_t2.py-test_reset_device_192": "d304d9902accbe23af2dcf73758a2e81b00da17e89f754ca83f209bac8eb8ee1", "test_msg_resetdevice_bip39_t2.py-test_reset_device_pin": "1e4cef983f25b66931db5a8286c83f8faa3e09eb865896c7e946d2c8992e6d28", +"test_msg_resetdevice_bip39_t2.py-test_reset_failed_check": "b07b90b0c33200f431f1464750cf0934e558ae80a25ecb013c9ce652997df70c", "test_msg_resetdevice_slip39_advanced.py-test_reset_device_slip39_advanced": "92dde100ab37934b7c39a9bfff15a4e73419b4f029c377f25f0b428c6e4a005c", "test_msg_resetdevice_slip39_basic.py-test_reset_device_slip39_basic": "650ebacd885fe8c34237aceb835827472a845d8951f2ee573d94c97030603db3", "test_msg_resetdevice_slip39_basic.py-test_reset_device_slip39_basic_256": "aa7f19f34dafbd5bf205858cd8f694f3278b309a3c8837e6596759d6a1066634", @@ -586,7 +587,7 @@ "test_msg_verifymessage_segwit_native.py-test_message_verify": "3aeca0b02254b83988008b5129812a749f320add09146d189fa294f2b5c80c34", "test_msg_verifymessage_segwit_native.py-test_verify_utf": "62d12291ee0f0d4639d861ea61d55c9944c37aad24bd70dd35877e9d12a2b731", "test_msg_webauthn.py::test_add_remove": "a9cdefeb089f197427257e097d07179b23de4fcad4ee91af0191ed767f80577c", -"test_msg_wipedevice.py::test_autolock_not_retained": "b519a86b80151e5c1a4849fc5d6e8e6c8f2953c80befee8dd05f7caec254b464", +"test_msg_wipedevice.py::test_autolock_not_retained": "b07784bf0739fcc5d5877f2a1670b88e10019346c56f9864b48d5cb56944b07b", "test_msg_wipedevice.py::test_wipe_device": "bc6acd0386b9d009e6550519917d6e08632b3badde0b0cf04c95abe5f773038a", "test_multisig.py-test_15_of_15": "9d1799a199b45785ac69ae6af715c251b10aebb60f981c9b73d78e53e1a91374", "test_multisig.py-test_2_of_3": "2be92556edf4ff8eed340d535f379ee6915eae34fef25d669ce865848e7b4705",