Skip to content

feat(zcash): seed_fingerprint client + tests - #15

Merged
BitHighlander merged 2 commits into
masterfrom
feat/zcash-seed-fingerprint
Apr 29, 2026
Merged

feat(zcash): seed_fingerprint client + tests#15
BitHighlander merged 2 commits into
masterfrom
feat/zcash-seed-fingerprint

Conversation

@BitHighlander

Copy link
Copy Markdown
Owner

Summary

Wires the ZIP-32 §6.1 seed fingerprint binding into the python-keepkey client to mirror the firmware-side validation introduced on `keepkey-firmware:feature-zcash`.

SeedFingerprint := BLAKE2b-256(
    \"Zcash_HD_Seed_FP\", I2LEBSP_8(len(seed)) || seed
)

A 32-byte stable seed identifier. Lets a host pin an FVK/address/sign-session to a specific device-seed identity.

Changes

`device-protocol` submodule

  • URL: `keepkey/device-protocol` → `BitHighlander/device-protocol` (zcash work pins to fork master while seed_fingerprint sits in long-term review for upstream; will revert when upstream merges).
  • pin: `d0b8d80` → `4337c452` (BitHighlander/master with PR #27 merged).

Selective proto regen — `messages_zcash_pb2.py` only

Regenerated via `docker_build_pb.sh` (kktech/firmware:v8 → libprotoc 3.5.1, the canonical toolchain). Other pb2 files intentionally NOT regenerated because they currently include content from BitHighlander/device-protocol open PRs (#18 SolanaTokenInfo, #19 TRON clear-signing, #20 TON clear-signing, #21 EthereumTxMetadata). Until those merge, regenerating against current master would back out work that the existing python-keepkey client relies on.

New helper: `keepkeylib/zcash.py`

Pure-Python `calculate_seed_fingerprint(seed) -> bytes` matching the firmware C implementation byte-for-byte and the keystone3-firmware reference vector.

Client method updates: `keepkeylib/client.py`

  • `zcash_display_address` — add `expected_seed_fingerprint` kwarg
  • `zcash_sign_pczt` — add `expected_seed_fingerprint` kwarg
  • Both pass through unchanged when omitted (backward compatible)

Tests: `tests/test_msg_zcash_seed_fingerprint.py`

Pure-Python helper tests:

  • Reference vector cross-check (Keystone3)
  • Trivial-seed rejection (all-zero, all-0xFF)
  • Out-of-range rejection (short, long)

Device-backed tests:

  • `ZcashGetOrchardFVK` returns non-empty seed_fingerprint
  • Fingerprint stable across accounts (bound to seed, not account)
  • `ZcashDisplayAddress` succeeds with matching expected_seed_fingerprint, response carries seed_fingerprint
  • `ZcashDisplayAddress` rejects wrong expected_seed_fingerprint
  • `ZcashDisplayAddress` backward compatible without the field
  • `ZcashSignPCZT` rejects wrong expected_seed_fingerprint before signing

Reference vector

```
seed = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
fp = deff604c246710f7176dead02aa746f2fd8d5389f7072556dcb555fdbe5e3ae3
```
Cross-checked with `keystone3-firmware` `rust/keystore/src/algorithms/zcash/mod.rs::test_keystore_derive_zcash_ufvk`.

Test plan

  • Pure-Python helper tests pass: `pytest tests/test_msg_zcash_seed_fingerprint.py -k 'helper or rejects'`
  • Device-backed tests pass against firmware on `feature-zcash` (commits 0f8c2ef2 + 91f29998 + f97b8f14)
  • Existing zcash tests still pass (no regression on `zcash_get_orchard_fvk`, `zcash_display_address`, `zcash_sign_pczt`)

Review path

This is the fork-master review PR. After merge here, firmware `feature-zcash` bumps its `deps/python-keepkey` pin to the new fork master HEAD. A future PR will land the same content on `keepkey/python-keepkey:master` upstream alongside the corresponding `keepkey/device-protocol` PR (long-term external review).

Wires the ZIP-32 §6.1 seed fingerprint binding into the python-keepkey
client to mirror the firmware-side validation.

  device-protocol submodule
    - URL: keepkey/device-protocol -> BitHighlander/device-protocol
      (zcash work pins to fork master while seed_fingerprint sits in
      long-term review for upstream; revert when upstream merges.)
    - pin: d0b8d80 -> 4337c452 (BitHighlander/master with PR #27 merged).
    - messages_zcash_pb2.py regenerated via docker_build_pb.sh
      (kktech/firmware:v8 → libprotoc 3.5.1, the canonical toolchain).
      Selective regen — other pb2 files are intentionally NOT
      regenerated because they currently include content from
      BitHighlander/device-protocol open PRs (#18 SolanaTokenInfo,
      #19 TRON clear-signing, #20 TON clear-signing, #21
      EthereumTxMetadata). Until those merge, regenerating them
      against current master would back out work that the existing
      python-keepkey client relies on.

  keepkeylib/zcash.py (new)
    calculate_seed_fingerprint(seed) -> 32 bytes
      Pure-Python helper. BLAKE2b-256("Zcash_HD_Seed_FP",
      I2LEBSP_8(len) || seed). Matches the firmware C
      implementation byte-for-byte and the keystone3-firmware
      reference vector
        seed = 000102...1f
        fp   = deff604c246710f7176dead02aa746f2fd8d5389f7072556dcb555fdbe5e3ae3

  keepkeylib/client.py
    zcash_display_address  — add expected_seed_fingerprint kwarg
    zcash_sign_pczt         — add expected_seed_fingerprint kwarg
    Both pass through unchanged when the kwarg is None
    (backward compatible).

  tests/test_msg_zcash_seed_fingerprint.py (new)
    Pure-Python helper:
      - reference vector (Keystone3 cross-check)
      - rejects all-zero, all-0xFF, short, long
    Device-backed:
      - GetOrchardFVK returns non-empty seed_fingerprint
      - fingerprint stable across accounts (bound to seed, not account)
      - DisplayAddress: matching expected_seed_fingerprint succeeds,
        response carries seed_fingerprint
      - DisplayAddress: wrong expected_seed_fingerprint rejected
      - DisplayAddress: omitting expected_seed_fingerprint still works
      - SignPCZT: wrong expected_seed_fingerprint rejected before
        any signing crypto runs
Addresses review of PR #15.

Test structure
  Helper tests (no device) move to a dedicated module:
    tests/test_zcash_seed_fingerprint_helper.py
  This module deliberately does NOT import common, transport, or any
  protobuf bindings, so it runs on a stock dev box:
    pytest tests/test_zcash_seed_fingerprint_helper.py
  The previous file inherited common.KeepKeyTest, whose setUp wipes
  the device — pytest -k 'helper' was never actually offline.

Client wrapper coverage
  Device-backed tests now go through the public client helpers
  (self.client.zcash_display_address(... expected_seed_fingerprint=...)
  and self.client.zcash_sign_pczt(... expected_seed_fingerprint=...))
  rather than building raw protobuf messages with self.client.call().
  Confirms the kwarg pass-through end-to-end.

New test
  test_device_fingerprint_matches_python_helper: cross-checks the
  device-computed fingerprint against the python-keepkey helper for
  the same seed (all-allallall mnemonic, empty passphrase). Ties the
  firmware C, python-keepkey helper, and ZIP-32 §6.1 reference vector
  to the same byte-for-byte output.
@BitHighlander
BitHighlander merged commit 1688716 into master Apr 29, 2026
3 of 4 checks passed
@BitHighlander
BitHighlander deleted the feat/zcash-seed-fingerprint branch April 29, 2026 03:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant