Skip to content

Commit

Permalink
chore: add DebugLinkPairingInfo
Browse files Browse the repository at this point in the history
[no changelog]
  • Loading branch information
M1nd3r committed Jan 30, 2025
1 parent 1383de8 commit 95c5535
Show file tree
Hide file tree
Showing 15 changed files with 1,192 additions and 637 deletions.
28 changes: 23 additions & 5 deletions common/protob/messages-debug.proto
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@ message DebugLinkGetState {
// trezor-core only - wait until current layout changes
// changed in 2.6.4: multiple wait types instead of true/false.
optional DebugWaitType wait_layout = 3 [default=IMMEDIATE];
// THP only - it is used to get information from specified channel
optional bytes thp_channel_id=4;
}

/**
Expand All @@ -132,9 +130,29 @@ message DebugLinkState {
optional uint32 reset_word_pos = 11; // index of mnemonic word the device is expecting during ResetDevice workflow
optional management.BackupType mnemonic_type = 12; // current mnemonic type (BIP-39/SLIP-39)
repeated string tokens = 13; // current layout represented as a list of string tokens
optional uint32 thp_pairing_code_entry_code = 14;
optional bytes thp_pairing_code_qr_code = 15;
optional bytes thp_pairing_code_nfc = 16;
}

/**
* Request: Computer asks for device pairing info
* @start
* @next DebugLinkPairingInfo
*/
message DebugLinkGetPairingInfo {
optional bytes channel_id = 1; // ID of the THP channel to get pairing info from
optional bytes handshake_hash = 2; // handshake hash of the THP channel
optional bytes nfc_secret_host = 3; // host's NFC secret (In case of NFC pairing)
}

/**
* Response: Device pairing info
* @end
*/
message DebugLinkPairingInfo {
optional bytes channel_id = 1; // ID of the THP channel the pairing info is from
optional bytes handshake_hash = 2; // handshake hash of the THP channel
optional uint32 code_entry_code = 3; // CodeEntry pairing code
optional bytes code_qr_code = 4; // QrCode pairing code
optional bytes nfc_secret_trezor = 5; // NFC secret used in NFC pairing
}

