Hi everyone! I’ve submitted a retroactive grant proposal for the Open-Source Zcash Hardware-Wallet SDK and wanted to share the details with the community. This proposal builds on my initial exploration and focuses on bringing Zcash Orchard shielded transactions to resource-constrained embedded systems.
The project started as a personal challenge—bringing an Orchard wallet to the Flipper Zero—and has grown into a vendor-neutral SDK focused on making shielded Zcash accessible on low-cost, off-the-shelf hardware.
@hahn You were right to push on this. Quick map of where each ZIP-244 sighash component sits today:
header_digest → on-device, recomputed from TxMeta (zip244_header_digest)
orchard_digest → on-device, incremental hashing of streamed actions (three parallel BLAKE2b ctx for compact / memos / noncompact, finalized at sentinel)
transparent_sig_digest → on-device when transparent inputs/outputs are streamed, recomputed and constant-time matched against the companion-supplied value via orchard_signer_verify_transparent. For tx with empty transparent bundle, verified against the empty-bundle constant.
sapling_digest → enforced equal to BLAKE2b-256(“ZTxIdSaplingHash”, ) at TxMeta receipt (commit 0107c79, landed today). Non-empty values abort the session with SIGNER_ERR_SAPLING_NOT_EMPTY before any action is hashed.
Final root sighash is recomputed and constant-time compared against the companion’s value; mismatch aborts before any RedPallas authorization.
So to your question directly: every component of the ZIP-244 sighash is now either recomputed on-device from streamed inputs or constrained to its ZIP-244 empty-bundle constant. None is taken on faith from the companion.
The Sapling enforcement is structurally consistent with the wallet being Orchard-only by design (no Sapling keys derived, no Sapling notes ever in the wallet, no Sapling-only recipients in scope). Defense-in-depth on the host side too — zcash-hw-wallet-sdk rejects Sapling-bearing PCZTs before transmission so users get a clean error instead of bouncing off the device.
Commits:
libzcash-orchard-c 0107c79
zcash-hw-wallet-sdk f10a80a
zcash-hw-wallet-esp32 d21206c
The GitHub Lockbox submission has been updated to reflect the new HEADs (FPF confirmed edits are allowed during the submission window). Thanks for the follow-up — closing this gap before the coinholder vote was the right call.
You are still hashing the orchard action encrypted by the companion app. That app cannot be trusted. Consider what would happen if it was hacked to show one destination address but uses the attacker’s address for the derivation of the note commitment…
Right, my earlier reply addressed only the sighash components and missed the point you were actually making, which is that the per-action cmx itself was still being trusted from the companion. That was the real gap.
It has since been closed. The companion now transmits the output-note plaintext — recipient = d ‖ pk_d, value, and rseed — alongside each action, and the device recomputes the note commitment on-chip via Sinsemilla NoteCommit (with rho = action.nullifier, per Orchard’s split-action construction) and constant-time compares it against the cmx field at offset 96 of the action bytes. On mismatch the session is reset before any RedPallas authorization is issued (SIGNER_ERR_NOTE_COMMITMENT_MISMATCH).
In the scenario you described — companion displays one recipient but embeds an attacker cmx in the action — the device recomputes NoteCommit from the displayed recipient’s (d, pk_d, value, rseed), finds it does not match the embedded cmx, and aborts. The recipient the user confirms on-screen is therefore the same (d, pk_d) that just had to satisfy the commitment check, not a value the companion can desynchronize from what it pretends to send to. The no-blind-signing invariant on top requires explicit per-action confirmation before orchard_signer_verify() will transition to VERIFIED, so a firmware build that skips the UI step cannot extract a signature either.
Commits: libzcash-orchard-c 1955ad1 (on-device cmx recomputation) and ba9fce6 (per-action confirmation); zcash-hw-wallet-sdk 7ec754a (companion sends note plaintext per action). Genuinely appreciate the review — this was the right thing to catch before the vote.
It checks the destination is valid but does it check the rest of the tx? I think a malicious attacker can still overspend and/or corrupt the memos… He won’t gain directly but the damage can be serious still.