test(core): reimplement some THP transport-related tests
What changed, and why it matters
This commit only adds new automated tests for the Trezor hardware wallet's THP (Trezor Host Protocol) transport layer. It checks that the device correctly rejects old protocol v1 packets and unallocated channel messages. No production firmware code is changed, so this commit does not introduce or fix a security vulnerability by itself.
No security action required; this is a test-only commit. Reviewers may optionally run the new THP tests to confirm the existing firmware behavior matches expectations.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a new test file tests/device_tests/thp/test_basic.py with two pytest cases: test_v1 verifies that a protocol v1 init packet (?##) receives a Failure(InvalidProtocol) and a continuation packet (?) is silently ignored; test_v2_unallocated verifies that a message to an unallocated THP channel 0x789a returns the expected error frame. The second changed file, tests/ui_tests/fixtures.json, only records expected UI hashes for these new tests across device locales. There are no changes to firmware source, transport implementation, or cryptographic code.
Changed components
tests/device_tests/thp/test_basic.pytests/ui_tests/fixtures.jsonInspect captured patch +53 / −0
diff --git a/tests/device_tests/thp/test_basic.py b/tests/device_tests/thp/test_basic.py
new file mode 100644
index 000000000..385299fda
--- /dev/null
+++ b/tests/device_tests/thp/test_basic.py
@@ -0,0 +1,41 @@
+import pytest
+
+from trezorlib import messages
+from trezorlib.debuglink import TrezorClientDebugLink as Client
+from trezorlib.mapping import DEFAULT_MAPPING
+from trezorlib.transport import Transport
+from trezorlib.transport.thp.protocol_v1 import ProtocolV1Channel
+
+pytestmark = [
+ pytest.mark.protocol("protocol_v2"),
+ pytest.mark.invalidate_client,
+ pytest.mark.setup_client(uninitialized=True),
+]
+
+
+def write_padded(transport: Transport, msg: bytes):
+ padded = msg.ljust(transport.CHUNK_SIZE, b"\x00")
+ transport.write_chunk(padded)
+
+
+def test_v1(client: Client):
+ protocol_v1 = ProtocolV1Channel(client.protocol.transport, DEFAULT_MAPPING)
+ transport = protocol_v1.transport
+
+ # There should be a failure response to received init packet (starts with "?##")
+ write_padded(transport, b"?## Init packet")
+ res = protocol_v1.read()
+ assert res == messages.Failure(code=messages.FailureType.InvalidProtocol)
+
+ # There should be no response for continuation packet (starts with "?" only)
+ write_padded(transport, b"? Cont packet")
+
+
+def test_v2_unallocated(client: Client):
+ transport = client.protocol.transport
+
+ # A message to unallocated THP channel 0x789a should result in an error
+ write_padded(transport, bytes.fromhex("04789a000c001122334455667796643c6c"))
+ actual = transport.read_chunk()
+ expected_error = bytes.fromhex("42789a0005027b743563")
+ assert actual == expected_error.ljust(len(actual), b"\x00")
diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json
index 6b561a717..6f16d4533 100644
--- a/tests/ui_tests/fixtures.json
+++ b/tests/ui_tests/fixtures.json
@@ -30513,6 +30513,8 @@
"T3W1_cs_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "a89a28f2d9df1860b027f792311fbb769ea8a0f18e0b6de0ee952b1ea6cf89c8",
"T3W1_cs_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "0accfff4a0ef6e1b4754d5d1e21e9a2d1fc86747b3f96a50c696ff5976496522",
"T3W1_cs_thp-test_abp.py::test_abp": "12f9d9816525497df8e45deebdc7009d17707eac493ab2734e16203f5f180019",
+"T3W1_cs_thp-test_basic.py::test_v1": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_cs_thp-test_basic.py::test_v2_unallocated": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_cs_thp-test_handshake.py::test_allocate_channel": "c24521e569c08e3605b164212c876f8ac57c5eef6cca6f2ca53a635a883ebc4b",
"T3W1_cs_thp-test_handshake.py::test_handshake": "12f9d9816525497df8e45deebdc7009d17707eac493ab2734e16203f5f180019",
"T3W1_cs_thp-test_multiple_hosts.py::test_concurrent_handshakes": "c24521e569c08e3605b164212c876f8ac57c5eef6cca6f2ca53a635a883ebc4b",
@@ -31989,6 +31991,8 @@
"T3W1_de_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "7dfbfaefb537aa222df8c9c45bfa0d3c73e44ef72043f36a30bf6d49e7e5bd80",
"T3W1_de_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "5b563c3d8d1fa886fbac5651357d07c774f825103e6c6a784783c0133513facd",
"T3W1_de_thp-test_abp.py::test_abp": "3ff993544bd4ee4a119c39f6a4d40b28b746f959bf7edd572c369b2823a37eda",
+"T3W1_de_thp-test_basic.py::test_v1": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_de_thp-test_basic.py::test_v2_unallocated": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_de_thp-test_handshake.py::test_allocate_channel": "98735f888f827501b25c78c6a737c1983a800c620f709c65b1d95f6c8c9a2b90",
"T3W1_de_thp-test_handshake.py::test_handshake": "3ff993544bd4ee4a119c39f6a4d40b28b746f959bf7edd572c369b2823a37eda",
"T3W1_de_thp-test_multiple_hosts.py::test_concurrent_handshakes": "98735f888f827501b25c78c6a737c1983a800c620f709c65b1d95f6c8c9a2b90",
@@ -33465,6 +33469,8 @@
"T3W1_en_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "65610eddb98c30dd39b18fbd3baa382e10e085b8b88d507e8d6b915a9a14bfa7",
"T3W1_en_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "7ee7a3b21969cc30051cbfbb998bdfa5c168406f785047f99d064c0a4e636cb7",
"T3W1_en_thp-test_abp.py::test_abp": "75dc9de942f7e6834a41f3b69c33df0d40fbddcf2b60c308c6ad8ec9ad97a450",
+"T3W1_en_thp-test_basic.py::test_v1": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_en_thp-test_basic.py::test_v2_unallocated": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_en_thp-test_handshake.py::test_allocate_channel": "931d9afceb0ba1e4faae891775819277242d889644d5c0c5863fc8c9fcf859b1",
"T3W1_en_thp-test_handshake.py::test_handshake": "75dc9de942f7e6834a41f3b69c33df0d40fbddcf2b60c308c6ad8ec9ad97a450",
"T3W1_en_thp-test_multiple_hosts.py::test_concurrent_handshakes": "931d9afceb0ba1e4faae891775819277242d889644d5c0c5863fc8c9fcf859b1",
@@ -34941,6 +34947,8 @@
"T3W1_es_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "92e5a60e6615ba88197dda72a1c303d78dd77711b238a628877848ab11c7ad51",
"T3W1_es_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "5af976156b760290cb866e9b6cd8bf5f01d6c4a27a5875d6244a6ce4f83a2c3f",
"T3W1_es_thp-test_abp.py::test_abp": "493816d5f53ce0deeab5e20eeec662ae26865300e4e429f26613f91a073c7626",
+"T3W1_es_thp-test_basic.py::test_v1": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_es_thp-test_basic.py::test_v2_unallocated": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_es_thp-test_handshake.py::test_allocate_channel": "56536ae9cd7c4ff8022def4ec3350031d3614064d961f53a2850f2be425af201",
"T3W1_es_thp-test_handshake.py::test_handshake": "493816d5f53ce0deeab5e20eeec662ae26865300e4e429f26613f91a073c7626",
"T3W1_es_thp-test_multiple_hosts.py::test_concurrent_handshakes": "56536ae9cd7c4ff8022def4ec3350031d3614064d961f53a2850f2be425af201",
@@ -36417,6 +36425,8 @@
"T3W1_fr_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "d7b2c9177609865b6fc638ca4daf49255a3e1fdea186020d96a43be4d9277626",
"T3W1_fr_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "0f83aa7c86555385270382dcb63e4d2518cae0bf205f4730f44f8504671e53f5",
"T3W1_fr_thp-test_abp.py::test_abp": "219d6bd329c8235165741613d81501fdc7f0c7cefffa486b6b1d1bec0d56a95c",
+"T3W1_fr_thp-test_basic.py::test_v1": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_fr_thp-test_basic.py::test_v2_unallocated": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_fr_thp-test_handshake.py::test_allocate_channel": "e8156cf4eda1d29060f05b4a18d50032b0b344230609b7de7cababfe0a86b20b",
"T3W1_fr_thp-test_handshake.py::test_handshake": "219d6bd329c8235165741613d81501fdc7f0c7cefffa486b6b1d1bec0d56a95c",
"T3W1_fr_thp-test_multiple_hosts.py::test_concurrent_handshakes": "e8156cf4eda1d29060f05b4a18d50032b0b344230609b7de7cababfe0a86b20b",
@@ -37893,6 +37903,8 @@
"T3W1_pt_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "42fe9d9a504fc37de9079a1fb3f418fa90e169bbadf3e42e7d5831bf7b5ab5b7",
"T3W1_pt_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "0889c974d42a4256c283bb355172e1089bdb35fdacf66a705d5f33ee95d0b5be",
"T3W1_pt_thp-test_abp.py::test_abp": "74cd25dcefa829e0580aa355ee78dcef4770d2a2dd7463b6146d455e005035a8",
+"T3W1_pt_thp-test_basic.py::test_v1": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_pt_thp-test_basic.py::test_v2_unallocated": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_pt_thp-test_handshake.py::test_allocate_channel": "1c74667c078e25e7e0d37c7b2aa35f7c8ab02cd88e74695ecb355c82a293b0cc",
"T3W1_pt_thp-test_handshake.py::test_handshake": "74cd25dcefa829e0580aa355ee78dcef4770d2a2dd7463b6146d455e005035a8",
"T3W1_pt_thp-test_multiple_hosts.py::test_concurrent_handshakes": "1c74667c078e25e7e0d37c7b2aa35f7c8ab02cd88e74695ecb355c82a293b0cc",
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.