diff --git a/tests/test_msg_ethereum_signtx.py b/tests/test_msg_ethereum_signtx.py index 15a61d3f..f7534375 100644 --- a/tests/test_msg_ethereum_signtx.py +++ b/tests/test_msg_ethereum_signtx.py @@ -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")