What changed, and why it matters
This commit only adds a new automated test to the Trezor firmware test suite. The test checks that the host-side software correctly raises an error when it receives a tampered handshake response from a Trezor device during the THP (Trezor Host Protocol) handshake. It does not change any device firmware code, fix a bug, or introduce a security vulnerability.
No security action required. This is a test-only addition. Reviewers may optionally verify that the new test passes in CI and that the UI fixture hashes are correctly generated.
Security signals we found
New regression test for AEAD tag integrity verification during THP handshake
No changes to firmware, cryptography, or protocol implementation
No changelog entry (marked [no changelog])
Evidence from the diff
The diff adds a single test function, test_read_malformed_response, in tests/device_tests/thp/test_handshake.py. The test patches the channel’s _read method to flip the last byte of a handshake_init_response message (the final byte of the Noise AEAD tag) and asserts that channel.open(...) raises cryptography.exceptions.InvalidTag. The remainder of the diff updates UI test fixture hashes in tests/ui_tests/fixtures.json for the new test case across multiple languages. No production code is modified.
Changed components
tests/device_tests/thp/test_handshake.pytests/ui_tests/fixtures.jsonInspect captured patch +37 / −0
diff --git a/tests/device_tests/thp/test_handshake.py b/tests/device_tests/thp/test_handshake.py
index a5bf6eb5..7f82edf0 100644
--- a/tests/device_tests/thp/test_handshake.py
+++ b/tests/device_tests/thp/test_handshake.py
@@ -1,8 +1,11 @@
import pytest
+from cryptography import exceptions
from trezorlib.debuglink import TrezorTestContext
from trezorlib.exceptions import DeviceLockedError
+from trezorlib.thp.channel import Channel
from trezorlib.thp.client import TrezorClientThp
+from trezorlib.thp.message import Message
from trezorlib.thp.pairing import PairingController
from .connect import prepare_channel_for_handshake
@@ -12,6 +15,34 @@ PIN4 = "1234"
pytestmark = [pytest.mark.protocol("thp"), pytest.mark.setup_client(pin=PIN4)]
+@pytest.mark.setup_client(pin=None)
+def test_read_malformed_response(test_ctx: TrezorTestContext) -> None:
+ channel = Channel.allocate(test_ctx.transport)
+ original_read = channel._read
+
+ def _patched_read(timeout: float | None = None) -> Message:
+ message = original_read(timeout)
+
+ if message.is_ack():
+ # do not modify ACK
+ return message
+
+ assert message.is_handshake_init_response()
+
+ # modify last byte of the noise AEAD tag
+ modified_data = bytearray(message.data)
+ modified_data[-1] = (message.data[-1] + 1) % 256
+ return Message(
+ ctrl_byte=message.ctrl_byte,
+ cid=message.cid,
+ data=bytes(modified_data),
+ )
+
+ channel._read = _patched_read
+ with pytest.raises(exceptions.InvalidTag):
+ channel.open([], force_unlock=True)
+
+
def test_no_unlock(test_ctx: TrezorTestContext):
prepare_channel_for_handshake(test_ctx)
with pytest.raises(DeviceLockedError):
diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json
index 6943f5bc..2cbb33c8 100644
--- a/tests/ui_tests/fixtures.json
+++ b/tests/ui_tests/fixtures.json
@@ -33357,6 +33357,7 @@
"T3W1_cs_thp-test_basic.py::test_v1": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_cs_thp-test_basic.py::test_v2_unallocated": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_cs_thp-test_handshake.py::test_no_unlock": "e7a289ba97c6294ef2a78b4cae23df7891685dece344807b65b8f341d38e06cd",
+"T3W1_cs_thp-test_handshake.py::test_read_malformed_response": "cda715bca8af82c3d4193edcf9f89c0eb8080f8e89da3015e048381df7dbba51",
"T3W1_cs_thp-test_handshake.py::test_unlock_cancel": "106c9408773a4bacfabb89bb37c3d5b65e070435a9b5c910cf22c09a671f5b1e",
"T3W1_cs_thp-test_handshake.py::test_unlock_pin": "0d9e1cde2d989ed9401c5ec0322f16f50d290ed1d63181c11e320fa01a05b681",
"T3W1_cs_thp-test_handshake.py::test_unlock_pin_wrong": "05cf067ed5a23addaad9df603fba31d6c8c9157d21d8ceb3a93fa500c4305aa6",
@@ -34990,6 +34991,7 @@
"T3W1_de_thp-test_basic.py::test_v1": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_de_thp-test_basic.py::test_v2_unallocated": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_de_thp-test_handshake.py::test_no_unlock": "5c8e7ec5150b5185340f14eb0306768c2cf77aae8b1ba15e508a45ab4a23f67b",
+"T3W1_de_thp-test_handshake.py::test_read_malformed_response": "7b20040a41c27acc266bfa7d7fbc65a635c56e447b611313bad150375c8f5a71",
"T3W1_de_thp-test_handshake.py::test_unlock_cancel": "8d302db8e3d671a2eecf2d5bcc7b20bba87db1cc3d2e18c10829178a032f8805",
"T3W1_de_thp-test_handshake.py::test_unlock_pin": "611ac913c0e4d75975c13bfb869fc41f6b1657b2d038d89a309db203c792bf0d",
"T3W1_de_thp-test_handshake.py::test_unlock_pin_wrong": "7317c17fd61d91d6115b3ef77c84e57ad2c34d37b56a7bfb9108fcfdd057fa8a",
@@ -36623,6 +36625,7 @@
"T3W1_en_thp-test_basic.py::test_v1": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_en_thp-test_basic.py::test_v2_unallocated": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_en_thp-test_handshake.py::test_no_unlock": "958f3e465c392be0461aeb44b1b553c533353d7b46affc2a0cfcac2151487fdb",
+"T3W1_en_thp-test_handshake.py::test_read_malformed_response": "aaee82b0a5a12540268d2ec9058b44ab6545a32b48f92a454dc88c271a289bd9",
"T3W1_en_thp-test_handshake.py::test_unlock_cancel": "73c33bcb097fa6a45fe03e6cba0556905f5a5014be50ce246e2704f81b483010",
"T3W1_en_thp-test_handshake.py::test_unlock_pin": "7db28a1fe6a60070dca8e45d36d61c0875e54c6a8bbe670c88901b0374bb7c44",
"T3W1_en_thp-test_handshake.py::test_unlock_pin_wrong": "05f3284b504fd25c6383529fd3ff06f430aacaa52331431fc5a7d859034281be",
@@ -38256,6 +38259,7 @@
"T3W1_es_thp-test_basic.py::test_v1": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_es_thp-test_basic.py::test_v2_unallocated": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_es_thp-test_handshake.py::test_no_unlock": "1874b1bfb73b7597a9f64cfdaa3f3da8010288595040855b7f835064a9fd5a5b",
+"T3W1_es_thp-test_handshake.py::test_read_malformed_response": "fd67c9612e908e486146fce97119c1511c04cac43d0504f1628176f0023269bc",
"T3W1_es_thp-test_handshake.py::test_unlock_cancel": "8217faa1772d6cecb5e8d5c817631f3d3397930c8dfbd36a3d0580a0e0943903",
"T3W1_es_thp-test_handshake.py::test_unlock_pin": "4ffbcf026f1db11a59ccdf1f65a54bccd48f8efed46a39ec80f59e150aa96748",
"T3W1_es_thp-test_handshake.py::test_unlock_pin_wrong": "72b50012a20885eb3a498883e62118632e23d73e820eb1fcd31441762fccaa4f",
@@ -39889,6 +39893,7 @@
"T3W1_fr_thp-test_basic.py::test_v1": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_fr_thp-test_basic.py::test_v2_unallocated": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_fr_thp-test_handshake.py::test_no_unlock": "971069eb5ff7920b099ec518b52e95c5d6820c04dec680a606158b664f7eeb8b",
+"T3W1_fr_thp-test_handshake.py::test_read_malformed_response": "04d89411e9120ad288a77fc42dd55359f8fa05f47f2933c26c7fa2bff9c5eaae",
"T3W1_fr_thp-test_handshake.py::test_unlock_cancel": "a22c181109b3db59706924b8a903955fcd36d6175bcc1cfc9bf5c69cac6dfdbe",
"T3W1_fr_thp-test_handshake.py::test_unlock_pin": "a6b2b20aa85652033f844be497782353087224430a8191267704a4335c0df3f4",
"T3W1_fr_thp-test_handshake.py::test_unlock_pin_wrong": "8b023b46542ded6b9aac0f925ba1e6b35f6e0d6eb51891f728c9e69ea25b8e6c",
@@ -41527,6 +41532,7 @@
"T3W1_pt_thp-test_basic.py::test_v1": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_pt_thp-test_basic.py::test_v2_unallocated": "9ed3462f56542da1b63575819a3294f03c135bd64815e1767eadb2859192ce06",
"T3W1_pt_thp-test_handshake.py::test_no_unlock": "1874b1bfb73b7597a9f64cfdaa3f3da8010288595040855b7f835064a9fd5a5b",
+"T3W1_pt_thp-test_handshake.py::test_read_malformed_response": "f9d1a5b26bc583452c9e4ddc52b95a774e6bf17dcca6b7c47f1e52de62d766ef",
"T3W1_pt_thp-test_handshake.py::test_unlock_cancel": "75d80e421691e5be5bedd7eb61393fa9b082007f411fcc8a17d112ce62281745",
"T3W1_pt_thp-test_handshake.py::test_unlock_pin": "b93bf3adec417bfda498ac7b82806b30bb4096e97f7623dc25a21645c8d7cdd1",
"T3W1_pt_thp-test_handshake.py::test_unlock_pin_wrong": "9448066b25e870010db8544bfac2a04d6ad79fce3cc92903f8c2461b4ab1dcc0",
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.