diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index 015b8e97d8..73e557edf0 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -125,6 +125,7 @@ message ApplySettings { optional uint32 auto_lock_delay_ms = 6; optional uint32 display_rotation = 7; // in degrees from North optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry + optional bool unsafe_prompts = 9; // allow or disallow unsafe prompts } /** diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index 485d296970..071b8d9656 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -6,7 +6,7 @@ from trezor.strings import format_duration_ms from trezor.ui.text import Text from apps.base import lock_device -from apps.common.confirm import require_confirm +from apps.common.confirm import require_confirm, require_hold_to_confirm if False: from trezor.messages.ApplySettings import ApplySettings @@ -22,6 +22,7 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings): and msg.passphrase_always_on_device is None and msg.display_rotation is None and msg.auto_lock_delay_ms is None + and msg.unsafe_prompts is None ): raise wire.ProcessError("No setting provided") @@ -60,6 +61,10 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings): autolock_delay_ms=msg.auto_lock_delay_ms, ) + if msg.unsafe_prompts is not None: + await require_confirm_unsafe_prompts(ctx, msg.unsafe_prompts) + storage.device.set_unsafe_prompts_allowed(msg.unsafe_prompts) + if msg.display_rotation is not None: ui.display.orientation(storage.device.get_rotation()) @@ -124,3 +129,16 @@ async def require_confirm_change_autolock_delay(ctx, delay_ms): 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) + + +async def require_confirm_unsafe_prompts(ctx, allow: bool) -> None: + if allow: + text = Text("Unsafe prompts", ui.ICON_WIPE) + text.normal("Trezor will allow you to", "confirm actions which", "might be dangerous.") + text.br_half() + text.bold("Allow unsafe prompts?") + await require_hold_to_confirm(ctx, text, ButtonRequestType.ProtectCall) + else: + text = Text("Unsafe prompts", ui.ICON_CONFIG) + text.normal("Do you really want to", "disable unsafe prompts?") + await require_confirm(ctx, text, ButtonRequestType.ProtectCall) diff --git a/core/src/storage/device.py b/core/src/storage/device.py index 6a66de9c77..b4eabd3ba5 100644 --- a/core/src/storage/device.py +++ b/core/src/storage/device.py @@ -34,6 +34,7 @@ _SLIP39_IDENTIFIER = const(0x10) # bool _SLIP39_ITERATION_EXPONENT = const(0x11) # int _SD_SALT_AUTH_KEY = const(0x12) # bytes INITIALIZED = const(0x13) # bool (0x01 or empty) +_UNSAFE_PROMPTS_ALLOWED = const(0x14) # bool (0x01 or empty) _DEFAULT_BACKUP_TYPE = BackupType.Bip39 # fmt: on @@ -291,3 +292,11 @@ def set_sd_salt_auth_key(auth_key: Optional[bytes]) -> None: return common.set(_NAMESPACE, _SD_SALT_AUTH_KEY, auth_key, public=True) else: return common.delete(_NAMESPACE, _SD_SALT_AUTH_KEY, public=True) + + +def unsafe_prompts_allowed() -> bool: + return common.get_bool(_NAMESPACE, _UNSAFE_PROMPTS_ALLOWED) + + +def set_unsafe_prompts_allowed(allowed: bool) -> None: + common.set_bool(_NAMESPACE, _UNSAFE_PROMPTS_ALLOWED, allowed) diff --git a/core/src/trezor/messages/ApplySettings.py b/core/src/trezor/messages/ApplySettings.py index 74f06c92d4..1b7aecafdb 100644 --- a/core/src/trezor/messages/ApplySettings.py +++ b/core/src/trezor/messages/ApplySettings.py @@ -22,6 +22,7 @@ class ApplySettings(p.MessageType): auto_lock_delay_ms: int = None, display_rotation: int = None, passphrase_always_on_device: bool = None, + unsafe_prompts: bool = None, ) -> None: self.language = language self.label = label @@ -30,6 +31,7 @@ class ApplySettings(p.MessageType): self.auto_lock_delay_ms = auto_lock_delay_ms self.display_rotation = display_rotation self.passphrase_always_on_device = passphrase_always_on_device + self.unsafe_prompts = unsafe_prompts @classmethod def get_fields(cls) -> Dict: @@ -41,4 +43,5 @@ class ApplySettings(p.MessageType): 6: ('auto_lock_delay_ms', p.UVarintType, 0), 7: ('display_rotation', p.UVarintType, 0), 8: ('passphrase_always_on_device', p.BoolType, 0), + 9: ('unsafe_prompts', p.BoolType, 0), } diff --git a/python/docs/OPTIONS.rst b/python/docs/OPTIONS.rst index d82ca1b923..695ccaee01 100644 --- a/python/docs/OPTIONS.rst +++ b/python/docs/OPTIONS.rst @@ -383,6 +383,7 @@ Device settings. label Set new device label. passphrase Enable, disable or configure passphrase protection. pin Set, change or remove PIN. + unsafe-prompts Allow or disallow unsafe prompts. wipe-code Set or remove the wipe code. Stellar commands. diff --git a/python/src/trezorlib/cli/settings.py b/python/src/trezorlib/cli/settings.py index 9f0e0237a5..759cf9cbd9 100644 --- a/python/src/trezorlib/cli/settings.py +++ b/python/src/trezorlib/cli/settings.py @@ -132,6 +132,20 @@ def homescreen(client, filename): return device.apply_settings(client, homescreen=img) +@cli.command() +@click.argument("allow", type=click.Choice(("on", "off"))) +@with_client +def unsafe_prompts(client, allow): + """Allow or disallow unsafe prompts. + + This is a power-user feature. With unsafe prompts enabled, Trezor will ask the user + to confirm possibly dangerous actions instead of rejecting them outright. + Use with caution. + """ + allowed = allow == "on" + return device.apply_settings(client, unsafe_prompts=allowed) + + # # passphrase operations # @@ -140,6 +154,8 @@ def homescreen(client, filename): @cli.group() def passphrase(): """Enable, disable or configure passphrase protection.""" + # this exists in order to support command aliases for "enable-passphrase" + # and "disable-passphrase". Otherwise `passphrase` would just take an argument. @passphrase.command(name="enabled") diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py index f0053e8ff0..7095163753 100644 --- a/python/src/trezorlib/device.py +++ b/python/src/trezorlib/device.py @@ -34,22 +34,18 @@ def apply_settings( passphrase_always_on_device=None, auto_lock_delay_ms=None, display_rotation=None, + unsafe_prompts=None, ): - settings = messages.ApplySettings() - if label is not None: - settings.label = label - if language: - settings.language = language - if use_passphrase is not None: - settings.use_passphrase = use_passphrase - if homescreen is not None: - settings.homescreen = homescreen - if passphrase_always_on_device is not None: - settings.passphrase_always_on_device = passphrase_always_on_device - if auto_lock_delay_ms is not None: - settings.auto_lock_delay_ms = auto_lock_delay_ms - if display_rotation is not None: - settings.display_rotation = display_rotation + settings = messages.ApplySettings( + label=label, + language=language, + use_passphrase=use_passphrase, + homescreen=homescreen, + passphrase_always_on_device=passphrase_always_on_device, + auto_lock_delay_ms=auto_lock_delay_ms, + display_rotation=display_rotation, + unsafe_prompts=unsafe_prompts, + ) out = client.call(settings) client.init_device() # Reload Features diff --git a/python/src/trezorlib/messages/ApplySettings.py b/python/src/trezorlib/messages/ApplySettings.py index 8a05ae1d63..0f25641880 100644 --- a/python/src/trezorlib/messages/ApplySettings.py +++ b/python/src/trezorlib/messages/ApplySettings.py @@ -22,6 +22,7 @@ class ApplySettings(p.MessageType): auto_lock_delay_ms: int = None, display_rotation: int = None, passphrase_always_on_device: bool = None, + unsafe_prompts: bool = None, ) -> None: self.language = language self.label = label @@ -30,6 +31,7 @@ class ApplySettings(p.MessageType): self.auto_lock_delay_ms = auto_lock_delay_ms self.display_rotation = display_rotation self.passphrase_always_on_device = passphrase_always_on_device + self.unsafe_prompts = unsafe_prompts @classmethod def get_fields(cls) -> Dict: @@ -41,4 +43,5 @@ class ApplySettings(p.MessageType): 6: ('auto_lock_delay_ms', p.UVarintType, 0), 7: ('display_rotation', p.UVarintType, 0), 8: ('passphrase_always_on_device', p.BoolType, 0), + 9: ('unsafe_prompts', p.BoolType, 0), }