test(7.15): ERC-7730 block 7 remediation and Phase 0 capability mirror - #227
Open
BitHighlander wants to merge 12 commits into
Open
BitHighlander wants to merge 12 commits into
BitHighlander wants to merge 12 commits into
Conversation
…oder Array-of-structs, Permit2 batch and multidimensional device walks now compare the device's domain/message hashes with a spec-derived reference encoder and recover the signature to the device address. The multidim walk is Stack 07 work and is gated by the structured-EIP-712 probe, not by the later evm-unknown-token-review capability.
…e wire set_expected_responses() raised a CallException whose text embedded the expected message, so the old assertIn passed whatever the device sent. Send EthereumSignTx with call_raw and require the first response to be Failure_ActionCancelled with the exact message (no ButtonRequest first).
…nputs The firmware-validation step ran only behind 'if validator:' so compile tests passed without ever reaching firmware. Missing validator or registry is now an explicit skip, and a failure when CI sets KK_REQUIRE_ERC7730_EVIDENCE=1.
…ong bytes Every signing walk now requires exactly one ButtonRequest_SignTx "Sign Typed Data" screen at the end. New device tests: declining that screen returns ActionCancelled and no signature; primaryType EIP712Domain signs keccak(0x1901||domainSeparator) (checked by the independent encoder and signer recovery); unlimited EIP-2612 and Permit2 amounts are refused before their leaf is displayed; a 1024-byte bytes leaf signs and verifies independently.
The registry conformance test had never run against a firmware validator. Run through the firmware's own catalog verifier, 56 of the 1,450 official calldata formats compiled to programs the device refuses: - 46 tokenAmount fields with no token compiled to formatter kind 3, which the verifier refuses without a token argument. With no token the device can only show the raw integer, so compile the raw formatter. - 8 paths iterated two nested arrays; the verifier accepts one "[]" step per path. Refuse them by name. - 2 ABIs nested deeper than the device's 8 levels. Refuse them by name. The registry test now requires every format to pass the firmware validator or be refused for one of those named device limits, and pins that count at 10.
The device response buffer is shared with DebugLink, and a state read during the final sign screen erased an address written before it. The signature still recovered to the right key, so only the reported address was wrong. Under screenshot capture the unfixed firmware returns an empty address and these assertions fail.
Firmware calldata and typed-data captures refuse ERC7730_ABI_MAX_DEPTH (8) or more path steps, and the catalog verifier now does too. Refuse such paths by name at compile time. No official registry format is affected.
Firmware now refuses at preload every ERC-7730 program its runtime cannot finish, using one capability table shared by the verifier and the runtime. Mirror that table here as DEVICE_CAPABILITIES: - compile_calldata/compile_eip712 raise DeviceCannotExecute with the exact reason unless executable_only=False; - device_refusal() reports why a compiled program would be refused; - the registry test asserts that the device verifier and this mirror agree on every compiled format in both directions, and that exactly 92 of the 1,450 official formats are signable (not 94: two raw fields read the @.from container, which the runtime never captured). Also pin device-protocol at the canonical release protocol plus the Solana LUT merge and DebugLinkState.confirm_title/confirm_body, and add DebugLink.read_confirm_text() so tests can assert exact screen text.
BitHighlander
added a commit
to BitHighlander/keepkey-firmware
that referenced
this pull request
Sep 25, 2026
…ot run Phase 0 of docs/security/HANDOFF-ERC7730-715-FORMATTERS.md (issue #861). The preload verifier accepted formatter kinds, display opcodes, path shapes and conditions that the signing runtime cannot execute, so a signed program could load and then fail after the user had approved the signer, the intent and earlier fields. Copilot found three narrower cases of this drift. - erc7730_capabilities.{h,c}: one table of what the runtime executes (intent at pc 0, raw fields without conditions, end; formatter kind 1 with one role-1 value; path source 1 with index steps; no conditions). The verifier and the runtime call the same predicates. - The verifier walks every value path against the ABI at preload: tuple index in range, fixed-array index in [-length, length), dynamic-array index in [-64, 64), and a leaf target. The walk table lives in the delegate-record buffer, unused until the bindings, so SRAM is unchanged (full reserve 18,336 B). - DebugLinkState.confirm_title/confirm_body (DEBUG_LINK only) record the last confirmation, so wire tests assert exact text instead of counts. - Pins device-protocol 5fec9e6 (keepkey/device-protocol#123 into up/release-protocol) and python-keepkey 67f561c (keepkey/python-keepkey#227 into reconcile/upstream-sync): both canonical heads plus these changes, and dp now also carries dd9c85d, which canonical pyk already pinned. Registry: 92 of 1,450 formats are signable (the plan said 94; two raw fields read the @.from container, which the runtime never captured). The device and the compiler's mirror agree on all 1,440 compiled formats.
…actly The capability mirror treated every permitted role as required, walked non-value paths against the ABI and read an index after every step opcode. None of that could fire under the Phase 0 table, but each would misfire as soon as the table widens. Separate required roles, list executable containers, and parse whole-array and slice steps as the firmware does.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Into the canonical 7.15 test branch (PR #197).
9cea0ad) onto canonical98c717f.erc7730_compiler.DEVICE_CAPABILITIESmirrors that table.compile_calldata/compile_eip712raiseDeviceCannotExecutewith the exact reason unlessexecutable_only=False.@.fromcontainer, which the runtime never captured.feat/debuglink-confirm-text(5fec9e6, PR intoup/release-protocol), regeneratesmessages_pb2.pywith protoc 3.5.1, and addsDebugLink.read_confirm_text().Local evidence, run against the firmware validator of the matching firmware head:
test_erc7730_compiler.py,test_erc7730_catalog.pyandtest_erc7730_protocol_bindings.pyall pass (25 tests).