Skip to main content

Default Keys Specification

Cheat Sheet

The protocol does not enforce the usage of any of the following keys, and does not enforce the keys to conform to a particular length or algorithm. Users are expected to pick a set of keys valid for the encryption and tagging precompile they choose for their account.

Cat.KeyDerivationLink
Seedseed\seed$F\stackrel{\$}{\leftarrow} \mathbb{F}Seed
sk\sk$F\stackrel{\$}{\leftarrow} \mathbb{F}Master Secret Key
Master Secret Keysnskm\nskmposeidon2(“az_nsk_m”,sk)\text{poseidon2}(\text{``az\_nsk\_m''}, \sk)Master Nullifier Secret Key
ovskm\ovskmposeidon2(“az_ovsk_m”,sk)\text{poseidon2}(\text{``az\_ovsk\_m''}, \sk)Master Outgoing Viewing Secret Key
ivskm\ivskmposeidon2(“az_ivsk_m”,sk)\text{poseidon2}(\text{``az\_ivsk\_m''}, \sk)Master Incoming Viewing Secret Key
tskm\tskmposeidon2(“az_tsk_m”,sk)\text{poseidon2}(\text{``az\_tsk\_m''}, \sk)Master Tagging Secret Key
Master Public KeysNpkm\NpkmnskmG\nskm \cdot GMaster Nullifier Public Key
Ovpkm\OvpkmovskmG\ovskm \cdot GMaster Outgoing Viewing Public Key
Ivpkm\IvpkmivskmG\ivskm \cdot GMaster Incoming Viewing Public Key
Tpkm\TpkmtskmG\tskm \cdot GMaster Tagging Public Key
Hardened App-Siloed Secret Keysnskapp\nskappposeidon2(“az_nsk_app”,app_address,nskm)\text{poseidon2}(\text{``az\_nsk\_app''}, \text{app\_address}, \nskm)Hardened, App-siloed Nullifier Secret Key
ovskapp\ovskappposeidon2(“az_ovsk_app”,app_address,ovskm)\text{poseidon2}(\text{``az\_ovsk\_app''}, \text{app\_address}, \ovskm)Hardened, App-siloed Outgoing Viewing Secret Key
Other App-siloed KeysNkapp\Nkappposeidon2(“az_nk_app”,nskapp)\text{poseidon2}(\text{``az\_nk\_app''}, \nskapp)App-siloed Nullifier Key

Colour Key

  • green\color{green}{green} = Publicly shareable information.
  • red\color{red}{red} = Very secret information. A user MUST NOT share this information.
    • TODO: perhaps we distinguish between information that must not be shared to prevent theft, and information that must not be shared to preserve privacy?
  • orange\color{orange}{orange} = Secret information. A user MAY elect to share this information with a trusted 3rd party, but it MUST NOT be shared with the wider world.
  • violet\color{violet}{violet} = Secret information. Information that is shared between a sender and recipient (and possibly with a 3rd party who is entrusted with viewing rights by the recipient).

Diagrams

danger

Diagram is out of date vs the content on this page

The red boxes are uncertainties, which are explained later in this doc.

Preliminaries

Fr\mathbb{F}_r denotes the AltBN254 scalar field (i.e. the Grumpkin base field).

Fq\mathbb{F}_q denotes the AltBN254 base field (i.e. the Grumpkin scalar field).

Let GGrumpkin\mathbb{G}_{\text{Grumpkin}} be the Grumpkin elliptic curve group (E(Fr)E(\mathbb{F}_r)).

Let GGGrumpkinG \in \mathbb{G}_{\text{Grumpkin}} be a generator point for the public key cryptography outlined below. TODO: decide on how to generate this point.

Elliptic curve operators ++ and \cdot are used to denote addition and scalar multiplication, respectively.

poseidon2:FrtF\text{poseidon2}: \mathbb{F}_r^t \rightarrow \mathbb{F} is the Poseidon2 hash function (and tt can take values as per the Poseidon2 spec).

