feat(protocol): 7.15 release additions — hive signing (1614-1617), clearsign identity icons, thorchain denom, ripple memo, zcash - #112
Open
BitHighlander wants to merge 105 commits into
Open
BitHighlander wants to merge 105 commits into
BitHighlander wants to merge 105 commits into
Conversation
- PCZT streaming protocol: ZcashSignPCZT, ZcashPCZTAction, ZcashPCZTActionAck - Orchard FVK: ZcashGetOrchardFVK, ZcashOrchardFVK - Transparent shielding: ZcashTransparentInput, ZcashTransparentSig - Wire IDs 1300-1307 - nanopb options for all fields Multi-phase protocol: session init → action streaming → transparent signing. Supports on-device Orchard digest verification via sub-digest fields.
New messages for displaying a Zcash unified address on the device screen with FVK verification. The host provides the UA string and FVK components; the device independently derives FVK from seed and verifies the match before displaying the address with a QR code. - ZcashDisplayAddress (wire_in 1308): address + ak/nk/rivk for verification - ZcashAddress (wire_out 1309): confirmed address after user approval - nanopb options: address max_size:128, key fields max_size:32
feat(zcash): add ZcashDisplayAddress protocol (IDs 1308/1309)
The device only verifies the Orchard FVK — it cannot verify transparent or Sapling receivers that may also be bundled in a Unified Address. Updated proto comments to explicitly state the guarantee: "This UA contains an Orchard receiver from this account" rather than implying full address ownership. Also clarified that account or address_n is required (no silent fallback to account 0).
docs(zcash): clarify ZcashDisplayAddress verification scope
…TIP-712) Adds proto definitions for TRON message-signing parity: - TronSignMessage / TronMessageSignature (1404/1405) — TIP-191 personal_sign - TronVerifyMessage (1406) — host-asserted signature verification - TronSignTypedHash / TronTypedDataSignature (1407/1408) — TIP-712 hash mode Mirrors the Ethereum personal_sign + EIP-712 hash-mode shape. Firmware implementation will reuse the secp256k1 + keccak256 primitives already present for Ethereum, swapping the message prefix to '\x19TRON Signed Message:\n' for TIP-191 and using '\x19\x01' for TIP-712. Reserves IDs 1404-1408 contiguous to existing TRON range (1400-1403).
Adds TonSignMessage / TonMessageSignature (1504/1505) — basic Ed25519 arbitrary-bytes signing, mirroring SolanaSignMessage's shape. This primitive lacks domain separation by design (raw Ed25519 over message bytes). Firmware should gate it behind the AdvancedMode policy — same fence used for SolanaSignMessage in fsm_msg_solana.h — until a TON Connect ton_proof envelope is added as a separate proto. Reserves IDs 1504-1505 contiguous to existing TON range (1500-1503).
Adds SolanaSignOffchainMessage / SolanaOffchainMessageSignature (756/757) implementing the Solana off-chain message spec: '\xff' || 'solana offchain' || version || format || length || message The '\xff' lead byte is invalid as a Solana transaction prefix, providing the domain separation that plain SolanaSignMessage (754/755) lacks. With this primitive, firmware can drop the AdvancedMode policy gate currently required for SolanaSignMessage (fsm_msg_solana.h:461-472) for ASCII/UTF8 off-chain messages, since the envelope makes transaction-shaped attacks impossible. message_format values per spec: 0 = Restricted ASCII (max 1212 bytes) — display-renderable 1 = UTF-8 limited (max 1212 bytes) — display-renderable with care 2 = UTF-8 extended (max 65515) — blind-sign only Reserves IDs 756-757 contiguous to existing Solana range (750-755). Bumped message max_size to 1212 to match the spec ceiling for formats 0/1.
…sages
ZIP-32 §6.1 seed fingerprint:
SeedFingerprint := BLAKE2b-256("Zcash_HD_Seed_FP", seed)
A 32-byte stable identity of the device's seed. Adds optional bytes
seed_fingerprint fields across the existing zcash messages so hosts
and devices can bind FVKs, addresses, and signing sessions to a
specific seed identity.
Four new fields, all optional, fully backward compatible:
ZcashOrchardFVK.seed_fingerprint (4)
Returned alongside (ak, nk, rivk). Lets a host pin an FVK to
this device's seed.
ZcashAddress.seed_fingerprint (2)
Returned alongside the confirmed UA after on-device verification.
Lets a host record "this address is on this device's seed."
ZcashSignPCZT.expected_seed_fingerprint (31)
Sent by host. If present, device checks against its own
fingerprint and rejects with Failure on mismatch before signing.
Mirrors Keystone3's PCZT zip32_derivation seed_fingerprint check
at the session level (one tx = one seed, no per-action duplication
needed for our flow).
ZcashDisplayAddress.expected_seed_fingerprint (7)
Sent by host. Same rejection semantics as above before displaying.
Matching nanopb max_size:32 entries added to messages-zcash.options.
No existing fields modified. Devices and hosts that don't populate
the new fields continue to work unchanged.
feat(zcash): add seed_fingerprint binding to FVK / address / sign messages
Brings in upstream's 7.14.0 release + features/7.15 commits: d0b8d80 feat: 7.14.0 protocol — BIP-85, EVM metadata, Solana, TRON, TON, Zcash 18bb4a7 Merge pull request #100 from keepkey/release/7.14.0 bbcfcb0 feat: add ZcashDisplayAddress protocol messages (IDs 1308-1309) bf8646b Merge pull request #101 from keepkey/features/7.15 Note: ZcashDisplayAddress was developed in parallel on both sides. Conflict resolution prefers upstream's version (max_size:256 for the unified-address fields, more correct than fork's :128 since UAs can be long when shielded receivers are present). # Conflicts: # messages-zcash.options # messages-zcash.proto # messages.proto # package.json
Brings in TRON/TON/Solana message-signing proto definitions: 0e3dc97 feat(tron): TIP-191 SignMessage, VerifyMessage, TIP-712 SignTypedHash 20e646a feat(ton): Ed25519 SignMessage primitive c0ef415 feat(solana): SignOffchainMessage with domain-separated envelope After this merge, fork master is the single source of truth that firmware branches pin to during fork-only testing. A clean upstream PR can be assembled later by cherry-picking these 3 commits onto a fresh branch off keepkey/master.
Field comments documented the formula as
BLAKE2b-256("Zcash_HD_Seed_FP", seed)
but ZIP-32 §6.1 (and the actual conforming implementations in the
upstream zip32 Rust crate, keystone3-firmware, and our own firmware)
prepend a 1-byte length:
BLAKE2b-256("Zcash_HD_Seed_FP", I2LEBSP_8(len(seed)) || seed)
A host implementer following the proto comments would compute the
wrong fingerprint and have the device reject every signing/display
request with "seed fingerprint mismatch."
Comment-only change. No wire-format impact.
…formula-doc fix(zcash): correct seed_fingerprint formula in proto comments
Remove fields 3-6 (address, ak, nk, rivk) from ZcashDisplayAddress. Field numbers are reserved to prevent reuse. The on-device UA derivation (Sinsemilla + SWU hash-to-curve) shipped — FVK-match attestation against a host-built UA is strictly weaker than device-derived display and is no longer supported. What stays: address_n / account / expected_seed_fingerprint. What ZcashAddress returns: address (now device-derived) + seed_fingerprint.
Add optional string memo field (field 7) to RippleSignTx protobuf message. This enables THORChain swap routing memos and other arbitrary memo data to be included in XRP transactions signed by the device.
feat(ripple): add memo field to RippleSignTx for 7.14.2
Merges upstream 7.14.1 (Tron TIP-191/712, TON SignMessage, Solana SignOffchainMessage) with fork's Zcash UA/seed_fingerprint/display address additions. Conflict resolution: kept upstream's format-2 removal from SolanaSignOffchainMessage docs.
Adds messages-hive.proto with HiveGetPublicKey, HivePublicKey, HiveSignTx, and HiveSignedTx. Assigns message type IDs 1600-1603 in messages.proto. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* release: device-protocol 7.14.1 * feat(tron): add SignMessage (TIP-191), VerifyMessage, SignTypedHash (TIP-712) Adds proto definitions for TRON message-signing parity: - TronSignMessage / TronMessageSignature (1404/1405) — TIP-191 personal_sign - TronVerifyMessage (1406) — host-asserted signature verification - TronSignTypedHash / TronTypedDataSignature (1407/1408) — TIP-712 hash mode Mirrors the Ethereum personal_sign + EIP-712 hash-mode shape. Firmware implementation will reuse the secp256k1 + keccak256 primitives already present for Ethereum, swapping the message prefix to '\x19TRON Signed Message:\n' for TIP-191 and using '\x19\x01' for TIP-712. Reserves IDs 1404-1408 contiguous to existing TRON range (1400-1403). * feat(ton): add SignMessage Ed25519 message-signing primitive Adds TonSignMessage / TonMessageSignature (1504/1505) — basic Ed25519 arbitrary-bytes signing, mirroring SolanaSignMessage's shape. This primitive lacks domain separation by design (raw Ed25519 over message bytes). Firmware should gate it behind the AdvancedMode policy — same fence used for SolanaSignMessage in fsm_msg_solana.h — until a TON Connect ton_proof envelope is added as a separate proto. Reserves IDs 1504-1505 contiguous to existing TON range (1500-1503). * feat(solana): add SignOffchainMessage with domain-separated envelope Adds SolanaSignOffchainMessage / SolanaOffchainMessageSignature (756/757) implementing the Solana off-chain message spec: '\xff' || 'solana offchain' || version || format || length || message The '\xff' lead byte is invalid as a Solana transaction prefix, providing the domain separation that plain SolanaSignMessage (754/755) lacks. With this primitive, firmware can drop the AdvancedMode policy gate currently required for SolanaSignMessage (fsm_msg_solana.h:461-472) for ASCII/UTF8 off-chain messages, since the envelope makes transaction-shaped attacks impossible. message_format values per spec: 0 = Restricted ASCII (max 1212 bytes) — display-renderable 1 = UTF-8 limited (max 1212 bytes) — display-renderable with care 2 = UTF-8 extended (max 65515) — blind-sign only Reserves IDs 756-757 contiguous to existing Solana range (750-755). Bumped message max_size to 1212 to match the spec ceiling for formats 0/1. * feat(zcash): drop host-supplied UA from ZcashDisplayAddress Remove fields 3-6 (address, ak, nk, rivk) from ZcashDisplayAddress. Field numbers are reserved to prevent reuse. The on-device UA derivation (Sinsemilla + SWU hash-to-curve) shipped — FVK-match attestation against a host-built UA is strictly weaker than device-derived display and is no longer supported. What stays: address_n / account / expected_seed_fingerprint. What ZcashAddress returns: address (now device-derived) + seed_fingerprint. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * docs: update SolanaSignOffchainMessage to reflect 1212 byte limit and drop format 2 Agent-Logs-Url: https://github.com/keepkey/device-protocol/sessions/880cd954-b4b2-4f87-af05-8d715e1e0dc4 Co-authored-by: pastaghost <62026038+pastaghost@users.noreply.github.com> * feat(hive): add Hive blockchain message definitions Adds messages-hive.proto with HiveGetPublicKey, HivePublicKey, HiveSignTx, and HiveSignedTx. Assigns message type IDs 1600-1603 in messages.proto. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(hive): add messages-hive.proto to build, drop broken build:json step build:json used pbjs v0.0.5 which cannot parse proto3 reserved fields (present in zcash, cosmos, ethereum, etc). proto.json is unused by the vault — only messages_pb.js is imported. Build now runs build:js + build:postprocess only. * feat(hive): add HiveGetPublicKeys, HiveSignAccountCreate, HiveSignAccountUpdate + SLIP-0048 paths --------- Co-authored-by: pastaghost <62026038+pastaghost@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
NearGetAddress, NearAddress, NearSignTx, NearSignedTx. Ed25519 derivation m/44'/397'/0'. Implicit account = lowercase hex of the 32-byte Ed25519 pubkey. Numbered 1610-1613 to avoid colliding with Hive (1600-1609), which landed on alpha after the original NEAR spike.
feat(near): NEAR Protocol proto definitions (MessageType 1610-1613)
…oadcast (#35) account_create is authorized on-chain by the creator (sponsor) account's active authority, not by the new account. The device's owner-key signature here is a proof-of-control attestation; the sponsor recovers the owner pubkey to verify control, then rebuilds and signs the real account_create with the creator's active key server-side. Comment-only — no wire or generated-code change. Addresses review P1 on the (closed) feature/hive PR.
Adds the device-protocol messages required by the upcoming firmware release:
- thorchain: ThorchainMsgSend.denom (field 11) — non-RUNE assets (TCY, RUJI, IBC)
- ripple: RippleSignTx.memo (field 7) — XRP->THORChain swap routing
- hive: full Hive support — HiveGetPublicKey(s), HiveSignTx,
HiveSignAccountCreate/Update (MessageType 1600-1609)
- zcash: clear-signing + Orchard shielded protocol (transparent in/out/ack,
PCZT, FVK, display-address)
All additions are new optional fields / new message types — additive and
backward-compatible. lib/ bindings are gitignored build artifacts; package.json
build:js/json updated to include messages-hive.proto.
…itions Add canonical ERC-7730 compiled definition protocol
Reports that ResetDevice.dice_only, the on-device consent screen and the tagged MIXED derivation are implemented. Needed because nanopb skips unknown fields: a host that sends dice_only to older firmware gets the older ceremony and a different wallet, with no error. Hosts and the test suite gate on this bit rather than on a version. (cherry picked from commit fbaf8ec)
dice_entropy alone is now the MIXED mode: the device commits its own 32-byte
draw as 24 BIP-39 words before the rolls are entered, then derives
seed = SHA256d("KK\x01SM" || draw || SHA256("KK\x01D" || rolls)). With
dice_only the derivation is seed = SHA256(rolls) and the draw is discarded,
matching Coldcard's Dice-Rolls-Only byte for byte.
The mode is a host-side selection so a wallet can explain what is coming --
99 rolls, and for MIXED 24 words to copy down -- before the ceremony starts.
The device still shows a consent screen naming the mode the host chose, so a
host cannot select dice-only silently. In both modes the host's EntropyAck is
consumed and its bytes dropped; the wire flow is otherwise unchanged.
dice_only without dice_entropy is rejected with a SyntaxError.
(cherry picked from commit 451e9a7)
Reconcile alpha dice fields onto protocol master
…calization docs(erc7730): canonicalize catalog signature envelope
docs(erc7730): define canonical format-1 sections
docs(erc7730): complete formatter operand roles
…replay docs(erc7730): require authenticated envelope replay
…y-paths docs(erc7730): define nested array path selectors
…tuple docs(erc7730): define empty root tuples
…en-fallback docs(erc7730): define unknown-token fallback
docs(erc7730): bound ABI depth at twelve
…19.0 release: publish ERC-7730 protocol 7.19.0
This was referenced Sep 20, 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.
Upstream branch → upstream master.
keepkey:up/release-protocolis the single protocol PR for the firmware 7.15 / RC18 release line. It replaces the mis-structured #111 and remains blocked on human review.Scope
This branch carries the whole 7.15 / RC18 protocol surface, which is wider than the title suggests. Every new message ID and feature flag in the diff is listed below so the release contract can be reviewed as a whole.
All changes outside Zcash are additive; existing field numbers and wire behavior remain compatible. The Zcash surface is a breaking change — see "Zcash wire protocol v2" below.
New message surfaces
GetPublicKey(s),SignTx,SignAccountCreate/SignAccountUpdate, and newHiveSignMessage/HiveSignedMessage(1614/1615),HiveSignOperations/HiveSignedOperations(1616/1617)NearGetAddress/NearAddress/NearSignTx/NearSignedTxFailure_UnexpectedMessage. Hosts must not advertise NEAR support on the basis of these bindings.ClearsignAttestorGetPublicKey/PublicKey/Sign/Signature; advanced-mode ClearSign studio schema attestationEthereumSignTypedDataplus device-drivenTypedDataStructRequest/StructAck/ValueRequest/ValueAckstreamingZcashTransparentSigned(renamed, reshaped),ZcashTransparentOutput,ZcashTransparentAckLoadClearsignSigner— clearsign identity icons: bounded RLE icon data,icon_width,icon_height, legacy-compatiblepersistField-level additions
token_recipient_ownerATA candidates (tag 12),SolanaTokenInfo.signature/signer_key_id, andSolanaSignOffchainMessagedomain-separated envelope signing.supports_taprootcapability flag (lets a host detect taproot directly instead of inferring it),dice_entropy(collect dice rolls on-device and mix them into internal entropy before it is displayed or committed),ResetDevice.dice_digest, andButtonRequest_DiceRoll(types.proto).shielded_pool,ironwood_digest, tags 19-20) is schema only: RC18 is Orchard-only and rejects a non-Orchard pool or a presentironwood_digestwithFailure. Pinned bytools/check_zcash_contract.py.Packaging
build:postprocessis nowtools/postprocess-lib.js. The previoussedmatched only the generator's old single-line global bootstrap, so the CSP-hostileFunction('return this')()fallback was shipping in every generated file (CI now reportspatched 18 of 18); the script rewrites the call itself and fails the build if any survives. It also replaces the BSD-onlysed -i ''.preparenow runs the full build, so Git-based installs getlib/proto.jsonand not just the JavaScript. This also made CI exercisebuild:jsonfor the first time, which surfaced a latent break:build:jsoncalled the barepbjsbin, but bothprotobufjsand the unrelatedpbjs@0.0.5declare one. A cleannpm cilinked the latter, whose parser cannot readreservedand failed withExpected = but found 3. It is now invoked as./node_modules/protobufjs/bin/pbjs, andpbjsis dropped from dependencies (nothing required it at runtime; its only effect was shadowing the real binary).lib/messages-ripple_pb.jsand its.d.tswere committed into the gitignoredlib/; both are untracked and regenerated by the build.Zcash wire protocol v2 (BREAKING)
Zcash shielded support shipped as a preview with no stable host implementations, so the flow was corrected in place rather than duplicated behind new message IDs. A host written against firmware <= 7.14.x will not interoperate with 7.15 and must be updated. The full migration is documented at the top of
messages-zcash.proto:1307renamedZcashTransparentSig->ZcashTransparentSignedand reshaped from one signature per request to a batchedrepeated bytes signatures. The wire ID is unchanged, so a v1 host decodes1307into the wrong message.ZcashTransparentOutput(1310) andZcashTransparentAck(1311). Outputs stream before inputs and all inputs are buffered before any signature, because ZIP-244 per-input transparent sighashes commit to every transparent prevout, value, script, sequence and output.ZcashTransparentInput.sighashis demoted to optional and rejected when present.ZcashDisplayAddressfields 3-6 (address,ak,nk,rivk) removed and reserved; the device derives and displays its own Orchard UA. Eitheraccountor a fulladdress_nis now required.ZcashPCZTAction.is_spendis required, andZcashSignedPCZT.signaturesis compact — a v1 host indexing signatures by action index reads the wrong signature.ZcashSignPCZT.sapling_digestis reserved for future Sapling support and rejected when set.RC18 Zcash wire contract
The regular/full RC18 firmware includes Orchard privacy. Only
bitcoin-onlycompiles non-Bitcoin features out.all Orchard actions are streamed to and validated by the device
is_spendis required by firmware 7.15dummy shield/change actions use
is_spend=falseand receive no RedPallas signaturereal spends use
is_spend=trueZcashSignedPCZT.signaturesis compact: exactly one 64-byte signature per real spend, in ascending action orderan all-dummy shield transaction therefore returns zero Orchard signatures
shielded_pool/ironwood_digest(tags 19-20) andZCASH_SHIELDED_POOL_IRONWOODare schema only: RC18 is Orchard-only and rejects a non-Orchard pool or a presentironwood_digestwithFailure. The tags are allocated so they cannot be reused; no host should read them as supported behavior.These semantics are documented on the protocol fields and pinned by a machine-readable contract check.
Validation
27d3fa1f6215139cde6411f9a2882f36bb373fc9is green in Protocol CI, CodeQL, and CircleCI. The earlier run below is historical evidence for the initial release surface4125e1c740(#755) andd33f1711c3(#756) pin the release-line protocol twin8545cd5b6; their full/bitcoin-only CI gates are green. The twin exists because those product branches omit intervening EIP-712 options. This PR is the canonical upstream protocol target for Python #197.Review and merge gates
masterduring release validationNo merge, tag, or release is requested before human review.