/**
Expand Down
2 changes: 2 additions & 0 deletions common/protob/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ enum MessageType {
MessageType_DebugLinkWatchLayout = 9006 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkResetDebugEvents = 9007 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkOptigaSetSecMax = 9008 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkGetPairingInfo = 9009 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkPairingInfo = 9010 [(bitcoin_only) = true, (wire_debug_out) = true];

// Ethereum
MessageType_EthereumGetPublicKey = 450 [(wire_in) = true];
Expand Down
85 changes: 42 additions & 43 deletions core/src/apps/debug/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
from trezor.messages import (
DebugLinkDecision,
DebugLinkEraseSdCard,
DebugLinkGetPairingInfo,
DebugLinkGetState,
DebugLinkOptigaSetSecMax,
DebugLinkPairingInfo,
DebugLinkRecordScreen,
DebugLinkReseedRandom,
DebugLinkState,
Expand Down Expand Up @@ -249,11 +251,7 @@ async def dispatch_DebugLinkDecision(
# If no exception was raised, the layout did not shut down. That means that it
# just updated itself. The update is already live for the caller to retrieve.

def _state(
thp_pairing_code_entry_code: int | None = None,
thp_pairing_code_qr_code: bytes | None = None,
thp_pairing_code_nfc: bytes | None = None,
) -> DebugLinkState:
def _state() -> DebugLinkState:
from trezor.messages import DebugLinkState

from apps.common import mnemonic, passphrase
Expand All @@ -272,46 +270,50 @@ def callback(*args: str) -> None:
passphrase_protection=passphrase.is_enabled(),
reset_entropy=storage.reset_internal_entropy,
tokens=tokens,
thp_pairing_code_entry_code=thp_pairing_code_entry_code,
thp_pairing_code_qr_code=thp_pairing_code_qr_code,
thp_pairing_code_nfc=thp_pairing_code_nfc,
)

async def dispatch_DebugLinkGetPairingInfo(
msg: DebugLinkGetPairingInfo,
) -> DebugLinkPairingInfo | None:
if not utils.USE_THP:
raise RuntimeError("Trezor does not support THP")
if msg.channel_id is None:
raise RuntimeError("Invalid DebugLinkGetPairingInfo message")

from trezor.wire.thp.channel import Channel
from trezor.wire.thp.pairing_context import PairingContext
from trezor.wire.thp.thp_main import _CHANNELS

channel_id = int.from_bytes(msg.channel_id, "big")
channel: Channel | None = None
ctx: PairingContext | None = None
try:
channel = _CHANNELS[channel_id]
ctx = channel.connection_context
except KeyError:
pass

if ctx is None or not isinstance(ctx, PairingContext):
raise RuntimeError("Trezor is not in pairing mode")

ctx.nfc_secret_host = msg.nfc_secret_host
ctx.handshake_hash_host = msg.handshake_hash
from trezor.messages import DebugLinkPairingInfo

return DebugLinkPairingInfo(
channel_id=ctx.channel_id,
handshake_hash=ctx.channel_ctx.get_handshake_hash(),
code_entry_code=ctx.display_data.code_code_entry,
code_qr_code=ctx.display_data.code_qr_code,
nfc_secret_trezor=ctx.nfc_secret,
)

async def dispatch_DebugLinkGetState(
msg: DebugLinkGetState,
) -> DebugLinkState | None:

thp_pairing_code_entry_code: int | None = None
thp_pairing_code_qr_code: bytes | None = None
thp_pairing_code_nfc: bytes | None = None
if utils.USE_THP and msg.thp_channel_id is not None:
channel_id = int.from_bytes(msg.thp_channel_id, "big")

from trezor.wire.thp.channel import Channel
from trezor.wire.thp.pairing_context import PairingContext
from trezor.wire.thp.thp_main import _CHANNELS

channel: Channel | None = None
ctx: PairingContext | None = None
try:
channel = _CHANNELS[channel_id]
ctx = channel.connection_context
except KeyError:
pass
if ctx is not None and isinstance(ctx, PairingContext):
thp_pairing_code_entry_code = ctx.display_data.code_code_entry
thp_pairing_code_qr_code = ctx.display_data.code_qr_code
thp_pairing_code_nfc = ctx.display_data.code_nfc
# if msg.host_nfc_secret is not None:
# ctx.host_nfc_secret = msg.host_nfc_secret

if msg.wait_layout == DebugWaitType.IMMEDIATE:
return _state(
thp_pairing_code_entry_code,
thp_pairing_code_qr_code,
thp_pairing_code_nfc,
)

return _state()
assert DEBUG_CONTEXT is not None
if msg.wait_layout == DebugWaitType.NEXT_LAYOUT:
layout_change_box.clear()
Expand All @@ -321,11 +323,7 @@ async def dispatch_DebugLinkGetState(
if not layout_is_ready():
return await return_layout_change(DEBUG_CONTEXT, detect_deadlock=True)
else:
return _state(
thp_pairing_code_entry_code,
thp_pairing_code_qr_code,
thp_pairing_code_nfc,
)
return _state()

async def dispatch_DebugLinkRecordScreen(msg: DebugLinkRecordScreen) -> Success:
if msg.target_directory:
Expand Down Expand Up @@ -466,6 +464,7 @@ async def handle_session(iface: WireInterface) -> None:
WORKFLOW_HANDLERS: dict[int, Handler] = {
MessageType.DebugLinkDecision: dispatch_DebugLinkDecision,
MessageType.DebugLinkGetState: dispatch_DebugLinkGetState,
MessageType.DebugLinkGetPairingInfo: dispatch_DebugLinkGetPairingInfo,
MessageType.DebugLinkReseedRandom: dispatch_DebugLinkReseedRandom,
MessageType.DebugLinkRecordScreen: dispatch_DebugLinkRecordScreen,
MessageType.DebugLinkEraseSdCard: dispatch_DebugLinkEraseSdCard,
Expand Down
3 changes: 2 additions & 1 deletion core/src/apps/thp/pairing.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ async def _handle_qr_code_tag(
hexlify(ctx.display_data.code_qr_code).decode(),
) # TODO remove after testing
print(
"expected secret:", hexlify(ctx.qr_code_secret).decode()
"expected secret:", hexlify(ctx.qr_code_secret or b"").decode()
) # TODO remove after testing
raise ThpError("Unexpected QR Code Tag")

Expand All @@ -329,6 +329,7 @@ async def _handle_nfc_tag(
assert isinstance(message, ThpNfcTagHost)
sha_ctx = sha256(ThpPairingMethod.NFC.to_bytes(1, "big"))
sha_ctx.update(ctx.channel_ctx.get_handshake_hash())
assert ctx.nfc_secret is not None
sha_ctx.update(ctx.nfc_secret)
expected_tag = sha_ctx.digest()
if expected_tag != message.tag:
Expand Down
2 changes: 2 additions & 0 deletions core/src/trezor/enums/MessageType.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions core/src/trezor/enums/__init__.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 40 additions & 8 deletions core/src/trezor/messages.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions core/src/trezor/wire/thp/pairing_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,16 @@ def __init__(self, channel_ctx: Channel) -> None:
super().__init__(channel_ctx.iface, channel_ctx.channel_id)
self.channel_ctx: Channel = channel_ctx
self.incoming_message = loop.mailbox()
self.nfc_secret: bytes
self.qr_code_secret: bytes
self.nfc_secret: bytes | None = None
self.qr_code_secret: bytes | None = None
self.code_entry_secret: bytes | None = None

self.selected_method: ThpPairingMethod

# The 2 following attributes are important for NFC pairing
self.nfc_secret_host: bytes | None = None
self.handshake_hash_host: bytes | None = None

self.display_data: PairingDisplayData = PairingDisplayData()
self.cpace: Cpace
self.host_name: str
Expand Down
5 changes: 0 additions & 5 deletions legacy/firmware/protob/messages-debug.options
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ DebugLinkDecision.input max_size:33
DebugLinkDecision.x type:FT_IGNORE
DebugLinkDecision.y type:FT_IGNORE

DebugLinkGetState.thp_channel_id type:FT_IGNORE

DebugLinkState.layout max_size:1024
DebugLinkState.pin max_size:51
DebugLinkState.matrix max_size:10
Expand All @@ -12,9 +10,6 @@ DebugLinkState.reset_word max_size:12
DebugLinkState.reset_entropy max_size:128
DebugLinkState.recovery_fake_word max_size:12
DebugLinkState.tokens type:FT_IGNORE
DebugLinkState.thp_pairing_code_entry_code type:FT_IGNORE
DebugLinkState.thp_pairing_code_qr_code type:FT_IGNORE
DebugLinkState.thp_pairing_code_nfc type:FT_IGNORE

DebugLinkLog.bucket max_size:33
DebugLinkLog.text max_size:256
Expand Down
23 changes: 18 additions & 5 deletions python/src/trezorlib/debuglink.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,21 +539,34 @@ def _call(self, msg: protobuf.MessageType) -> t.Any:
def state(
self,
wait_type: DebugWaitType | None = None,
thp_channel_id: bytes | None = None,
) -> messages.DebugLinkState:
if wait_type is None:
wait_type = (
DebugWaitType.CURRENT_LAYOUT
if self.has_global_layout
else DebugWaitType.IMMEDIATE
)
result = self._call(messages.DebugLinkGetState(wait_layout=wait_type))
while not isinstance(result, (messages.Failure, messages.DebugLinkState)):
result = self._read()
if isinstance(result, messages.Failure):
raise TrezorFailure(result)
return result

def pairing_info(
self,
thp_channel_id: bytes | None = None,
handshake_hash: bytes | None = None,
nfc_secret_host: bytes | None = None,
) -> messages.DebugLinkPairingInfo:
result = self._call(
messages.DebugLinkGetState(
wait_layout=wait_type,
thp_channel_id=thp_channel_id,
messages.DebugLinkGetPairingInfo(
channel_id=thp_channel_id,
handshake_hash=handshake_hash,
nfc_secret_host=nfc_secret_host,
)
)
while not isinstance(result, (messages.Failure, messages.DebugLinkState)):
while not isinstance(result, (messages.Failure, messages.DebugLinkPairingInfo)):
result = self._read()
if isinstance(result, messages.Failure):
raise TrezorFailure(result)
Expand Down
Loading

0 comments on commit 95c5535

Please sign in to comment.