Note that q>rq > r. Below, we'll often define secret keys as an element of Fr\mathbb{F}_r, as this is most efficient within a snark circuit. We'll then use such secret keys in scalar multiplications with Grumpkin points (E(Fr)E(\mathbb{F}_r) whose affine points are of the form Fr×Fr\mathbb{F}_r \times \mathbb{F}_r). Strictly speaking, such scalars in Grumpkin scalar multiplication should be in Fq\mathbb{F}_q.
A potential consequence of using elements of Fr\mathbb{F}_r as secret keys could be that the resulting public keys are not uniformly-distributed in the Grumpkin group, so we should check this. The distribution of such public keys will have a statistical distance of 2(qr)q\frac{2(q - r)}{q} from uniform. It turns out that 12126<2(qr)q<12125\frac{1}{2^{126}} < \frac{2(q - r)}{q} < \frac{1}{2^{125}}, so the statistical distance from uniform is broadly negligible, especially considering that the AltBN254 curve has fewer than 125-bits of security.

Key Derivation

Derive Master Secret Key from Secret Key

derive_master_secret_key_from_secret_key:string×FrFrderive_master_secret_key_from_secret_key(domain_separator_string,secret_key):=poseidon2(be_string_to_field(domain_separator_string),secret_key)\begin{aligned} &\text{derive\_master\_secret\_key\_from\_secret\_key}: \text{string} \times \mathbb{F}_r \to \mathbb{F}_r \\ &\text{derive\_master\_secret\_key\_from\_secret\_key}(\text{domain\_separator\_string}, \text{secret\_key}) \\ &:= \text{poseidon2}(\text{be\_string\_to\_field(domain\_separator\_string)}, \text{secret\_key}) \end{aligned}

Note: Here, poseidon2\text{poseidon2} is assumed to be a pseudo-random function.

Derive Hardened App-siloed Secret Key

derive_hardened_app_siloed_secret_key:string×Fr×FrFrderive_hardened_app_siloed_secret_key(domain_separator_string,app_address,parent_secret_key):=poseidon2(be_string_to_field(domain_separator_string),app_address,parent_secret_key)\begin{aligned} &\text{derive\_hardened\_app\_siloed\_secret\_key}: \text{string} \times \mathbb{F}_r \times \mathbb{F}_r \to \mathbb{F}_r \\ &\text{derive\_hardened\_app\_siloed\_secret\_key}(\text{domain\_separator\_string}, \text{app\_address}, \text{parent\_secret\_key}) \\ &:= \text{poseidon2}(\text{be\_string\_to\_field(domain\_separator\_string)}, \text{app\_address}, \text{parent\_secret\_key}) \end{aligned}

Note: Here, poseidon2\text{poseidon2} is assumed to be a pseudo-random function.

Note: this deviates significantly from the 'conventional' BIP-32 style method for deriving a "hardened child secret key", to reduce complexity and as an optimization. Such a deviation will need to be validated as secure. In particular:

  • the notion of a "chain code" has been removed;
  • the notion of an "index" has been replaced by an app_address;
  • HMAC-SHA512 has been replaced with Poseidon2. Note: we don't need a 512-bit output, since we've removed the notion of a "chain code", and so we don't need to split the output of the Poseidon2 function into two outputs.

Derive Public Key (from Secret Key)

derive_public_key:FrGGrumpkinderive_public_key(secret_key):=secret_keyG\begin{aligned} &\text{derive\_public\_key}: \mathbb{F}_r \to \mathbb{G}_{\text{Grumpkin}} \\ &\text{derive\_public\_key}(\text{secret\_key}) := \text{secret\_key} \cdot G \end{aligned}

Seed

A seed secret from which all of a user's other keys may be derived. The seed\seed can live on an offline device, such as a hardware wallet.

seed$Fr\seed \stackrel{\$}{\leftarrow} \mathbb{F}_r

Master Secret Key

This sk\sk must never enter a circuit. A user or wallet may wish to derive this sk\sk from a cold wallet seed\seed.

sk$Fr\sk \stackrel{\$}{\leftarrow} \mathbb{F}_r

Note: Often sk=hash(seed)\sk = hash(\seed) for some hardware-wallet-supported hash function, would be recommended. Although, care would need to be taken if the hardware wallet doesn't support hashing directly to Fr\mathbb{F}_r, since a truncated hash output could be non-uniformly distributed in Fr\mathbb{F}_r.
For example, if the hardware wallet only supports sha256, then it would not be acceptable to compute sk\sk as sha256(seed)modr\text{sha256}(\seed) \mod r, since the resulting output (of reducing a 256-bit number modulo rr) would be biased towards smaller values in Fr\mathbb{F_r}. More uniformity might be achieved by instead computing sk\sk as (sha256(seed,1)sha256(seed,2))modr( \text{sha256}(\seed, 1) || \text{sha256}(\seed, 2) ) \mod r, for example, as a modulo reduction of a 512-bit number is closer to being uniformly distributed in Fr\mathbb{F_r}.
This note is informal, and expert advice should be sought before adopting this approach.

