mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-18 17:16:06 +00:00
temp: update pairing process, part 1
This commit is contained in:
parent
53a53a8d04
commit
a82a0b3af2
@ -13,28 +13,28 @@ option (include_in_bitcoin_only) = true;
|
||||
* Mapping between Trezor wire identifier (uint) and a Thp protobuf message
|
||||
*/
|
||||
enum ThpMessageType {
|
||||
reserved 0 to 999; // Values reserved by other messages, see messages.proto
|
||||
reserved 0 to 999; // Values reserved by other messages, see messages.proto
|
||||
|
||||
ThpMessageType_ThpCreateNewSession = 1000[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpNewSession = 1001[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpStartPairingRequest = 1008 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpCreateNewSession = 1000 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpPairingRequest = 1006 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpPairingRequestAck = 1007 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpSelectMethod = 1008 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpPairingPreparationsFinished = 1009 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpCredentialRequest = 1010 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpCredentialResponse = 1011 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpEndRequest = 1012 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpEndResponse = 1013[(bitcoin_only) = true];
|
||||
ThpMessageType_ThpCodeEntryCommitment = 1016[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryChallenge = 1017[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryCpaceHost = 1018[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryCpaceTrezor = 1019[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryTag = 1020[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntrySecret = 1021[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpQrCodeTag = 1024[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpQrCodeSecret = 1025[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpNfcUnidirectionalTag = 1032[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpNfcUnidirectionalSecret = 1033[(bitcoin_only)=true];
|
||||
ThpMessageType_ThpEndResponse = 1013 [(bitcoin_only) = true];
|
||||
ThpMessageType_ThpCodeEntryCommitment = 1016 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryChallenge = 1017 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryCpaceTrezor = 1018 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntryCpaceHostTag = 1019 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpCodeEntrySecret = 1020 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpQrCodeTag = 1024 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpQrCodeSecret = 1025 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpNfcTagHost = 1032 [(bitcoin_only)=true];
|
||||
ThpMessageType_ThpNfcTagTrezor = 1033 [(bitcoin_only)=true];
|
||||
|
||||
reserved 1100 to 2147483647; // Values reserved by other messages, see messages.proto
|
||||
reserved 1100 to 2147483647; // Values reserved by other messages, see messages.proto
|
||||
}
|
||||
|
||||
|
||||
@ -43,10 +43,10 @@ enum ThpMessageType {
|
||||
* @embed
|
||||
*/
|
||||
enum ThpPairingMethod {
|
||||
NoMethod = 1; // Trust without MITM protection.
|
||||
SkipPairing = 1; // Trust without MITM protection.
|
||||
CodeEntry = 2; // User types code diplayed on Trezor into the host application.
|
||||
QrCode = 3; // User scans code displayed on Trezor into host application.
|
||||
NFC_Unidirectional = 4; // Trezor transmits an authentication key to the host device via NFC.
|
||||
NFC = 4; // Trezor and host application exchange authentication secrets via NFC.
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,8 +55,8 @@ enum ThpPairingMethod {
|
||||
message ThpDeviceProperties {
|
||||
optional string internal_model = 1; // Internal model name e.g. "T2B1".
|
||||
optional uint32 model_variant = 2; // Encodes the device properties such as color.
|
||||
optional bool bootloader_mode = 3; // Indicates whether the device is in bootloader or firmware mode.
|
||||
optional uint32 protocol_version = 4; // The communication protocol version supported by the firmware.
|
||||
optional uint32 protocol_version_major = 3; // The major version of the communication protocol used by the firmware.
|
||||
optional uint32 protocol_version_minor = 4; // The minor version of the communication protocol used by the firmware.
|
||||
repeated ThpPairingMethod pairing_methods = 5; // The pairing methods supported by the Trezor.
|
||||
}
|
||||
|
||||
@ -65,13 +65,11 @@ message ThpDeviceProperties {
|
||||
*/
|
||||
message ThpHandshakeCompletionReqNoisePayload {
|
||||
optional bytes host_pairing_credential = 1; // Host's pairing credential
|
||||
repeated ThpPairingMethod pairing_methods = 2; // The pairing methods chosen by the host
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: Ask device for a new session with given passphrase.
|
||||
* @start
|
||||
* @next ThpNewSession
|
||||
* @next Success
|
||||
*/
|
||||
message ThpCreateNewSession{
|
||||
@ -80,31 +78,41 @@ message ThpCreateNewSession{
|
||||
optional bool derive_cardano = 3; // If True, Cardano keys will be derived. Ignored with BTC-only
|
||||
}
|
||||
|
||||
/**
|
||||
* Response: Contains session_id of the newly created session.
|
||||
* @end
|
||||
*/
|
||||
message ThpNewSession{
|
||||
optional uint32 new_session_id = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: Start pairing process.
|
||||
* @start
|
||||
* @next ThpCodeEntryCommitment
|
||||
* @next ThpPairingPreparationsFinished
|
||||
* @next ThpPairingRequestAck
|
||||
*/
|
||||
message ThpStartPairingRequest{
|
||||
optional string host_name = 1; // Human-readable host name
|
||||
message ThpPairingRequest{
|
||||
optional string host_name = 1; // Human-readable host name
|
||||
}
|
||||
|
||||
/**
|
||||
* Response: Host is allowed to start pairing process.
|
||||
* @start
|
||||
* @next ThpSelectMethod
|
||||
*/
|
||||
message ThpPairingRequestAck{
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: Start pairing using the method selected.
|
||||
* @start
|
||||
* @next ThpPairingPreparationsFinished
|
||||
* @next ThpCodeEntryCommitment
|
||||
*/
|
||||
message ThpSelectMethod {
|
||||
optional ThpPairingMethod selected_pairing_method = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response: Pairing is ready for user input / OOB communication.
|
||||
* @next ThpCodeEntryCpace
|
||||
* @next ThpQrCodeTag
|
||||
* @next ThpNfcUnidirectionalTag
|
||||
* @next ThpNfcTagHost
|
||||
*/
|
||||
message ThpPairingPreparationsFinished{
|
||||
message ThpPairingPreparationsFinished{
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,34 +125,30 @@ message ThpCodeEntryCommitment {
|
||||
|
||||
/**
|
||||
* Response: Host responds to Trezor's Code Entry commitment with a challenge.
|
||||
* @next ThpPairingPreparationsFinished
|
||||
*/
|
||||
message ThpCodeEntryChallenge {
|
||||
optional bytes challenge = 1; // host's random 32-byte challenge
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: User selected Code Entry option in Host. Host starts CPACE protocol with Trezor.
|
||||
* @next ThpCodeEntryCpaceTrezor
|
||||
*/
|
||||
message ThpCodeEntryCpaceHost {
|
||||
optional bytes cpace_host_public_key = 1; // Host's ephemeral CPace public key
|
||||
message ThpCodeEntryChallenge {
|
||||
optional bytes challenge = 1; // Host's random 32-byte challenge
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Response: Trezor continues with the CPACE protocol.
|
||||
* @next ThpCodeEntryTag
|
||||
* @next ThpCodeEntryCpaceHostTag
|
||||
*/
|
||||
message ThpCodeEntryCpaceTrezor {
|
||||
optional bytes cpace_trezor_public_key = 1; // Trezor's ephemeral CPace public key
|
||||
}
|
||||
|
||||
/**
|
||||
* Response: Host continues with the CPACE protocol.
|
||||
* Request: User selected Code Entry option in Host. Host starts CPACE protocol with Trezor.
|
||||
* @next ThpCodeEntrySecret
|
||||
*/
|
||||
message ThpCodeEntryTag {
|
||||
optional bytes tag = 2; // SHA-256 of shared secret
|
||||
message ThpCodeEntryCpaceHostTag {
|
||||
optional bytes cpace_host_public_key = 1; // Host's ephemeral CPace public key
|
||||
optional bytes tag = 2; // SHA-256 of shared secret
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,10 +179,10 @@ message ThpQrCodeSecret {
|
||||
|
||||
/**
|
||||
* Request: User selected Unidirectional NFC pairing option. Host sends an Unidirectional NFC Tag.
|
||||
* @next ThpNfcUnidirectionalSecret
|
||||
* @next ThpNfcTagTrezor
|
||||
*/
|
||||
message ThpNfcUnidirectionalTag {
|
||||
optional bytes tag = 1; // SHA-256 of shared secret
|
||||
message ThpNfcTagHost {
|
||||
optional bytes tag = 1; // Host's tag
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,8 +190,8 @@ message ThpNfcUnidirectionalTag {
|
||||
* @next ThpCredentialRequest
|
||||
* @next ThpEndRequest
|
||||
*/
|
||||
message ThpNfcUnidirectionalSecret {
|
||||
optional bytes secret = 1; // Trezor's secret
|
||||
message ThpNfcTagTrezor {
|
||||
optional bytes tag = 1; // Trezor's tag
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,6 +201,7 @@ message ThpNfcUnidirectionalSecret {
|
||||
*/
|
||||
message ThpCredentialRequest {
|
||||
optional bytes host_static_pubkey = 1; // Host's static public key used in the handshake.
|
||||
optional bool autoconnect = 2; // Whether host wants to autoconnect without user confirmation
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,6 +234,7 @@ message ThpEndResponse {}
|
||||
message ThpCredentialMetadata {
|
||||
option (internal_only) = true;
|
||||
optional string host_name = 1; // Human-readable host name
|
||||
optional bool autoconnect = 2; // Whether host is allowed to autoconnect without user confirmation
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -301,9 +301,7 @@ if __debug__:
|
||||
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_unidirectional = (
|
||||
ctx.display_data.code_nfc_unidirectional
|
||||
)
|
||||
thp_pairing_code_nfc_unidirectional = ctx.display_data.code_nfc
|
||||
|
||||
if msg.wait_layout == DebugWaitType.IMMEDIATE:
|
||||
return _state(
|
||||
|
@ -2,30 +2,32 @@ from typing import TYPE_CHECKING
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor import loop, protobuf
|
||||
from trezor.crypto import random
|
||||
from trezor.crypto.hashlib import sha256
|
||||
from trezor.enums import ThpMessageType, ThpPairingMethod
|
||||
from trezor.messages import (
|
||||
Cancel,
|
||||
ThpCodeEntryChallenge,
|
||||
ThpCodeEntryCommitment,
|
||||
ThpCodeEntryCpaceHost,
|
||||
ThpCodeEntryCpaceHostTag,
|
||||
ThpCodeEntryCpaceTrezor,
|
||||
ThpCodeEntrySecret,
|
||||
ThpCodeEntryTag,
|
||||
ThpCredentialMetadata,
|
||||
ThpCredentialRequest,
|
||||
ThpCredentialResponse,
|
||||
ThpEndRequest,
|
||||
ThpEndResponse,
|
||||
ThpNfcUnidirectionalSecret,
|
||||
ThpNfcUnidirectionalTag,
|
||||
ThpNfcTagHost,
|
||||
ThpNfcTagTrezor,
|
||||
ThpPairingPreparationsFinished,
|
||||
ThpPairingRequest,
|
||||
ThpPairingRequestAck,
|
||||
ThpQrCodeSecret,
|
||||
ThpQrCodeTag,
|
||||
ThpStartPairingRequest,
|
||||
ThpSelectMethod,
|
||||
)
|
||||
from trezor.wire.errors import ActionCancelled, SilentError, UnexpectedMessage
|
||||
from trezor.wire.thp import ChannelState, ThpError, crypto
|
||||
from trezor.wire.thp import ChannelState, ThpError, crypto, get_enabled_pairing_methods
|
||||
from trezor.wire.thp.pairing_context import PairingContext
|
||||
|
||||
from .credential_manager import issue_credential
|
||||
@ -64,12 +66,13 @@ def check_state_and_log(
|
||||
return decorator
|
||||
|
||||
|
||||
def check_method_is_allowed(
|
||||
def check_method_is_allowed_and_selected(
|
||||
pairing_method: ThpPairingMethod,
|
||||
) -> Callable[[FuncWithContext], FuncWithContext]:
|
||||
def decorator(f: FuncWithContext) -> FuncWithContext:
|
||||
def inner(context: PairingContext, *args: P.args, **kwargs: P.kwargs) -> object:
|
||||
_check_method_is_allowed(context, pairing_method)
|
||||
_check_method_is_selected(context, pairing_method)
|
||||
return f(context, *args, **kwargs)
|
||||
|
||||
return inner
|
||||
@ -81,31 +84,47 @@ def check_method_is_allowed(
|
||||
# Pairing handlers
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP1)
|
||||
@check_state_and_log(ChannelState.TP0)
|
||||
async def handle_pairing_request(
|
||||
ctx: PairingContext, message: protobuf.MessageType
|
||||
) -> ThpEndResponse:
|
||||
|
||||
if not ThpStartPairingRequest.is_type_of(message):
|
||||
if not ThpPairingRequest.is_type_of(message):
|
||||
raise UnexpectedMessage("Unexpected message")
|
||||
|
||||
ctx.host_name = message.host_name or ""
|
||||
|
||||
skip_pairing = _is_method_included(ctx, ThpPairingMethod.NoMethod)
|
||||
if skip_pairing:
|
||||
# TODO show dialog
|
||||
|
||||
select_method_msg: ThpSelectMethod = await ctx.call(
|
||||
ThpPairingRequestAck(), ThpSelectMethod
|
||||
)
|
||||
assert select_method_msg.selected_pairing_method is not None
|
||||
|
||||
ctx.set_selected_method(select_method_msg.selected_pairing_method)
|
||||
|
||||
if ctx.selected_method == ThpPairingMethod.SkipPairing:
|
||||
return await _end_pairing(ctx)
|
||||
|
||||
await _prepare_pairing(ctx)
|
||||
await ctx.write(ThpPairingPreparationsFinished())
|
||||
ctx.channel_ctx.set_channel_state(ChannelState.TP3)
|
||||
response = await show_display_data(
|
||||
ctx, _get_possible_pairing_methods_and_cancel(ctx)
|
||||
)
|
||||
while True:
|
||||
|
||||
if Cancel.is_type_of(response):
|
||||
ctx.channel_ctx.clear()
|
||||
raise SilentError("Action was cancelled by the Host")
|
||||
# TODO disable NFC (if enabled)
|
||||
await _prepare_pairing(ctx)
|
||||
|
||||
ctx.channel_ctx.set_channel_state(ChannelState.TP3)
|
||||
response = await show_display_data(ctx, _get_accepted_messages(ctx))
|
||||
|
||||
if Cancel.is_type_of(response):
|
||||
ctx.channel_ctx.clear()
|
||||
raise SilentError("Action was cancelled by the Host")
|
||||
|
||||
if ThpSelectMethod.is_type_of(response):
|
||||
assert response.selected_pairing_method is not None
|
||||
ctx.set_selected_method(response.selected_pairing_method)
|
||||
ctx.channel_ctx.set_channel_state(ChannelState.TP1)
|
||||
else:
|
||||
break
|
||||
|
||||
# TODO disable NFC (if enabled)
|
||||
response = await _handle_different_pairing_methods(ctx, response)
|
||||
|
||||
while ThpCredentialRequest.is_type_of(response):
|
||||
@ -115,15 +134,16 @@ async def handle_pairing_request(
|
||||
|
||||
|
||||
async def _prepare_pairing(ctx: PairingContext) -> None:
|
||||
ctx.channel_ctx.set_channel_state(ChannelState.TP1)
|
||||
|
||||
if _is_method_included(ctx, ThpPairingMethod.CodeEntry):
|
||||
await _handle_code_entry_is_included(ctx)
|
||||
|
||||
if _is_method_included(ctx, ThpPairingMethod.QrCode):
|
||||
_handle_qr_code_is_included(ctx)
|
||||
|
||||
if _is_method_included(ctx, ThpPairingMethod.NFC_Unidirectional):
|
||||
_handle_nfc_unidirectional_is_included(ctx)
|
||||
if ctx.selected_method == ThpPairingMethod.CodeEntry:
|
||||
await _handle_code_entry_is_selected(ctx)
|
||||
elif ctx.selected_method == ThpPairingMethod.NFC:
|
||||
_handle_nfc_is_selected(ctx)
|
||||
elif ctx.selected_method == ThpPairingMethod.QrCode:
|
||||
_handle_qr_code_is_selected(ctx)
|
||||
else:
|
||||
raise Exception() # TODO unknown pairing method
|
||||
|
||||
|
||||
async def show_display_data(
|
||||
@ -143,10 +163,20 @@ async def show_display_data(
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP1)
|
||||
async def _handle_code_entry_is_included(ctx: PairingContext) -> None:
|
||||
commitment = sha256(ctx.secret).digest()
|
||||
async def _handle_code_entry_is_selected(ctx: PairingContext) -> None:
|
||||
if ctx.code_entry_secret is None:
|
||||
await _handle_code_entry_is_selected_first_time(ctx)
|
||||
else:
|
||||
await ctx.write(ThpPairingPreparationsFinished())
|
||||
|
||||
challenge_message = await ctx.call( # noqa: F841
|
||||
|
||||
async def _handle_code_entry_is_selected_first_time(ctx: PairingContext):
|
||||
from trezor.wire.thp.cpace import Cpace
|
||||
|
||||
ctx.code_entry_secret = random.bytes(16)
|
||||
commitment = sha256(ctx.code_entry_secret).digest()
|
||||
|
||||
challenge_message = await ctx.call(
|
||||
ThpCodeEntryCommitment(commitment=commitment), ThpCodeEntryChallenge
|
||||
)
|
||||
ctx.channel_ctx.set_channel_state(ChannelState.TP2)
|
||||
@ -157,84 +187,76 @@ async def _handle_code_entry_is_included(ctx: PairingContext) -> None:
|
||||
if challenge_message.challenge is None:
|
||||
raise Exception("Invalid message")
|
||||
sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.secret)
|
||||
sha_ctx.update(ctx.code_entry_secret)
|
||||
sha_ctx.update(challenge_message.challenge)
|
||||
sha_ctx.update(bytes("PairingMethod_CodeEntry", "utf-8"))
|
||||
code_code_entry_hash = sha_ctx.digest()
|
||||
ctx.display_data.code_code_entry = (
|
||||
int.from_bytes(code_code_entry_hash, "big") % 1000000
|
||||
)
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP1, ChannelState.TP2)
|
||||
def _handle_qr_code_is_included(ctx: PairingContext) -> None:
|
||||
sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.secret)
|
||||
sha_ctx.update(bytes("PairingMethod_QrCode", "utf-8"))
|
||||
ctx.display_data.code_qr_code = sha_ctx.digest()[:16]
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP1, ChannelState.TP2)
|
||||
def _handle_nfc_unidirectional_is_included(ctx: PairingContext) -> None:
|
||||
sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.secret)
|
||||
sha_ctx.update(bytes("PairingMethod_NfcUnidirectional", "utf-8"))
|
||||
ctx.display_data.code_nfc_unidirectional = sha_ctx.digest()[:16]
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP3)
|
||||
async def _handle_different_pairing_methods(
|
||||
ctx: PairingContext, response: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
if ThpCodeEntryCpaceHost.is_type_of(response):
|
||||
return await _handle_code_entry_cpace(ctx, response)
|
||||
if ThpQrCodeTag.is_type_of(response):
|
||||
return await _handle_qr_code_tag(ctx, response)
|
||||
if ThpNfcUnidirectionalTag.is_type_of(response):
|
||||
return await _handle_nfc_unidirectional_tag(ctx, response)
|
||||
raise UnexpectedMessage("Unexpected message")
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP3)
|
||||
@check_method_is_allowed(ThpPairingMethod.CodeEntry)
|
||||
async def _handle_code_entry_cpace(
|
||||
ctx: PairingContext, message: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
from trezor.wire.thp.cpace import Cpace
|
||||
|
||||
# TODO check that ThpCodeEntryCpaceHost message is valid
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(message, ThpCodeEntryCpaceHost)
|
||||
if message.cpace_host_public_key is None:
|
||||
raise ThpError("Message ThpCodeEntryCpaceHost has no public key")
|
||||
|
||||
ctx.cpace = Cpace(
|
||||
message.cpace_host_public_key,
|
||||
ctx.channel_ctx.get_handshake_hash(),
|
||||
)
|
||||
assert ctx.display_data.code_code_entry is not None
|
||||
ctx.cpace.generate_keys_and_secret(
|
||||
ctx.display_data.code_code_entry.to_bytes(6, "big")
|
||||
)
|
||||
|
||||
ctx.channel_ctx.set_channel_state(ChannelState.TP4)
|
||||
response = await ctx.call(
|
||||
ThpCodeEntryCpaceTrezor(cpace_trezor_public_key=ctx.cpace.trezor_public_key),
|
||||
ThpCodeEntryTag,
|
||||
await ctx.write(
|
||||
ThpCodeEntryCpaceTrezor(cpace_trezor_public_key=ctx.cpace.trezor_public_key)
|
||||
)
|
||||
return await _handle_code_entry_tag(ctx, response)
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP4)
|
||||
@check_method_is_allowed(ThpPairingMethod.CodeEntry)
|
||||
async def _handle_code_entry_tag(
|
||||
@check_state_and_log(ChannelState.TP1)
|
||||
async def _handle_nfc_is_selected(ctx: PairingContext) -> None:
|
||||
ctx.nfc_secret = random.bytes(16)
|
||||
sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.nfc_secret)
|
||||
sha_ctx.update(bytes("PairingMethod_NfcUnidirectional", "utf-8"))
|
||||
ctx.display_data.code_nfc = sha_ctx.digest()[:16]
|
||||
await ctx.write(ThpPairingPreparationsFinished())
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP1)
|
||||
async def _handle_qr_code_is_selected(ctx: PairingContext) -> None:
|
||||
ctx.qr_code_secret = random.bytes(16)
|
||||
|
||||
sha_ctx = sha256(ThpPairingMethod.QrCode.to_bytes(1, "big"))
|
||||
sha_ctx.update(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.qr_code_secret)
|
||||
|
||||
ctx.display_data.code_qr_code = sha_ctx.digest()[:16]
|
||||
await ctx.write(ThpPairingPreparationsFinished())
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP3)
|
||||
async def _handle_different_pairing_methods(
|
||||
ctx: PairingContext, response: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
if ThpCodeEntryCpaceHostTag.is_type_of(response):
|
||||
return await _handle_code_entry_cpace(ctx, response)
|
||||
if ThpQrCodeTag.is_type_of(response):
|
||||
return await _handle_qr_code_tag(ctx, response)
|
||||
if ThpNfcTagHost.is_type_of(response):
|
||||
return await _handle_nfc_tag(ctx, response)
|
||||
raise UnexpectedMessage("Unexpected message")
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP3)
|
||||
@check_method_is_allowed_and_selected(ThpPairingMethod.CodeEntry)
|
||||
async def _handle_code_entry_cpace(
|
||||
ctx: PairingContext, message: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(message, ThpCodeEntryTag)
|
||||
assert isinstance(message, ThpCodeEntryCpaceHostTag)
|
||||
if message.cpace_host_public_key is None:
|
||||
raise ThpError(
|
||||
"Message ThpCodeEntryCpaceHostTag is missing cpace_host_public_key"
|
||||
)
|
||||
if message.tag is None:
|
||||
raise ThpError("Message ThpCodeEntryCpaceHostTag is missing tag")
|
||||
|
||||
ctx.cpace.compute_shared_secret(message.cpace_host_public_key)
|
||||
expected_tag = sha256(ctx.cpace.shared_secret).digest()
|
||||
if expected_tag != message.tag:
|
||||
print(
|
||||
@ -248,19 +270,21 @@ async def _handle_code_entry_tag(
|
||||
|
||||
return await _handle_secret_reveal(
|
||||
ctx,
|
||||
msg=ThpCodeEntrySecret(secret=ctx.secret),
|
||||
msg=ThpCodeEntrySecret(secret=ctx.code_entry_secret),
|
||||
)
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP3)
|
||||
@check_method_is_allowed(ThpPairingMethod.QrCode)
|
||||
@check_method_is_allowed_and_selected(ThpPairingMethod.QrCode)
|
||||
async def _handle_qr_code_tag(
|
||||
ctx: PairingContext, message: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(message, ThpQrCodeTag)
|
||||
assert ctx.display_data.code_qr_code is not None
|
||||
expected_tag = sha256(ctx.display_data.code_qr_code).digest()
|
||||
sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.display_data.code_qr_code)
|
||||
expected_tag = sha_ctx.digest()
|
||||
if expected_tag != message.tag:
|
||||
print(
|
||||
"expected qr code tag:", hexlify(expected_tag).decode()
|
||||
@ -276,28 +300,35 @@ async def _handle_qr_code_tag(
|
||||
|
||||
return await _handle_secret_reveal(
|
||||
ctx,
|
||||
msg=ThpQrCodeSecret(secret=ctx.secret),
|
||||
msg=ThpQrCodeSecret(secret=ctx.qr_code_secret),
|
||||
)
|
||||
|
||||
|
||||
@check_state_and_log(ChannelState.TP3)
|
||||
@check_method_is_allowed(ThpPairingMethod.NFC_Unidirectional)
|
||||
async def _handle_nfc_unidirectional_tag(
|
||||
@check_method_is_allowed_and_selected(ThpPairingMethod.NFC)
|
||||
async def _handle_nfc_tag(
|
||||
ctx: PairingContext, message: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(message, ThpNfcUnidirectionalTag)
|
||||
|
||||
expected_tag = sha256(ctx.display_data.code_nfc_unidirectional).digest()
|
||||
assert isinstance(message, ThpNfcTagHost)
|
||||
sha_ctx = sha256(ThpPairingMethod.NFC.to_bytes(1, "big"))
|
||||
sha_ctx.update(ctx.channel_ctx.get_handshake_hash())
|
||||
sha_ctx.update(ctx.nfc_secret)
|
||||
expected_tag = sha_ctx.digest()
|
||||
if expected_tag != message.tag:
|
||||
print(
|
||||
"expected nfc tag:", hexlify(expected_tag).decode()
|
||||
) # TODO remove after testing
|
||||
raise ThpError("Unexpected NFC Unidirectional Tag")
|
||||
|
||||
sha_ctx = sha256(ThpPairingMethod.NFC.to_bytes(1, "big"))
|
||||
sha_ctx.update(ctx.channel_ctx.get_handshake_hash())
|
||||
# TODO add Host's secret from NFC message transferred over NFC
|
||||
# sha_ctx.update(host's secret)
|
||||
trezor_tag = sha_ctx.digest()
|
||||
return await _handle_secret_reveal(
|
||||
ctx,
|
||||
msg=ThpNfcUnidirectionalSecret(secret=ctx.secret),
|
||||
msg=ThpNfcTagTrezor(tag=trezor_tag),
|
||||
)
|
||||
|
||||
|
||||
@ -318,7 +349,6 @@ async def _handle_secret_reveal(
|
||||
async def _handle_credential_request(
|
||||
ctx: PairingContext, message: protobuf.MessageType
|
||||
) -> protobuf.MessageType:
|
||||
ctx.secret
|
||||
|
||||
if not ThpCredentialRequest.is_type_of(message):
|
||||
raise UnexpectedMessage("Unexpected message")
|
||||
@ -362,42 +392,39 @@ def _check_state(ctx: PairingContext, *allowed_states: ChannelState) -> None:
|
||||
|
||||
|
||||
def _check_method_is_allowed(ctx: PairingContext, method: ThpPairingMethod) -> None:
|
||||
if not _is_method_included(ctx, method):
|
||||
if method not in get_enabled_pairing_methods(ctx.iface):
|
||||
raise ThpError("Unexpected pairing method")
|
||||
|
||||
|
||||
def _is_method_included(ctx: PairingContext, method: ThpPairingMethod) -> bool:
|
||||
return method in ctx.channel_ctx.selected_pairing_methods
|
||||
def _check_method_is_selected(ctx: PairingContext, method: ThpPairingMethod) -> None:
|
||||
if method is not ctx.selected_method:
|
||||
raise ThpError("Not selected pairing method")
|
||||
|
||||
|
||||
#
|
||||
# Helpers - getters
|
||||
|
||||
|
||||
def _get_possible_pairing_methods_and_cancel(ctx: PairingContext) -> Tuple[int, ...]:
|
||||
def _get_accepted_messages(ctx: PairingContext) -> Tuple[int, ...]:
|
||||
r = _get_possible_pairing_methods(ctx)
|
||||
mtype = Cancel.MESSAGE_WIRE_TYPE
|
||||
return r + ((mtype,) if mtype is not None else ())
|
||||
r += (mtype,) if mtype is not None else ()
|
||||
mtype = ThpSelectMethod.MESSAGE_WIRE_TYPE
|
||||
r += (mtype,) if mtype is not None else ()
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def _get_possible_pairing_methods(ctx: PairingContext) -> Tuple[int, ...]:
|
||||
r = tuple(
|
||||
_get_message_type_for_method(method)
|
||||
for method in ctx.channel_ctx.selected_pairing_methods
|
||||
)
|
||||
if __debug__:
|
||||
from trezor.messages import DebugLinkGetState
|
||||
|
||||
mtype = DebugLinkGetState.MESSAGE_WIRE_TYPE
|
||||
return r + ((mtype,) if mtype is not None else ())
|
||||
r = tuple(_get_message_type_for_method(ctx.selected_method))
|
||||
return r
|
||||
|
||||
|
||||
def _get_message_type_for_method(method: int) -> int:
|
||||
if method is ThpPairingMethod.CodeEntry:
|
||||
return ThpMessageType.ThpCodeEntryCpaceHost
|
||||
if method is ThpPairingMethod.NFC_Unidirectional:
|
||||
return ThpMessageType.ThpNfcUnidirectionalTag
|
||||
return ThpMessageType.ThpCodeEntryCpaceHostTag
|
||||
if method is ThpPairingMethod.NFC:
|
||||
return ThpMessageType.ThpNfcTagHost
|
||||
if method is ThpPairingMethod.QrCode:
|
||||
return ThpMessageType.ThpQrCodeTag
|
||||
raise ValueError("Unexpected pairing method - no message type available")
|
||||
|
16
core/src/trezor/enums/ThpMessageType.py
generated
16
core/src/trezor/enums/ThpMessageType.py
generated
@ -3,8 +3,9 @@
|
||||
# isort:skip_file
|
||||
|
||||
ThpCreateNewSession = 1000
|
||||
ThpNewSession = 1001
|
||||
ThpStartPairingRequest = 1008
|
||||
ThpPairingRequest = 1006
|
||||
ThpPairingRequestAck = 1007
|
||||
ThpSelectMethod = 1008
|
||||
ThpPairingPreparationsFinished = 1009
|
||||
ThpCredentialRequest = 1010
|
||||
ThpCredentialResponse = 1011
|
||||
@ -12,11 +13,10 @@ ThpEndRequest = 1012
|
||||
ThpEndResponse = 1013
|
||||
ThpCodeEntryCommitment = 1016
|
||||
ThpCodeEntryChallenge = 1017
|
||||
ThpCodeEntryCpaceHost = 1018
|
||||
ThpCodeEntryCpaceTrezor = 1019
|
||||
ThpCodeEntryTag = 1020
|
||||
ThpCodeEntrySecret = 1021
|
||||
ThpCodeEntryCpaceTrezor = 1018
|
||||
ThpCodeEntryCpaceHostTag = 1019
|
||||
ThpCodeEntrySecret = 1020
|
||||
ThpQrCodeTag = 1024
|
||||
ThpQrCodeSecret = 1025
|
||||
ThpNfcUnidirectionalTag = 1032
|
||||
ThpNfcUnidirectionalSecret = 1033
|
||||
ThpNfcTagHost = 1032
|
||||
ThpNfcTagTrezor = 1033
|
||||
|
4
core/src/trezor/enums/ThpPairingMethod.py
generated
4
core/src/trezor/enums/ThpPairingMethod.py
generated
@ -2,7 +2,7 @@
|
||||
# fmt: off
|
||||
# isort:skip_file
|
||||
|
||||
NoMethod = 1
|
||||
SkipPairing = 1
|
||||
CodeEntry = 2
|
||||
QrCode = 3
|
||||
NFC_Unidirectional = 4
|
||||
NFC = 4
|
||||
|
20
core/src/trezor/enums/__init__.py
generated
20
core/src/trezor/enums/__init__.py
generated
@ -353,8 +353,9 @@ if TYPE_CHECKING:
|
||||
|
||||
class ThpMessageType(IntEnum):
|
||||
ThpCreateNewSession = 1000
|
||||
ThpNewSession = 1001
|
||||
ThpStartPairingRequest = 1008
|
||||
ThpPairingRequest = 1006
|
||||
ThpPairingRequestAck = 1007
|
||||
ThpSelectMethod = 1008
|
||||
ThpPairingPreparationsFinished = 1009
|
||||
ThpCredentialRequest = 1010
|
||||
ThpCredentialResponse = 1011
|
||||
@ -362,20 +363,19 @@ if TYPE_CHECKING:
|
||||
ThpEndResponse = 1013
|
||||
ThpCodeEntryCommitment = 1016
|
||||
ThpCodeEntryChallenge = 1017
|
||||
ThpCodeEntryCpaceHost = 1018
|
||||
ThpCodeEntryCpaceTrezor = 1019
|
||||
ThpCodeEntryTag = 1020
|
||||
ThpCodeEntrySecret = 1021
|
||||
ThpCodeEntryCpaceTrezor = 1018
|
||||
ThpCodeEntryCpaceHostTag = 1019
|
||||
ThpCodeEntrySecret = 1020
|
||||
ThpQrCodeTag = 1024
|
||||
ThpQrCodeSecret = 1025
|
||||
ThpNfcUnidirectionalTag = 1032
|
||||
ThpNfcUnidirectionalSecret = 1033
|
||||
ThpNfcTagHost = 1032
|
||||
ThpNfcTagTrezor = 1033
|
||||
|
||||
class ThpPairingMethod(IntEnum):
|
||||
NoMethod = 1
|
||||
SkipPairing = 1
|
||||
CodeEntry = 2
|
||||
QrCode = 3
|
||||
NFC_Unidirectional = 4
|
||||
NFC = 4
|
||||
|
||||
class MessageType(IntEnum):
|
||||
Initialize = 0
|
||||
|
84
core/src/trezor/messages.py
generated
84
core/src/trezor/messages.py
generated
@ -6175,8 +6175,8 @@ if TYPE_CHECKING:
|
||||
class ThpDeviceProperties(protobuf.MessageType):
|
||||
internal_model: "str | None"
|
||||
model_variant: "int | None"
|
||||
bootloader_mode: "bool | None"
|
||||
protocol_version: "int | None"
|
||||
protocol_version_major: "int | None"
|
||||
protocol_version_minor: "int | None"
|
||||
pairing_methods: "list[ThpPairingMethod]"
|
||||
|
||||
def __init__(
|
||||
@ -6185,8 +6185,8 @@ if TYPE_CHECKING:
|
||||
pairing_methods: "list[ThpPairingMethod] | None" = None,
|
||||
internal_model: "str | None" = None,
|
||||
model_variant: "int | None" = None,
|
||||
bootloader_mode: "bool | None" = None,
|
||||
protocol_version: "int | None" = None,
|
||||
protocol_version_major: "int | None" = None,
|
||||
protocol_version_minor: "int | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -6196,12 +6196,10 @@ if TYPE_CHECKING:
|
||||
|
||||
class ThpHandshakeCompletionReqNoisePayload(protobuf.MessageType):
|
||||
host_pairing_credential: "bytes | None"
|
||||
pairing_methods: "list[ThpPairingMethod]"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
pairing_methods: "list[ThpPairingMethod] | None" = None,
|
||||
host_pairing_credential: "bytes | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
@ -6228,21 +6226,7 @@ if TYPE_CHECKING:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpCreateNewSession"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpNewSession(protobuf.MessageType):
|
||||
new_session_id: "int | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
new_session_id: "int | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpNewSession"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpStartPairingRequest(protobuf.MessageType):
|
||||
class ThpPairingRequest(protobuf.MessageType):
|
||||
host_name: "str | None"
|
||||
|
||||
def __init__(
|
||||
@ -6253,7 +6237,27 @@ if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpStartPairingRequest"]:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpPairingRequest"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpPairingRequestAck(protobuf.MessageType):
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpPairingRequestAck"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpSelectMethod(protobuf.MessageType):
|
||||
selected_pairing_method: "ThpPairingMethod | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
selected_pairing_method: "ThpPairingMethod | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpSelectMethod"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpPairingPreparationsFinished(protobuf.MessageType):
|
||||
@ -6290,20 +6294,6 @@ if TYPE_CHECKING:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpCodeEntryChallenge"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpCodeEntryCpaceHost(protobuf.MessageType):
|
||||
cpace_host_public_key: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
cpace_host_public_key: "bytes | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpCodeEntryCpaceHost"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpCodeEntryCpaceTrezor(protobuf.MessageType):
|
||||
cpace_trezor_public_key: "bytes | None"
|
||||
|
||||
@ -6318,18 +6308,20 @@ if TYPE_CHECKING:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpCodeEntryCpaceTrezor"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpCodeEntryTag(protobuf.MessageType):
|
||||
class ThpCodeEntryCpaceHostTag(protobuf.MessageType):
|
||||
cpace_host_public_key: "bytes | None"
|
||||
tag: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
cpace_host_public_key: "bytes | None" = None,
|
||||
tag: "bytes | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpCodeEntryTag"]:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpCodeEntryCpaceHostTag"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpCodeEntrySecret(protobuf.MessageType):
|
||||
@ -6374,7 +6366,7 @@ if TYPE_CHECKING:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpQrCodeSecret"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpNfcUnidirectionalTag(protobuf.MessageType):
|
||||
class ThpNfcTagHost(protobuf.MessageType):
|
||||
tag: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
@ -6385,30 +6377,32 @@ if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpNfcUnidirectionalTag"]:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpNfcTagHost"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpNfcUnidirectionalSecret(protobuf.MessageType):
|
||||
secret: "bytes | None"
|
||||
class ThpNfcTagTrezor(protobuf.MessageType):
|
||||
tag: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
secret: "bytes | None" = None,
|
||||
tag: "bytes | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpNfcUnidirectionalSecret"]:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ThpNfcTagTrezor"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ThpCredentialRequest(protobuf.MessageType):
|
||||
host_static_pubkey: "bytes | None"
|
||||
autoconnect: "bool | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
host_static_pubkey: "bytes | None" = None,
|
||||
autoconnect: "bool | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -6446,11 +6440,13 @@ if TYPE_CHECKING:
|
||||
|
||||
class ThpCredentialMetadata(protobuf.MessageType):
|
||||
host_name: "str | None"
|
||||
autoconnect: "bool | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
host_name: "str | None" = None,
|
||||
autoconnect: "bool | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
@ -66,13 +66,14 @@ class ChannelState(IntEnum):
|
||||
UNALLOCATED = 0
|
||||
TH1 = 1
|
||||
TH2 = 2
|
||||
TP1 = 3
|
||||
TP2 = 4
|
||||
TP3 = 5
|
||||
TP4 = 6
|
||||
TC1 = 7
|
||||
ENCRYPTED_TRANSPORT = 8
|
||||
INVALIDATED = 9
|
||||
TP0 = 3
|
||||
TP1 = 4
|
||||
TP2 = 5
|
||||
TP3 = 6
|
||||
TP4 = 7
|
||||
TC1 = 8
|
||||
ENCRYPTED_TRANSPORT = 9
|
||||
INVALIDATED = 10
|
||||
|
||||
|
||||
class SessionState(IntEnum):
|
||||
@ -134,7 +135,7 @@ class PacketHeader:
|
||||
_DEFAULT_ENABLED_PAIRING_METHODS = [
|
||||
ThpPairingMethod.CodeEntry,
|
||||
ThpPairingMethod.QrCode,
|
||||
ThpPairingMethod.NFC_Unidirectional,
|
||||
ThpPairingMethod.NFC,
|
||||
]
|
||||
|
||||
|
||||
@ -149,7 +150,7 @@ def get_enabled_pairing_methods(
|
||||
|
||||
methods = _DEFAULT_ENABLED_PAIRING_METHODS.copy()
|
||||
if iface is not None and iface is usb.iface_wire:
|
||||
methods.append(ThpPairingMethod.NoMethod)
|
||||
methods.append(ThpPairingMethod.SkipPairing)
|
||||
return methods
|
||||
|
||||
|
||||
@ -159,8 +160,8 @@ def _get_device_properties(iface: WireInterface) -> ThpDeviceProperties:
|
||||
pairing_methods=get_enabled_pairing_methods(iface),
|
||||
internal_model=utils.INTERNAL_MODEL,
|
||||
model_variant=0,
|
||||
bootloader_mode=False,
|
||||
protocol_version=2,
|
||||
protocol_version_major=2,
|
||||
protocol_version_minor=0,
|
||||
)
|
||||
|
||||
|
||||
|
@ -69,7 +69,6 @@ class Channel:
|
||||
self.bytes_read: int = 0
|
||||
self.expected_payload_length: int = 0
|
||||
self.is_cont_packet_expected: bool = False
|
||||
self.selected_pairing_methods = []
|
||||
self.sessions: dict[int, GenericSessionContext] = {}
|
||||
|
||||
# Objects for writing a message to a wire
|
||||
|
@ -11,9 +11,8 @@ class Cpace:
|
||||
CPace, a balanced composable PAKE: https://datatracker.ietf.org/doc/draft-irtf-cfrg-cpace/
|
||||
"""
|
||||
|
||||
def __init__(self, cpace_host_public_key: bytes, handshake_hash: bytes) -> None:
|
||||
def __init__(self, handshake_hash: bytes) -> None:
|
||||
self.handshake_hash: bytes = handshake_hash
|
||||
self.host_public_key: bytes = cpace_host_public_key
|
||||
self.shared_secret: bytes
|
||||
self.trezor_private_key: bytes
|
||||
self.trezor_public_key: bytes
|
||||
@ -34,3 +33,8 @@ class Cpace:
|
||||
self.shared_secret = curve25519.multiply(
|
||||
self.trezor_private_key, self.host_public_key
|
||||
)
|
||||
|
||||
def compute_shared_secret(self, host_public_key: bytes):
|
||||
self.shared_secret = curve25519.multiply(
|
||||
self.trezor_private_key, host_public_key
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from trezor.wire.thp import get_enabled_pairing_methods
|
||||
from ubinascii import hexlify
|
||||
|
||||
import trezorui_api
|
||||
@ -13,6 +14,7 @@ if TYPE_CHECKING:
|
||||
from typing import Container
|
||||
|
||||
from trezor import ui
|
||||
from trezor.enums import ThpPairingMethod
|
||||
|
||||
from .channel import Channel
|
||||
from .cpace import Cpace
|
||||
@ -28,7 +30,7 @@ class PairingDisplayData:
|
||||
def __init__(self) -> None:
|
||||
self.code_code_entry: int | None = None
|
||||
self.code_qr_code: bytes | None = None
|
||||
self.code_nfc_unidirectional: bytes | None = None
|
||||
self.code_nfc: bytes | None = None
|
||||
|
||||
def get_display_layout(self) -> ui.Layout:
|
||||
from trezor import ui
|
||||
@ -77,20 +79,16 @@ class PairingContext(Context):
|
||||
super().__init__(channel_ctx.iface, channel_ctx.channel_id)
|
||||
self.channel_ctx: Channel = channel_ctx
|
||||
self.incoming_message = loop.mailbox()
|
||||
self.secret: bytes = random.bytes(16)
|
||||
self.nfc_secret: bytes
|
||||
self.qr_code_secret: bytes
|
||||
self.code_entry_secret: bytes | None = None
|
||||
self.selected_method: ThpPairingMethod
|
||||
|
||||
self.display_data: PairingDisplayData = PairingDisplayData()
|
||||
self.cpace: Cpace
|
||||
self.host_name: str
|
||||
|
||||
async def handle(self, is_debug_session: bool = False) -> None:
|
||||
# if __debug__:
|
||||
# log.debug(__name__, "handle - start")
|
||||
# if is_debug_session:
|
||||
# import apps.debug
|
||||
|
||||
# apps.debug.DEBUG_CONTEXT = self
|
||||
|
||||
async def handle(self) -> None:
|
||||
next_message: Message | None = None
|
||||
|
||||
while True:
|
||||
@ -189,6 +187,11 @@ class PairingContext(Context):
|
||||
del msg
|
||||
return await self.read(expected_types)
|
||||
|
||||
def set_selected_method(self, selected_method: ThpPairingMethod) -> None:
|
||||
if selected_method not in get_enabled_pairing_methods(self.iface):
|
||||
raise Exception("Not allowed to set this method")
|
||||
self.selected_method = selected_method
|
||||
|
||||
|
||||
async def handle_pairing_request_message(
|
||||
pairing_ctx: PairingContext,
|
||||
|
@ -315,12 +315,7 @@ async def _handle_state_TH2(ctx: Channel, message_length: int, ctrl_byte: int) -
|
||||
)
|
||||
if TYPE_CHECKING:
|
||||
assert ThpHandshakeCompletionReqNoisePayload.is_type_of(noise_payload)
|
||||
enabled_methods = get_enabled_pairing_methods(ctx.iface)
|
||||
for method in noise_payload.pairing_methods:
|
||||
if method not in enabled_methods:
|
||||
raise ThpInvalidDataError()
|
||||
if method not in ctx.selected_pairing_methods:
|
||||
ctx.selected_pairing_methods.append(method)
|
||||
|
||||
if __debug__ and utils.ALLOW_DEBUG_MESSAGES:
|
||||
log.debug(
|
||||
__name__,
|
||||
@ -357,9 +352,9 @@ async def _handle_state_TH2(ctx: Channel, message_length: int, ctrl_byte: int) -
|
||||
ctx.handshake = None
|
||||
|
||||
if paired:
|
||||
ctx.set_channel_state(ChannelState.ENCRYPTED_TRANSPORT)
|
||||
ctx.set_channel_state(ChannelState.TC1)
|
||||
else:
|
||||
ctx.set_channel_state(ChannelState.TP1)
|
||||
ctx.set_channel_state(ChannelState.TP0)
|
||||
|
||||
|
||||
async def _handle_state_ENCRYPTED_TRANSPORT(ctx: Channel, message_length: int) -> None:
|
||||
@ -457,6 +452,7 @@ def _decode_message(
|
||||
|
||||
def _is_channel_state_pairing(state: int) -> bool:
|
||||
if state in (
|
||||
ChannelState.TP0,
|
||||
ChannelState.TP1,
|
||||
ChannelState.TP2,
|
||||
ChannelState.TP3,
|
||||
|
@ -27,7 +27,7 @@ if utils.USE_THP:
|
||||
ThpCodeEntryTag,
|
||||
ThpCredentialRequest,
|
||||
ThpEndRequest,
|
||||
ThpStartPairingRequest,
|
||||
ThpPairingRequest,
|
||||
)
|
||||
from trezor.wire.thp import (
|
||||
ChannelState,
|
||||
@ -280,13 +280,13 @@ class TestTrezorHostProtocol(unittest.TestCase):
|
||||
config.wipe()
|
||||
channel = next(iter(thp_main._CHANNELS.values()))
|
||||
channel.selected_pairing_methods = [
|
||||
ThpPairingMethod.NoMethod,
|
||||
ThpPairingMethod.SkipPairing,
|
||||
ThpPairingMethod.CodeEntry,
|
||||
ThpPairingMethod.NFC_Unidirectional,
|
||||
ThpPairingMethod.QrCode,
|
||||
]
|
||||
pairing_ctx = PairingContext(channel)
|
||||
request_message = ThpStartPairingRequest()
|
||||
request_message = ThpPairingRequest()
|
||||
channel.set_channel_state(ChannelState.TP1)
|
||||
gen = pairing.handle_pairing_request(pairing_ctx, request_message)
|
||||
|
||||
@ -310,7 +310,7 @@ class TestTrezorHostProtocol(unittest.TestCase):
|
||||
ThpPairingMethod.QrCode,
|
||||
]
|
||||
pairing_ctx = PairingContext(channel)
|
||||
request_message = ThpStartPairingRequest()
|
||||
request_message = ThpPairingRequest()
|
||||
with self.assertRaises(UnexpectedMessage) as e:
|
||||
pairing.handle_pairing_request(pairing_ctx, request_message)
|
||||
print(e.value.message)
|
||||
@ -346,7 +346,7 @@ class TestTrezorHostProtocol(unittest.TestCase):
|
||||
msg = ThpCodeEntryCpaceHost(cpace_host_public_key=cpace_host_public_key)
|
||||
|
||||
# msg = ThpQrCodeTag(tag=tag_qrc)
|
||||
# msg = ThpNfcUnidirectionalTag(tag=tag_nfc)
|
||||
# msg = ThpNfcTagHost(tag=tag_nfc)
|
||||
buffer: bytearray = bytearray(protobuf.encoded_length(msg))
|
||||
|
||||
protobuf.encode(buffer, msg)
|
||||
|
112
python/src/trezorlib/messages.py
generated
112
python/src/trezorlib/messages.py
generated
@ -406,8 +406,9 @@ class TezosBallotType(IntEnum):
|
||||
|
||||
class ThpMessageType(IntEnum):
|
||||
ThpCreateNewSession = 1000
|
||||
ThpNewSession = 1001
|
||||
ThpStartPairingRequest = 1008
|
||||
ThpPairingRequest = 1006
|
||||
ThpPairingRequestAck = 1007
|
||||
ThpSelectMethod = 1008
|
||||
ThpPairingPreparationsFinished = 1009
|
||||
ThpCredentialRequest = 1010
|
||||
ThpCredentialResponse = 1011
|
||||
@ -415,21 +416,20 @@ class ThpMessageType(IntEnum):
|
||||
ThpEndResponse = 1013
|
||||
ThpCodeEntryCommitment = 1016
|
||||
ThpCodeEntryChallenge = 1017
|
||||
ThpCodeEntryCpaceHost = 1018
|
||||
ThpCodeEntryCpaceTrezor = 1019
|
||||
ThpCodeEntryTag = 1020
|
||||
ThpCodeEntrySecret = 1021
|
||||
ThpCodeEntryCpaceTrezor = 1018
|
||||
ThpCodeEntryCpaceHostTag = 1019
|
||||
ThpCodeEntrySecret = 1020
|
||||
ThpQrCodeTag = 1024
|
||||
ThpQrCodeSecret = 1025
|
||||
ThpNfcUnidirectionalTag = 1032
|
||||
ThpNfcUnidirectionalSecret = 1033
|
||||
ThpNfcTagHost = 1032
|
||||
ThpNfcTagTrezor = 1033
|
||||
|
||||
|
||||
class ThpPairingMethod(IntEnum):
|
||||
NoMethod = 1
|
||||
SkipPairing = 1
|
||||
CodeEntry = 2
|
||||
QrCode = 3
|
||||
NFC_Unidirectional = 4
|
||||
NFC = 4
|
||||
|
||||
|
||||
class MessageType(IntEnum):
|
||||
@ -7909,8 +7909,8 @@ class ThpDeviceProperties(protobuf.MessageType):
|
||||
FIELDS = {
|
||||
1: protobuf.Field("internal_model", "string", repeated=False, required=False, default=None),
|
||||
2: protobuf.Field("model_variant", "uint32", repeated=False, required=False, default=None),
|
||||
3: protobuf.Field("bootloader_mode", "bool", repeated=False, required=False, default=None),
|
||||
4: protobuf.Field("protocol_version", "uint32", repeated=False, required=False, default=None),
|
||||
3: protobuf.Field("protocol_version_major", "uint32", repeated=False, required=False, default=None),
|
||||
4: protobuf.Field("protocol_version_minor", "uint32", repeated=False, required=False, default=None),
|
||||
5: protobuf.Field("pairing_methods", "ThpPairingMethod", repeated=True, required=False, default=None),
|
||||
}
|
||||
|
||||
@ -7920,30 +7920,27 @@ class ThpDeviceProperties(protobuf.MessageType):
|
||||
pairing_methods: Optional[Sequence["ThpPairingMethod"]] = None,
|
||||
internal_model: Optional["str"] = None,
|
||||
model_variant: Optional["int"] = None,
|
||||
bootloader_mode: Optional["bool"] = None,
|
||||
protocol_version: Optional["int"] = None,
|
||||
protocol_version_major: Optional["int"] = None,
|
||||
protocol_version_minor: Optional["int"] = None,
|
||||
) -> None:
|
||||
self.pairing_methods: Sequence["ThpPairingMethod"] = pairing_methods if pairing_methods is not None else []
|
||||
self.internal_model = internal_model
|
||||
self.model_variant = model_variant
|
||||
self.bootloader_mode = bootloader_mode
|
||||
self.protocol_version = protocol_version
|
||||
self.protocol_version_major = protocol_version_major
|
||||
self.protocol_version_minor = protocol_version_minor
|
||||
|
||||
|
||||
class ThpHandshakeCompletionReqNoisePayload(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = None
|
||||
FIELDS = {
|
||||
1: protobuf.Field("host_pairing_credential", "bytes", repeated=False, required=False, default=None),
|
||||
2: protobuf.Field("pairing_methods", "ThpPairingMethod", repeated=True, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
pairing_methods: Optional[Sequence["ThpPairingMethod"]] = None,
|
||||
host_pairing_credential: Optional["bytes"] = None,
|
||||
) -> None:
|
||||
self.pairing_methods: Sequence["ThpPairingMethod"] = pairing_methods if pairing_methods is not None else []
|
||||
self.host_pairing_credential = host_pairing_credential
|
||||
|
||||
|
||||
@ -7967,22 +7964,8 @@ class ThpCreateNewSession(protobuf.MessageType):
|
||||
self.derive_cardano = derive_cardano
|
||||
|
||||
|
||||
class ThpNewSession(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1001
|
||||
FIELDS = {
|
||||
1: protobuf.Field("new_session_id", "uint32", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
new_session_id: Optional["int"] = None,
|
||||
) -> None:
|
||||
self.new_session_id = new_session_id
|
||||
|
||||
|
||||
class ThpStartPairingRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1008
|
||||
class ThpPairingRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1006
|
||||
FIELDS = {
|
||||
1: protobuf.Field("host_name", "string", repeated=False, required=False, default=None),
|
||||
}
|
||||
@ -7995,6 +7978,24 @@ class ThpStartPairingRequest(protobuf.MessageType):
|
||||
self.host_name = host_name
|
||||
|
||||
|
||||
class ThpPairingRequestAck(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1007
|
||||
|
||||
|
||||
class ThpSelectMethod(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1008
|
||||
FIELDS = {
|
||||
1: protobuf.Field("selected_pairing_method", "ThpPairingMethod", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
selected_pairing_method: Optional["ThpPairingMethod"] = None,
|
||||
) -> None:
|
||||
self.selected_pairing_method = selected_pairing_method
|
||||
|
||||
|
||||
class ThpPairingPreparationsFinished(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1009
|
||||
|
||||
@ -8027,22 +8028,8 @@ class ThpCodeEntryChallenge(protobuf.MessageType):
|
||||
self.challenge = challenge
|
||||
|
||||
|
||||
class ThpCodeEntryCpaceHost(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1018
|
||||
FIELDS = {
|
||||
1: protobuf.Field("cpace_host_public_key", "bytes", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
cpace_host_public_key: Optional["bytes"] = None,
|
||||
) -> None:
|
||||
self.cpace_host_public_key = cpace_host_public_key
|
||||
|
||||
|
||||
class ThpCodeEntryCpaceTrezor(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1019
|
||||
MESSAGE_WIRE_TYPE = 1018
|
||||
FIELDS = {
|
||||
1: protobuf.Field("cpace_trezor_public_key", "bytes", repeated=False, required=False, default=None),
|
||||
}
|
||||
@ -8055,22 +8042,25 @@ class ThpCodeEntryCpaceTrezor(protobuf.MessageType):
|
||||
self.cpace_trezor_public_key = cpace_trezor_public_key
|
||||
|
||||
|
||||
class ThpCodeEntryTag(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1020
|
||||
class ThpCodeEntryCpaceHostTag(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1019
|
||||
FIELDS = {
|
||||
1: protobuf.Field("cpace_host_public_key", "bytes", repeated=False, required=False, default=None),
|
||||
2: protobuf.Field("tag", "bytes", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
cpace_host_public_key: Optional["bytes"] = None,
|
||||
tag: Optional["bytes"] = None,
|
||||
) -> None:
|
||||
self.cpace_host_public_key = cpace_host_public_key
|
||||
self.tag = tag
|
||||
|
||||
|
||||
class ThpCodeEntrySecret(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1021
|
||||
MESSAGE_WIRE_TYPE = 1020
|
||||
FIELDS = {
|
||||
1: protobuf.Field("secret", "bytes", repeated=False, required=False, default=None),
|
||||
}
|
||||
@ -8111,7 +8101,7 @@ class ThpQrCodeSecret(protobuf.MessageType):
|
||||
self.secret = secret
|
||||
|
||||
|
||||
class ThpNfcUnidirectionalTag(protobuf.MessageType):
|
||||
class ThpNfcTagHost(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1032
|
||||
FIELDS = {
|
||||
1: protobuf.Field("tag", "bytes", repeated=False, required=False, default=None),
|
||||
@ -8125,32 +8115,35 @@ class ThpNfcUnidirectionalTag(protobuf.MessageType):
|
||||
self.tag = tag
|
||||
|
||||
|
||||
class ThpNfcUnidirectionalSecret(protobuf.MessageType):
|
||||
class ThpNfcTagTrezor(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1033
|
||||
FIELDS = {
|
||||
1: protobuf.Field("secret", "bytes", repeated=False, required=False, default=None),
|
||||
1: protobuf.Field("tag", "bytes", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
secret: Optional["bytes"] = None,
|
||||
tag: Optional["bytes"] = None,
|
||||
) -> None:
|
||||
self.secret = secret
|
||||
self.tag = tag
|
||||
|
||||
|
||||
class ThpCredentialRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 1010
|
||||
FIELDS = {
|
||||
1: protobuf.Field("host_static_pubkey", "bytes", repeated=False, required=False, default=None),
|
||||
2: protobuf.Field("autoconnect", "bool", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
host_static_pubkey: Optional["bytes"] = None,
|
||||
autoconnect: Optional["bool"] = None,
|
||||
) -> None:
|
||||
self.host_static_pubkey = host_static_pubkey
|
||||
self.autoconnect = autoconnect
|
||||
|
||||
|
||||
class ThpCredentialResponse(protobuf.MessageType):
|
||||
@ -8182,14 +8175,17 @@ class ThpCredentialMetadata(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = None
|
||||
FIELDS = {
|
||||
1: protobuf.Field("host_name", "string", repeated=False, required=False, default=None),
|
||||
2: protobuf.Field("autoconnect", "bool", repeated=False, required=False, default=None),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
host_name: Optional["str"] = None,
|
||||
autoconnect: Optional["bool"] = None,
|
||||
) -> None:
|
||||
self.host_name = host_name
|
||||
self.autoconnect = autoconnect
|
||||
|
||||
|
||||
class ThpPairingCredential(protobuf.MessageType):
|
||||
|
@ -306,7 +306,7 @@ class ProtocolV2(ProtocolAndChannel):
|
||||
|
||||
def _do_pairing(self):
|
||||
# Send StartPairingReqest message
|
||||
message = messages.ThpStartPairingRequest()
|
||||
message = messages.ThpPairingRequest()
|
||||
message_type, message_data = self.mapping.encode(message)
|
||||
|
||||
self._encrypt_and_write(MANAGEMENT_SESSION_ID, message_type, message_data)
|
||||
|
1269
rust/trezor-client/src/protos/generated/messages_thp.rs
generated
1269
rust/trezor-client/src/protos/generated/messages_thp.rs
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user