Keys and Addresses
1. Why This Chapter Exists
A shielded wallet holds a 32-byte secret and exposes a tree of
derived keys, each with a specific capability (spend, view
incoming, view outgoing, diversify). A contributor changing
anything in
src/keys.rs
risks breaking the capability separation that protects users.
After this chapter the reader can identify which key is needed
for each operation and prove that no extra capability leaks.
2. Definitions
Definition 2.1 (Spending Key)
, the root of the key hierarchy. Hierarchical derivation via ZIP 32 produces an extended spending key with chain code .
Definition 2.2 (Derived Scalars and Bases)
Definition 2.3 (IVK and Full Viewing Key)
and the Full Viewing Key is the triple .
Definition 2.4 (Diversifier and Address)
A diversifier is produced from an integer index by FF1 encryption with key . and . A payment address is .
Invariant 2.5 (Capability Hierarchy)
The arrows below are deterministic and one-way under standard assumptions:
A holder of can detect incoming notes but cannot spend; a holder of can decrypt the outgoing ciphertext; a holder of can enumerate the addresses; only a holder of can sign spend authorisations.
3. The Code
3.1 The Spending Key
loading...
SpendingKey is a newtype around [u8; 32] with a Debug impl
that does not leak the secret bytes. Access goes through
derivation methods that return wrapper types
(SpendAuthorizingKey, NullifierDerivingKey,
CommitIvkRandomness); the inner bytes never escape the module.
3.2 The Derivation Tree
The methods in
src/keys.rs
implement the tree in order:
SpendingKey -> SpendAuthorizingKeyvia with tag0x06.SpendingKey -> NullifierDerivingKeywith tag0x07.SpendingKey -> CommitIvkRandomnesswith tag0x08.(ak, nk, rivk) -> FullViewingKey.FullViewingKey -> IncomingViewingKey | OutgoingViewingKey | DiversifierKey.(IncomingViewingKey, Diversifier) -> Address.
The KDF personalisation KDF_ORCHARD_PERSONALIZATION = b"Zcash_OrchardKDF" parametrises the Blake2b expansions used by
the various branches.
3.3 Addresses
loading...
Address::from_parts is the canonical (crate-private) constructor.
It does not perform any algebraic check beyond what its argument
types enforce: callers must guarantee pk_d is derived from d.
The doc comment in the source explicitly notes that the public
parsing API trusts the encoded form.
3.4 ZIP 32
src/zip32.rs
implements the Orchard branch of
ZIP 32, hardened-only derivation:
with
in the hardened range. There is no non-hardened path; see
Failure Modes.
4. Failure Modes
- Non-hardened derivation reintroduced. Orchard's ZIP 32 path is hardened-only. Adding a non-hardened branch lets a child and one child private key together reveal the parent . This is the same property that broke certain BIP 32 wallets and is the reason Sapling/Orchard hard-coded hardening.
- Identity
akorrk. ASpendingKeywhose derived is the curve identity should be rejected at construction. The corresponding rule forrkis enforced by #492; a similar rule for the construction-time check onaklives insrc/keys.rs. - Capability leak via debug printing.
Debugimpls on key types must not leak the raw scalar. The crate uses opaqueDebugformatters; review carefully when adding new key types. - Deep
ExtendedSpendingKeyderivation panic. See #464: chains longer than 255 levels panic.
5. Spec Pointers
- Zcash Protocol Specification, Section 4.2.3: Orchard key components.
- ZIP 32: shielded hardened-only derivation.
- ZIP 316: Unified Addresses, which bundle Orchard, Sapling, and transparent addresses.
zcash_spec: shared primitive used by the derivation code.
6. Exercises
- Search
src/keys.rsfor every call toPrfExpand. For each call, identify the one-byte domain tag and cross-check against Section 4.2.3 of the spec. - Read the
IncomingViewingKey::address_atmethod. How does it use FF1 to encrypt an integer index to a diversifier? What are the implications for the unlinkability of addresses produced by adjacent indices? - Code task. Write a unit test that derives an
ExtendedSpendingKeyfrom a fixed seed, enumerates the first three diversified addresses, and asserts they are distinct. Place it insrc/zip32.rsunder the#[cfg(test)]module. Runcargo test --lib zip32::tests.
7. Further Reading
- The Orchard Book, Keys and addresses walkthrough of the same hierarchy.
- BIP 32 hardened-derivation discussion in BIP 32 for the historical reason behind the hardened-only choice.