Nullifier Keys

App-siloed Nullifier Keys can be used by app developers when deriving their apps' nullifiers. By inserting some secret nullifier key into a nullifier's preimage, it makes the resulting nullifier look random, meaning observers cannot determine which note has been nullified.

Note that not all nullifiers will require a secret key in their computation, e.g. plume nullifiers, or state variable initialization nullifiers. But the keys outlined in this section should prove useful to many app developers.

Master Nullifier Secret Key

nskmFrnskm=derive_master_secret_key_from_secret_key(“az_nsk_m”,seed)\begin{aligned} & \nskm \in \mathbb{F}_r \\ & \nskm = \text{derive\_master\_secret\_key\_from\_secret\_key}(\text{``az\_nsk\_m''}, \seed) \end{aligned}

See derive_master_secret_key_from_secret_key.

nskm\nskm MUST NOT enter an app circuit.
nskm\nskm MAY enter the kernel circuit.

Master Nullifier Public Key

The Master Nullifier Public Key is only included so that other people can derive the user's address from some public information (i.e. from Npkm\Npkm), in such a way that nskm\nskm is tied to the user's address.

NpkmGGrumpkinNpkm=derive_public_key(nskm)\begin{aligned} & \Npkm \in \mathbb{G}_{\text{Grumpkin}} \\ & \Npkm = \text{derive\_public\_key}(\nskm) \end{aligned}

See derive_public_key.

App-siloed Nullifier Secret Key

The App-siloed Nullifier Secret Key is a hardened child key, and so is only derivable by the owner of the master nullifier secret key. It is hardened so as to enable the nskapp\nskapp to be passed into an app circuit, without the threat of nskm\nskm being reverse-derivable by a malicious app. Only when an app-siloed public key needs to be derivable by the general public is a normal (non-hardened) key derivation scheme used.

nskappFrnskapp=derive_hardened_app_siloed_secret_key(“az_nsk_app”,app_address,nskm)\begin{aligned} & \nskapp \in \mathbb{F}_r \\ & \nskapp = \text{derive\_hardened\_app\_siloed\_secret\_key}(\text{``az\_nsk\_app''}, \text{app\_address}, \nskm) \end{aligned}

See derive_hardened_app_siloed_secret_key.

App-siloed Nullifier Key

If an app developer thinks some of their users might wish to have the option to enable some trusted 3rd party to see when a particular user's notes are nullified, then this nullifier key might be of use. This Nkapp\Nkapp can be used in a nullifier's preimage, rather than nskapp\nskapp in such cases, to enable said 3rd party to brute-force identify nullifications.

Note: this key can be optionally shared with a trusted 3rd party, and they would not be able to derive the user's secret keys.
Note: knowledge of this key enables someone to identify when an emitted nullifier belongs to the user, and to identify which note hashes have been nullified.
Note: knowledge of this key would not enable a 3rd party to view the contents of any notes; knowledge of the ivskapp\ivskapp / ovskapp\ovskapp would be needed for that.
Note: this is intentionally not named as a "public" key, since it must not be shared with the wider public.

NkappFrNkapp=poseidon2(“az_nk_app”,nskapp)\begin{aligned} & \Nkapp \in \mathbb{F}_r \\ & \Nkapp = \text{poseidon2}(\text{``az\_nk\_app''}, \nskapp) \end{aligned}
TODO

We could also have derived Nkapp\Nkapp as nskappG\nskapp \cdot G, but this would have resulted in a Grumpkin point, which is more cumbersome to insert into the preimage of a nullifier. We might still change our minds to adopt this scalar-multiplication approach, since it might enable us to prove knowledge of nskm\nskm to the app circuit without having to add key derivation logic to the kernel circuit.

Outgoing Viewing Keys

App-siloed Outgoing Viewing Secret Keys can be used to derive ephemeral symmetric encryption keys, which can then be used to encrypt/decrypt data which the user has created for their own future consumption. I.e. these keys are for decrypting "outgoing" data from the pov of a sender. This is useful if the user's DB is wiped, and they need to sync from scratch (starting with only seed\seed).

