diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index d3a90de37f..eab9208544 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -89,6 +89,7 @@ message Features { optional bool sd_protection = 33; // is SD Protect enabled optional bool wipe_code_protection = 34; // is wipe code protection enabled optional bytes session_id = 35; + optional bool passphrase_always_on_device = 36; // device enforces passphrase entry on Trezor } /** @@ -110,17 +111,10 @@ message ApplySettings { optional string label = 2; optional bool use_passphrase = 3; optional bytes homescreen = 4; - optional PassphraseSourceType passphrase_source = 5; +// optional PassphraseSourceType passphrase_source = 5; DEPRECATED optional uint32 auto_lock_delay_ms = 6; optional uint32 display_rotation = 7; // in degrees from North - /** - * Structure representing passphrase source - */ - enum PassphraseSourceType { - ASK = 0; - DEVICE = 1; - HOST = 2; - } + optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry } /** diff --git a/core/src/apps/homescreen/__init__.py b/core/src/apps/homescreen/__init__.py index 0a91487dae..8134d8d2f7 100644 --- a/core/src/apps/homescreen/__init__.py +++ b/core/src/apps/homescreen/__init__.py @@ -74,6 +74,7 @@ def get_features() -> Features: f.sd_protection = storage.sd_salt.is_enabled() f.wipe_code_protection = config.has_wipe_code() f.session_id = cache.get_session_id() + f.passphrase_always_on_device = storage.device.get_passphrase_always_on_device() return f diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index f72a0255b5..a235e7b7be 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -1,6 +1,6 @@ import storage.device from trezor import ui, wire -from trezor.messages import ButtonRequestType, PassphraseSourceType +from trezor.messages import ButtonRequestType from trezor.messages.Success import Success from trezor.ui.text import Text @@ -12,7 +12,7 @@ async def apply_settings(ctx, msg): msg.homescreen is None and msg.label is None and msg.use_passphrase is None - and msg.passphrase_source is None + and msg.passphrase_always_on_device is None and msg.display_rotation is None ): raise wire.ProcessError("No setting provided") @@ -28,8 +28,10 @@ async def apply_settings(ctx, msg): if msg.use_passphrase is not None: await require_confirm_change_passphrase(ctx, msg.use_passphrase) - if msg.passphrase_source is not None: - await require_confirm_change_passphrase_source(ctx, msg.passphrase_source) + if msg.passphrase_always_on_device is not None: + await require_confirm_change_passphrase_source( + ctx, msg.passphrase_always_on_device + ) if msg.display_rotation is not None: await require_confirm_change_display_rotation(ctx, msg.display_rotation) @@ -38,7 +40,7 @@ async def apply_settings(ctx, msg): label=msg.label, use_passphrase=msg.use_passphrase, homescreen=msg.homescreen, - passphrase_source=msg.passphrase_source, + passphrase_always_on_device=msg.passphrase_always_on_device, display_rotation=msg.display_rotation, ) @@ -69,16 +71,16 @@ async def require_confirm_change_passphrase(ctx, use): await require_confirm(ctx, text, ButtonRequestType.ProtectCall) -async def require_confirm_change_passphrase_source(ctx, source): - if source == PassphraseSourceType.DEVICE: - desc = "ON DEVICE" - elif source == PassphraseSourceType.HOST: - desc = "ON HOST" - else: - desc = "ASK" +async def require_confirm_change_passphrase_source( + ctx, passphrase_always_on_device: bool +): text = Text("Passphrase source", ui.ICON_CONFIG) - text.normal("Do you really want to", "change the passphrase", "source to") - text.bold("ALWAYS %s?" % desc) + if passphrase_always_on_device: + text.normal( + "Do you really want to", "entry 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) diff --git a/core/src/storage/device.py b/core/src/storage/device.py index adbbf78d9a..47f549426a 100644 --- a/core/src/storage/device.py +++ b/core/src/storage/device.py @@ -24,7 +24,7 @@ _HOMESCREEN = const(0x06) # bytes _NEEDS_BACKUP = const(0x07) # bool (0x01 or empty) _FLAGS = const(0x08) # int U2F_COUNTER = const(0x09) # int -_PASSPHRASE_SOURCE = const(0x0A) # int +_PASSPHRASE_ALWAYS_ON_DEVICE = const(0x0A) # bool (0x01 or empty) _UNFINISHED_BACKUP = const(0x0B) # bool (0x01 or empty) _AUTOLOCK_DELAY_MS = const(0x0C) # int _NO_BACKUP = const(0x0D) # bool (0x01 or empty) @@ -143,21 +143,24 @@ def no_backup() -> bool: return common.get_bool(_NAMESPACE, _NO_BACKUP) -def get_passphrase_source() -> int: - b = common.get(_NAMESPACE, _PASSPHRASE_SOURCE) - if b == b"\x01": - return 1 - elif b == b"\x02": - return 2 +def get_passphrase_always_on_device() -> bool: + b = common.get(_NAMESPACE, _PASSPHRASE_ALWAYS_ON_DEVICE) + # backwards compatible for _PASSPHRASE_SOURCE.HOST = 2 + if b == b"\x02": + return False + # backwards compatible for _PASSPHRASE_SOURCE.DEVICE = 1 + # and also \x01 is TRUE_BYTE so it is future compatible as well + elif b == b"\x01": + return True else: - return 0 + return False def load_settings( label: str = None, use_passphrase: bool = None, homescreen: bytes = None, - passphrase_source: int = None, + passphrase_always_on_device: bool = None, display_rotation: int = None, ) -> None: if label is not None: @@ -170,9 +173,10 @@ def load_settings( common.set(_NAMESPACE, _HOMESCREEN, homescreen, True) # public else: common.set(_NAMESPACE, _HOMESCREEN, b"", True) # public - if passphrase_source is not None: - if passphrase_source in (0, 1, 2): - common.set(_NAMESPACE, _PASSPHRASE_SOURCE, bytes([passphrase_source])) + if passphrase_always_on_device is not None: + common.set_bool( + _NAMESPACE, _PASSPHRASE_ALWAYS_ON_DEVICE, passphrase_always_on_device + ) if display_rotation is not None: if display_rotation not in (0, 90, 180, 270): raise ValueError( diff --git a/core/src/trezor/messages/ApplySettings.py b/core/src/trezor/messages/ApplySettings.py index 986d105918..74f06c92d4 100644 --- a/core/src/trezor/messages/ApplySettings.py +++ b/core/src/trezor/messages/ApplySettings.py @@ -6,7 +6,6 @@ if __debug__: try: from typing import Dict, List # noqa: F401 from typing_extensions import Literal # noqa: F401 - EnumTypePassphraseSourceType = Literal[0, 1, 2] except ImportError: pass @@ -20,17 +19,17 @@ class ApplySettings(p.MessageType): label: str = None, use_passphrase: bool = None, homescreen: bytes = None, - passphrase_source: EnumTypePassphraseSourceType = None, auto_lock_delay_ms: int = None, display_rotation: int = None, + passphrase_always_on_device: bool = None, ) -> None: self.language = language self.label = label self.use_passphrase = use_passphrase self.homescreen = homescreen - self.passphrase_source = passphrase_source self.auto_lock_delay_ms = auto_lock_delay_ms self.display_rotation = display_rotation + self.passphrase_always_on_device = passphrase_always_on_device @classmethod def get_fields(cls) -> Dict: @@ -39,7 +38,7 @@ class ApplySettings(p.MessageType): 2: ('label', p.UnicodeType, 0), 3: ('use_passphrase', p.BoolType, 0), 4: ('homescreen', p.BytesType, 0), - 5: ('passphrase_source', p.EnumType("PassphraseSourceType", (0, 1, 2)), 0), 6: ('auto_lock_delay_ms', p.UVarintType, 0), 7: ('display_rotation', p.UVarintType, 0), + 8: ('passphrase_always_on_device', p.BoolType, 0), } diff --git a/core/src/trezor/messages/Features.py b/core/src/trezor/messages/Features.py index 639a811397..8f37f01560 100644 --- a/core/src/trezor/messages/Features.py +++ b/core/src/trezor/messages/Features.py @@ -51,6 +51,7 @@ class Features(p.MessageType): sd_protection: bool = None, wipe_code_protection: bool = None, session_id: bytes = None, + passphrase_always_on_device: bool = None, ) -> None: self.vendor = vendor self.major_version = major_version @@ -86,6 +87,7 @@ class Features(p.MessageType): self.sd_protection = sd_protection self.wipe_code_protection = wipe_code_protection self.session_id = session_id + self.passphrase_always_on_device = passphrase_always_on_device @classmethod def get_fields(cls) -> Dict: @@ -124,4 +126,5 @@ class Features(p.MessageType): 33: ('sd_protection', p.BoolType, 0), 34: ('wipe_code_protection', p.BoolType, 0), 35: ('session_id', p.BytesType, 0), + 36: ('passphrase_always_on_device', p.BoolType, 0), } diff --git a/core/src/trezor/messages/PassphraseSourceType.py b/core/src/trezor/messages/PassphraseSourceType.py deleted file mode 100644 index d62ea047d3..0000000000 --- a/core/src/trezor/messages/PassphraseSourceType.py +++ /dev/null @@ -1,8 +0,0 @@ -# Automatically generated by pb2py -# fmt: off -if False: - from typing_extensions import Literal - -ASK = 0 # type: Literal[0] -DEVICE = 1 # type: Literal[1] -HOST = 2 # type: Literal[2] diff --git a/core/src/trezor/ui/passphrase.py b/core/src/trezor/ui/passphrase.py index 7dc66e6371..c2713fce1a 100644 --- a/core/src/trezor/ui/passphrase.py +++ b/core/src/trezor/ui/passphrase.py @@ -1,7 +1,6 @@ from micropython import const from trezor import io, loop, res, ui -from trezor.messages import PassphraseSourceType from trezor.ui import display from trezor.ui.button import Button, ButtonClear, ButtonConfirm from trezor.ui.swipe import SWIPE_HORIZONTAL, SWIPE_LEFT, Swipe @@ -246,25 +245,3 @@ class PassphraseKeyboard(ui.Layout): def create_tasks(self) -> Tuple[loop.Task, ...]: return self.handle_input(), self.handle_rendering(), self.handle_paging() - - -class PassphraseSource(ui.Layout): - def __init__(self, content: ui.Component) -> None: - self.content = content - - self.device = Button(ui.grid(8, n_y=4, n_x=4, cells_x=4), "Device") - self.device.on_click = self.on_device # type: ignore - - self.host = Button(ui.grid(12, n_y=4, n_x=4, cells_x=4), "Host") - self.host.on_click = self.on_host # type: ignore - - def dispatch(self, event: int, x: int, y: int) -> None: - self.content.dispatch(event, x, y) - self.device.dispatch(event, x, y) - self.host.dispatch(event, x, y) - - def on_device(self) -> None: - raise ui.Result(PassphraseSourceType.DEVICE) - - def on_host(self) -> None: - raise ui.Result(PassphraseSourceType.HOST) diff --git a/python/src/trezorlib/cli/settings.py b/python/src/trezorlib/cli/settings.py index 2bf72cc389..5368875462 100644 --- a/python/src/trezorlib/cli/settings.py +++ b/python/src/trezorlib/cli/settings.py @@ -16,15 +16,9 @@ import click -from .. import device, messages +from .. import device from . import ChoiceType -PASSPHRASE_SOURCE = { - "ask": messages.PassphraseSourceType.ASK, - "device": messages.PassphraseSourceType.DEVICE, - "host": messages.PassphraseSourceType.HOST, -} - ROTATION = {"north": 0, "east": 90, "south": 180, "west": 270} @@ -145,10 +139,24 @@ def passphrase(): @passphrase.command(name="enabled") +@click.option("-f", "--force-on-device", is_flag=True) +@click.option("-F", "--no-force-on-device", is_flag=True) @click.pass_obj -def passphrase_enable(connect): +def passphrase_enable(connect, force_on_device: bool, no_force_on_device: bool): """Enable passphrase.""" - return device.apply_settings(connect(), use_passphrase=True) + if force_on_device and no_force_on_device: + raise ValueError( + "Only one option of --force-on-device/-no-force-on-device makes sense." + ) + on_device = None + if force_on_device: + on_device = True + if no_force_on_device: + on_device = False + + return device.apply_settings( + connect(), use_passphrase=True, passphrase_always_on_device=on_device + ) @passphrase.command(name="disabled") @@ -156,19 +164,3 @@ def passphrase_enable(connect): def passphrase_disable(connect): """Disable passphrase.""" return device.apply_settings(connect(), use_passphrase=False) - - -@passphrase.command(name="source") -@click.argument("source", type=ChoiceType(PASSPHRASE_SOURCE)) -@click.pass_obj -def passphrase_source(connect, source): - """Set passphrase source. - - Configure how to enter passphrase on Trezor Model T. The options are: - - \b - ask - always ask where to enter passphrase - device - always enter passphrase on device - host - always enter passphrase on host - """ - return device.apply_settings(connect(), passphrase_source=source) diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py index f90071e0fb..9f59e73924 100755 --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -51,7 +51,6 @@ COMMAND_ALIASES = { "change-pin": settings.pin, "enable-passphrase": settings.passphrase_enable, "disable-passphrase": settings.passphrase_disable, - "set-passphrase-source": settings.passphrase_source, "wipe-device": device.wipe, "reset-device": device.setup, "recovery-device": device.recover, diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py index ddd89ba2b0..0f51c2dddb 100644 --- a/python/src/trezorlib/device.py +++ b/python/src/trezorlib/device.py @@ -51,7 +51,7 @@ def apply_settings( language=None, use_passphrase=None, homescreen=None, - passphrase_source=None, + passphrase_always_on_device=None, auto_lock_delay_ms=None, display_rotation=None, ): @@ -64,8 +64,8 @@ def apply_settings( settings.use_passphrase = use_passphrase if homescreen is not None: settings.homescreen = homescreen - if passphrase_source is not None: - settings.passphrase_source = passphrase_source + 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: diff --git a/python/src/trezorlib/messages/ApplySettings.py b/python/src/trezorlib/messages/ApplySettings.py index 329a179f2a..8a05ae1d63 100644 --- a/python/src/trezorlib/messages/ApplySettings.py +++ b/python/src/trezorlib/messages/ApplySettings.py @@ -6,7 +6,6 @@ if __debug__: try: from typing import Dict, List # noqa: F401 from typing_extensions import Literal # noqa: F401 - EnumTypePassphraseSourceType = Literal[0, 1, 2] except ImportError: pass @@ -20,17 +19,17 @@ class ApplySettings(p.MessageType): label: str = None, use_passphrase: bool = None, homescreen: bytes = None, - passphrase_source: EnumTypePassphraseSourceType = None, auto_lock_delay_ms: int = None, display_rotation: int = None, + passphrase_always_on_device: bool = None, ) -> None: self.language = language self.label = label self.use_passphrase = use_passphrase self.homescreen = homescreen - self.passphrase_source = passphrase_source self.auto_lock_delay_ms = auto_lock_delay_ms self.display_rotation = display_rotation + self.passphrase_always_on_device = passphrase_always_on_device @classmethod def get_fields(cls) -> Dict: @@ -39,7 +38,7 @@ class ApplySettings(p.MessageType): 2: ('label', p.UnicodeType, 0), 3: ('use_passphrase', p.BoolType, 0), 4: ('homescreen', p.BytesType, 0), - 5: ('passphrase_source', p.EnumType("PassphraseSourceType", (0, 1, 2)), 0), 6: ('auto_lock_delay_ms', p.UVarintType, 0), 7: ('display_rotation', p.UVarintType, 0), + 8: ('passphrase_always_on_device', p.BoolType, 0), } diff --git a/python/src/trezorlib/messages/Features.py b/python/src/trezorlib/messages/Features.py index fc4be15108..db0f3e5b26 100644 --- a/python/src/trezorlib/messages/Features.py +++ b/python/src/trezorlib/messages/Features.py @@ -51,6 +51,7 @@ class Features(p.MessageType): sd_protection: bool = None, wipe_code_protection: bool = None, session_id: bytes = None, + passphrase_always_on_device: bool = None, ) -> None: self.vendor = vendor self.major_version = major_version @@ -86,6 +87,7 @@ class Features(p.MessageType): self.sd_protection = sd_protection self.wipe_code_protection = wipe_code_protection self.session_id = session_id + self.passphrase_always_on_device = passphrase_always_on_device @classmethod def get_fields(cls) -> Dict: @@ -124,4 +126,5 @@ class Features(p.MessageType): 33: ('sd_protection', p.BoolType, 0), 34: ('wipe_code_protection', p.BoolType, 0), 35: ('session_id', p.BytesType, 0), + 36: ('passphrase_always_on_device', p.BoolType, 0), } diff --git a/python/src/trezorlib/messages/PassphraseSourceType.py b/python/src/trezorlib/messages/PassphraseSourceType.py deleted file mode 100644 index d62ea047d3..0000000000 --- a/python/src/trezorlib/messages/PassphraseSourceType.py +++ /dev/null @@ -1,8 +0,0 @@ -# Automatically generated by pb2py -# fmt: off -if False: - from typing_extensions import Literal - -ASK = 0 # type: Literal[0] -DEVICE = 1 # type: Literal[1] -HOST = 2 # type: Literal[2] diff --git a/python/src/trezorlib/messages/__init__.py b/python/src/trezorlib/messages/__init__.py index f829461b61..dea408822d 100644 --- a/python/src/trezorlib/messages/__init__.py +++ b/python/src/trezorlib/messages/__init__.py @@ -278,7 +278,6 @@ from . import NEMModificationType from . import NEMMosaicLevy from . import NEMSupplyChangeType from . import OutputScriptType -from . import PassphraseSourceType from . import PinMatrixRequestType from . import RecoveryDeviceType from . import RequestType diff --git a/tests/conftest.py b/tests/conftest.py index fecfa8d8b2..2fceb7ea76 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,8 +20,7 @@ import pytest from trezorlib import debuglink, log from trezorlib.debuglink import TrezorClientDebugLink -from trezorlib.device import apply_settings, wipe as wipe_device -from trezorlib.messages.PassphraseSourceType import HOST as PASSPHRASE_ON_HOST +from trezorlib.device import wipe as wipe_device from trezorlib.transport import enumerate_devices, get_transport from . import ui_tests @@ -130,8 +129,8 @@ def client(request): needs_backup=setup_params["needs_backup"], no_backup=setup_params["no_backup"], ) - if setup_params["passphrase"] and client.features.model != "1": - apply_settings(client, passphrase_source=PASSPHRASE_ON_HOST) + if setup_params["passphrase"]: + client.passphrase_on_host = True if setup_params["pin"]: # ClearSession locks the device. We only do that if the PIN is set. diff --git a/tests/device_tests/test_msg_loaddevice.py b/tests/device_tests/test_msg_loaddevice.py index e308a4216f..44d0d89f86 100644 --- a/tests/device_tests/test_msg_loaddevice.py +++ b/tests/device_tests/test_msg_loaddevice.py @@ -18,7 +18,6 @@ import pytest from trezorlib import btc, debuglink, device from trezorlib.messages import BackupType -from trezorlib.messages.PassphraseSourceType import HOST as PASSPHRASE_ON_HOST from ..common import ( MNEMONIC12, @@ -53,8 +52,7 @@ class TestDeviceLoad: passphrase_protection=True, label="test", ) - if client.features.model == "T": - device.apply_settings(client, passphrase_source=PASSPHRASE_ON_HOST) + client.passphrase_on_host = True client.set_passphrase("passphrase") state = client.debug.state() assert state.mnemonic_secret == MNEMONIC12.encode() @@ -108,7 +106,7 @@ class TestDeviceLoad: u"Neuve\u030cr\u030citelne\u030c bezpec\u030cne\u0301 hesli\u0301c\u030cko" ) - device.wipe(client) + client.passphrase_on_host = True debuglink.load_device( client, mnemonic=words_nfkd, @@ -118,8 +116,6 @@ class TestDeviceLoad: language="en-US", skip_checksum=True, ) - if client.features.model == "T": - device.apply_settings(client, passphrase_source=PASSPHRASE_ON_HOST) client.set_passphrase(passphrase_nfkd) address_nfkd = btc.get_address(client, "Bitcoin", []) @@ -133,8 +129,6 @@ class TestDeviceLoad: language="en-US", skip_checksum=True, ) - if client.features.model == "T": - device.apply_settings(client, passphrase_source=PASSPHRASE_ON_HOST) client.set_passphrase(passphrase_nfc) address_nfc = btc.get_address(client, "Bitcoin", []) @@ -148,8 +142,6 @@ class TestDeviceLoad: language="en-US", skip_checksum=True, ) - if client.features.model == "T": - device.apply_settings(client, passphrase_source=PASSPHRASE_ON_HOST) client.set_passphrase(passphrase_nfkc) address_nfkc = btc.get_address(client, "Bitcoin", []) @@ -163,8 +155,6 @@ class TestDeviceLoad: language="en-US", skip_checksum=True, ) - if client.features.model == "T": - device.apply_settings(client, passphrase_source=PASSPHRASE_ON_HOST) client.set_passphrase(passphrase_nfd) address_nfd = btc.get_address(client, "Bitcoin", [])