7.15 audit 4/6: Zcash Orchard - #362
Conversation
Adds the signed-metadata engine: a host may supply a description of what an EVM transaction does (contract alias, method, argument labels), and the device displays it only if the blob carries a signature from a trust anchor it already holds. Unsigned or wrongly signed metadata is ignored — it can never turn a blind-signing prompt into a friendly one. - Anchor verification over secp256k1 with the schema bound to the request, so a signature captured from one transaction cannot dress up another. - Runtime signer table only; nothing is persisted to public flash. A rogue entry surviving a reboot would suppress the raw-data screen, which is exactly the failure this design refuses. - tiny-json gains the bounds needed to parse metadata safely on device. - unittests/firmware/signed_metadata.cpp covers the accept/reject matrix, including malformed blobs and signature/schema mismatches. Message plumbing and the Ethereum call sites arrive in the clear-sign EVM branch; this commit is the engine and its tests only.
…hemas Solana signing moves from "confirm this blob" to describing the actual instructions, using schemas that are signed once and reused: - v0 (versioned) transactions and address-table lookups are parsed, and the instruction set is rendered per program: transfers, token transfers, memo bodies and offchain messages. - A schema describes a program's instruction layout (discriminator, argument types and labels) and carries a signature with no transaction hash bound to it, so one blob covers every call of that shape instead of needing a fresh signature per transaction. - x402 payment requests are clear-signed through the same path: the payment amount, asset and recipient are shown from schema-described fields rather than trusting host-supplied text. - Anything without a verified schema still reaches the raw-data screen. - unittests/firmware/solana.cpp covers v0 parsing, schema decode, argument rendering and the reject paths for malformed or unsigned schemas.
Uses the signed-metadata engine on the Ethereum path so a user sees what a contract call actually does instead of a hex blob: - EthereumTxMetadata carries the signed description and EthereumMetadataAck acknowledges it; LoadClearsignSigner installs a trust anchor for the current session only. Metadata that fails verification is dropped and the transaction falls back to raw display — it never degrades to a friendlier screen. - EIP-712 typed data: domain/primary-type handling is corrected, the policy gate decides when a typed hash may be signed at all, and the recursion and length limits are enforced rather than assumed. - EIP-7702 authorization lists are recognised and shown. - ERC-20 and the bundled contract decoders (THORChain router, liquidity and swap proxies) check the selector against the argument shape before claiming to know what a call means. - A factory reset clears loaded signers. - unittests/firmware/eip712.cpp and the extended ethereum.cpp cover the typed data limits, selector mismatches and the metadata accept/reject paths.
Adds messages 1700-1703 so a KeepKey can issue the schema signatures the clear-sign path verifies, instead of every schema having to come from an offline signing ceremony. - ClearsignAttestorGetPublicKey exports the attestor key; ClearsignAttestorSign signs a schema after showing the user what they are attesting to — program identity, discriminator, and each argument label with its type and width. - Self-service attestation is gated behind advanced mode, so a default device cannot be talked into minting trust for an attacker's schema. - The signed variant ships the built-in anchor table, so schemas issued this way verify on a stock device with nothing loaded. - scripts/emulator/capture-clearsign-attestor.py reproduces the OLED sequence, and docs/security/evidence/7.15.0-rc21-clearsign-attestor holds the captured frames including the 44-character program id and 16-character label bounds.
Storage: - The PIN key-derivation function is versioned so its cost can be raised without breaking existing wallets, with the migration path documented in docs/security/pin-kdf-v19-migration.md. - A wallet created by bitcoin-only firmware is stamped, and multi-chain firmware refuses to touch it (storage_isBitcoinOnlyLocked) rather than silently operating on a wallet whose owner chose a reduced attack surface. - Storage layout reserves the clear-sign identity block and zeroes it on read and on wipe, so nothing there can outlive a factory reset. - Orchard key derivation and seed fingerprinting are reachable through storage with progress reporting, guarded by the privacy build flag. - docs/security/anti-rollback-security-epoch-rfc.md records the proposed security-epoch scheme; it is a design note, nothing here implements it. BIP-85: - GetBip85Mnemonic derives a child mnemonic and displays it under constant power, with PIN and initialization checks. Always available — it is a seed derivation, not a coin engine. Recovery and authenticator: - The cipher-recovery wordlist permutation now borrows the shared frame arena instead of another multi-kilobyte stack buffer. - wipeAuthData reports failure instead of always claiming success, a cancelled authenticator action is distinguishable from an error, and a cancelled passphrase prompt aborts the request instead of continuing without one.
…isplay RNG: - The RNG source selection is explicit and reportable, and a host can pull bulk samples for a health audit: a per-boot 64 KB budget replaces the press-per-kilobyte prompt that made auditing real hardware impossible. Scope is stated honestly in the code and docs — bulk output supports HEALTH testing (stuck/biased output, repeated buffers, transport caching, gross correlation), not a bound on the entropy of the generator's internal state. - On an uninitialized device, one press unlocks the bulk audit; on an initialized device the confirm still gates entropy after the budget is spent. - The emulator gets a real CSPRNG (lib/emulator/random.c, BCryptGenRandom on Windows) instead of libc random(), which CI now enforces. Dice: - ResetDevice can request on-device dice entropy: short press advances 1-6, long press confirms, undo is available, and the roll digest is confirmed before the seed is mixed. Pre-initialization only. - The abort path disarms EntropyAck, so an aborted reset can no longer leave the device accepting host-supplied entropy for the seed. - Evidence frames under docs/security/evidence/dice-entropy-reset, reproducible with scripts/emulator/capture-dice-flow.py. Internal entropy is no longer displayed or returned: it is seed pre-image material. display_random stays in the wire schema for host compatibility and is ignored.
The device-side Orchard engine behind Zcash shielded sends: Orchard key derivation from the wallet seed, note/action handling, RedPallas signing over the Pallas curve, and unified-address encoding (ZIP-316). - Signing is constant-time on the secret scalar path; the Pallas arithmetic comes from the pinned trezor-firmware fork and is compiled in only when the privacy engine is enabled, so the bitcoin-only image carries none of it. - Progress is reported to the host during action signing — multi-action transactions take long enough that a silent device looks hung. - messages-zcash proto/options are registered with the transport generator. Handlers, host-visible messages, unit tests, the constant-time CI gates and the coin-integration docs land in the Zcash wiring branch; this commit adds the engine and its build wiring only.
Wires the Orchard engine to the host and puts it under test: - ZcashSignPCZT / ZcashPCZTAction / ZcashGetOrchardFVK / transparent input and output / ZcashDisplayAddress handlers, registered privacy-guarded so the bitcoin-only image exposes none of them. - unittests/firmware/zcash.cpp covers PCZT parsing, action signing and unified address encoding; a second binary links the engine against the host crypto so the Orchard paths run under the normal unit-test job. - unittests/crypto/pallas_ct.cpp plus tools/check_pallas_api_boundary.py and tools/check_pallas_ct_disassembly.py enforce the constant-time contract at three levels: operation counters in the test build, an API boundary check on the sources, and a disassembly scan of the shipped ELF. CI runs the pinned fork's Pallas suite under Valgrind and ASan/UBSan as well. - docs/coin-integration documents the PCZT flow, on-device unified addresses and the clear-sign handoff.
|
CI queue note: this head was explicitly dispatched at https://github.com/BitHighlander/keepkey-firmware/actions/runs/31128231385 after the superseded 17-PR stack saturated the Actions queue. GitHub currently shows the jobs queued before runner assignment. The complete aggregate independently passed the CI-equivalent container suite locally: 405 firmware, 2 board, 18 crypto, and 7 Pallas constant-time tests, plus actionlint, gitleaks, the Pallas API-boundary gate, and git diff --check. |
e3457e0 to
39555bd
Compare
|
Superseded by #364. Slices 04 (Zcash), 05 (chains) and 06 (Bitcoin/release security) are mutually dependent and do not compile apart: audit-04 failed every target on |
What changed
Why
The engine and its tests were previously separated across two PRs. This bundle makes Zcash self-contained: reviewers see the cryptographic implementation, firmware wiring, policy, and evidence in one diff.
Validation
git diff --checkStack: 4 of 6. Review only the diff from
release/715-audit-03-recovery-entropy.