Master Outgoing Viewing Secret Key

ovskmFrovskm=derive_master_secret_key_from_seed(“az_ovsk_m”,seed)\begin{aligned} & \ovskm \in \mathbb{F}_r \\ & \ovskm = \text{derive\_master\_secret\_key\_from\_seed}(\text{``az\_ovsk\_m''}, \seed) \end{aligned}

See derive_master_secret_key_from_seed.

ovskm\ovskm MUST NOT enter an app circuit.
ovskm\ovskm MAY enter the kernel circuit.

Master Outgoing Viewing Public Key

The Master Outgoing Viewing Public Key is only included so that other people can derive the user's address from some public information (i.e. from Ovpkm\Ovpkm), in such a way that ovskm\ovskm is tied to the user's address.

OvpkmGGrumpkinOvpkm=derive_public_key(ovskm)\begin{aligned} & \Ovpkm \in \mathbb{G}_{\text{Grumpkin}} \\ & \Ovpkm = \text{derive\_public\_key}(\ovskm) \end{aligned}

See derive_public_key.

App-siloed Outgoing Viewing Secret Key

The App-siloed Outgoing Viewing Secret Key is a hardened child key, and so is only derivable by the owner of the Master Outgoing Viewing Secret Key. It is hardened so as to enable the ovskapp\ovskapp to be passed into an app circuit, without the threat of ovskm\ovskm being reverse-derivable by a malicious app. Only when an app-siloed public key needs to be derivable by the general public is a normal (non-hardened) key derivation scheme used.

ovskappFrovskapp=derive_hardened_app_siloed_secret_key(“az_ovsk_app”,app_address,ovskm)\begin{aligned} & \ovskapp \in \mathbb{F}_r \\ & \ovskapp = \text{derive\_hardened\_app\_siloed\_secret\_key}(\text{``az\_ovsk\_app''}, \text{app\_address}, \ovskm) \end{aligned}

See derive_hardened_app_siloed_secret_key.

Incoming Viewing Keys

If a sender wants to send some recipient a private message or note, they can derive an ephemeral symmetric encryption key from the recipient's Master Incoming Viewing Public Key. I.e. these keys are for decrypting "incoming" data from the pov of a recipient.

Master Incoming Viewing Secret Key

ivskmFrivskm=derive_master_secret_key_from_seed(“az_ivsk_m”,seed)\begin{aligned} & \ivskm \in \mathbb{F}_r \\ & \ivskm = \text{derive\_master\_secret\_key\_from\_seed}(\text{``az\_ivsk\_m''}, \seed) \end{aligned}

See derive_master_secret_key_from_seed.

ivskm\ivskm MUST NOT enter an app circuit.

Master Incoming Viewing Public Key

The Master Incoming Viewing Public Key can be used by a sender to encrypt messages and notes to the owner of this key.

IvpkmGGrumpkinIvpkm=derive_public_key(ivskm)\begin{aligned} & \Ivpkm \in \mathbb{G}_{\text{Grumpkin}} \\ & \Ivpkm = \text{derive\_public\_key}(\ivskm) \end{aligned}

See derive_public_key.

App-siloed Incoming Viewing Secret Key

An App-siloed Incoming Viewing Secret Key is not prescribed in this spec, because depending on how an app developer wishes to make use of such a key, it could have implications on the security of the Master Incoming Viewing Secret Key.

TODO: more discussion needed here, to explain everything we've thought about.

Tagging Keys

The "tagging" key pair can be used to flag "this ciphertext is for you", without requiring decryption.

Master Tagging Secret Key

tskmFrtskm=derive_master_secret_key_from_seed(“az_tvsk_m”,seed)\begin{aligned} & \tskm \in \mathbb{F}_r \\ & \tskm = \text{derive\_master\_secret\_key\_from\_seed}(\text{``az\_tvsk\_m''}, \seed) \end{aligned}

See derive_master_secret_key_from_seed.

ivskm\ivskm MUST NOT enter an app circuit.

Master Tagging Public Key

TpkmGGrumpkinTpkm=derive_public_key(tskm)\begin{aligned} & \Tpkm \in \mathbb{G}_{\text{Grumpkin}} \\ & \Tpkm = \text{derive\_public\_key}(\tskm) \end{aligned}

See derive_public_key.

Acknowledgements

Much of this is inspired by the ZCash Sapling and Orchard specs.