1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-03-27 13:35:44 +00:00

feat(core): improve pairing screens

This commit is contained in:
M1nd3r 2025-03-12 15:35:43 +01:00
parent 45372fd93e
commit bb512ff96f
4 changed files with 67 additions and 16 deletions

View File

@ -93,8 +93,10 @@ async def handle_pairing_request(
if not ThpPairingRequest.is_type_of(message): if not ThpPairingRequest.is_type_of(message):
raise UnexpectedMessage("Unexpected message") raise UnexpectedMessage("Unexpected message")
ctx.host_name = message.host_name or "" if not message.host_name:
raise Exception("Missing host_name.")
ctx.host_name = message.host_name
await ctx.show_pairing_dialogue() await ctx.show_pairing_dialogue()
assert ThpSelectMethod.MESSAGE_WIRE_TYPE is not None assert ThpSelectMethod.MESSAGE_WIRE_TYPE is not None
select_method_msg = await ctx.read( select_method_msg = await ctx.read(
@ -385,6 +387,27 @@ async def _handle_credential_request(
if message.autoconnect is not None: if message.autoconnect is not None:
autoconnect = message.autoconnect autoconnect = message.autoconnect
if autoconnect:
# Cannot ask for autoconnect=True credential directly after pairing
if ctx.channel_ctx.credential is None:
raise Exception("Cannot ask for autoconnect credential after pairing!")
from storage.cache_common import CHANNEL_HOST_STATIC_PUBKEY
from .credential_manager import validate_credential
host_static_pubkey = ctx.channel_ctx.channel_cache.get(
CHANNEL_HOST_STATIC_PUBKEY
)
if not host_static_pubkey or not validate_credential(
credential=ctx.channel_ctx.credential, host_static_pubkey=host_static_pubkey
):
raise Exception(
"Cannot ask for autoconnect credential without a valid credential!"
)
await ctx.show_autoconnec_credential_confirmation_screen()
trezor_static_pubkey = crypto.get_trezor_static_pubkey() trezor_static_pubkey = crypto.get_trezor_static_pubkey()
credential_metadata = ThpCredentialMetadata( credential_metadata = ThpCredentialMetadata(
host_name=ctx.host_name, host_name=ctx.host_name,

View File

@ -142,15 +142,20 @@ class PairingContext(Context):
raise Exception("Not allowed to set this method") raise Exception("Not allowed to set this method")
self.selected_method = selected_method self.selected_method = selected_method
async def show_pairing_dialogue(self) -> None: async def show_pairing_dialogue(self, device_name: str | None = None) -> None:
from trezor.messages import ThpPairingRequestApproved from trezor.messages import ThpPairingRequestApproved
from trezor.ui.layouts.common import interact from trezor.ui.layouts.common import interact
if not device_name:
action_string = f"Allow {self.host_name} to pair with this Trezor?"
else:
action_string = (
f"Allow {self.host_name} on {device_name} to pair with this Trezor?"
)
result = await interact( result = await interact(
trezorui_api.confirm_action( trezorui_api.confirm_action(
title="Pairing dialogue", title="Before you continue", action=action_string, description=None
action="Do you want to start pairing?",
description="Choose wisely!",
), ),
br_name="pairing_request", br_name="pairing_request",
br_code=ButtonRequestType.Other, br_code=ButtonRequestType.Other,
@ -158,16 +163,33 @@ class PairingContext(Context):
if result == trezorui_api.CONFIRMED: if result == trezorui_api.CONFIRMED:
await self.write(ThpPairingRequestApproved()) await self.write(ThpPairingRequestApproved())
async def show_connection_dialogue(self) -> None: async def show_connection_dialogue(self, device_name: str | None = None) -> None:
from trezor.ui.layouts.common import interact
if not device_name:
action_string = f"Allow {self.host_name} to pair with this Trezor?"
else:
action_string = (
f"Allow {self.host_name} on {device_name} to pair with this Trezor?"
)
await interact(
trezorui_api.confirm_action(
title="Connection dialogue", action=action_string, description=None
),
br_name="connection_request",
br_code=ButtonRequestType.Other,
)
async def show_autoconnec_credential_confirmation_screen(self) -> None:
from trezor.ui.layouts.common import interact from trezor.ui.layouts.common import interact
await interact( await interact(
trezorui_api.confirm_action( trezorui_api.confirm_action(
title="Connection dialogue", title="Autoconnect credential",
action="Do you want previously connected device to connect?", action=f"Do you want to pair with {self.host_name} without confirmation?",
description="Choose wisely! (or not)", description=None,
), ),
br_name="connection_request", br_name="autoconnect_credential_request",
br_code=ButtonRequestType.Other, br_code=ButtonRequestType.Other,
) )
@ -192,8 +214,8 @@ class PairingContext(Context):
return await interact( return await interact(
trezorui_api.show_simple( trezorui_api.show_simple(
title="Copy the following", title="One more step",
text=self._get_code_code_entry_str(), text=f"Enter this one-time security code on {self.host_name}\n{self._get_code_code_entry_str()}",
button="Cancel", button="Cancel",
), ),
br_name=None, br_name=None,
@ -204,8 +226,8 @@ class PairingContext(Context):
return await interact( return await interact(
trezorui_api.show_simple( trezorui_api.show_simple(
title="NFC Pairing", title=None,
text="Move your device close to Trezor", text="Keep your Trezor near your phone to complete the setup.",
button="Cancel", button="Cancel",
), ),
br_name=None, br_name=None,

View File

@ -266,7 +266,7 @@ class ProtocolV2Channel(Channel):
def _do_pairing(self, helper_debug: DebugLink | None): def _do_pairing(self, helper_debug: DebugLink | None):
self._send_message(messages.ThpPairingRequest()) self._send_message(messages.ThpPairingRequest(host_name="Trezorlib"))
self._read_message(messages.ButtonRequest) self._read_message(messages.ButtonRequest)
self._send_message(messages.ButtonAck()) self._send_message(messages.ButtonAck())

View File

@ -317,11 +317,17 @@ def test_credential_phase(client: Client) -> None:
protocol._send_message( protocol._send_message(
ThpCredentialRequest(host_static_pubkey=host_static_pubkey, autoconnect=True) ThpCredentialRequest(host_static_pubkey=host_static_pubkey, autoconnect=True)
) )
# Confirmation dialog is shown. (Channel replacement is not triggered.) # Connection confirmation dialog is shown. (Channel replacement is not triggered.)
button_req = protocol._read_message(ButtonRequest) button_req = protocol._read_message(ButtonRequest)
assert button_req.name == "connection_request" assert button_req.name == "connection_request"
protocol._send_message(ButtonAck()) protocol._send_message(ButtonAck())
client.debug.press_yes() client.debug.press_yes()
# Autoconnect issuance confirmation dialog is shown.
button_req = protocol._read_message(ButtonRequest)
assert button_req.name == "autoconnect_credential_request"
protocol._send_message(ButtonAck())
client.debug.press_yes()
# Autoconnect credential is received
credential_response_2 = protocol._read_message(ThpCredentialResponse) credential_response_2 = protocol._read_message(ThpCredentialResponse)
assert credential_response_2.credential is not None assert credential_response_2.credential is not None
credential_auto = credential_response_2.credential credential_auto = credential_response_2.credential