Skip to content

Token table chain_id is uint8_t, so Base/Arbitrum/Avalanche cannot be represented at all #455

Description

@BitHighlander

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    evmEthereum / EVM signingsecuritySecurity-relevant defect or hardening

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions