Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions tests/test_msg_ethereum_signtx.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,60 @@ def test_ethereum_eip_1559(self):
"67297089e0ba53c29dda1aafc23fce64a772c5433e127e5885edc03ece4670c9",
)

def test_ethereum_eip_1559_multibyte_chain_id(self):
"""EIP-1559 must hash the WHOLE chain_id, not just its low byte.

Regression for the multi-byte chain_id bug: the EIP-1559 hash step used
hash_rlp_field((uint8_t*)&chain_id, 1), which on little-endian ARM fed
only the least-significant byte into keccak. For Base (8453 = 0x2105)
that hashed 0x05, so the signature recovered to an unrelated address
with no funds. The RLP *length* was computed correctly from the full
value, and the legacy EIP-155 path was always correct — only the
EIP-1559 hash was wrong. Affected: Base (8453), Arbitrum (42161),
Avalanche (43114). Unaffected: ETH (1), OP (10), BSC (56), Polygon (137).

Asserting a golden r/s would need a device run to produce it. Instead
sign one identical transaction under two chain ids chosen so the BUGGY
firmware cannot tell them apart:

8453 = 0x2105 low byte 0x05, two-byte value
4357 = 0x1105 low byte 0x05, two-byte value

Same low byte AND same RLP length header, so the broken code hashes a
byte-identical pre-image for both. Signing is deterministic (RFC 6979),
so buggy firmware returns the SAME signature twice and this fails.
Correct firmware hashes 0x21 0x05 vs 0x11 0x05, which must differ.

Vault cannot reach this path: it forces legacy txs for chainId >= 256
(eip1559Ok = chainId < 256). The BEX builds type-2 with no such guard,
which is why the failures only ever showed up there.
"""
self.requires_fullFeature()
self.requires_firmware("7.15.0")
self.setup_mnemonic_nopin_nopassphrase()

def sign(chain_id):
return self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 60, 0x80000000, 0, 0],
nonce=0,
gas_limit=0x5ac3,
max_fee_per_gas=0x16854be509,
max_priority_fee_per_gas=0x540ae480,
to=binascii.unhexlify("fc0cc6e85dff3d75e3985e0cb83b090cfd498dd1"),
value=0x1550f7dca70000,
chain_id=chain_id,
)

_, base_r, base_s = sign(8453)
_, twin_r, twin_s = sign(4357)

self.assertNotEqual(
(binascii.hexlify(base_r), binascii.hexlify(base_s)),
(binascii.hexlify(twin_r), binascii.hexlify(twin_s)),
"chain_id 8453 and 4357 produced the same signature — only the low "
"byte of chain_id reached the EIP-1559 hash",
)

def test_ethereum_signtx_nodata_eip_1559(self):
self.requires_fullFeature()
self.requires_firmware("7.2.1")
Expand Down