diff --git a/core/src/apps/thp/pairing.py b/core/src/apps/thp/pairing.py index 367c85f8e2..289070a542 100644 --- a/core/src/apps/thp/pairing.py +++ b/core/src/apps/thp/pairing.py @@ -221,10 +221,6 @@ async def _handle_code_entry_is_selected_first_time(ctx: PairingContext) -> None @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_force(ThpPairingPreparationsFinished()) @@ -327,9 +323,13 @@ async def _handle_nfc_tag( ) -> protobuf.MessageType: if TYPE_CHECKING: assert isinstance(message, ThpNfcTagHost) + + assert ctx.nfc_secret is not None + assert ctx.handshake_hash_host is not None + assert ctx.nfc_secret_host is not None + 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: @@ -338,10 +338,12 @@ async def _handle_nfc_tag( ) # TODO remove after testing raise ThpError("Unexpected NFC Unidirectional Tag") + if ctx.handshake_hash_host[:16] != ctx.channel_ctx.get_handshake_hash()[:16]: + raise ThpError("Handshake hash mismatch") + 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) + sha_ctx.update(ctx.nfc_secret_host) trezor_tag = sha_ctx.digest() return await _handle_secret_reveal( ctx, diff --git a/tests/device_tests/thp/test_thp.py b/tests/device_tests/thp/test_thp.py index c436213694..8c0b16433d 100644 --- a/tests/device_tests/thp/test_thp.py +++ b/tests/device_tests/thp/test_thp.py @@ -18,6 +18,8 @@ from trezorlib.messages import ( ThpCodeEntrySecret, ThpEndRequest, ThpEndResponse, + ThpNfcTagHost, + ThpNfcTagTrezor, ThpPairingMethod, ThpPairingPreparationsFinished, ThpPairingRequest, @@ -252,3 +254,69 @@ def test_pairing_code_entry(client: Client) -> None: _read_message(ThpEndResponse) protocol._has_valid_channel = True + + +def test_pairing_nfc(client: Client) -> None: + global protocol + _prepare_protocol(client) + + # Generate ephemeral keys + host_ephemeral_privkey = curve25519.get_private_key(os.urandom(32)) + host_ephemeral_pubkey = curve25519.get_public_key(host_ephemeral_privkey) + + protocol._do_channel_allocation() + + protocol._do_handshake(host_ephemeral_privkey, host_ephemeral_pubkey) + + _send_message(ThpPairingRequest()) + + _read_message(ButtonRequest) + + _send_message(ButtonAck()) + + client.debug.press_yes() + + _read_message(ThpPairingRequestApproved) + + _send_message(ThpSelectMethod(selected_pairing_method=ThpPairingMethod.NFC)) + + _read_message(ThpPairingPreparationsFinished) + + # NFC screen shown + _read_message(ButtonRequest) + _send_message(ButtonAck()) + + nfc_secret_host = b"\x02\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF" # TODO generate randomly + + # Read `nfc_secret` and `handshake_hash` from Trezor using debuglink + pairing_info = client.debug.pairing_info( + thp_channel_id=protocol.channel_id.to_bytes(2, "big"), + handshake_hash=protocol.handshake_hash, + nfc_secret_host=nfc_secret_host, + ) + handshake_hash_trezor = pairing_info.handshake_hash + nfc_secret_trezor = pairing_info.nfc_secret_trezor + + assert handshake_hash_trezor[:16] == protocol.handshake_hash[:16] + + # Compute tag for response + sha_ctx = hashlib.sha256(ThpPairingMethod.NFC.to_bytes(1, "big")) + sha_ctx.update(protocol.handshake_hash) + sha_ctx.update(nfc_secret_trezor) + tag_host = sha_ctx.digest() + + _send_message(ThpNfcTagHost(tag=tag_host)) + + tag_trezor_msg = _read_message(ThpNfcTagTrezor) + + # Check that the `code` was derived from the revealed secret + sha_ctx = hashlib.sha256(ThpPairingMethod.NFC.to_bytes(1, "big")) + sha_ctx.update(protocol.handshake_hash) + sha_ctx.update(nfc_secret_host) + computed_tag = sha_ctx.digest() + assert tag_trezor_msg.tag == computed_tag + + _send_message(ThpEndRequest()) + _read_message(ThpEndResponse) + + protocol._has_valid_channel = True