Triaged during the 7.14.2 residual pass and deferred out of 7.14.2 as low. Still present at release/7.14.2 head.
Evidence
include/keepkey/firmware/ethereum_tokens.h:42 (uint8_t chain_id;) and :54 (tokenByChainAddress(uint8_t chain_id, ...)), narrowed from the uint32_t at lib/firmware/ethereum.c:71 at call sites :116, :733 and :738
Assessment
Still present - neither ethereum_tokens.h nor ethereum_tokens.c received any commit on this branch. TokenType::chain_id is uint8_t (ethereum_tokens.h:42), tokenByChainAddress takes uint8_t (:54), and the transaction's chain id is uint32_t (ethereum.c:71) assigned straight from the host's protobuf with no bound (ethereum.c:628). There are now three narrowing call sites, one more than the finding recorded: ethereum_getStandardERC20Coin (ethereum.c:116) plus the transfer and approve lookups (ethereum.c:733, :738). The reachable path is concrete: chain_id = 257 with to set to a mainnet token address matches that mainnet entry (257 & 0xFF == 1) at ethereum_tokens.c:48, so layoutEthereumConfirmTx renders the mainnet ticker and decimals for a transaction that is EIP-155-bound to chain 257.
I re-rated from the code and low holds, for two reasons I verified rather than assumed. First, the dangerous half of the original impact is gone: the UnknownToken sentinel no longer skips the blind-sign gate - ethereum.c:780 now reads if ((token == NULL || token == UnknownToken) && data_total > 0 && data_needs_confirm) and routes into the AdvancedMode block plus raw-data disclosure (GH #433), so a token on a chain >= 256 that fails to resolve fails CLOSED to raw-data review rather than to silent narration. Second, the false-match half requires the same contract address to carry different meaning on two chains congruent mod 256, and I enumerated the table: every chain in deps/python-keepkey/keepkeylib/eth/ethereum_networks.json is < 256 (max 137) and the uniswap generator hardcodes chain_id = 1, so no table entry is itself truncated at build time and no real chain of interest collides with a populated net (Base 8453 -> 5/Goerli, Linea 59144 -> 8/UBIQ, neither populated). For the record it should also be stated that the confirm screen never names the network at all (layoutEthereumConfirmTx, ethereum.c:407-462, renders only amount and address), so chain misrepresentation on the ERC-20 screen is pre-existing and not created by this narrowing. File it: the fix (uint32_t chain_id in ethereum_tokens.{c,h}, already written off-line as d54e8fc) is correct but changes the generated table format, which is not a change to make late in a tag for a low finding.
Deferred because 7.14.2 is a minimal security release; this did not clear the bar for a late change. Filed so it is not lost.
Triaged during the 7.14.2 residual pass and deferred out of 7.14.2 as
low. Still present atrelease/7.14.2head.Evidence
include/keepkey/firmware/ethereum_tokens.h:42 (
uint8_t chain_id;) and :54 (tokenByChainAddress(uint8_t chain_id, ...)), narrowed from the uint32_t at lib/firmware/ethereum.c:71 at call sites :116, :733 and :738Assessment
Still present - neither ethereum_tokens.h nor ethereum_tokens.c received any commit on this branch. TokenType::chain_id is uint8_t (ethereum_tokens.h:42), tokenByChainAddress takes uint8_t (:54), and the transaction's chain id is uint32_t (ethereum.c:71) assigned straight from the host's protobuf with no bound (ethereum.c:628). There are now three narrowing call sites, one more than the finding recorded: ethereum_getStandardERC20Coin (ethereum.c:116) plus the transfer and approve lookups (ethereum.c:733, :738). The reachable path is concrete: chain_id = 257 with
toset to a mainnet token address matches that mainnet entry (257 & 0xFF == 1) at ethereum_tokens.c:48, so layoutEthereumConfirmTx renders the mainnet ticker and decimals for a transaction that is EIP-155-bound to chain 257.I re-rated from the code and low holds, for two reasons I verified rather than assumed. First, the dangerous half of the original impact is gone: the UnknownToken sentinel no longer skips the blind-sign gate - ethereum.c:780 now reads
if ((token == NULL || token == UnknownToken) && data_total > 0 && data_needs_confirm)and routes into the AdvancedMode block plus raw-data disclosure (GH #433), so a token on a chain >= 256 that fails to resolve fails CLOSED to raw-data review rather than to silent narration. Second, the false-match half requires the same contract address to carry different meaning on two chains congruent mod 256, and I enumerated the table: every chain in deps/python-keepkey/keepkeylib/eth/ethereum_networks.json is < 256 (max 137) and the uniswap generator hardcodes chain_id = 1, so no table entry is itself truncated at build time and no real chain of interest collides with a populated net (Base 8453 -> 5/Goerli, Linea 59144 -> 8/UBIQ, neither populated). For the record it should also be stated that the confirm screen never names the network at all (layoutEthereumConfirmTx, ethereum.c:407-462, renders only amount and address), so chain misrepresentation on the ERC-20 screen is pre-existing and not created by this narrowing. File it: the fix (uint32_t chain_id in ethereum_tokens.{c,h}, already written off-line as d54e8fc) is correct but changes the generated table format, which is not a change to make late in a tag for a low finding.Deferred because 7.14.2 is a minimal security release; this did not clear the bar for a late change. Filed so it is not lost.