1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-19 08:39:03 +00:00
This commit is contained in:
M1nd3r 2025-04-11 15:22:04 +02:00
parent 89d0c545c7
commit e5d92ceb2f
14 changed files with 326 additions and 33 deletions

View File

@ -157,6 +157,16 @@ message DebugLinkState {
optional bytes nfc_secret_trezor = 5; // NFC secret used in NFC pairing
}
/**
* Request: Disable or enable the pairing dialog for a given THP channel
* @start
* @next Success
*/
message DebugLinkToggleThpPairingDialog{
optional bytes channel_id=1;
optional bool show_dialog=2 [default=false];
}
/**
* Request: Ask device to restart
* @start

View File

@ -31,6 +31,7 @@ if __debug__:
DebugLinkRecordScreen,
DebugLinkReseedRandom,
DebugLinkState,
DebugLinkToggleThpPairingDialog,
)
from trezor.ui import Layout
from trezor.wire import WireInterface
@ -268,6 +269,25 @@ if __debug__:
tokens=tokens,
)
async def dispatch_DebugLinkTogglePairingDialog(
msg: DebugLinkToggleThpPairingDialog,
) -> Success:
if not utils.USE_THP:
raise RuntimeError("Trezor does not support THP")
if msg.channel_id is None:
raise RuntimeError("Invalid DebugLinkToggleThpPairingDialog message")
from trezor.wire.thp.channel import Channel
from trezor.wire.thp.thp_main import _CHANNELS
channel_id = int.from_bytes(msg.channel_id, "big")
channel: Channel | None = None
try:
channel = _CHANNELS[channel_id]
except KeyError:
raise RuntimeError("Provided channel is invalid")
channel.should_show_pairing_dialog = msg.show_dialog
return Success()
async def dispatch_DebugLinkGetPairingInfo(
msg: DebugLinkGetPairingInfo,
) -> DebugLinkPairingInfo | None:

View File

@ -97,7 +97,10 @@ async def handle_pairing_request(
raise Exception("Missing host_name.")
ctx.host_name = message.host_name
await ctx.show_pairing_dialogue()
if __debug__ and not ctx.channel_ctx.should_show_pairing_dialog:
_skip_pairing_dialog(ctx)
else:
await ctx.show_pairing_dialog()
assert ThpSelectMethod.MESSAGE_WIRE_TYPE is not None
select_method_msg = await ctx.read(
[
@ -165,7 +168,7 @@ async def handle_credential_phase(
raise Exception("Credential does not have a hostname")
if show_connection_dialog and not autoconnect:
await ctx.show_connection_dialogue()
await ctx.show_connection_dialog()
while ThpCredentialRequest.is_type_of(message):
message = await _handle_credential_request(ctx, message)
@ -489,3 +492,18 @@ def _get_message_type_for_method(method: int) -> int:
if method is ThpPairingMethod.QrCode:
return ThpMessageType.ThpQrCodeTag
raise ValueError("Unexpected pairing method - no message type available")
if __debug__:
async def _skip_pairing_dialog(ctx: PairingContext) -> None:
from trezor.enums import ButtonRequestType
from trezor.messages import ButtonAck, ButtonRequest, ThpPairingRequestApproved
from trezor.wire.errors import ActionCancelled
resp = await ctx.call(
ButtonRequest(code=ButtonRequestType.Other, name="thp_pairing_request")
)
if isinstance(resp, ButtonAck):
await ctx.write(ThpPairingRequestApproved())
else:
raise ActionCancelled

View File

@ -3006,6 +3006,22 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["DebugLinkPairingInfo"]:
return isinstance(msg, cls)
class DebugLinkToggleThpPairingDialog(protobuf.MessageType):
channel_id: "bytes | None"
show_dialog: "bool"
def __init__(
self,
*,
channel_id: "bytes | None" = None,
show_dialog: "bool | None" = None,
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["DebugLinkToggleThpPairingDialog"]:
return isinstance(msg, cls)
class DebugLinkStop(protobuf.MessageType):
@classmethod

View File

@ -90,6 +90,9 @@ class Channel:
self.temp_crc_compare: bytearray | None = None
self.temp_tag: bytearray | None = None
if __debug__:
self.should_show_pairing_dialog: bool = True
def clear(self) -> None:
clear_sessions_with_channel_id(self.channel_id)
memory_manager.release_lock_if_owner(self.get_channel_id_int())

View File

@ -142,7 +142,7 @@ class PairingContext(Context):
raise Exception("Not allowed to set this method")
self.selected_method = selected_method
async def show_pairing_dialogue(self, device_name: str | None = None) -> None:
async def show_pairing_dialog(self, device_name: str | None = None) -> None:
from trezor.messages import ThpPairingRequestApproved
from trezor.ui.layouts.common import interact
@ -157,13 +157,13 @@ class PairingContext(Context):
trezorui_api.confirm_action(
title="Before you continue", action=action_string, description=None
),
br_name="pairing_request",
br_name="thp_pairing_request",
br_code=ButtonRequestType.Other,
)
if result == trezorui_api.CONFIRMED:
await self.write(ThpPairingRequestApproved())
async def show_connection_dialogue(self, device_name: str | None = None) -> None:
async def show_connection_dialog(self, device_name: str | None = None) -> None:
from trezor.ui.layouts.common import interact
if not device_name:
@ -174,9 +174,9 @@ class PairingContext(Context):
)
await interact(
trezorui_api.confirm_action(
title="Connection dialogue", action=action_string, description=None
title="Connection dialog", action=action_string, description=None
),
br_name="connection_request",
br_name="thp_connection_request",
br_code=ButtonRequestType.Other,
)

View File

@ -128,6 +128,9 @@ class TrezorClient:
if isinstance(self.protocol, ProtocolV2Channel):
from .transport.session import SessionV2
if passphrase is SEEDLESS:
return SessionV2(self, id=b"\x00")
assert isinstance(passphrase, str) or passphrase is None
session_id = b"\x01" # TODO fix this with ProtocolV2 session rework
if session_id is not None:

View File

@ -38,7 +38,7 @@ from .log import DUMP_BYTES
from .messages import DebugWaitType
from .tools import parse_path
from .transport import Timeout
from .transport.session import Session
from .transport.session import ProtocolV2Channel, Session
from .transport.thp.protocol_v1 import ProtocolV1Channel
if t.TYPE_CHECKING:
@ -865,6 +865,9 @@ class DebugUI:
self.debuglink.snapshot_legacy()
return self.passphrase
def confirm_screen(self) -> None:
self.debuglink.press_yes()
class MessageFilter:
@ -1070,6 +1073,10 @@ class TrezorClientDebugLink(TrezorClient):
# So that we can choose right screenshotting logic (T1 vs TT)
# and know the supported debug capabilities
if self.protocol_version is ProtocolVersion.V2:
assert isinstance(self.protocol, ProtocolV2Channel)
self.protocol._helper_debug = self.debug
self.protocol = self.protocol.get_channel()
self.debug.model = self.model
self.debug.version = self.version

View File

@ -4302,6 +4302,23 @@ class DebugLinkPairingInfo(protobuf.MessageType):
self.nfc_secret_trezor = nfc_secret_trezor
class DebugLinkToggleThpPairingDialog(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("channel_id", "bytes", repeated=False, required=False, default=None),
2: protobuf.Field("show_dialog", "bool", repeated=False, required=False, default=False),
}
def __init__(
self,
*,
channel_id: Optional["bytes"] = None,
show_dialog: Optional["bool"] = False,
) -> None:
self.channel_id = channel_id
self.show_dialog = show_dialog
class DebugLinkStop(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 103

View File

@ -265,10 +265,9 @@ class SessionV2(Session):
super().__init__(client, id)
assert isinstance(client.protocol, ProtocolV2Channel)
helper_debug = None
if isinstance(client, TrezorClientDebugLink):
helper_debug = client.debug
self.channel: ProtocolV2Channel = client.protocol.get_channel(helper_debug)
client.protocol._helper_debug = client.debug
self.channel: ProtocolV2Channel = client.protocol.get_channel()
self.update_id_and_sid(id)
def _write(self, msg: t.Any) -> None:

View File

@ -33,6 +33,7 @@ class ProtocolV2Channel(Channel):
_has_valid_channel: bool = False
_features: messages.Features | None = None
_helper_debug: DebugLink | None = None
def __init__(
self,
@ -41,9 +42,9 @@ class ProtocolV2Channel(Channel):
) -> None:
super().__init__(transport, mapping)
def get_channel(self, helper_debug: DebugLink | None = None) -> ProtocolV2Channel:
def get_channel(self) -> ProtocolV2Channel:
if not self._has_valid_channel:
self._establish_new_channel(helper_debug)
self._establish_new_channel(self._helper_debug)
return self
def read(self, session_id: int) -> t.Any:
@ -60,7 +61,7 @@ class ProtocolV2Channel(Channel):
def get_features(self) -> messages.Features:
if not self._has_valid_channel:
self._establish_new_channel()
self._establish_new_channel(self._helper_debug)
if self._features is None:
self.update_features()
assert self._features is not None

View File

@ -2741,6 +2741,201 @@ impl ::protobuf::reflect::ProtobufValue for DebugLinkPairingInfo {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
// @@protoc_insertion_point(message:hw.trezor.messages.debug.DebugLinkToggleThpPairingDialog)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct DebugLinkToggleThpPairingDialog {
// message fields
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkToggleThpPairingDialog.channel_id)
pub channel_id: ::std::option::Option<::std::vec::Vec<u8>>,
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkToggleThpPairingDialog.show_dialog)
pub show_dialog: ::std::option::Option<bool>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.debug.DebugLinkToggleThpPairingDialog.special_fields)
pub special_fields: ::protobuf::SpecialFields,
}
impl<'a> ::std::default::Default for &'a DebugLinkToggleThpPairingDialog {
fn default() -> &'a DebugLinkToggleThpPairingDialog {
<DebugLinkToggleThpPairingDialog as ::protobuf::Message>::default_instance()
}
}
impl DebugLinkToggleThpPairingDialog {
pub fn new() -> DebugLinkToggleThpPairingDialog {
::std::default::Default::default()
}
// optional bytes channel_id = 1;
pub fn channel_id(&self) -> &[u8] {
match self.channel_id.as_ref() {
Some(v) => v,
None => &[],
}
}
pub fn clear_channel_id(&mut self) {
self.channel_id = ::std::option::Option::None;
}
pub fn has_channel_id(&self) -> bool {
self.channel_id.is_some()
}
// Param is passed by value, moved
pub fn set_channel_id(&mut self, v: ::std::vec::Vec<u8>) {
self.channel_id = ::std::option::Option::Some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_channel_id(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.channel_id.is_none() {
self.channel_id = ::std::option::Option::Some(::std::vec::Vec::new());
}
self.channel_id.as_mut().unwrap()
}
// Take field
pub fn take_channel_id(&mut self) -> ::std::vec::Vec<u8> {
self.channel_id.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
// optional bool show_dialog = 2;
pub fn show_dialog(&self) -> bool {
self.show_dialog.unwrap_or(false)
}
pub fn clear_show_dialog(&mut self) {
self.show_dialog = ::std::option::Option::None;
}
pub fn has_show_dialog(&self) -> bool {
self.show_dialog.is_some()
}
// Param is passed by value, moved
pub fn set_show_dialog(&mut self, v: bool) {
self.show_dialog = ::std::option::Option::Some(v);
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(2);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"channel_id",
|m: &DebugLinkToggleThpPairingDialog| { &m.channel_id },
|m: &mut DebugLinkToggleThpPairingDialog| { &mut m.channel_id },
));
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"show_dialog",
|m: &DebugLinkToggleThpPairingDialog| { &m.show_dialog },
|m: &mut DebugLinkToggleThpPairingDialog| { &mut m.show_dialog },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DebugLinkToggleThpPairingDialog>(
"DebugLinkToggleThpPairingDialog",
fields,
oneofs,
)
}
}
impl ::protobuf::Message for DebugLinkToggleThpPairingDialog {
const NAME: &'static str = "DebugLinkToggleThpPairingDialog";
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
while let Some(tag) = is.read_raw_tag_or_eof()? {
match tag {
10 => {
self.channel_id = ::std::option::Option::Some(is.read_bytes()?);
},
16 => {
self.show_dialog = ::std::option::Option::Some(is.read_bool()?);
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u64 {
let mut my_size = 0;
if let Some(v) = self.channel_id.as_ref() {
my_size += ::protobuf::rt::bytes_size(1, &v);
}
if let Some(v) = self.show_dialog {
my_size += 1 + 1;
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
if let Some(v) = self.channel_id.as_ref() {
os.write_bytes(1, v)?;
}
if let Some(v) = self.show_dialog {
os.write_bool(2, v)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
fn special_fields(&self) -> &::protobuf::SpecialFields {
&self.special_fields
}
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
&mut self.special_fields
}
fn new() -> DebugLinkToggleThpPairingDialog {
DebugLinkToggleThpPairingDialog::new()
}
fn clear(&mut self) {
self.channel_id = ::std::option::Option::None;
self.show_dialog = ::std::option::Option::None;
self.special_fields.clear();
}
fn default_instance() -> &'static DebugLinkToggleThpPairingDialog {
static instance: DebugLinkToggleThpPairingDialog = DebugLinkToggleThpPairingDialog {
channel_id: ::std::option::Option::None,
show_dialog: ::std::option::Option::None,
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
}
}
impl ::protobuf::MessageFull for DebugLinkToggleThpPairingDialog {
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
descriptor.get(|| file_descriptor().message_by_package_relative_name("DebugLinkToggleThpPairingDialog").unwrap()).clone()
}
}
impl ::std::fmt::Display for DebugLinkToggleThpPairingDialog {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for DebugLinkToggleThpPairingDialog {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
// @@protoc_insertion_point(message:hw.trezor.messages.debug.DebugLinkStop)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct DebugLinkStop {
@ -4337,21 +4532,24 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x20\x01(\x0cR\tchannelId\x12%\n\x0ehandshake_hash\x18\x02\x20\x01(\x0cR\
\rhandshakeHash\x12&\n\x0fcode_entry_code\x18\x03\x20\x01(\rR\rcodeEntry\
Code\x12\x20\n\x0ccode_qr_code\x18\x04\x20\x01(\x0cR\ncodeQrCode\x12*\n\
\x11nfc_secret_trezor\x18\x05\x20\x01(\x0cR\x0fnfcSecretTrezor\"\x0f\n\r\
DebugLinkStop\"P\n\x0cDebugLinkLog\x12\x14\n\x05level\x18\x01\x20\x01(\r\
R\x05level\x12\x16\n\x06bucket\x18\x02\x20\x01(\tR\x06bucket\x12\x12\n\
\x04text\x18\x03\x20\x01(\tR\x04text\"G\n\x13DebugLinkMemoryRead\x12\x18\
\n\x07address\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06length\x18\x02\
\x20\x01(\rR\x06length\")\n\x0fDebugLinkMemory\x12\x16\n\x06memory\x18\
\x01\x20\x01(\x0cR\x06memory\"^\n\x14DebugLinkMemoryWrite\x12\x18\n\x07a\
ddress\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06memory\x18\x02\x20\
\x01(\x0cR\x06memory\x12\x14\n\x05flash\x18\x03\x20\x01(\x08R\x05flash\"\
-\n\x13DebugLinkFlashErase\x12\x16\n\x06sector\x18\x01\x20\x01(\rR\x06se\
ctor\".\n\x14DebugLinkEraseSdCard\x12\x16\n\x06format\x18\x01\x20\x01(\
\x08R\x06format\"0\n\x14DebugLinkWatchLayout\x12\x14\n\x05watch\x18\x01\
\x20\x01(\x08R\x05watch:\x02\x18\x01\"\x1f\n\x19DebugLinkResetDebugEvent\
s:\x02\x18\x01\"\x1a\n\x18DebugLinkOptigaSetSecMaxB=\n#com.satoshilabs.t\
rezor.lib.protobufB\x12TrezorMessageDebug\x80\xa6\x1d\x01\
\x11nfc_secret_trezor\x18\x05\x20\x01(\x0cR\x0fnfcSecretTrezor\"h\n\x1fD\
ebugLinkToggleThpPairingDialog\x12\x1d\n\nchannel_id\x18\x01\x20\x01(\
\x0cR\tchannelId\x12&\n\x0bshow_dialog\x18\x02\x20\x01(\x08:\x05falseR\n\
showDialog\"\x0f\n\rDebugLinkStop\"P\n\x0cDebugLinkLog\x12\x14\n\x05leve\
l\x18\x01\x20\x01(\rR\x05level\x12\x16\n\x06bucket\x18\x02\x20\x01(\tR\
\x06bucket\x12\x12\n\x04text\x18\x03\x20\x01(\tR\x04text\"G\n\x13DebugLi\
nkMemoryRead\x12\x18\n\x07address\x18\x01\x20\x01(\rR\x07address\x12\x16\
\n\x06length\x18\x02\x20\x01(\rR\x06length\")\n\x0fDebugLinkMemory\x12\
\x16\n\x06memory\x18\x01\x20\x01(\x0cR\x06memory\"^\n\x14DebugLinkMemory\
Write\x12\x18\n\x07address\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06m\
emory\x18\x02\x20\x01(\x0cR\x06memory\x12\x14\n\x05flash\x18\x03\x20\x01\
(\x08R\x05flash\"-\n\x13DebugLinkFlashErase\x12\x16\n\x06sector\x18\x01\
\x20\x01(\rR\x06sector\".\n\x14DebugLinkEraseSdCard\x12\x16\n\x06format\
\x18\x01\x20\x01(\x08R\x06format\"0\n\x14DebugLinkWatchLayout\x12\x14\n\
\x05watch\x18\x01\x20\x01(\x08R\x05watch:\x02\x18\x01\"\x1f\n\x19DebugLi\
nkResetDebugEvents:\x02\x18\x01\"\x1a\n\x18DebugLinkOptigaSetSecMaxB=\n#\
com.satoshilabs.trezor.lib.protobufB\x12TrezorMessageDebug\x80\xa6\x1d\
\x01\
";
/// `FileDescriptorProto` object which was a source for this generated file
@ -4372,7 +4570,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
deps.push(super::messages_common::file_descriptor().clone());
deps.push(super::messages_management::file_descriptor().clone());
deps.push(super::options::file_descriptor().clone());
let mut messages = ::std::vec::Vec::with_capacity(18);
let mut messages = ::std::vec::Vec::with_capacity(19);
messages.push(DebugLinkDecision::generated_message_descriptor_data());
messages.push(DebugLinkLayout::generated_message_descriptor_data());
messages.push(DebugLinkReseedRandom::generated_message_descriptor_data());
@ -4381,6 +4579,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
messages.push(DebugLinkState::generated_message_descriptor_data());
messages.push(DebugLinkGetPairingInfo::generated_message_descriptor_data());
messages.push(DebugLinkPairingInfo::generated_message_descriptor_data());
messages.push(DebugLinkToggleThpPairingDialog::generated_message_descriptor_data());
messages.push(DebugLinkStop::generated_message_descriptor_data());
messages.push(DebugLinkLog::generated_message_descriptor_data());
messages.push(DebugLinkMemoryRead::generated_message_descriptor_data());

View File

@ -39,7 +39,7 @@ def handle_pairing_request(
) -> None:
protocol._send_message(ThpPairingRequest(host_name=host_name))
button_req = protocol._read_message(ButtonRequest)
assert button_req.name == "pairing_request"
assert button_req.name == "thp_pairing_request"
protocol._send_message(ButtonAck())

View File

@ -220,7 +220,7 @@ def test_credential_phase(client: Client) -> None:
protocol._do_handshake(credential, randomness_static)
protocol._send_message(ThpEndRequest())
button_req = protocol._read_message(ButtonRequest)
assert button_req.name == "connection_request"
assert button_req.name == "thp_connection_request"
protocol._send_message(ButtonAck())
client.debug.press_yes()
protocol._read_message(ThpEndResponse)
@ -243,7 +243,7 @@ def test_credential_phase(client: Client) -> None:
)
# Connection confirmation dialog is shown. (Channel replacement is not triggered.)
button_req = protocol._read_message(ButtonRequest)
assert button_req.name == "connection_request"
assert button_req.name == "thp_connection_request"
protocol._send_message(ButtonAck())
client.debug.press_yes()
# Autoconnect issuance confirmation dialog is shown.