Skip to content

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
masterfrom
up/release-protocol
Open

BitHighlander wants to merge 105 commits into
masterfrom
up/release-protocol

Conversation

@BitHighlander

@BitHighlander BitHighlander commented Jul 16, 2026 •

Copy link
Copy Markdown
Collaborator

Upstream branch → upstream master. keepkey:up/release-protocol is 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

IDs Surface Status
1600-1609, 1614-1617 Hive — GetPublicKey(s), SignTx, SignAccountCreate / SignAccountUpdate, and new HiveSignMessage / HiveSignedMessage (1614/1615), HiveSignOperations / HiveSignedOperations (1616/1617) implemented
1610-1613 NEAR — NearGetAddress / NearAddress / NearSignTx / NearSignedTx schema only — no firmware handler in 7.15/RC18; requests answered with Failure_UnexpectedMessage. Hosts must not advertise NEAR support on the basis of these bindings.
1700-1703 Clearsign attestor — ClearsignAttestorGetPublicKey / PublicKey / Sign / Signature; advanced-mode ClearSign studio schema attestation implemented
1704-1708 EIP-712 structured typed data — EthereumSignTypedData plus device-driven TypedDataStructRequest / StructAck / ValueRequest / ValueAck streaming implemented
1307, 1310, 1311 Zcash transparent phase — ZcashTransparentSigned (renamed, reshaped), ZcashTransparentOutput, ZcashTransparentAck breaking, see below
117 LoadClearsignSigner — clearsign identity icons: bounded RLE icon data, icon_width, icon_height, legacy-compatible persist implemented

Field-level additions

  • Solana — KKSOLSW1 transaction-bound LUT account attestation (tags 5-7), KKSOLSC1 instruction schema (tags 9-11), token_recipient_owner ATA candidates (tag 12), SolanaTokenInfo.signature / signer_key_id, and SolanaSignOffchainMessage domain-separated envelope signing.
  • Features / ResetDevice — supports_taproot capability 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, and ButtonRequest_DiceRoll (types.proto).
  • Zcash — Ironwood / transaction-v6 pool selection (shielded_pool, ironwood_digest, tags 19-20) is schema only: RC18 is Orchard-only and rejects a non-Orchard pool or a present ironwood_digest with Failure. Pinned by tools/check_zcash_contract.py.
  • Earlier 7.x additions accumulated on this release branch: thorchain any-denom and Ripple memo.

Packaging

  • build:postprocess is now tools/postprocess-lib.js. The previous sed matched only the generator's old single-line global bootstrap, so the CSP-hostile Function('return this')() fallback was shipping in every generated file (CI now reports patched 18 of 18); the script rewrites the call itself and fails the build if any survives. It also replaces the BSD-only sed -i ''.
  • prepare now runs the full build, so Git-based installs get lib/proto.json and not just the JavaScript. This also made CI exercise build:json for the first time, which surfaced a latent break: build:json called the bare pbjs bin, but both protobufjs and the unrelated pbjs@0.0.5 declare one. A clean npm ci linked the latter, whose parser cannot read reserved and failed with Expected = but found 3. It is now invoked as ./node_modules/protobufjs/bin/pbjs, and pbjs is dropped from dependencies (nothing required it at runtime; its only effect was shadowing the real binary).
  • lib/messages-ripple_pb.js and its .d.ts were committed into the gitignored lib/; 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:

  1. MessageType 1307 renamed ZcashTransparentSig -> ZcashTransparentSigned and reshaped from one signature per request to a batched repeated bytes signatures. The wire ID is unchanged, so a v1 host decodes 1307 into the wrong message.
  2. New ZcashTransparentOutput (1310) and ZcashTransparentAck (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.sighash is demoted to optional and rejected when present.
  3. ZcashDisplayAddress fields 3-6 (address, ak, nk, rivk) removed and reserved; the device derives and displays its own Orchard UA. Either account or a full address_n is now required.
  4. ZcashPCZTAction.is_spend is required, and ZcashSignedPCZT.signatures is compact — a v1 host indexing signatures by action index reads the wrong signature.
  5. ZcashSignPCZT.sapling_digest is reserved for future Sapling support and rejected when set.

RC18 Zcash wire contract

The regular/full RC18 firmware includes Orchard privacy. Only bitcoin-only compiles non-Bitcoin features out.

  • all Orchard actions are streamed to and validated by the device

  • is_spend is required by firmware 7.15

  • dummy shield/change actions use is_spend=false and receive no RedPallas signature

  • real spends use is_spend=true

  • ZcashSignedPCZT.signatures is compact: exactly one 64-byte signature per real spend, in ascending action order

  • an all-dummy shield transaction therefore returns zero Orchard signatures

  • shielded_pool / ironwood_digest (tags 19-20) and ZCASH_SHIELDED_POOL_IRONWOOD are schema only: RC18 is Orchard-only and rejects a non-Orchard pool or a present ironwood_digest with Failure. 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

Review and merge gates

  • singular master-targeting protocol branch
  • hosted descriptor and RC18 contract validation
  • human review of this canonical PR; keep it open against master during release validation
  • Python #197 uses this canonical protocol head and has green exact-head CI
  • verify firmware #755/#756 on the shared Python and protocol PR heads

No merge, tag, or release is requested before human review.

BitHighlander and others added 30 commits March 23, 2026 15:26
- 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>
Resolves conflict between:
- feature/hive branch (2b25cf7): 10 Hive message types
- alpha branch (c1dea44): Zcash clear-signing + THORChain denom field

Kept all 10 Hive message types; added alpha's Zcash/THORChain proto additions.
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.
BitHighlander and others added 24 commits September 16, 2026 17:50
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants