1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-23 13:51:00 +00:00

feat(core): ability to not show passphrase coming from host

[no changelog]
This commit is contained in:
grdddj 2023-02-15 15:43:04 +01:00 committed by Jiří Musil
parent afb5c55416
commit 20d280d7e0
8 changed files with 147 additions and 15 deletions

View File

@ -122,6 +122,7 @@ message Features {
optional bool experimental_features = 40; // are experimental message types enabled? optional bool experimental_features = 40; // are experimental message types enabled?
optional bool busy = 41; // is the device busy, showing "Do not disconnect"? optional bool busy = 41; // is the device busy, showing "Do not disconnect"?
optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf 144x144, 2 = jpg 240x240 optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf 144x144, 2 = jpg 240x240
optional bool hide_passphrase_from_host = 43; // should we hide the passphrase when it comes from host?
} }
/** /**
@ -168,6 +169,7 @@ message ApplySettings {
optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry
optional SafetyCheckLevel safety_checks = 9; // Safety check level, set to Prompt to limit path namespace enforcement optional SafetyCheckLevel safety_checks = 9; // Safety check level, set to Prompt to limit path namespace enforcement
optional bool experimental_features = 10; // enable experimental message types optional bool experimental_features = 10; // enable experimental message types
optional bool hide_passphrase_from_host = 11; // do not show passphrase coming from host
} }
/** /**

View File

@ -128,6 +128,7 @@ def get_features() -> Features:
f.auto_lock_delay_ms = storage_device.get_autolock_delay_ms() f.auto_lock_delay_ms = storage_device.get_autolock_delay_ms()
f.display_rotation = storage_device.get_rotation() f.display_rotation = storage_device.get_rotation()
f.experimental_features = storage_device.get_experimental_features() f.experimental_features = storage_device.get_experimental_features()
f.hide_passphrase_from_host = storage_device.get_hide_passphrase_from_host()
return f return f

View File

@ -59,19 +59,29 @@ async def _request_on_host(ctx: Context) -> str:
if passphrase: if passphrase:
from trezor.ui.layouts import confirm_action, confirm_blob from trezor.ui.layouts import confirm_action, confirm_blob
await confirm_action( # We want to hide the passphrase, or show it, according to settings.
ctx, if storage_device.get_hide_passphrase_from_host():
"passphrase_host1", explanation = "Passphrase provided by host will be used but will not be displayed due to the device settings."
"Hidden wallet", await confirm_action(
description="Access hidden wallet?\n\nNext screen will show the passphrase!", ctx,
) "passphrase_host1_hidden",
"Hidden wallet",
description=f"Access hidden wallet?\n\n{explanation}",
)
else:
await confirm_action(
ctx,
"passphrase_host1",
"Hidden wallet",
description="Access hidden wallet?\n\nNext screen will show the passphrase!",
)
await confirm_blob( await confirm_blob(
ctx, ctx,
"passphrase_host2", "passphrase_host2",
"Hidden wallet", "Hidden wallet",
passphrase, passphrase,
"Use this passphrase?\n", "Use this passphrase?\n",
) )
return passphrase return passphrase

View File

@ -58,6 +58,7 @@ async def apply_settings(ctx: Context, msg: ApplySettings) -> Success:
display_rotation = msg.display_rotation # local_cache_attribute display_rotation = msg.display_rotation # local_cache_attribute
msg_safety_checks = msg.safety_checks # local_cache_attribute msg_safety_checks = msg.safety_checks # local_cache_attribute
experimental_features = msg.experimental_features # local_cache_attribute experimental_features = msg.experimental_features # local_cache_attribute
hide_passphrase_from_host = msg.hide_passphrase_from_host # local_cache_attribute
if ( if (
homescreen is None homescreen is None
@ -68,6 +69,7 @@ async def apply_settings(ctx: Context, msg: ApplySettings) -> Success:
and auto_lock_delay_ms is None and auto_lock_delay_ms is None
and msg_safety_checks is None and msg_safety_checks is None
and experimental_features is None and experimental_features is None
and hide_passphrase_from_host is None
): ):
raise ProcessError("No setting provided") raise ProcessError("No setting provided")
@ -117,6 +119,12 @@ async def apply_settings(ctx: Context, msg: ApplySettings) -> Success:
await _require_confirm_experimental_features(ctx, experimental_features) await _require_confirm_experimental_features(ctx, experimental_features)
storage_device.set_experimental_features(experimental_features) storage_device.set_experimental_features(experimental_features)
if hide_passphrase_from_host is not None:
if safety_checks.is_strict():
raise ProcessError("Safety checks are strict")
await _require_confirm_hide_passphrase_from_host(ctx, hide_passphrase_from_host)
storage_device.set_hide_passphrase_from_host(hide_passphrase_from_host)
reload_settings_from_storage() reload_settings_from_storage()
return Success(message="Settings applied") return Success(message="Settings applied")
@ -269,3 +277,16 @@ async def _require_confirm_experimental_features(
reverse=True, reverse=True,
br_code=BRT_PROTECT_CALL, br_code=BRT_PROTECT_CALL,
) )
async def _require_confirm_hide_passphrase_from_host(
ctx: GenericContext, enable: bool
) -> None:
if enable:
await confirm_action(
ctx,
"set_hide_passphrase_from_host",
"Hide passphrase",
description="Hide passphrase coming from host?",
br_code=BRT_PROTECT_CALL,
)

View File

@ -34,6 +34,7 @@ _SD_SALT_AUTH_KEY = const(0x12) # bytes
INITIALIZED = const(0x13) # bool (0x01 or empty) INITIALIZED = const(0x13) # bool (0x01 or empty)
_SAFETY_CHECK_LEVEL = const(0x14) # int _SAFETY_CHECK_LEVEL = const(0x14) # int
_EXPERIMENTAL_FEATURES = const(0x15) # bool (0x01 or empty) _EXPERIMENTAL_FEATURES = const(0x15) # bool (0x01 or empty)
_HIDE_PASSPHRASE_FROM_HOST = const(0x16) # bool (0x01 or empty)
SAFETY_CHECK_LEVEL_STRICT : Literal[0] = const(0) SAFETY_CHECK_LEVEL_STRICT : Literal[0] = const(0)
SAFETY_CHECK_LEVEL_PROMPT : Literal[1] = const(1) SAFETY_CHECK_LEVEL_PROMPT : Literal[1] = const(1)
@ -334,3 +335,17 @@ def set_experimental_features(enabled: bool) -> None:
cached_bytes = b"\x01" if enabled else b"" cached_bytes = b"\x01" if enabled else b""
storage_cache.set(storage_cache.STORAGE_DEVICE_EXPERIMENTAL_FEATURES, cached_bytes) storage_cache.set(storage_cache.STORAGE_DEVICE_EXPERIMENTAL_FEATURES, cached_bytes)
common.set_true_or_delete(_NAMESPACE, _EXPERIMENTAL_FEATURES, enabled) common.set_true_or_delete(_NAMESPACE, _EXPERIMENTAL_FEATURES, enabled)
def set_hide_passphrase_from_host(hide: bool) -> None:
"""
Whether we should hide the passphrase from the host.
"""
common.set_bool(_NAMESPACE, _HIDE_PASSPHRASE_FROM_HOST, hide)
def get_hide_passphrase_from_host() -> bool:
"""
Whether we should hide the passphrase from the host.
"""
return common.get_bool(_NAMESPACE, _HIDE_PASSPHRASE_FROM_HOST)

View File

@ -2104,6 +2104,7 @@ if TYPE_CHECKING:
experimental_features: "bool | None" experimental_features: "bool | None"
busy: "bool | None" busy: "bool | None"
homescreen_format: "HomescreenFormat | None" homescreen_format: "HomescreenFormat | None"
hide_passphrase_from_host: "bool | None"
def __init__( def __init__(
self, self,
@ -2147,6 +2148,7 @@ if TYPE_CHECKING:
experimental_features: "bool | None" = None, experimental_features: "bool | None" = None,
busy: "bool | None" = None, busy: "bool | None" = None,
homescreen_format: "HomescreenFormat | None" = None, homescreen_format: "HomescreenFormat | None" = None,
hide_passphrase_from_host: "bool | None" = None,
) -> None: ) -> None:
pass pass
@ -2190,6 +2192,7 @@ if TYPE_CHECKING:
passphrase_always_on_device: "bool | None" passphrase_always_on_device: "bool | None"
safety_checks: "SafetyCheckLevel | None" safety_checks: "SafetyCheckLevel | None"
experimental_features: "bool | None" experimental_features: "bool | None"
hide_passphrase_from_host: "bool | None"
def __init__( def __init__(
self, self,
@ -2203,6 +2206,7 @@ if TYPE_CHECKING:
passphrase_always_on_device: "bool | None" = None, passphrase_always_on_device: "bool | None" = None,
safety_checks: "SafetyCheckLevel | None" = None, safety_checks: "SafetyCheckLevel | None" = None,
experimental_features: "bool | None" = None, experimental_features: "bool | None" = None,
hide_passphrase_from_host: "bool | None" = None,
) -> None: ) -> None:
pass pass

View File

@ -18,9 +18,10 @@ import random
import pytest import pytest
from trezorlib import exceptions, messages from trezorlib import device, exceptions, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import FailureType from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import FailureType, SafetyCheckLevel
from trezorlib.tools import parse_path from trezorlib.tools import parse_path
XPUB_PASSPHRASES = { XPUB_PASSPHRASES = {
@ -375,6 +376,83 @@ def test_passphrase_length(client: Client):
call(passphrase="A" * 49 + "š", expected_result=False) call(passphrase="A" * 49 + "š", expected_result=False)
@pytest.mark.skip_t1
@pytest.mark.setup_client(passphrase=True)
def test_hide_passphrase_from_host(client: Client):
# Without safety checks, turning it on fails
with pytest.raises(TrezorFailure, match="Safety checks are strict"), client:
device.apply_settings(client, hide_passphrase_from_host=True)
device.apply_settings(client, safety_checks=SafetyCheckLevel.PromptTemporarily)
# Turning it on
device.apply_settings(client, hide_passphrase_from_host=True)
passphrase = "abc"
with client:
def input_flow():
yield
layout = client.debug.wait_layout()
assert (
"Passphrase provided by host will be used but will not be displayed due to the device settings."
in layout.get_content()
)
client.debug.press_yes()
client.watch_layout()
client.set_input_flow(input_flow)
client.set_expected_responses(
[
messages.PassphraseRequest,
messages.ButtonRequest,
messages.PublicKey,
]
)
client.use_passphrase(passphrase)
result = client.call(XPUB_REQUEST)
assert isinstance(result, messages.PublicKey)
xpub_hidden_passphrase = result.xpub
# Turning it off
device.apply_settings(client, hide_passphrase_from_host=False)
# Starting new session, otherwise the passphrase would be cached
_init_session(client)
with client:
def input_flow():
yield
layout = client.debug.wait_layout()
assert "Next screen will show the passphrase!" in layout.get_content()
client.debug.press_yes()
yield
layout = client.debug.wait_layout()
assert "Use this passphrase?" in layout.get_content()
assert passphrase in layout.get_content()
client.debug.press_yes()
client.watch_layout()
client.set_input_flow(input_flow)
client.set_expected_responses(
[
messages.PassphraseRequest,
messages.ButtonRequest,
messages.ButtonRequest,
messages.PublicKey,
]
)
client.use_passphrase(passphrase)
result = client.call(XPUB_REQUEST)
assert isinstance(result, messages.PublicKey)
xpub_shown_passphrase = result.xpub
assert xpub_hidden_passphrase == xpub_shown_passphrase
def _get_xpub_cardano(client: Client, passphrase): def _get_xpub_cardano(client: Client, passphrase):
msg = messages.CardanoGetPublicKey( msg = messages.CardanoGetPublicKey(
address_n=parse_path("m/44h/1815h/0h/0/0"), address_n=parse_path("m/44h/1815h/0h/0/0"),

View File

@ -1705,6 +1705,7 @@
"TT_test_session.py::test_end_session_only_current": "bd83a31d0fc4c23953dfd0d138e4441984e34698ace96aad5308a4ae51b712ae", "TT_test_session.py::test_end_session_only_current": "bd83a31d0fc4c23953dfd0d138e4441984e34698ace96aad5308a4ae51b712ae",
"TT_test_session.py::test_session_recycling": "07f265234a3269c8c99172e2f67abf626e17a542e6f4c00b45c5b685d0e4c240", "TT_test_session.py::test_session_recycling": "07f265234a3269c8c99172e2f67abf626e17a542e6f4c00b45c5b685d0e4c240",
"TT_test_session_id_and_passphrase.py::test_cardano_passphrase": "26e3b8bc6b1ba85fc89d5b237096685309a95f73350d2fca646720fb750d0e8f", "TT_test_session_id_and_passphrase.py::test_cardano_passphrase": "26e3b8bc6b1ba85fc89d5b237096685309a95f73350d2fca646720fb750d0e8f",
"TT_test_session_id_and_passphrase.py::test_hide_passphrase_from_host": "ccdb34cba3b759d91500b3c2b2ee5d427167d21b6aefa709b659aed298760605",
"TT_test_session_id_and_passphrase.py::test_max_sessions_with_passphrases": "5c85cb246fe4bb07076ed88f426e6133368f3417472cf2dfbef2daac32a088aa", "TT_test_session_id_and_passphrase.py::test_max_sessions_with_passphrases": "5c85cb246fe4bb07076ed88f426e6133368f3417472cf2dfbef2daac32a088aa",
"TT_test_session_id_and_passphrase.py::test_multiple_passphrases": "12231bae2ea96b600c96ade946e5dda29d6625d128906cf14728869c1e697987", "TT_test_session_id_and_passphrase.py::test_multiple_passphrases": "12231bae2ea96b600c96ade946e5dda29d6625d128906cf14728869c1e697987",
"TT_test_session_id_and_passphrase.py::test_multiple_sessions": "bd83a31d0fc4c23953dfd0d138e4441984e34698ace96aad5308a4ae51b712ae", "TT_test_session_id_and_passphrase.py::test_multiple_sessions": "bd83a31d0fc4c23953dfd0d138e4441984e34698ace96aad5308a4ae51b712ae",