23 - The complete key catalog
1. Why this chapter exists
Every chapter of this course names keying-material symbols (, and so on). A reader who needs the exact derivation, type, or code location for any of these should not have to recover it from prose. This chapter is the authoritative reference: every Zcash key is listed with its domain, derivation formula, role, and the source file that defines its Rust type. It also points at the related catalog of circuit clauses in chapter 24 and at chapter 06 for HD derivation.
2. Definitions
This chapter assumes the cryptographic primitives defined in chapter 03 (groups, fields, PRFs, commitments, signatures). Specific to this chapter:
Definition (Field notation). denotes a prime field of order . We use for the Jubjub subgroup order, for the BLS12-381 scalar field (which equals the Jubjub base field), for the Pallas scalar field, for the Pallas base field. The Pallas and Vesta cofactors are ; the Jubjub cofactor is .
Definition (Generator notation). with a superscript (, etc.) denotes a fixed generator of the relevant prime-order subgroup, derived deterministically from a personalisation string and a hash-to-curve construction.
Definition (Scalar mul). denotes scalar multiplication of point by scalar .
Definition (Canonical encoding). means "canonical encoding into ", e.g. of a Jubjub point is its -coordinate encoded as a little-endian field element.
Definition (To-scalar / to-base reduction). for a 64-byte string means "interpret as a little-endian integer and reduce modulo the relevant scalar field order". is analogous for the base field.
Definition (Expand-from-seed PRF). The Sapling and Orchard expansions use
where is one or more bytes that distinguish derivation purposes.
Per-key catalog entry. Each entry below records:
- Symbol and short name.
- Type: field element, curve point, or byte string.
- Domain: which field or group.
- Derivation.
- Role: who knows it and what it enables.
- Code: where the type lives in this workspace (or in the
external
sapling-crypto/orchardcrates that this workspace depends on).
3. The code
The catalog is organised by pool. Within each pool, keys are listed in derivation order: spending key first, then derived material, then per-transaction ephemerals.
3.1 Sprout (legacy)
Sprout is closed for new outputs but historical notes still exist.
| Symbol | Type | Derivation | Role |
|---|---|---|---|
| uniform | Sprout spending key | ||
| Sprout paying key (public) | |||
| derived | Curve25519 secret for in-band encryption | ||
| Curve25519 point | Curve25519 public, part of the address | ||
| per-note unique | Nullifier seed in note | ||
| (Sprout) | per-note random | Commitment randomness | |
| per-JoinSplit random | Used to derive new values | ||
| hash of tx context + JoinSplit pubkey | Binds JoinSplit to the signature |
Nullifier: .
Sprout PRFs are SHA-256 with 4-bit tag prefixes; exact tags are in protocol spec section 5.4.2.
The Sprout circuit definition lives in
zcash_proofs/src/circuit/sprout/mod.rs:
loading...
3.2 Sapling
The Sapling key tree implementation lives in the external
sapling-crypto
crate; this workspace consumes it via
zcash_keys/src/keys.rs.
Root spending key
| Symbol | |
|---|---|
| Type | 32-byte string |
| Domain | |
| Derivation | ZIP 32 hardened path from seed, e.g. |
| Role | Holder can do everything: spend, view, derive |
| Code | sapling-crypto::keys::SpendingKey |
Sometimes called the "expanded spending key" or, with a chain code, the "extended spending key" (ZIP 32, Extended).
Spend-authorisation private key
| Symbol | |
|---|---|
| Type | scalar |
| Domain | |
| Role | Signs Sapling spend-auth signatures (after re-randomisation) |
| Code | sapling-crypto::keys::ExpandedSpendingKey::ask |
Nullifier private key
| Symbol | |
|---|---|
| Type | scalar |
| Domain | |
| Role | Derives and (inside the circuit) the nullifier |
| Code | sapling-crypto::keys::ExpandedSpendingKey::nsk |
Outgoing viewing key
| Symbol | |
|---|---|
| Type | 32-byte string |
| Domain | |
| Role | Decrypts outputs sent by the holder (via ) |
| Code | sapling-crypto::keys::ExpandedSpendingKey::ovk |
The truncation is the first 32 bytes of the 64-byte BLAKE2b output.
Diversifier key
| Symbol | |
|---|---|
| Type | 32-byte AES-128/256 key (used as FF1 key) |
| Role | Enumerates this account's diversifiers via FF1 format-preserving encryption |
| Code | sapling-crypto::zip32::DiversifierKey |
Spend-authorisation public key
with a fixed Jubjub generator
(SpendAuthSig.BasePoint) in the prime-order subgroup.
| Symbol | |
|---|---|
| Type | curve point |
| Domain | |
| Role | Public spend-authority key; published (after re-randomisation) as |
| Code | sapling-crypto::keys::ProofGenerationKey::ak |
Nullifier deriving key
with a fixed Jubjub generator
(ProvingPublicKey.BasePoint) in the prime-order subgroup.
| Symbol | |
|---|---|
| Type | curve point |
| Domain | |
| Role | Keys the nullifier PRF; part of the full viewing key |
| Code | sapling-crypto::keys::ProofGenerationKey::nk |
Incoming viewing key
extracting each point's -coordinate as the 255-bit little-endian field-element encoding; the top bit is cleared before reducing modulo to ensure uniform reduction.
| Symbol | |
|---|---|
| Type | scalar |
| Domain | |
| Role | Decrypts outputs received by this account |
| Code | sapling-crypto::keys::SaplingIvk |
Full viewing key
With the diversifier key included (per ZIP 316): .
The full viewing key can: compute and decrypt incoming notes; decrypt outgoing notes via ; enumerate diversified addresses via . It cannot spend or authorise (no ).
| Code | sapling-crypto::keys::FullViewingKey |
Proof generation key
The witness the prover supplies for the Sapling Spend circuit: public and private . A hardware-wallet flow that delegates proving hands this pair (and the per-spend ) to the prover without sending (which signs only).
| Code | sapling-crypto::keys::ProofGenerationKey |
Diversifier
for index . Eleven bytes. Not every produces a valid Jubjub generator (probability roughly 1/2 per ); invalid ones are skipped.
| Code | sapling-crypto::keys::Diversifier, DiversifierIndex |
Diversified base
where is a try-and-increment hash-to-curve
into using BLAKE2s with personalisation
"Zcash_gd". The output is multiplied by the cofactor () to
land in the prime-order subgroup.
| Symbol | |
|---|---|
| Type | curve point |
| Domain |
Diversified transmission key
| Symbol | |
|---|---|
| Type | curve point |
| Domain | |
| Role | Public key tied to diversifier ; combined to form a payment address |
Sapling payment address
encoded as bytes, then bech32 with HRP zs
(mainnet) or ztestsapling (testnet).
Note plaintext
A Sapling note is
where is a 32-byte seed (post-ZIP 212) from which and are derived. Pre-Canopy notes used directly.
Commitment randomness
Post-ZIP 212:
| Symbol | |
|---|---|
| Type | scalar |
| Domain | |
| Role | Hides the note in |
Ephemeral secret key
Post-ZIP 212:
| Symbol | |
|---|---|
| Type | scalar |
| Domain | |
| Role | Sender's secret for ECDH note encryption |
| Code | Local to builder; not part of any persisted key type |
Ephemeral public key
Published in the OutputDescription / Action.
| Type | curve point in |
Note commitment
Then is what is published.
| Code | sapling-crypto::primitives::NoteCommitment |
Value commitment randomness and value commitment
, uniform per spend/output.
with fixed Jubjub generators
(ValueCommitValueBase, ValueCommitRandomnessBase).
| Symbol | |
|---|---|
| Type | curve point in |
Position and rho
For a Sapling note at position in the commitment tree:
| Type | curve point in |
Nullifier
Published in the SpendDescription.
Re-randomisation
, uniform per spend.
stays private; is published.
Spend-authorisation signature
A RedJubjub signature under over the sighash:
Verified under .
Outgoing cipher key
| Symbol | (also in chapter 08) |
|---|---|
| Type | 32-byte symmetric key |
| Role | AEAD key for ; recovers from |
Note encryption key
where .
| Type | 32-byte AEAD key | | Role | Encrypts the note plaintext to the recipient |
Binding signature keys
If everything balances, ; the spender holds and signs the sighash under it.
| Code | sapling-crypto::bundle::Bundle::binding_signature |
Internal vs external addresses
ZIP 316 introduces an "internal" full-viewing-key tree for change addresses, distinct from user-facing external addresses. The internal sub-tree has its own , , and diversifier index space. An external party that sees change outputs cannot link them to user-visible addresses. The internal keys derive from the external ones via further hardened ZIP-32 children with index .
3.3 Orchard
The Orchard key tree is implemented in the external
orchard crate; this
workspace consumes it via
zcash_keys/src/keys.rs.
Structurally parallel to Sapling with several simplifications.
Root spending key
| Symbol | |
|---|---|
| Type | 32-byte string |
| Code | orchard::keys::SpendingKey |
Spend-authorisation private key
Nullifier deriving key
Unlike Sapling, Orchard's is a field element, not a curve point. This is a key Orchard simplification: the nullifier PRF feeds directly into a Poseidon hash inside the circuit, avoiding the cost of decoding a point.
Randomiser for ivk commitment
Used as the randomness in the Sinsemilla-based .
Outgoing viewing key and diversifier key
The 64-byte output is split: first 32 bytes are , next 32 are . The dependence on and binds these to the rest of the tree.
Spend-authorisation public key
Incoming viewing key
with "z.cash:Orchard-CommitIvk".
SinsemillaCommit is a randomised commitment: Sinsemilla hash
plus a randomness term.
Note this differs from Sapling: Sapling's is a hash; Orchard's is a commitment with a fresh randomiser. The commitment lets the circuit prove derivation more efficiently.
Full viewing key
are derived from deterministically.
Diversifier, diversified base, transmission key
with derived from as in Sapling. .
Orchard payment address
Encoded in 43 bytes, then packaged inside a Unified Address (no standalone bech32 form).
Note plaintext
An Orchard note is
The additional fields and are uniqueness nonces that chain across the bundle: each new note's is the nullifier of the spent note in the same action.
is derived from and :
Commitment randomness
Note commitment
Then is published.
Ephemeral keys for note encryption
Value commitment
with the net value of the Action (positive if the old note was larger than the new one). The bundle balance equation aggregates these.
Nullifier
with a fixed Pallas generator and a Poseidon-based PRF keyed by .
Re-randomisation, binding sig, OCK, K_enc
Parallel to Sapling:
The binding signature is RedPallas; ZIP 224 spells out the constants.
Issuance keys (ZSA, NU7-track)
Future-only:
| Symbol | Role |
|---|---|
| Issuer's spending-authority root for issuance | |
| Public issuance key | |
| 64-byte digest binding an asset to its issuer |
See chapter 21 for ZSA context.
3.4 Transparent
Standard BIP-32 / SLIP-10 over secp256k1. Path .
| Symbol | Type |
|---|---|
| Extended private key (32-byte priv + 32-byte chain code) | |
| Extended public key | |
| secp256k1 scalar | |
| secp256k1 point | |
| RIPEMD160(SHA256(pubkey)), the address payload |
The transparent key implementation:
loading...
ZIP 48 account-level keys live alongside in
zcash_transparent/src/zip48.rs.
3.5 Unified
Defined by ZIP 316. Encoded via F4Jumble in
components/f4jumble/src/lib.rs
and bech32m in
components/zcash_address/src.
Unified spending key
with components present per the account's policy.
loading...
Unified full viewing key
includes the diversifier key .
Unified incoming viewing key
decrypts incoming but not outgoing notes; a weaker capability than UFVK, suitable for read-only services.
Unified address
A bundle of receivers:
Typecodes per ZIP 316. Encoded as F4Jumble(TLV concat || HMAC),
then bech32m with HRP u.
3.6 Note encryption keys at a glance
For both Sapling and Orchard:
| Key | Sender knows | Recipient knows | Purpose |
|---|---|---|---|
| yes | no | ECDH secret | |
| yes (publishes) | yes (sees on-chain) | ECDH public | |
| DH output | |||
| yes | yes | AEAD key (recipient side) | |
| yes (via ) | no | AEAD key (sender side) |
3.7 Cross-pool relationships
Every Zcash account in this codebase has:
- One transparent extended key per account (ZIP 48).
- One Sapling extended spending key per account.
- One Orchard spending key per account.
These are independent: knowing one does not reveal the others. The wallet stitches them together via the Unified containers. The same seed deterministically produces all three (via different ZIP 32 paths). Backing up the seed phrase backs up the full account.
3.8 Privacy hierarchy
Per pool, the capability ladder (top: most powerful):
- - can spend.
- , - jointly authorise and prove a spend; cannot derive the address (need ).
- - view incoming and outgoing; enumerate addresses; cannot spend.
- - decrypt incoming; cannot view outgoing.
- - public receiver; only sendable-to.
The PCZT design respects this hierarchy: each role gets the minimum capability it needs.
3.9 Lifetime: where each key lives
| Phase | Stored | Notes |
|---|---|---|
| At rest | Seed (encrypted), USK and UFVK in wallet DB | USK should be encrypted in DB or held in a hardware signer |
| Scanning | UIVK, nullifier set | No spending capability needed |
| Building (single key) | USK, proving parameters, Merkle paths | Local proving |
| PCZT constructor | Read-only wallet state | No private keys |
| PCZT prover | + per-spend | No |
| PCZT signer | + sighash | No proving |
3.10 Code reference table
| Type | Crate :: Module |
|---|---|
SpendingKey (Sapling) | sapling-crypto::keys::SpendingKey |
ExpandedSpendingKey (Sapling) | sapling-crypto::keys::ExpandedSpendingKey |
ProofGenerationKey (Sapling) | sapling-crypto::keys::ProofGenerationKey |
FullViewingKey (Sapling) | sapling-crypto::keys::FullViewingKey |
SaplingIvk | sapling-crypto::keys::SaplingIvk |
OutgoingViewingKey (Sapling) | sapling-crypto::keys::OutgoingViewingKey |
DiversifierKey (Sapling) | sapling-crypto::zip32::DiversifierKey |
Diversifier (Sapling) | sapling-crypto::keys::Diversifier |
PaymentAddress (Sapling) | sapling-crypto::PaymentAddress |
SpendingKey (Orchard) | orchard::keys::SpendingKey |
FullViewingKey (Orchard) | orchard::keys::FullViewingKey |
IncomingViewingKey (Orchard) | orchard::keys::IncomingViewingKey |
DiversifierKey (Orchard) | orchard::keys::DiversifierKey |
Address (Orchard) | orchard::Address |
| Transparent extended key | zcash_transparent::keys |
UnifiedSpendingKey | zcash_keys::keys::UnifiedSpendingKey |
UnifiedFullViewingKey | zcash_keys::keys::UnifiedFullViewingKey |
UnifiedAddressRequest | zcash_keys::keys |
UnifiedAddress | zcash_keys::address::UnifiedAddress |
3.11 Derivation graphs
Sapling derivation:
seed (32 B)
| ZIP 32: m / 32' / 133' / acct'
v
sk (Sapling, 32 B)
|
|---- PRF^expand(.,0x00) -> ask (F_l)
|---- PRF^expand(.,0x01) -> nsk (F_l)
|---- PRF^expand(.,0x02) -> ovk (32 B)
|---- PRF^expand(.,0x10) -> dk (32 B)
|
v v
ak = [ask]G^ak nk = [nsk]G^nk
\_____________ ______/
\/
ivk = CRH^ivk(ak, nk) mod l
|
dk -- FF1 --> d (per index)
|
g_d = DiversifyHash(d)
|
pk_d = [ivk] g_d
|
address = (d, pk_d)
Orchard derivation:
sk_O
|
|---- PRF^expand(.,0x06) -> ask (F_q)
|---- PRF^expand(.,0x07) -> nk (F_p, field elt!)
|---- PRF^expand(.,0x08) -> rivk (F_q)
|
v v
ak = [ask]G^ak_O (nk is a scalar; no point op)
\_____________ ______/
\/
ivk = Extract(SinsemillaCommit^rivk(ak, nk))
|
ovk || dk = PRF^expand(., 0x82, ak, nk)
|
dk -- FF1 --> d
|
g_d = DiversifyHash(d)
|
pk_d = [ivk] g_d
|
receiver = (d, pk_d)
Per-transaction ephemerals (both pools):
rseed rcv alpha
| | |
v v v
rcm, esk (used in rsk = ask + alpha
| cv = [v]V |
v + [rcv]R) rk = ak + [alpha] G^ak
epk = [esk]g_d |
sigma_spendAuth =
RedSig.Sign_rsk(sighash)
5. Spec pointers
- Zcash Protocol Specification, section 4: high-level definitions for every key in this catalog.
- Zcash Protocol Specification, section 5: concrete formulas for , , , and the nullifier PRFs.
- ZIP 32: HD derivation paths feeding the per-pool root spending key.
- ZIP 212: the
rseed-derived and that this catalog records. - ZIP 224: Orchard key tree.
- ZIP 316: Unified Addresses, full viewing keys, and the internal/external split for change addresses.
- ZIP 48: transparent account- level keys included in a UFVK.
6. Exercises
- Look up a symbol. A PR comments references
without context. Use this catalog to identify
which pool the key belongs to, its derivation, and the line
in
zcash_keys/src/keys.rsor the externalorchardcrate where the type is defined. - Diversifier count. Compute the maximum number of diversifiers a Sapling account can produce and the expected fraction of valid ones (where lies in the prime-order subgroup). Cite the protocol spec section that gives the numbers.
- Trace a derivation in code. Open
zcash_keys/src/keys.rsand follow the call path from a seed to aUnifiedSpendingKeyto aUnifiedAddress. Cite the file and line of each intermediate type. - Cross-pool capability. A user shares their UFVK with a read-only auditor. Which keys in this catalog does the auditor gain access to? Which do they not? Answer per pool.
Answers in the code
- Sapling expanded-key type:
sapling-crypto::keys::ExpandedSpendingKey(external crate). - Orchard spending-key type:
orchard::keys::SpendingKey(external crate). - Transparent ZIP 48:
zcash_transparent/src/zip48.rs. - Unified containers:
zcash_keys/src/keys.rs. - F4Jumble encoding:
components/f4jumble/src/lib.rs.
7. Further reading
- chapter 06: the HD derivation layer that produces the root spending keys this catalog itemises.
- chapter 24: the circuits that consume these keys as witnesses.
- Hopwood, Bowe, Hornby, Wilcox, Sapling design notes: the original treatment of the Sapling key tree.