Skip to content

Protocol additions for firmware 7.15 - #112

Merged
pastaghost merged 133 commits into
masterfrom
up/release-protocol
Oct 16, 2026
Merged

pastaghost merged 133 commits into
masterfrom
up/release-protocol

Conversation

@BitHighlander

@BitHighlander BitHighlander commented Jul 16, 2026 •

Copy link
Copy Markdown
Collaborator

Adds protocol definitions for firmware 7.15: capability reporting, Taproot and dice modes, Ethereum runtime signers, streamed EIP-712, ERC-7730 definitions, Hive messages, NEAR schemas, Solana attestations, certified contacts, and Zcash wire v2 with breaking changes to batched transparent signatures. NEAR remains schema-only; firmware 7.15 has no handler.

EthereumClearSignDefinitionRequest.chain_id uses strings in generated JavaScript bindings to preserve the full uint64 range. Regressions check exact serialization and decoding through the maximum uint64 value. Documentation now specifies firmware 7.15's executable ERC-7730 subset, formatter roles and limits, and explains that legacy Zcash address tags are ignored: hosts must compare the returned address with the intended address.

Validation: Protocol CI passed at 6fd3e2700a175f80ad678432d9ca939d44a3f695, including package/binding validation, descriptor compilation and the Zcash wire-contract check.

Currently pinned by Python #197 at 45e2651a2 and the int12 firmware fork candidates. Updates for upstream firmware #483 and #477–#482 are staged locally and remain unpushed. Human review remains required.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Signing trust and response-sequence documentation contradict the linked firmware and can mislead host implementations.

0 open findings

Previously missed (3)

In code that hasn't changed since last review

Low severity Clarify runtime-only trust model for ERC-7730 catalogs

docs/​erc7730-compiled-format.md:32

The linked 7.15 firmware's erc7730_catalog.c::finish() calls verify_runtime_delegate(): it verifies the catalog signature using a user-loaded signer, not a KeepKey-root certificate, and does not verify the record's certificate-signature bytes. A root-certified catalog therefore still needs its signer loaded, and successful preload does not establish KeepKey certification. Make the trust-boundary section, envelope table and signature description consistently describe the implemented runtime-only trust model.

Low severity Certified Solana signing is unsupported in linked firmware

messages-solana.proto:123

The firmware linked in keepkey/keepkey-firmware#482 rejects every nonempty clearsign_certificate with Failure_UnexpectedMessage ("Certified Solana signing is unsupported") before processing the schema. A valid certificate and 0x80 signer IDs therefore cannot enable this flow. Mark this field and the certified path described above as schema-only for that release; supports_solana_lut_attestation must not be treated as support for certified signing.

Low severity Document dual terminal replies for Zcash PCZT signing

messages-zcash.proto:276

The linked firmware returns ZcashPCZTActionAck(0) after the last transparent input when shielded actions remain. It withholds transparent signatures until the final shielded action passes validation, then sends ZcashTransparentSigned followed immediately by ZcashSignedPCZT. Transparent-only transactions also receive both terminal replies. Document this sequence so a v2 host does not wait for signatures before sending actions or leave the second reply unread.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 15, 2026 07:40
@BitHighlander

Copy link
Copy Markdown
Collaborator Author

Addressed the three body-only findings from review 5528118531 in cd62d20:

  • ERC-7730 now documents verification against a user-loaded runtime signer, the unchecked certificate-signature bytes, and the fact that preload does not confer KeepKey-root certification.
  • Solana now marks the certificate path as schema-only for 7.15 and states that nonempty certificates are rejected; LUT-attestation capability does not imply certified signing.
  • Zcash now documents the action request after transparent inputs, deferred signature release, and both terminal replies (including transparent-only transactions).

Checked these descriptions against the integrated 7.15 handlers. Protocol descriptors generated before and after the edit are byte-identical (SHA-256 1e0ac51572771b63c33f6442a358e69d6bd7292a7d5587fb24a664cf98c08b6f). These changes affect comments and documentation only.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Security-sensitive signing contracts and breaking Zcash interoperability require cross-repository human review, alongside correction of the retained binding issue.

1 open finding

🧠 Review effort: Balanced

Comment thread messages-ethereum.proto Outdated
Copilot AI balanced review requested due to automatic review settings October 15, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Security-sensitive signing contracts and the breaking Zcash migration require coordinated human compatibility review.

0 open findings

1 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Low severity Document required token role for formatter kind 3

docs/​erc7730-compiled-format.md:244

Firmware 7.15's formatter validation requires both value and token roles for formatter kind 3. A compiler following this paragraph would therefore produce a definition rejected at preload. Document the required token role and lower tokenless descriptors to raw formatter kind 1, as the linked Python compiler already does.

Low severity Correct documented ABI nesting and path execution limits

docs/​erc7730-compiled-format.md:306

These advertised execution limits exceed those enforced by the linked release: ABI nesting is limited to 8 levels including the root, and erc7730_cap_path() rejects 8 or more steps. The Python compiler enforces both bounds. Definitions using the documented 12-level nesting or 16-step paths will be rejected. Distinguish the wire format's capacity from firmware's executable limits.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 15, 2026 08:24
@BitHighlander

Copy link
Copy Markdown
Collaborator Author

Addressed both body-only findings from review 5530118906 in 8ba3985: formatter kind 3 requires value and token roles; tokenless descriptors lower to raw kind 1. The execution limits now state 8 ABI levels including the root and fewer than 8 path steps, separately from wire capacity. Checked against erc7730_capabilities.c, ERC7730_ABI_MAX_DEPTH, and the compiler. This commit changes documentation only.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Security-sensitive signing contracts and the breaking Zcash migration require final human validation against companion firmware and clients.

0 open findings

Previously missed (2)

In code that hasn't changed since last review

Low severity Document firmware 7.15 opcode subset and reject unsupported descriptors

docs/​erc7730-compiled-format.md:303

The linked 7.15 firmware's erc7730_capabilities.h permits only condition opcode 3, path-step opcodes 1–2, formatter kinds 1–8/10/13, and display opcodes 1–8/10. Several operations described above therefore produce definitions that firmware refuses even when all listed numeric limits are met. Document the executable 7.15 subset separately from the broader wire grammar so host compilers can reject unsupported descriptors before preload.

Low severity Prevent silently ignoring removed fields in v1 address requests

messages-zcash.proto:36

Reserving protobuf tags prevents reuse, not receipt of unknown fields. The linked firmware decodes requests with pb_decode, and fsm_msgZcashDisplayAddress checks the account/path and optional seed fingerprint but cannot inspect the removed fields. An otherwise valid v1 request can therefore succeed while its supplied address/FVK is ignored, rather than returning Failure. Correct this migration guarantee and tell hosts to compare the returned address with their intended address.

🧠 Review effort: Balanced

@BitHighlander

Copy link
Copy Markdown
Collaborator Author

Addressed the two body-only findings in review 5530256418 with 6fd3e27. The ERC-7730 guide now separates 7.15 executable opcodes, formatters, paths, and containers from the wire grammar and points compilers to device_refusal(). Zcash migration notes now explain unknown-field skipping and require hosts to compare the returned address with their intended address. Zcash contract checks pass; all protocol descriptors are byte-identical to 8ba3985 (SHA-256 c0c27769b18815dc2952eef759b45a819631ee049c85df11baf815624fd0ffe8). This documentation-only follow-up leaves the frozen firmware/client pins at 8ba3985; production dependency merges and repinning remain a later release gate.

@pastaghost
pastaghost merged commit bd34091 into master Oct 16, 2026
4 checks passed
BitHighlander added a commit to keepkey/python-keepkey that referenced this pull request Oct 16, 2026
keepkey/device-protocol#112 merged as bd34091. Same tree as the
previous pin 6fd3e27, so no generated code changes.
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