fix(bitbox): sign the registration with a chainId-extended EIP-712 domain - #877
Merged
TaprootFreak merged 3 commits intoJul 31, 2026
Conversation
…main The BitBox02 firmware refuses to sign typed data whose EIP712Domain has no chainId and aborts with 'typed data has no chain ID' on the device, so BitBox users could not complete the registration at all. Hardware wallets now sign with the chainId-extended domain; software wallets keep the legacy domain (pinned by the golden signature test) until Aktionariat confirms its re-verification accepts the extended variant. Pair PR: DFXswiss/api#4354 (accepts both domain variants, must deploy first).
14 tasks
TaprootFreak
requested changes
Jul 24, 2026
TaprootFreak
left a comment
Contributor
There was a problem hiding this comment.
wir brauchen mehr daten um sicher zu sein ob das der echte fix ist
…-PR reference The new test asserted chainId against the same literal the helper passed and checked the EIP712Domain members with containsAll, so three wrong implementations passed it: hardcoding chainId to 1 (breaks every testnet registration), changing the chainId type to uint64, and reordering the domain members. All three alter the domain typehash or value, so the API recovers a foreign address and rejects. Sign with a non-default chainId, assert the value reaching signETHTypedMessage as well as the JSON, and assert the member list exactly. Verified: each of the three mutations now fails. The comment pointed at DFXswiss/api#4354, which was closed unmerged; the shipped counterpart is #4542.
Replacing .captured.single with indexed access dropped the implicit "signed exactly once" assertion, so a second signETHTypedMessage call went unnoticed — on a hardware wallet that is a second on-device confirmation prompt. Restore it via ..called(1), matching the signCallCount checks in the integration tests. The comment pointed at eip712_delegation_bitbox_test.dart, which does not exist; the file is test/integration/eip7702_delegation_bitbox_test.dart.
Collaborator
|
Completed 3 review passes (conformity + logic) before marking this ready. Validated end-to-end on production — this is the real-firmware data the earlier review asked for. Same device and firmware that produced the NACK (BitBox02 Nova, main fw v9.26.4) completed a registration against Fixed during review:
Note the API side is already deployed, so the original merge-order prerequisite no longer applies. |
Danswar
marked this pull request as ready for review
July 31, 2026 17:47
TaprootFreak
approved these changes
Jul 31, 2026
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.
Problem
BitBox users cannot complete the RealUnit registration: on submit, the BitBox02 (Nova) rejects the signing request and shows "typed data has no chain ID" on the device (first affected customer: userData 412822, 23.07.). The firmware requires a
chainIdmember in the EIP712Domain; our registration payload signs the chainId-less domain{ name: 'RealUnitUser', version: '1' }. Software wallets sign it regardless, which is why this never surfaced.Change
Eip712Signer.signRegistrationincludeschainId(value:apiConfig.asset.chainId, already plumbed through) in the EIP712Domain for BitboxCredentials only.Pair PR
DFXswiss/api#4542 — merged and live on production since 2026-07-31 16:47Z. Verification there accepts both domain variants, trying the legacy one first, so software wallets are untouched. The API side is already deployed, so this PR no longer has a merge-order prerequisite.
(The original pair PR #4354 was closed unmerged and superseded by #4542.)
Validated end-to-end on production
Run on 2026-07-31 with a real BitBox02 Nova (
bb02p-multi, main firmware v9.26.4 — the build measured to refuse the chainId-less envelope) on an iPhone 17e, againstapi.dfx.swiss:[RealUnitService] RealUnit registration signature matched chainId 1 domain / …at 16:54:28ZPOST /v1/realunit/register/complete → 201, noFailed to forward RealUnit registrationaktionariat_registrationrow →status = Completed,active = trueThis is the scenario the earlier review asked for real-firmware data on: the same device and firmware that produced the NACK now completes a registration end to end.
Note on scope of that evidence:
Completedis written after Aktionariat's/registerUserreturns 2xx. It proves their registration endpoint accepted the forwarded payload; it does not by itself prove which step re-verifies the EIP-712 signature on their side, so a later step exercising the signature is still worth watching.Tests
domain.chainId+chainIdmember in EIP712Domain typesflutter analyze: no issues