Skip to main content
This document covers the key hierarchy, how transaction metadata is encrypted so that both the TEE and the recipient can independently decrypt it, and how privacy addresses work. Prerequisites: Understanding of ECDH (Elliptic Curve Diffie-Hellman), symmetric encryption (AES-GCM), and basic key derivation concepts.

Key Hierarchy

Privacy Boost uses four types of keys. Each serves a distinct purpose and has different security implications if compromised.

1. EOA Key (Account Owner)

Your existing ECDSA private key (secp256k1) from your Ethereum wallet (MetaMask, hardware wallet, etc.).
  • Used for: Managing auth keys in the AuthRegistry (register, rotate, revoke) via EIP-712 signatures. This is the root of account ownership onchain.
  • Never enters the TEE in raw form.
  • If compromised: Attacker can register new auth keys or revoke existing ones. This is your standard Ethereum key — protect it the way you normally would.

2. Auth Key

An EdDSA key pair on the BabyJubjub curve, registered in the AuthRegistry contract.
  • Used for: Authorizing transfers and withdrawals inside ZK circuits. Every transfer requires an EdDSA signature from a registered auth key.
  • Why EdDSA? EdDSA on BabyJubjub is efficient to verify inside arithmetic circuits. ECDSA verification inside a ZK circuit would cost 10-100x more constraints.
  • Multi-device: A single account can have multiple auth keys (one per device). Keys can be rotated or permanently revoked.
  • If compromised: Attacker can authorize transfers from your account. Mitigated by revoking the compromised key via your EOA key and cancelling any unauthorized forced withdrawal requests.

3. Viewing Key

An elliptic curve key pair (secp256k1) used for ECDH-based encryption and decryption of transaction metadata.
  • Used for: Encrypting note metadata for recipients (as a sender) and decrypting incoming note metadata (as a recipient). This is how you discover your balance and transaction history.
  • Part of your privacy address: The viewing public key is included in your privacy address so senders can encrypt data for you.
  • If compromised: Attacker can see your balance, transaction history, and identify your notes. They cannot spend your funds (that requires the auth key).

4. Nullifying Key

A secret scalar value (not a key pair — just a number), unique per account. This is the core cryptographic binding that ties your identity to your notes.
  • Used for: Deriving your Master Public Key (MPK) and computing nullifiers when spending notes. See Protocol Deep Dive for the full formulas.
  • If compromised: Attacker can identify your notes and track your UTXO set. They still cannot spend your funds (spending requires an auth key signature). But they can link your transactions.
The key insight: privacy and custody are separated. Viewing key and nullifying key compromises break privacy but not custody. Only auth key or EOA key compromises can lead to fund loss, and both have recovery paths.

Key Derivation

All protocol-level keys are deterministically derived from a single seed using HKDF-SHA256:
Seed → HKDF-SHA256 → Viewing Key (secp256k1 key pair)
                    → Nullifying Key (secret scalar)
                    → Auth Key (EdDSA on BabyJubjub)
The Account ID and Master Public Key (MPK) are then derived from these values using domain-separated Poseidon2 hashing. The MPK is your identity within Privacy Boost — it’s public and safe to share. Full derivation formulas are in the Protocol Deep Dive.

Seed Sources

The SDK supports three ways to produce the seed:
SourceHow the seed is produced
Wallet-derivedThe wallet signs a deterministic message; the signature becomes the seed. Same wallet always produces the same keys. No extra backup needed.
MnemonicA BIP-39 12-word phrase is used as the seed. Allows key portability across different wallets, but the phrase must be backed up.
Raw seedAny 32-byte hex value that is used directly.
All three sources produce the same key types (viewing key, nullifying key, auth key) through the same HKDF derivation — they only differ in where the input seed comes from. For practical usage (code examples, persistence options, and recovery), see Key Management in the SDK documentation.

Privacy Address

Your privacy address is a compact encoding of your MPK and viewing public key:
PrivacyAddress = 0x{MPK}{ViewingPubKeyX}{ViewingPubKeyY}
96 bytes total. Share this to receive private transfers. The sender needs your MPK to construct the output commitment, and your viewing public key to encrypt the metadata so you can decrypt it.

Dual-Path ECDH Encryption

This is the core encryption scheme that enables two things simultaneously:
  1. The TEE server can decrypt transaction metadata for fast indexing and balance queries
  2. The recipient can independently decrypt the same metadata for manual recovery (forced withdrawal)
Neither party needs the other’s private key. The sender performs all encryption client-side.

Why “Dual-Path”?

The sender performs two independent ECDH operations — one with the TEE’s public key, one with the recipient’s viewing public key — using the same blinded key as input. This produces two separate decryption paths from a single encryption operation.
PartyKey UsedRole
SenderViewing private key (sender’s)Performs both ECDH computations and encrypts the payload
TEETxPrivKey (published as TxPubKey onchain)Decrypts via ECDH with the sender’s blinded viewing key
RecipientViewing private key (recipient’s)Decrypts via ECDH with the sender’s blinded viewing key

How It Works

Dual-path ECDH encryption: single encryption producing two independent decryption paths for TEE and recipient 1. Blinding — For each transaction, the sender generates a random scalar r and blinds their viewing public key: blindedKey = viewingPubKeySender * r. This goes onchain and ensures transactions from the same sender can’t be linked. 2. Derive encryption keys — The sender performs ECDH with the TEE’s public key and the recipient’s viewing public key (both scaled by r), then derives two symmetric keys via HKDF. 3. Encrypt — The sender generates a random ephemeral key (EphTxEncKey) and encrypts the payload with AES-256-GCM. 4. Wrap — The ephemeral key is XOR’d with each derived key, producing two wrap keys: one for the TEE, one for the recipient. 5. Publish — The blinded key, both wrap keys, and the ciphertext go onchain.

How Decryption Works

Both the TEE and recipient can independently recover the ephemeral key:
  • TEE: Uses its TxPrivKey + the onchain blinded key to perform ECDH, derives the same symmetric key, and XORs the TEE wrap key to recover the ephemeral key.
  • Recipient: Uses their viewing private key + the onchain blinded key to perform ECDH, derives the same symmetric key, and XORs the receiver wrap key.
This works because ECDH is commutative — the sender’s ECDH with the TEE’s key produces the same shared secret as the TEE’s ECDH with the sender’s blinded key.

What’s Encrypted

Each output’s ciphertext contains the sender and recipient account IDs, token ID, amount, and a random value for commitment uniqueness. Deposits contain the same fields minus the sender account ID.

Integrity Verification

The TEE verifies that both the TEE-targeted and receiver-targeted ciphertexts are valid. If either fails, the transaction is rejected — preventing a malicious sender from encrypting valid data for the TEE but garbage for the recipient.

How Recipients Discover Their Notes

Normal Operation (TEE Available)

The TEE decrypts all output ciphertexts as part of its normal indexing. It extracts the recipient’s account ID and indexes each note. Recipients query their balance from the TEE indexer — no scanning required, instant results. The TEE doesn’t store users’ private keys. It only decrypts the per-transaction ciphertexts using its own TxPrivKey to learn which account each note belongs to.

Manual Recovery (TEE Unavailable)

If the TEE is down, recipients can discover their notes independently by scanning onchain events and attempting to decrypt each output’s receiver-targeted ciphertext with their viewing key. If decryption succeeds (valid GCM auth tag), the note belongs to them. They then check the nullifier registry to find which notes are still unspent. This is the path used for forced withdrawal — it works with only onchain data and the user’s own keys.

Next Steps