From 22372e22c16cf4090eacbdf515934592ec53cacb Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Mon, 11 Mar 2024 15:16:48 +0000 Subject: [PATCH 1/5] docs(spec): hashing wip --- .../docs/addresses-and-keys/address.md | 70 ++- .../diversified-and-stealth-keys.md | 152 ++++++ .../example-usage/encrypt-and-tag.md | 203 ++++++++ .../example-usage/nullifier.md | 154 ++++++ .../example-usage/tag-sequence-derivation.md | 147 ++++++ yellow-paper/docs/addresses-and-keys/keys.md | 449 ++++++++---------- .../docs/circuits/private-function.md | 4 + yellow-paper/docs/constants.md | 4 - .../docs/contract-deployment/classes.md | 188 ++++++-- .../docs/contract-deployment/instances.md | 36 +- .../docs/cryptography/hashing/hashing.md | 29 ++ .../docs/cryptography/hashing/poseidon2.md | 0 .../docs/cryptography/merkle-trees.md | 120 +++++ yellow-paper/docs/intro.md | 197 +------- .../docs/pre-compiled-contracts/registry.md | 4 +- .../send-note-guidelines.md | 2 +- yellow-paper/docs/todo.md | 55 +++ yellow-paper/sidebars.js | 11 + 18 files changed, 1310 insertions(+), 515 deletions(-) create mode 100644 yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md create mode 100644 yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md create mode 100644 yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md create mode 100644 yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md create mode 100644 yellow-paper/docs/cryptography/hashing/poseidon2.md create mode 100644 yellow-paper/docs/todo.md diff --git a/yellow-paper/docs/addresses-and-keys/address.md b/yellow-paper/docs/addresses-and-keys/address.md index 221a7265085f..5cd2e3ac20d4 100644 --- a/yellow-paper/docs/addresses-and-keys/address.md +++ b/yellow-paper/docs/addresses-and-keys/address.md @@ -4,6 +4,8 @@ title: Address An address is computed as the hash of the following fields: + + | Field | Type | Description | |----------|----------|----------| @@ -27,21 +29,65 @@ We may remove the `portal_contract_address` as a first-class citizen. The hashing scheme for the address should then ensure that checks that are more frequent can be done cheaply, and that data shared out of band is kept manageable. We define the hash to be computed as follows: -``` -salted_initialization_hash = pedersen([salt, initialization_hash, deployer as Field, portal_contract_address as Field], GENERATOR__SALTED_INITIALIZATION_HASH) -partial_address = pedersen([contract_class_id, salted_initialization_hash], GENERATOR__CONTRACT_PARTIAL_ADDRESS_V1) -address = pedersen([public_keys_hash, partial_address], GENERATOR__CONTRACT_ADDRESS_V1) -``` + + +:::warning +Some of these draft domain separators might be too many bits; they need to fit inside a single field element. Version numbers might not be needed until we roll the _next_ version. +::: + +```rust +address_crh( + version: Field, + salt: Field, + deployer: AztecAddress, + contract_class_id: Field, + initialization_hash: Field, + portal_contract_address: EthereumAddress, + public_keys_hash: Field, +) -> Field { + + let salted_initialization_hash: Field = poseidon2( + be_string_to_field("az_salted_initialization_hash_v1"), -The `public_keys` array can vary depending on the format of keys used by the address, but it is suggested it includes the master keys defined in the [keys section](./keys.md). + salt, + initialization_hash, + deployer.to_field(), + be_bits_to_field(portal_contract_address) + ); + let partial_address: Field = poseidon2( + be_string_to_field("az_contract_partial_address_v1"), + + contract_class_id, + salted_initialization_hash + ); + + let address: Field = poseidon2( + be_string_to_field("az_contract_address_v1"), + + public_keys_hash, + partial_address + ); + + address +} ``` -public_keys_hash = pedersen([ - nullifier_pubkey.x, nullifier_pubkey.y, - tagging_pubkey.x, tagging_pubkey.y, - incoming_view_pubkey.x, incoming_view_pubkey.y, - outgoing_view_pubkey.x, outgoing_view_pubkey.y -], GENERATOR__PUBLIC_KEYS) + +The `public_keys` array can vary depending on the format of keys used by the address, but it is suggested it includes the master keys defined in the [keys section](./keys.md). For example: + +```rust +let public_keys_hash: Field = poseidon2( + be_string_to_field("az_public_keys_hash"), // TODO: does this need some unique ID, to disambiguate from other approaches people might have for other public keys? + + nullifier_pubkey.x, + nullifier_pubkey.y, + tagging_pubkey.x, + tagging_pubkey.y, + incoming_view_pubkey.x, + incoming_view_pubkey.y, + outgoing_view_pubkey.x, + outgoing_view_pubkey.y +); ``` This recommended hash format is compatible with the [encryption precompiles](./precompiles.md#encryption-and-tagging-precompiles) initially defined in the protocol and advertised in the canonical [registry](../pre-compiled-contracts/registry.md) for private message delivery. An address that chooses to use a different format for its keys will not be compatible with apps that rely on the registry for note encryption. Nevertheless, new precompiles introduced in future versions of the protocol could use different public keys formats. diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md b/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md new file mode 100644 index 000000000000..5f5516a2fc29 --- /dev/null +++ b/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md @@ -0,0 +1,152 @@ +$$ +\gdef\sk{\color{red}{sk}} + +\gdef\nskm{\color{red}{nsk_m}} +\gdef\tskm{\color{red}{tsk_m}} +\gdef\ivskm{\color{red}{ivsk_m}} +\gdef\ovskm{\color{red}{ovsk_m}} + +\gdef\Npkm{\color{green}{Npk_m}} +\gdef\Tpkm{\color{green}{Tpk_m}} +\gdef\Ivpkm{\color{green}{Ivpk_m}} +\gdef\Ovpkm{\color{green}{Ovpk_m}} + + +\gdef\address{\color{green}{address}} +\gdef\codehash{\color{green}{code\_hash}} +\gdef\constructorhash{\color{green}{constructor\_hash}} +\gdef\classid{\color{green}{class\id}} + + +\gdef\nskapp{\color{red}{nsk_{app}}} +\gdef\tskapp{\color{red}{tsk_{app}}} +\gdef\ivskapp{\color{red}{ivsk_{app}}} +\gdef\ovskapp{\color{red}{ovsk_{app}}} + +\gdef\Nkapp{\color{orange}{Nk_{app}}} + +\gdef\Npkapp{\color{green}{Npk_{app}}} + + +\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} + + +\gdef\happL{\color{green}{h_{app}^L}} +\gdef\happn{\color{green}{h_{app}^n}} +\gdef\happiv{\color{green}{h_{app}^{iv}}} + + +\gdef\d{\color{green}{d}} +\gdef\Gd{\color{green}{G_d}} + +\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} +\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} +\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} +\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} + + +\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} +\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} +\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} +\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} +\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} +\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} + +\gdef\hstealth{\color{violet}{h_{stealth}}} + + +\gdef\esk{\color{red}{esk}} +\gdef\Epk{\color{green}{Epk}} +\gdef\Epkd{\color{green}{Epk_d}} +\gdef\eskheader{\color{red}{esk_{header}}} +\gdef\Epkheader{\color{green}{Epk_{header}}} +\gdef\Epkdheader{\color{green}{Epk_{d,header}}} + +\gdef\sharedsecret{\color{violet}{\text{S}}} +\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} +\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} + + +\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} +\gdef\happencheader{\color{violet}{h_{app,enc,header}}} +\gdef\hmenc{\color{violet}{h_{m,enc}}} +\gdef\happenc{\color{violet}{h_{app,enc}}} +\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} + + +\gdef\plaintext{\color{red}{\text{plaintext}}} +\gdef\ciphertext{\color{green}{\text{ciphertext}}} +\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} +\gdef\payload{\color{green}{\text{payload}}} + + +\gdef\tagg{\color{green}{\text{tag}}} +\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} + + +$$ + +## Deriving diversified public keys + +A diversified public key can be derived from Alice's keys, to enhance Alice's transaction privacy. If Alice's counterparties' databases are compromised, it enables Alice to retain privacy from such leakages. Diversified public keys are used for generating diversified addresses. + +Basically, Alice must personally derive and provide Bob and Charlie with random-looking addresses (for Alice). Because Alice is the one deriving these Diversified Addresses (they can _only_ be derived by Alice), if Bob and Charlie chose to later collude, they would not be able to convince each-other that they'd interacted with Alice. + +This is not to be confused with 'Stealth Addresses', which 'flip' who derives: Bob and Charlie would each derive a random-looking Stealth Address for Alice. Alice would then discover her new Stealth Addresses through decryption. + +> All of the key information below is Alice's + +Alice derives a 'diversified' incoming viewing public key, and sends it to Bob: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\d$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ |diversifier | +$\Gd$ | $\d \cdot G$ | diversified generator | +$\Ivpkmd$ | $\ivskm \cdot \Gd$ | Diversified incoming viewing public key | + +> Notice: when $\d = 1$, $\Ivpkmd = \Ivpkm$. Often, it will be unncessary to diversify the below data, but we keep $\d$ around for the most generality. + +## Deriving stealth public keys + +> All of the key information below is Alice's + +Stealth Public Keys are used for generating Stealth Addresses. For Bob to derive a Stealth Address for Alice, Bob derives: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\d$ | Given by Alice | (Diversifier) | Remember, in most cases, $\d=1$ is sufficient. +$\Gd$ | $\d \cdot G$ | (Diversified) generator | Remember, when $\d = 1$, $\Gd = G$. +$\esk_{stealth}$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret, for deriving the stealth key shared secret | +$\Epkd,_{stealth}$ | $\esk_{stealth} \cdot \Gd$ | (Diversified) Ephemeral public key, for deriving the stealth key shared secret | +$\sharedsecret_{m, stealth}$ | $\esk_{stealth} \cdot \Ivpkmd$ | Stealth key shared secret | +$\hstealth$ | $\text{pos2}(\text{``az\_stealth\_key''}, \sharedsecret_{m, stealth})$ | stealth key | +$\Ivpkmdstealth$ | $\hstealth \cdot \Gd + \Ivpkmd$ | (Diversified) Stealth viewing public key | + +Having derived a Stealth Address for Alice, Bob can now share it with Alice as follows: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\tagg_{m, i}^{Bob \rightarrow Alice}$ | See earlier in this doc. | | Derive the next tag in the $Bob\rightarrow Alice$ sequence.
Note: we illustrate with a _master_ tag sequence, but an app-specific tag sequence could also be used (in which case an encryption of the app_address in a ciphertext header wouldn't be required; it could just be inferred from the tag used). | +$\esk_{header}$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key, for deriving the ciphertext header shared secret | +$\Epkd,_{header}$ | $\esk_{header} \cdot \Gd$ | (Diversified) Ephemeral public key, for deriving the ciphertext header shared secret | +$\sharedsecret_{m,header}$ | $\esk_{header} \cdot \Ivpkm$ | Ciphertext header shared secret | TODO: we might need to use a different ephemeral keypair from the one used to derive the stealth address. | +$\hmencheader$ | $\text{pos2}(\text{``az\_enc\_key''}, \sharedsecret_{m,header})$ | ciphertext header encryption key +$\ciphertextheader$ | $\text{encrypt}^{\Ivpkm}_{\hmencheader}$(app\_address) | | TODO: diversify this? | +$\payload$ | [ $\tagg_{m, i}^{Bob \rightarrow Alice}$, $\Epkd,_{header}$, $\ciphertextheader$, $\Epkd,_{stealth}$ ] | + +Alice can learn about her new Stealth Address as follows. First, she would identify the transaction has intended for her, either by observing $\tagg_{m, i}^{Bob \rightarrow Alice}$ on-chain herself (and then downloading the rest of the payload which accompanies the tag), or by making a privacy-preserving request to a server, to retrieve the payload which accompanies the tag. Assuming the $\payload$ has been identified as Alice's, we proceed: + + +| Thing | Derivation | Name | +|---|---|---| +$\sharedsecret_{m,header}$ | $\ivskm \cdot \Epkd,_{header}$ | Ciphertext header shared secret | +$\hmencheader$ | $\text{pos2}(\text{``az\_enc\_key''}, \sharedsecret_{m,header})$ | ciphertext header encryption key | +app_address | $\text{decrypt}_{\hmencheader}^{\ivskm}(\ciphertextheader)$ | +$\ivskm$ | See derivations above. Use the decrypted app_address in the derivation. | app-specific incoming viewing secret key | +$\sharedsecret_{m, stealth}$ | $\ivskm \cdot \Epkd,_{stealth}$ | Stealth key shared secret | +$\hstealth$ | $\text{pos2}(\text{``az\_stealth\_key''}, \sharedsecret_{m, stealth})$ | stealth key | +$\ivskmstealth$ | $\hstealth + \ivskm$ | +$\Ivpkmdstealth$ | $\ivskmstealth \cdot \Gd$ | (Diversified) Stealth viewing public key | diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md b/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md new file mode 100644 index 000000000000..ced34d223987 --- /dev/null +++ b/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md @@ -0,0 +1,203 @@ +$$ + +\gdef\sk{\color{red}{sk}} + +\gdef\nskm{\color{red}{nsk_m}} +\gdef\tskm{\color{red}{tsk_m}} +\gdef\ivskm{\color{red}{ivsk_m}} +\gdef\ovskm{\color{red}{ovsk_m}} + +\gdef\Npkm{\color{green}{Npk_m}} +\gdef\Tpkm{\color{green}{Tpk_m}} +\gdef\Ivpkm{\color{green}{Ivpk_m}} +\gdef\Ovpkm{\color{green}{Ovpk_m}} + + +\gdef\address{\color{green}{address}} +\gdef\codehash{\color{green}{code\_hash}} +\gdef\constructorhash{\color{green}{constructor\_hash}} +\gdef\classid{\color{green}{class\id}} + + +\gdef\nskapp{\color{red}{nsk_{app}}} +\gdef\tskapp{\color{red}{tsk_{app}}} +\gdef\ivskapp{\color{red}{ivsk_{app}}} +\gdef\ovskapp{\color{red}{ovsk_{app}}} + +\gdef\Nkapp{\color{orange}{Nk_{app}}} + +\gdef\Npkapp{\color{green}{Npk_{app}}} + + +\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} + + +\gdef\happL{\color{green}{h_{app}^L}} +\gdef\happn{\color{green}{h_{app}^n}} +\gdef\happiv{\color{green}{h_{app}^{iv}}} + + +\gdef\d{\color{green}{d}} +\gdef\Gd{\color{green}{G_d}} + +\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} +\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} +\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} +\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} + + +\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} +\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} +\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} +\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} +\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} +\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} + +\gdef\hstealth{\color{violet}{h_{stealth}}} + + +\gdef\esk{\color{red}{esk}} +\gdef\Epk{\color{green}{Epk}} +\gdef\Epkd{\color{green}{Epk_d}} +\gdef\eskheader{\color{red}{esk_{header}}} +\gdef\Epkheader{\color{green}{Epk_{header}}} +\gdef\Epkdheader{\color{green}{Epk_{d,header}}} + +\gdef\sharedsecret{\color{violet}{\text{S}}} +\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} +\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} + + +\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} +\gdef\happencheader{\color{violet}{h_{app,enc,header}}} +\gdef\hmenc{\color{violet}{h_{m,enc}}} +\gdef\happenc{\color{violet}{h_{app,enc}}} +\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} + + +\gdef\plaintext{\color{red}{\text{plaintext}}} +\gdef\ciphertext{\color{green}{\text{ciphertext}}} +\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} +\gdef\payload{\color{green}{\text{payload}}} + + +\gdef\tagg{\color{green}{\text{tag}}} +\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} + + +$$ + +## Encrypt and tag an incoming message + +Bob wants to send Alice a private message, e.g. the contents of a note, which we'll refer to as the $\plaintext$. Bob and Alice are using a "tag hopping" scheme to help with note discovery. Let's assume they've already handshaked to establish a shared secret $\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}$, from which a sequence of tags $\tagg_{m,i}^{Bob \rightarrow Alice}$ can be derived. + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\d$ | Given by Alice | (Diversifier) | Remember, in most cases, $\d=1$ is sufficient. +$\Gd$ | $\d \cdot G$ | (Diversified) generator | Remember, when $\d = 1$, $\Gd = G$. +$\eskheader$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | +$\Epkdheader$ | $\eskheader \cdot \Gd$ | (Diversified) Ephemeral public key | +$\sharedsecret_{m,header}$ | $\esk_{header} \cdot \Ivpkm$ | Shared secret, for ciphertext header encryption | TODO: can we use the same ephemeral keypair for both the ciphertext header and the ciphertext?
TODO: diversify the $\Ivpkm$? | +$\hmencheader$ | h("?", $\sharedsecret_{m,header}$) | Ciphertext header encryption key | +$\ciphertextheader$ | $enc^{\Ivpkm}_{\hmencheader}$(app\_address) | Ciphertext header | | +||||| +$\esk$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | +$\Epkd$ | $\esk \cdot \Gd$ | (Diversified) Ephemeral public key | +$\sharedsecret_{app,enc}$ | $\esk \cdot \Ivpkappdstealth$ | Shared secret, for ciphertext encryption | +$\happenc$ | h("?", $\sharedsecret_{app,enc}$) | Incoming data encryption key | +$\ciphertext$ | $enc^{\Ivpkappdstealth}_{\happenc}(\plaintext)$ | Ciphertext | +$\payload$ | [$\tagg_{m, i}^{Bob \rightarrow Alice}$, $\ciphertextheader$, $\ciphertext$, $\Epkdheader$, $\Epkd$] | Payload | + + + +Alice can learn about her new $\payload$ as follows. First, she would identify the transaction has intended for her, either by observing $\tagg_{m, i}^{Bob \rightarrow Alice}$ on-chain herself (and then downloading the rest of the payload which accompanies the tag), or by making a privacy-preserving request to a server, to retrieve the payload which accompanies the tag. Assuming the $\payload$ has been identified as Alice's, and retrieved by Alice, we proceed. + +> Given that the tag in this illustration was derived from Alice's master key, the tag itself doesn't convey which app_address to use, to derive the correct app-siloed incoming viewing secret key that would enable decryption of the ciphertext. So first Alice needs to decrypt the $\ciphertextheader$ using her master key: + + +| Thing | Derivation | Name | +|---|---|---| +$\sharedsecret_{m,header}$ | $\ivskm \cdot \Epkdheader$ | Shared secret, for encrypting the ciphertext header | +$\hmencheader$ | h("?", $\sharedsecret_{m,header}$) | Incoming encryption key | +app_address | $decrypt_{\hmencheader}^{\ivskm}(\ciphertextheader)$ | App address | +|||| +$\ivskappstealth$ | See derivations above. Use the decrypted app_address. | App-specific incoming viewing secret key | +$\sharedsecret_{app, enc}$ | $\ivskappstealth \cdot \Epkd$ | Shared secret, for ciphertext encryption | +$\happenc$ | h("?", $\sharedsecret_{app, enc}$) | Ciphertext encryption key | +$\plaintext$ | $decrypt_{\happenc}^{\ivskappstealth}(\ciphertext)$ | Plaintext | + +## Encrypt and tag an outgoing message + +Bob wants to send himself a private message (e.g. a record of the outgoing notes that he's created for other people) which we'll refer to as the $\plaintext$. Let's assume Bob has derived a sequence of tags $\tagg_{m,i}^{Bob \rightarrow Alice}$ for himself (see earlier). + +> Note: this illustration uses _master_ keys for tags, rather than app-specific keys for tags. App-specific keys for tags could be used instead, in which case a 'ciphertext header' wouldn't be needed for the 'app_address', since the address could be inferred from the tag. + +> Note: rather than copying the 'shared secret' approach of Bob sending to Alice, we can cut a corner (because Bob is the sender and recipient, and so knows his own secrets). + +> Note: if Bob has sent a private message to Alice, and he also wants to send himself a corresponding message: +> +> - he can likely re-use the ephemeral keypairs for himself. +> - he can include $\esk$ in the plaintext that he sends to himself, as a way of reducing the size of his $\ciphertext$ (since the $\esk$ will enable him to access all the information in the ciphertext that was sent to Alice). + + + +> Note: the violet symbols should actually be orange here. + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\d$ | Given by Alice | (Diversifier) | Remember, in most cases, $\d=1$ is sufficient. +$\Gd$ | $\d \cdot G$ | (Diversified) generator | Remember, when $\d = 1$, $\Gd = G$. +$\eskheader$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | +$\Epkdheader$ | $\eskheader \cdot \Gd$ | (Diversified) Ephemeral public key | +$\hmencheader$ | h("?", $\ovskm$, $\Epkdheader$) | Header encryption key | This uses a master secret key $\ovskm$, which MUST NOT be given to an app nor to an app circuit. However, it can still be given to a trusted precompile, which can handle this derivation securely. | +$\ciphertextheader$ | $enc_{\hmencheader}$(app\_address) | Ciphertext header encryption key | | +||||| +$\esk$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | +$\Epkd$ | $\esk \cdot \Gd$ | (Diversified) Ephemeral public key | +$\happenc$ | h("?", $\ovskapp$, $\Epkd$) | Outgoing data encryption key | Since $\ovskapp$ is a _hardened_ app-siloed secret key, it may be safely given to the dapp or passed into the app's circuit. | +$\ciphertext$ | $enc_{\happenc}(\plaintext)$ | Ciphertext | +$\payload$ | [$\tagg_{m, i}^{Bob \rightarrow Bob}$, $\ciphertextheader$, $\ciphertext$, $\Epkdheader$, $\Epkd$] | Payload | + +Alice can learn about her new $\payload$ as follows. First, she would identify the transaction has intended for her, either by observing $\tagg_{m, i}^{Bob \rightarrow Alice}$ on-chain herself (and then downloading the rest of the payload which accompanies the tag), or by making a privacy-preserving request to a server, to retrieve the payload which accompanies the tag. Assuming the $\payload$ has been identified as Alice's, and retrieved by Alice, we proceed. + +> Given that the tag in this illustration was derived from Alice's master key, the tag itself doesn't convey which app_address to use, to derive the correct app-siloed incoming viewing secret key that would enable decryption of the ciphertext. So first Alice needs to decrypt the $\ciphertextheader$ using her master key: + + +| Thing | Derivation | Name | +|---|---|---| +$\hmencheader$ | h("?", $\ovskm$, $\Epkdheader$) | | +app_address | $decrypt_{\hmencheader}(\ciphertextheader)$ | +|||| +$\ovskapp$ | See derivations above. Use the decrypted app_address. | | +$\happenc$ | h("?", $\ovskm$, $\Epkd$) | +$\plaintext$ | $decrypt_{\happenc}(\ciphertext)$ | + + + +### Doing this inside an app circuit + +Here's how an app circuit could constrain the app-siloed outgoing viewing secret key ($\ovskapp$) to be correct: + +The app circuit exposes, as public inputs, an "outgoing viewing key validation request": + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +`outgoing_viewing_key_validation_request` | app_address: app_address,
hardened_child_sk: $\nskapp$,
claimed_parent_pk: $\Npkm$ | + +The kernel circuit can then validate the request (having been given $\ovskm$ as a private input to the kernel circuit): + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\ovskapp$ | $h(\ovskm, \text{app\_address})$ | +$\Ovpkm$ | $\ovskm \cdot G$ | Outgoing viewing public key | +| | | | Copy-constrain $\ovskm$ with $\ovskm$. | + +If the kernel circuit succeeds in these calculations, then the $\ovskapp$ has been validated as the correct app-siled secret key for $\Ovpkm$. + +## Encrypt and tag an internal incoming message + +Internal incoming messages are handled analogously to outgoing messages, since in both cases the sender is the same as the recipient, who has access to the secret keys when encrypting and tagging the message. diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md b/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md new file mode 100644 index 000000000000..5bf59bff1b74 --- /dev/null +++ b/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md @@ -0,0 +1,154 @@ +$$ +\gdef\sk{\color{red}{sk}} + +\gdef\nskm{\color{red}{nsk_m}} +\gdef\tskm{\color{red}{tsk_m}} +\gdef\ivskm{\color{red}{ivsk_m}} +\gdef\ovskm{\color{red}{ovsk_m}} + +\gdef\Npkm{\color{green}{Npk_m}} +\gdef\Tpkm{\color{green}{Tpk_m}} +\gdef\Ivpkm{\color{green}{Ivpk_m}} +\gdef\Ovpkm{\color{green}{Ovpk_m}} + + +\gdef\address{\color{green}{address}} +\gdef\codehash{\color{green}{code\_hash}} +\gdef\constructorhash{\color{green}{constructor\_hash}} +\gdef\classid{\color{green}{class\id}} + + +\gdef\nskapp{\color{red}{nsk_{app}}} +\gdef\tskapp{\color{red}{tsk_{app}}} +\gdef\ivskapp{\color{red}{ivsk_{app}}} +\gdef\ovskapp{\color{red}{ovsk_{app}}} + +\gdef\Nkapp{\color{orange}{Nk_{app}}} + +\gdef\Npkapp{\color{green}{Npk_{app}}} + + +\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} + + +\gdef\happL{\color{green}{h_{app}^L}} +\gdef\happn{\color{green}{h_{app}^n}} +\gdef\happiv{\color{green}{h_{app}^{iv}}} + + +\gdef\d{\color{green}{d}} +\gdef\Gd{\color{green}{G_d}} + +\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} +\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} +\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} +\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} + + +\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} +\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} +\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} +\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} +\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} +\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} + +\gdef\hstealth{\color{violet}{h_{stealth}}} + + +\gdef\esk{\color{red}{esk}} +\gdef\Epk{\color{green}{Epk}} +\gdef\Epkd{\color{green}{Epk_d}} +\gdef\eskheader{\color{red}{esk_{header}}} +\gdef\Epkheader{\color{green}{Epk_{header}}} +\gdef\Epkdheader{\color{green}{Epk_{d,header}}} + +\gdef\sharedsecret{\color{violet}{\text{S}}} +\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} +\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} + + +\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} +\gdef\happencheader{\color{violet}{h_{app,enc,header}}} +\gdef\hmenc{\color{violet}{h_{m,enc}}} +\gdef\happenc{\color{violet}{h_{app,enc}}} +\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} + + +\gdef\plaintext{\color{red}{\text{plaintext}}} +\gdef\ciphertext{\color{green}{\text{ciphertext}}} +\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} +\gdef\payload{\color{green}{\text{payload}}} + + +\gdef\tagg{\color{green}{\text{tag}}} +\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} + + +$$ + +## Deriving a nullifier within an app contract + +Let's assume a developer wants a nullifier of a note to be derived as: + +`nullifier = h(note_hash, nullifier_key);` + +... where the `nullifier_key` ($\Nkapp$) belongs to the 'owner' of the note, and where the 'owner' is some $\address$. + +Here's example for how an app circuit _could_ constrain the nullifier key to be correct: + +### Diagram + +It's easiest to take a look at this first: + +![Alt text](../images/addresses-and-keys/image.png) + +### Within the app circuit + +Within the app, we can prove links between: + +- the user's [$\nskapp$](../keys.md#app-siloed-nullifier-secret-key) and their [$\Nkapp$](../keys.md#app-siloed-nullifier-key); and between +- the user's [$\Npkm$](../keys.md#master-nullifier-public-key) and their [$\address$](../address.md). + +The link that's missing is to prove that $\Npkm$ relates to $\nskapp$. To compute this missing link requires the $\nskm$, which MUST NOT be passed into an app circuit, and may only be passed into a kernel circuit. See the next ['Within the kernel circuit'](#within-the-kernel-circuit) section for details of this logic. + +#### The logic + +$$ +\begin{aligned} +\Nkapp &= \text{poseidon2}(\nskapp) \\ +\text{nullifier} &= \text{poseidon2}(\text{note\_hash}, \Nkapp) \\ +\text{public\_keys\_hash} &= \text{poseidon2}(\text{be\_string\_to\_field}(``\text{az\_public\_keys\_hash}"), \Npkm, \Tpkm, \Ivpkm, \Ovpkm) \\ +\address &= \text{poseidon2}(\text{be\_string\_to\_field}(``\text{az\_contract\_address\_v1}"), \text{public\_keys\_hash}, \text{partial\_address}) +\end{aligned} +$$ + +> Note: the passing of points directly into the poseidon function is lazy notation: the keys would need to be serialized appropriately as fields into the poseidon function. + +> Recall an important point: the app circuit MUST NOT be given $\nskm$. Indeed, $\nskapp$ is derived (see earlier) as a _hardened_ child of $\nskm$, to prevent $\nskm$ from being reverse-derived by a malicious circuit. The linking of $\nskapp$ to $\nskm$ is deferred to the kernel circuit (which can be trusted moreso than an app). + +> Recall also: $\Nkapp$ is used (instead of $\nskapp$) solely as a way of giving the user the option of sharing $\Nkapp$ with a trusted 3rd party, to give them the ability to view when a note has been nullified (although I'm not sure how useful this is, given that it would require brute-force effort from that party to determine which note hash has been nullified, with very little additional information). + +The app circuit exposes, as public inputs, a "nullifier key validation request": + +```rust +let nullifier_validation_request = KeyValidationRequest { + app_address: app_address, + claimed_hardened_child_sk: nsk_app, + claimed_parent_pk: Npk_m, +} +``` + +### Within the Kernel Circuit + +The kernel circuit can then validate the request (having been given $\nskm$ as a private input to the kernel circuit): + +$$ +\begin{aligned} +\nskapp &= \text{derive\_hardened\_app\_siloed\_secret\_key}(\text{``az\_nsk\_app"}, \text{app\_address}, \nskm) \\ +\Npkm &= \nskm \cdot G \\ +\nskapp &== \text{claimed\_hardened\_child\_sk} \\ +\Npkm &== \text{claimed\_parent\_pk} \\ +\end{aligned} +$$ + +If the kernel circuit succeeds in these calculations, then the $\Nkapp$ has been validated as having a known secret key, and belonging to the $\address$. diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md b/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md new file mode 100644 index 000000000000..198c15d7b047 --- /dev/null +++ b/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md @@ -0,0 +1,147 @@ +$$ +\gdef\sk{\color{red}{sk}} + +\gdef\nskm{\color{red}{nsk_m}} +\gdef\tskm{\color{red}{tsk_m}} +\gdef\ivskm{\color{red}{ivsk_m}} +\gdef\ovskm{\color{red}{ovsk_m}} + +\gdef\Npkm{\color{green}{Npk_m}} +\gdef\Tpkm{\color{green}{Tpk_m}} +\gdef\Ivpkm{\color{green}{Ivpk_m}} +\gdef\Ovpkm{\color{green}{Ovpk_m}} + + +\gdef\address{\color{green}{address}} +\gdef\codehash{\color{green}{code\_hash}} +\gdef\constructorhash{\color{green}{constructor\_hash}} +\gdef\classid{\color{green}{class\id}} + + +\gdef\nskapp{\color{red}{nsk_{app}}} +\gdef\tskapp{\color{red}{tsk_{app}}} +\gdef\ivskapp{\color{red}{ivsk_{app}}} +\gdef\ovskapp{\color{red}{ovsk_{app}}} + +\gdef\Nkapp{\color{orange}{Nk_{app}}} + +\gdef\Npkapp{\color{green}{Npk_{app}}} + + +\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} + + +\gdef\happL{\color{green}{h_{app}^L}} +\gdef\happn{\color{green}{h_{app}^n}} +\gdef\happiv{\color{green}{h_{app}^{iv}}} + + +\gdef\d{\color{green}{d}} +\gdef\Gd{\color{green}{G_d}} + +\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} +\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} +\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} +\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} + + +\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} +\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} +\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} +\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} +\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} +\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} + +\gdef\hstealth{\color{violet}{h_{stealth}}} + + +\gdef\esk{\color{red}{esk}} +\gdef\Epk{\color{green}{Epk}} +\gdef\Epkd{\color{green}{Epk_d}} +\gdef\eskheader{\color{red}{esk_{header}}} +\gdef\Epkheader{\color{green}{Epk_{header}}} +\gdef\Epkdheader{\color{green}{Epk_{d,header}}} + +\gdef\sharedsecret{\color{violet}{\text{S}}} +\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} +\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} + + +\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} +\gdef\happencheader{\color{violet}{h_{app,enc,header}}} +\gdef\hmenc{\color{violet}{h_{m,enc}}} +\gdef\happenc{\color{violet}{h_{app,enc}}} +\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} + + +\gdef\plaintext{\color{red}{\text{plaintext}}} +\gdef\ciphertext{\color{green}{\text{ciphertext}}} +\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} +\gdef\payload{\color{green}{\text{payload}}} + + +\gdef\tagg{\color{green}{\text{tag}}} +\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} + + +$$ + +# Handshaking for tag-hopping + +Deriving a sequence of tags for tag-hopping. + +## Deriving a sequence of tags between Alice and Bob across all apps + +For Bob to derive a shared secret for Alice: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\esk_{hs}$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key, for handshaking | $hs$ = handshake. +$\Epk_{hs}$ | $\esk_{hs} \cdot G$ | Ephemeral public key, for handshaking | +$\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}$ | $\esk_{hs} \cdot \Ivpkm$ | Shared secret, for tagging | Here, we're illustrating the derivation of a shared secret (for tagging) using _master_ keys. + +Having derived a Shared Secret, Bob can now share it with Alice as follows: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\Taghs$ | $\esk_{hs} \cdot \Tpkm$ | Handshake message identification tag | Note: the tagging public key $\Tpkm$ exists as an optimization, seeking to make brute-force message identification as fast as possible. In many cases, handshakes can be performed offchain via traditional web2 means, but in the case of on-chain handshakes, we have no preferred alternative over simply brute-force attempting to reconcile every 'Handshake message identification tag'. Note: this optimization reduces the recipient's work by 1 cpu-friendly hash per message (at the cost of 255-bits to broadcast a compressed encoding of $\Taghs$). We'll need to decide whether this is the right speed/communication trade-off. | +$\payload$ | [$\Taghs$, $\Epk_{hs}$] | Payload | This can be broadcast via L1.
Curve points can be compressed in the payload. | + +Alice can identify she is the indended the handshake recipient as follows: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\Taghs$ | $\tskm \cdot \Epk_{hs}$ | Handshake message identification tag | Alice can extract $\Taghs$ and $\Epk_{hs}$ from the $\payload$ and perform this scalar multiplication on _every_ handshake message. If the computed $\Taghs$ value matches that of the $\payload$, then the message is indented for Alice.
Clearly, handshake transactions will need to be identifiable as such (to save Alice time), e.g. by revealing the contract address of some canonical handshaking contract alongside the $\payload$.
Recall: this step is merely an optimization, to enable Alice to do a single scalar multiplication before moving on (in cases where she is not the intended recipient). | + +If Alice successfully identifies that she is the indended the handshake recipient, she can proceed with deriving the shared secret (for tagging) as follows: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}$ | $\ivskm \cdot \Epk_{hs}$ | Shared secret, for tagging | | + +A sequence of tags can then be derived by both Alice and Bob as: + + +| Thing | Derivation | Name | Comments | +|---|---|---|---| +$\tagg_{m,i}^{Bob \rightarrow Alice}$ | $\text{pos2}(\text{``az\_tag\_ss\_m''}, \sharedsecret_{m,tagging}^{Bob \rightarrow Alice}, i)$ | The i-th tag in the sequence. | | + +This tag can be used as the basis for note retreival schemes. Each time Bob sends Alice a $\ciphertext$, he can attach the next unused $\tagg_{m,i}^{Bob \rightarrow Alice}$ in the sequence. Alice - who is also able to derive the next $\tagg_{m,i}^{Bob \rightarrow Alice}$ in the sequence - can make privacy-preserving calls to a server, requesting the $\ciphertext$ associated with a particular $\tagg_{m,i}^{Bob \rightarrow Alice}$. + +> The colour key isn't quite clear for $\tagg_{m,i}^{Bob \rightarrow Alice}$. It will be a publicly-broadcast piece of information, but no one should learn that it relates to Bob nor Alice (except perhaps some trusted 3rd party whom Alice has entrusted with her $\ivskm$). + + + +## Deriving a sequence of tags from Bob to himself across all apps + +The benefit of Bob deriving a sequence of tags for himself, is that he can re-sync his _outgoing_ transaction data more quickly, if he ever needs to in future. + +This can be done by either: + +- Copying the approach used to derive a sequence of tags between Bob and Alice (but this time do it between Bob and Bob, and use Bob's outgoing keys). +- Generating a very basic sequence of tags $\tagg_{app, i}^{Bob \rightarrow Bob} = \text{pos2}(\text{``az\_tag\_ovsk\_app''}, \ovskapp, i)$ (at the app level) and $\tagg_{m, i}^{Bob \rightarrow Bob} = \text{pos2}(\text{``az\_tag\_ovsk\_m''}, \ovskm, i)$ (at the master level). + - Note: In the case of deriving app-specific sequences of tags, Bob might wish to also encrypt the app*address as a ciphertext header (and attach a master tag $\tagg*{m, i}^{Bob \rightarrow Bob}$), to remind himself of the apps that he should derive tags _for_. diff --git a/yellow-paper/docs/addresses-and-keys/keys.md b/yellow-paper/docs/addresses-and-keys/keys.md index 4e1885aaefdf..3a5f1daf4f55 100644 --- a/yellow-paper/docs/addresses-and-keys/keys.md +++ b/yellow-paper/docs/addresses-and-keys/keys.md @@ -6,7 +6,10 @@ description: Specification for default privacy keys format and derivation, and n $$ + + \gdef\sk{\color{red}{sk}} +\gdef\seed{\color{red}\text{{seed}}} \gdef\nskm{\color{red}{nsk_m}} \gdef\tskm{\color{red}{tsk_m}} @@ -93,6 +96,31 @@ $$ $$ +## 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. | Key | Derivation | Link | +|---|---|---|---| +| Seed | $\seed$ | $\stackrel{\$}{\leftarrow} \mathbb{F}$ | [Seed](#seed) | +| | $\sk$ | $\stackrel{\$}{\leftarrow} \mathbb{F}$ | [Master Secret Key](#master-secret-key) | +||||| +| Master Secret Keys | $\nskm$ | $\text{poseidon2}(\text{``az\_nsk\_m''}, \sk)$ | [Master Nullifier Secret Key](#master-nullifier-secret-key) | +| | $\ovskm$ | $\text{poseidon2}(\text{``az\_ovsk\_m''}, \sk)$ | [Master Outgoing Viewing Secret Key](#master-outgoing-viewing-secret-key) | +| | $\ivskm$ | $\text{poseidon2}(\text{``az\_ivsk\_m''}, \sk)$ | [Master Incoming Viewing Secret Key](#master-incoming-viewing-secret-key) | +| | $\tskm$ | $\text{poseidon2}(\text{``az\_tsk\_m''}, \sk)$ | [Master Tagging Secret Key](#master-tagging-secret-key) | +||||| +| Master Public Keys | $\Npkm$ | $\nskm \cdot G$ | [Master Nullifier Public Key](#master-nullifier-public-key) | +| | $\Ovpkm$ | $\ovskm \cdot G$ | [Master Outgoing Viewing Public Key](#master-outgoing-viewing-public-key) | +| | $\Ivpkm$ | $\ivskm \cdot G$ | [Master Incoming Viewing Public Key](#master-incoming-viewing-public-key) | +| | $\Tpkm$ | $\tskm \cdot G$ | [Master Tagging Public Key](#master-tagging-public-key) | +|||| +| Hardened App-Siloed Secret Keys | $\nskapp$ | $\text{poseidon2}(\text{``az\_nsk\_app''}, \text{app\_address}, \nskm)$ | [Hardened, App-siloed Nullifier Secret Key](#app-siloed-nullifier-secret-key) | +| | $\ovskapp$ | $\text{poseidon2}(\text{``az\_ovsk\_app''}, \text{app\_address}, \ovskm)$ | [Hardened, App-siloed Outgoing Viewing Secret Key](#app-siloed-outgoing-viewing-secret-key) | +||||| +| Other App-siloed Keys| $\Nkapp$ | $\text{poseidon2}(\text{``az\_nk\_app''}, \nskapp)$ | [App-siloed Nullifier Key](#app-siloed-nullifier-key) | + ## Colour Key - $\color{green}{green}$ = Publicly shareable information. @@ -105,339 +133,262 @@ $$ +:::Danger +Diagram is out of date vs the content on this page +::: + ![Alt text](images/addresses-and-keys/image-5.png) The red boxes are uncertainties, which are explained later in this doc. -## Master Keys +## Preliminaries -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. - - -| Key | Derivation | Name | Where? | Comments | -|---|---|---|---|---| -$\sk$ | $\stackrel{\$}{\leftarrow} \mathbb{F}$ | secret key | TEE/ PXE | A seed secret from which all these other keys may be derived. For future reference (in case we modify the schemes), this $\sk$ doesn't need to enter a circuit if all keys can be provably linked/tethered to some fixed public key/address. | -$\nskm$ | h(0x01, $\sk$) | nullifier secret key | PXE, K | Gives developers the option of using a secret key to derive their apps' nullifiers. (Not all nullifiers require a secret key, e.g. plume nullifiers). | -$\tskm$ | h(0x02, $\sk$) | tagging secret key | PXE* | The "tagging" key pair can be used to flag "this ciphertext is for you", without requiring decryption. This key exists merely as an optimization. We might choose to do away with it, in favour of using $\ivskm$. | -$\ivskm$ | h(0x03, $\sk$) | incoming viewing secret key | PXE* | The owner of this secret key can derive ephemeral symmetric encryption keys, to decrypt ciphertexts which _have been sent to them_ (i.e. "incoming" data from the pov of the recipient). | -$\ovskm$ | h(0x04, $\sk$) | outgoing viewing secret key | PXE* | The owner of this secret key can derive ephemeral symmetric encryption keys, to decrypt ciphertexts which _they have sent_ (i.e. "outgoing" data from the pov of the sender (and of the recipient, since they're the same person in this case)). This is useful if the user's DB is wiped, and they need to sync from scratch (starting with only $\sk$). | -|||||| -$\Npkm$ | $\nskm \cdot G$ | nullifier public key | | Only included so that other people can derive the user's address from some public information, in such a way that it's tied to the user's $\nskm$. -$\Tpkm$ | $\tskm \cdot G$ | tagging public key | | The "tagging" key pair can be used to flag "this ciphertext is for you", without requiring decryption. | -$\Ovpkm$ | $\ovskm \cdot G$ | outgoing viewing public key | | Only included so that other people can derive the user's address from some public information, in such a way that it's tied to the user's $\ovskm$. | - -> \*These keys could also be safely passed into the Kernel circuit, but there's no immediately obvious use, so "K" has been omitted, to make design intentions clearer. - -## Deriving siloed keys - -### Nullifier keys +$\mathbb{F}_r$ denotes the AltBN254 scalar field (i.e. the Grumpkin base field). - -| Key | Derivation | Name | Where? | Comments | -|---|---|---|---|---| -$\nskapp$ | $h(\nskm, \text{app\_address})$ | app-siloed nullifier secret key | PXE, K, App | Hardened, so only derivable by the owner of the master nullifier secret key. Hardened so as to enable the $\nskapp$ to be passed into an app circuit (without the threat of $\nskm$ being reverse-derivable). Only when a public key needs to be derivable by the general public is a normal (non-hardened) key used.
Deviates from 'conventional' hardened BIP-32-style derivation significantly, to reduce complexity and as an optimization. Such a deviation would need to be validated as secure. | -$\Nkapp$ | $h(\nskapp)$ | Shareable nullifier key | PXE, K, T3P, App| 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$ can be used in a nullifier's preimage, rather than $\nskapp$ in such cases, to enable said 3rd party to brute-force identify nullifications.
Note: this would not enable a 3rd party to view the contents of any notes; knowledge of the $\ivskapp$ / $\ovskapp$ would be needed for that.
Note: this is not a "public" key, since it must not be shared with the public. | - -See the appendix for an alternative derivation suggestion. - -### Incoming viewing keys +$\mathbb{F}_q$ denotes the AltBN254 base field (i.e. the Grumpkin scalar field). -The protocol does not support derivation of app-siloed incoming viewing keys. +Let $\mathbb{G}_{\text{Grumpkin}}$ be the Grumpkin elliptic curve group ($E(\mathbb{F}_r)$). -### Outgoing viewing keys - -An app-siloed outgoing viewing secret key is derived as a hardened child of the master outgoing viewing secret key. - - -| Key | Derivation | Name | Where? | Comments | -|---|---|---|---|---| -$\ovskapp$ | $h(\ovskm, \text{app\_address})$ | +Let $G \in \mathbb{G}_{\text{Grumpkin}}$ be a generator point for the public key cryptography outlined below. TODO: decide on how to generate this point. -This is a stripped-back non-hardened derivation. Such a hardened $\ovskapp$ may enter the app circuit, but $\ovskm$ must not, so the derivation of $\ovskapp$ must only be done in a trusted precompile contract. +Elliptic curve operators $+$ and $\cdot$ are used to denote addition and scalar multiplication, respectively. -### Internal incoming viewing keys +$\text{poseidon2}: \mathbb{F}_r^t \rightarrow \mathbb{F}$ is the Poseidon2 hash function (and $t$ can take values as per the [Poseidon2 spec](https://eprint.iacr.org/2023/323.pdf)). -Derivation of internal incoming viewing keys is equivalent to that of outgoing viewing keys. +Note that $q > r$. Below, we'll often define secret keys as an element of $\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(\mathbb{F}_r)$ whose affine points are of the form $\mathbb{F}_r \times \mathbb{F}_r$). Strictly speaking, such scalars in Grumpkin scalar multiplication should be in $\mathbb{F}_q$. +A potential consequence of using elements of $\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 $\frac{2(q - r)}{q}$ from uniform. It turns out that $\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. -## Handshaking for tag-hopping +## Key Derivation -Deriving a sequence of tags for tag-hopping. +### Derive Master Secret Key from Secret Key -### Deriving a sequence of tags between Alice and Bob across all apps - -For Bob to derive a shared secret for Alice: - - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\esk_{hs}$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key, for handshaking | $hs$ = handshake. -$\Epk_{hs}$ | $\esk_{hs} \cdot G$ | Ephemeral public key, for handshaking | -$\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}$ | $\esk_{hs} \cdot \Ivpkm$ | Shared secret, for tagging | Here, we're illustrating the derivation of a shared secret (for tagging) using _master_ keys. - -Having derived a Shared Secret, Bob can now share it with Alice as follows: - - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\Taghs$ | $\esk_{hs} \cdot \Tpkm$ | Handshake message identification tag | Note: the tagging public key $\Tpkm$ exists as an optimization, seeking to make brute-force message identification as fast as possible. In many cases, handshakes can be performed offchain via traditional web2 means, but in the case of on-chain handshakes, we have no preferred alternative over simply brute-force attempting to reconcile every 'Handshake message identification tag'. Note: this optimization reduces the recipient's work by 1 cpu-friendly hash per message (at the cost of 255-bits to broadcast a compressed encoding of $\Taghs$). We'll need to decide whether this is the right speed/communication trade-off. | -$\payload$ | [$\Taghs$, $\Epk_{hs}$] | Payload | This can be broadcast via L1.
Curve points can be compressed in the payload. | - -Alice can identify she is the indended the handshake recipient as follows: +$$ +\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} +$$ - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\Taghs$ | $\tskm \cdot \Epk_{hs}$ | Handshake message identification tag | Alice can extract $\Taghs$ and $\Epk_{hs}$ from the $\payload$ and perform this scalar multiplication on _every_ handshake message. If the computed $\Taghs$ value matches that of the $\payload$, then the message is indented for Alice.
Clearly, handshake transactions will need to be identifiable as such (to save Alice time), e.g. by revealing the contract address of some canonical handshaking contract alongside the $\payload$.
Recall: this step is merely an optimization, to enable Alice to do a single scalar multiplication before moving on (in cases where she is not the intended recipient). | +> Note: Here, $\text{poseidon2}$ is assumed to be a pseudo-random function. -If Alice successfully identifies that she is the indended the handshake recipient, she can proceed with deriving the shared secret (for tagging) as follows: +### Derive Hardened App-siloed Secret Key - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}$ | $\ivskm \cdot \Epk_{hs}$ | Shared secret, for tagging | | +$$ +\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} +$$ -A sequence of tags can then be derived by both Alice and Bob as: +> Note: Here, $\text{poseidon2}$ is assumed to be a pseudo-random function. - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\tagg_{m,i}^{Bob \rightarrow Alice}$ | $h(\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}, i)$ | The i-th tag in the sequence. | | +> Note: this deviates significantly from the 'conventional' [BIP-32 style method for deriving a "hardened child secret key"](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#private-parent-key--private-child-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. -This tag can be used as the basis for note retreival schemes. Each time Bob sends Alice a $\ciphertext$, he can attach the next unused $\tagg_{m,i}^{Bob \rightarrow Alice}$ in the sequence. Alice - who is also able to derive the next $\tagg_{m,i}^{Bob \rightarrow Alice}$ in the sequence - can make privacy-preserving calls to a server, requesting the $\ciphertext$ associated with a particular $\tagg_{m,i}^{Bob \rightarrow Alice}$. +### Derive Public Key (from Secret Key) -> The colour key isn't quite clear for $\tagg_{m,i}^{Bob \rightarrow Alice}$. It will be a publicly-broadcast piece of information, but no one should learn that it relates to Bob nor Alice (except perhaps some trusted 3rd party whom Alice has entrusted with her $\ivskm$). +$$ +\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 -### Deriving a sequence of tags from Bob to himself across all apps +A seed secret from which all of a user's other keys may be derived. The $\seed$ can live on an offline device, such as a hardware wallet. -The benefit of Bob deriving a sequence of tags for himself, is that he can re-sync his _outgoing_ transaction data more quickly, if he ever needs to in future. +$$\seed \stackrel{\$}{\leftarrow} \mathbb{F}_r$$ -This can be done by either: +## Master Secret Key -- Copying the approach used to derive a sequence of tags between Bob and Alice (but this time do it between Bob and Bob, and use Bob's outgoing keys). -- Generating a very basic sequence of tags $\tagg_{app, i}^{Bob \rightarrow Bob} = h(\ovskapp, i)$ (at the app level) and $\tagg_{m, i}^{Bob \rightarrow Bob} = h(\ovskm, i)$. - - Note: In the case of deriving app-specific sequences of tags, Bob might wish to also encrypt the app*address as a ciphertext header (and attach a master tag $\tagg*{m, i}^{Bob \rightarrow Bob}$), to remind himself of the apps that he should derive tags _for_. +This $\sk$ must never enter a circuit. A user or wallet may wish to derive this $\sk$ from a cold wallet [$\seed$](#seed). -## Deriving diversified public keys +$$\sk \stackrel{\$}{\leftarrow} \mathbb{F}_r$$ -A diversified public key can be derived from Alice's keys, to enhance Alice's transaction privacy. If Alice's counterparties' databases are compromised, it enables Alice to retain privacy from such leakages. Diversified public keys are used for generating diversified addresses. +> Note: Often $\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 $\mathbb{F}_r$, since a truncated hash output could be non-uniformly distributed in $\mathbb{F}_r$. +> For example, if the hardware wallet only supports sha256, then it would not be acceptable to compute $\sk$ as $\text{sha256}(\seed) \mod r$, since the resulting output (of reducing a 256-bit number modulo $r$) would be biased towards smaller values in $\mathbb{F_r}$. More uniformity might be achieved by instead computing $\sk$ as $( \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 $\mathbb{F_r}$. +> This note is informal, and expert advice should be sought before adopting this approach. -Basically, Alice must personally derive and provide Bob and Charlie with random-looking addresses (for Alice). Because Alice is the one deriving these Diversified Addresses (they can _only_ be derived by Alice), if Bob and Charlie chose to later collude, they would not be able to convince each-other that they'd interacted with Alice. +## Nullifier Keys -This is not to be confused with 'Stealth Addresses', which 'flip' who derives: Bob and Charlie would each derive a random-looking Stealth Address for Alice. Alice would then discover her new Stealth Addresses through decryption. +[App-siloed Nullifier Keys](#app-siloed-nullifier-key) 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. -> All of the key information below is Alice's +> 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. -Alice derives a 'diversified' incoming viewing public key, and sends it to Bob: +### Master Nullifier Secret Key - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\d$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ |diversifier | -$\Gd$ | $\d \cdot G$ | diversified generator | -$\Ivpkmd$ | $\ivskm \cdot \Gd$ | Diversified incoming viewing public key | +$$ +\begin{aligned} +& \nskm \in \mathbb{F}_r \\ +& \nskm = \text{derive\_master\_secret\_key\_from\_secret\_key}(\text{``az\_nsk\_m''}, \seed) +\end{aligned} +$$ -> Notice: when $\d = 1$, $\Ivpkmd = \Ivpkm$. Often, it will be unncessary to diversify the below data, but we keep $\d$ around for the most generality. +> See [`derive_master_secret_key_from_secret_key`](#derive-master-secret-key-from-secret-key). -## Deriving stealth public keys +> $\nskm$ MUST NOT enter an app circuit. +> $\nskm$ MAY enter the kernel circuit. -> All of the key information below is Alice's +### Master Nullifier Public Key -Stealth Public Keys are used for generating Stealth Addresses. For Bob to derive a Stealth Address for Alice, Bob derives: +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$), in such a way that $\nskm$ is tied to the user's address. - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\d$ | Given by Alice | (Diversifier) | Remember, in most cases, $\d=1$ is sufficient. -$\Gd$ | $\d \cdot G$ | (Diversified) generator | Remember, when $\d = 1$, $\Gd = G$. -$\esk$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | -$\Epkd$ | $\esk \cdot \Gd$ | (Diversified) Ephemeral public key | -$\sharedsecret_{m, stealth}$ | $\esk \cdot \Ivpkmd$ | Shared secret | -$\hstealth$ | h("?", $\sharedsecret_{m, stealth}$) | Stealth key | -$\Ivpkmdstealth$ | $\hstealth \cdot \Gd + \Ivpkmd$ | (Diversified) Stealth viewing public key | +$$ +\begin{aligned} +& \Npkm \in \mathbb{G}_{\text{Grumpkin}} \\ +& \Npkm = \text{derive\_public\_key}(\nskm) +\end{aligned} +$$ -Having derived a Stealth Address for Alice, Bob can now share it with Alice as follows: +> See [`derive_public_key`](#derive-public-key-from-secret-key). - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\tagg_{m, i}^{Bob \rightarrow Alice}$ | See earlier in this doc. | | Derive the next tag in the $Bob\rightarrow Alice$ sequence.
Note: we illustrate with a _master_ tag sequence, but an app-specific tag sequence could also be used (in which case an encryption of the app_address in a ciphertext header wouldn't be required; it could just be inferred from the tag used). | -$\sharedsecret_{m,header}$ | $\esk \cdot \Ivpkm$ | | TODO: we might need to use a different ephemeral keypair from the one used to derive the stealth address. | -$\hmencheader$ | h("?", $\sharedsecret_{m,header}$) | -$\ciphertextheader$ | $enc^{\Ivpkm}_{\hmencheader}$(app\_address) | | TODO: diversify this? | -$\payload$ | [$\tagg_{m, i}^{Bob \rightarrow Alice}$, $\ciphertextheader$, $\Epkd$] | +### App-siloed Nullifier Secret Key -Alice can learn about her new Stealth Address as follows. First, she would identify the transaction has intended for her, either by observing $\tagg_{m, i}^{Bob \rightarrow Alice}$ on-chain herself (and then downloading the rest of the payload which accompanies the tag), or by making a privacy-preserving request to a server, to retrieve the payload which accompanies the tag. Assuming the $\payload$ has been identified as Alice's, we proceed: +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$ to be passed into an app circuit, without the threat of $\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. - -| Thing | Derivation | Name | -|---|---|---| -$\sharedsecret_{m,header}$ | $\ivskm \cdot \Epkd$ | Shared secret, for encrypting the ciphertext header | -$\hmencheader$ | h("?", $\sharedsecret_{m,header}$) | Incoming encryption key | -app_address | $decrypt_{\hmencheader}^{\ivskm}(\ciphertextheader)$ | -$\ivskapp$ | See derivations above. Use the decrypted app_address. | app-specific incoming viewing secret key | -$\sharedsecret_{app, stealth}$ | $\ivskapp \cdot \Epkd$ | -$\hstealth$ | h("?", $\sharedsecret_{app, stealth}$) | -$\ivskappstealth$ | $\hstealth + \ivskapp$ | -$\Ivpkappdstealth$ | $\ivskappstealth \cdot \Gd$ | -$\Pkappdstealth$ | $\Ivpkappdstealth$ | Alias: "Alice's Stealth Public Key" | +$$ +\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`](#derive-hardened-app-siloed-secret-key). -## Deriving a nullifier within an app contract +### App-siloed Nullifier Key -Let's assume a developer wants a nullifier of a note to be derived as: +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$ can be used in a nullifier's preimage, rather than $\nskapp$ in such cases, to enable said 3rd party to brute-force identify nullifications. -`nullifier = h(note_hash, nullifier_key);` +> 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$ / $\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. -... where the `nullifier_key` ($\Nkapp$) belongs to the 'owner' of the note, and where the 'owner' is some $\address$. +$$ +\begin{aligned} +& \Nkapp \in \mathbb{F}_r \\ +& \Nkapp = \text{poseidon2}(\text{``az\_nk\_app''}, \nskapp) +\end{aligned} +$$ -Here's how an app circuit could constrain the nullifier key to be correct: +:::warning TODO +We could also have derived $\Nkapp$ as $\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$ to the app circuit without having to add key derivation logic to the kernel circuit. +::: - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\Nkapp$ | h($\nskapp$) | App-siloed nullifier key | Recall an important point: the app circuit MUST NOT be given $\nskm$. Indeed, $\nskapp$ is derived (see earlier) as a _hardened_ child of $\nskm$, to prevent $\nskm$ from being reverse-derived by a malicious circuit. The linking of $\nskapp$ to $\nskm$ is deferred to the kernel circuit (which can be trusted moreso than an app).
Recall also: $\Nkapp$ is used (instead of $\nskapp$) solely as a way of giving the user the option of sharing $\Nkapp$ with a trusted 3rd party, to give them the ability to view when a note has been nullified (although I'm not sure how useful this is, given that it would require brute-force effort from that party to determine which note hash has been nullified, with very little additional information).
| -`nullifier` | h(note_hash, $\Nkapp$) | -$\address$ | h($\Npkm$, $\Tpkm$, $\Ivpkm$, $\Ovpkm$, $\codehash$) | address | Proof that the $\Npkm$ belongs to the note owner's $\address$.
This isn't an optimized derivation. It's just one that works. | +## Outgoing Viewing Keys -The app circuit exposes, as public inputs, a "nullifier key validation request": +[App-siloed Outgoing Viewing Secret Keys](#app-siloed-outgoing-viewing-secret-key) 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$). - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -`nullifier_key_validation_request` | app_address: app_address,
hardened_child_sk: $\nskapp$,
claimed_parent_pk: $\Npkm$ | +### Master Outgoing Viewing Secret Key -The kernel circuit can then validate the request (having been given $\nskm$ as a private input to the kernel circuit): +$$ +\begin{aligned} +& \ovskm \in \mathbb{F}_r \\ +& \ovskm = \text{derive\_master\_secret\_key\_from\_seed}(\text{``az\_ovsk\_m''}, \seed) +\end{aligned} +$$ - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\nskapp$ | $h(\nskm, \text{app\_address})$ | -$\Npkm$ | $\nskm \cdot G$ | nullifier public key | -| | | | Copy-constrain $\nskm$ with $\nskm$. | +> See [`derive_master_secret_key_from_seed`](#derive-master-secret-key-from-seed). -If the kernel circuit succeeds in these calculations, then the $\Nkapp$ has been validated as having a known secret key, and belonging to the $\address$. +> $\ovskm$ MUST NOT enter an app circuit. +> $\ovskm$ MAY enter the kernel circuit. -![Alt text](images/addresses-and-keys/image.png) +### Master Outgoing Viewing Public Key -See the [Appendix](#appendix) for an alternative approach. +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$), in such a way that $\ovskm$ is tied to the user's address. -## Encrypt and tag an incoming message +$$ +\begin{aligned} +& \Ovpkm \in \mathbb{G}_{\text{Grumpkin}} \\ +& \Ovpkm = \text{derive\_public\_key}(\ovskm) +\end{aligned} +$$ -Bob wants to send Alice a private message, e.g. the contents of a note, which we'll refer to as the $\plaintext$. Bob and Alice are using a "tag hopping" scheme to help with note discovery. Let's assume they've already handshaked to establish a shared secret $\sharedsecret_{m,tagging}^{Bob \rightarrow Alice}$, from which a sequence of tags $\tagg_{m,i}^{Bob \rightarrow Alice}$ can be derived. +> See [`derive_public_key`](#derive-public-key-from-secret-key). - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\d$ | Given by Alice | (Diversifier) | Remember, in most cases, $\d=1$ is sufficient. -$\Gd$ | $\d \cdot G$ | (Diversified) generator | Remember, when $\d = 1$, $\Gd = G$. -$\eskheader$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | -$\Epkdheader$ | $\eskheader \cdot \Gd$ | (Diversified) Ephemeral public key | -$\sharedsecret_{m,header}$ | $\esk_{header} \cdot \Ivpkm$ | Shared secret, for ciphertext header encryption | TODO: can we use the same ephemeral keypair for both the ciphertext header and the ciphertext?
TODO: diversify the $\Ivpkm$? | -$\hmencheader$ | h("?", $\sharedsecret_{m,header}$) | Ciphertext header encryption key | -$\ciphertextheader$ | $enc^{\Ivpkm}_{\hmencheader}$(app\_address) | Ciphertext header | | -||||| -$\esk$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | -$\Epkd$ | $\esk \cdot \Gd$ | (Diversified) Ephemeral public key | -$\sharedsecret_{app,enc}$ | $\esk \cdot \Ivpkappdstealth$ | Shared secret, for ciphertext encryption | -$\happenc$ | h("?", $\sharedsecret_{app,enc}$) | Incoming data encryption key | -$\ciphertext$ | $enc^{\Ivpkappdstealth}_{\happenc}(\plaintext)$ | Ciphertext | -$\payload$ | [$\tagg_{m, i}^{Bob \rightarrow Alice}$, $\ciphertextheader$, $\ciphertext$, $\Epkdheader$, $\Epkd$] | Payload | +### 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$ to be passed into an app circuit, without the threat of $\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. -Alice can learn about her new $\payload$ as follows. First, she would identify the transaction has intended for her, either by observing $\tagg_{m, i}^{Bob \rightarrow Alice}$ on-chain herself (and then downloading the rest of the payload which accompanies the tag), or by making a privacy-preserving request to a server, to retrieve the payload which accompanies the tag. Assuming the $\payload$ has been identified as Alice's, and retrieved by Alice, we proceed. +$$ +\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} +$$ -> Given that the tag in this illustration was derived from Alice's master key, the tag itself doesn't convey which app_address to use, to derive the correct app-siloed incoming viewing secret key that would enable decryption of the ciphertext. So first Alice needs to decrypt the $\ciphertextheader$ using her master key: +> See [`derive_hardened_app_siloed_secret_key`](#derive-hardened-app-siloed-secret-key). - -| Thing | Derivation | Name | -|---|---|---| -$\sharedsecret_{m,header}$ | $\ivskm \cdot \Epkdheader$ | Shared secret, for encrypting the ciphertext header | -$\hmencheader$ | h("?", $\sharedsecret_{m,header}$) | Incoming encryption key | -app_address | $decrypt_{\hmencheader}^{\ivskm}(\ciphertextheader)$ | App address | -|||| -$\ivskappstealth$ | See derivations above. Use the decrypted app_address. | App-specific incoming viewing secret key | -$\sharedsecret_{app, enc}$ | $\ivskappstealth \cdot \Epkd$ | Shared secret, for ciphertext encryption | -$\happenc$ | h("?", $\sharedsecret_{app, enc}$) | Ciphertext encryption key | -$\plaintext$ | $decrypt_{\happenc}^{\ivskappstealth}(\ciphertext)$ | Plaintext | +## Incoming Viewing Keys -## Encrypt and tag an outgoing message +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. -Bob wants to send himself a private message (e.g. a record of the outgoing notes that he's created for other people) which we'll refer to as the $\plaintext$. Let's assume Bob has derived a sequence of tags $\tagg_{m,i}^{Bob \rightarrow Alice}$ for himself (see earlier). +### Master Incoming Viewing Secret Key -> Note: this illustration uses _master_ keys for tags, rather than app-specific keys for tags. App-specific keys for tags could be used instead, in which case a 'ciphertext header' wouldn't be needed for the 'app_address', since the address could be inferred from the tag. +$$ +\begin{aligned} +& \ivskm \in \mathbb{F}_r \\ +& \ivskm = \text{derive\_master\_secret\_key\_from\_seed}(\text{``az\_ivsk\_m''}, \seed) +\end{aligned} +$$ -> Note: rather than copying the 'shared secret' approach of Bob sending to Alice, we can cut a corner (because Bob is the sender and recipient, and so knows his own secrets). +> See [`derive_master_secret_key_from_seed`](#derive-master-secret-key-from-seed). -> Note: if Bob has sent a private message to Alice, and he also wants to send himself a corresponding message: -> -> - he can likely re-use the ephemeral keypairs for himself. -> - he can include $\esk$ in the plaintext that he sends to himself, as a way of reducing the size of his $\ciphertext$ (since the $\esk$ will enable him to access all the information in the ciphertext that was sent to Alice). +> $\ivskm$ MUST NOT enter an app circuit. - +### Master Incoming Viewing Public Key -> Note: the violet symbols should actually be orange here. +The Master Incoming Viewing Public Key can be used by a sender to encrypt messages and notes to the owner of this key. - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\d$ | Given by Alice | (Diversifier) | Remember, in most cases, $\d=1$ is sufficient. -$\Gd$ | $\d \cdot G$ | (Diversified) generator | Remember, when $\d = 1$, $\Gd = G$. -$\eskheader$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | -$\Epkdheader$ | $\eskheader \cdot \Gd$ | (Diversified) Ephemeral public key | -$\hmencheader$ | h("?", $\ovskm$, $\Epkdheader$) | Header encryption key | This uses a master secret key $\ovskm$, which MUST NOT be given to an app nor to an app circuit. However, it can still be given to a trusted precompile, which can handle this derivation securely. | -$\ciphertextheader$ | $enc_{\hmencheader}$(app\_address) | Ciphertext header encryption key | | -||||| -$\esk$ | $\stackrel{rand}{\leftarrow} \mathbb{F}$ | ephemeral secret key | -$\Epkd$ | $\esk \cdot \Gd$ | (Diversified) Ephemeral public key | -$\happenc$ | h("?", $\ovskapp$, $\Epkd$) | Outgoing data encryption key | Since $\ovskapp$ is a _hardened_ app-siloed secret key, it may be safely given to the dapp or passed into the app's circuit. | -$\ciphertext$ | $enc_{\happenc}(\plaintext)$ | Ciphertext | -$\payload$ | [$\tagg_{m, i}^{Bob \rightarrow Bob}$, $\ciphertextheader$, $\ciphertext$, $\Epkdheader$, $\Epkd$] | Payload | +$$ +\begin{aligned} +& \Ivpkm \in \mathbb{G}_{\text{Grumpkin}} \\ +& \Ivpkm = \text{derive\_public\_key}(\ivskm) +\end{aligned} +$$ -Alice can learn about her new $\payload$ as follows. First, she would identify the transaction has intended for her, either by observing $\tagg_{m, i}^{Bob \rightarrow Alice}$ on-chain herself (and then downloading the rest of the payload which accompanies the tag), or by making a privacy-preserving request to a server, to retrieve the payload which accompanies the tag. Assuming the $\payload$ has been identified as Alice's, and retrieved by Alice, we proceed. +> See [`derive_public_key`](#derive-public-key-from-secret-key). -> Given that the tag in this illustration was derived from Alice's master key, the tag itself doesn't convey which app_address to use, to derive the correct app-siloed incoming viewing secret key that would enable decryption of the ciphertext. So first Alice needs to decrypt the $\ciphertextheader$ using her master key: +### App-siloed Incoming Viewing Secret Key - -| Thing | Derivation | Name | -|---|---|---| -$\hmencheader$ | h("?", $\ovskm$, $\Epkdheader$) | | -app_address | $decrypt_{\hmencheader}(\ciphertextheader)$ | -|||| -$\ovskapp$ | See derivations above. Use the decrypted app_address. | | -$\happenc$ | h("?", $\ovskm$, $\Epkd$) | -$\plaintext$ | $decrypt_{\happenc}(\ciphertext)$ | +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. -### Doing this inside an app circuit +## Tagging Keys -Here's how an app circuit could constrain the app-siloed outgoing viewing secret key ($\ovskapp$) to be correct: +The "tagging" key pair can be used to flag "this ciphertext is for you", without requiring decryption. -The app circuit exposes, as public inputs, an "outgoing viewing key validation request": +### Master Tagging Secret Key - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -`outgoing_viewing_key_validation_request` | app_address: app_address,
hardened_child_sk: $\nskapp$,
claimed_parent_pk: $\Npkm$ | +$$ +\begin{aligned} +& \tskm \in \mathbb{F}_r \\ +& \tskm = \text{derive\_master\_secret\_key\_from\_seed}(\text{``az\_tvsk\_m''}, \seed) +\end{aligned} +$$ -The kernel circuit can then validate the request (having been given $\ovskm$ as a private input to the kernel circuit): +> See [`derive_master_secret_key_from_seed`](#derive-master-secret-key-from-seed). - -| Thing | Derivation | Name | Comments | -|---|---|---|---| -$\ovskapp$ | $h(\ovskm, \text{app\_address})$ | -$\Ovpkm$ | $\ovskm \cdot G$ | Outgoing viewing public key | -| | | | Copy-constrain $\ovskm$ with $\ovskm$. | +> $\ivskm$ MUST NOT enter an app circuit. -If the kernel circuit succeeds in these calculations, then the $\ovskapp$ has been validated as the correct app-siled secret key for $\Ovpkm$. +### Master Tagging Public Key -## Encrypt and tag an internal incoming message +$$ +\begin{aligned} +& \Tpkm \in \mathbb{G}_{\text{Grumpkin}} \\ +& \Tpkm = \text{derive\_public\_key}(\tskm) +\end{aligned} +$$ -Internal incoming messages are handled analogously to outgoing messages, since in both cases the sender is the same as the recipient, who has access to the secret keys when encrypting and tagging the message. +> See [`derive_public_key`](#derive-public-key-from-secret-key). ## Acknowledgements diff --git a/yellow-paper/docs/circuits/private-function.md b/yellow-paper/docs/circuits/private-function.md index 22cfa9b900e4..5e1de6d992b0 100644 --- a/yellow-paper/docs/circuits/private-function.md +++ b/yellow-paper/docs/circuits/private-function.md @@ -167,3 +167,7 @@ After generating a proof for a private function circuit, that proof (and associa | `public_data_tree_root` | `field` | Root of the public data tree. | | `archive_tree_root` | `field` | Root of the state roots tree archived at the block prior to when the transaction was assembled. | | `global_variables_hash` | `field` | Hash of the previous global variables. | + + + + diff --git a/yellow-paper/docs/constants.md b/yellow-paper/docs/constants.md index 08fce15758f8..969a98ffae47 100644 --- a/yellow-paper/docs/constants.md +++ b/yellow-paper/docs/constants.md @@ -124,7 +124,3 @@ These verbose names are designed to get more specific from left to right. ## Precompile Constants See the [precompiles](./addresses-and-keys/precompiles.md#constants) section. - -## Hashing constants - -See the [hashing] section. diff --git a/yellow-paper/docs/contract-deployment/classes.md b/yellow-paper/docs/contract-deployment/classes.md index 61192ccdd9c8..e431b9bdedba 100644 --- a/yellow-paper/docs/contract-deployment/classes.md +++ b/yellow-paper/docs/contract-deployment/classes.md @@ -32,20 +32,47 @@ Note that individual public functions are not first-class citizens in the protoc As for unconstrained functions, these are not used standalone within the protocol. They are either inlined within private functions, or called from a PXE as _getters_ for a contract. Calling from a private function to an unconstrained one in a different contract is forbidden, since the caller would have no guarantee of the code run by the callee. Considering this, unconstrained functions are not part of a contract class at the protocol level. -### Class Identifier +### `contract_class_id` Also known as `contract_class_id`, the Class Identifier is both a unique identifier and a commitment to the struct contents. It is computed as: -``` -private_function_leaves = private_functions.map(fn => pedersen([fn.function_selector as Field, fn.vk_hash], GENERATOR__FUNCTION_LEAF)) -private_functions_root = merkleize(private_function_leaves) -public_bytecode_commitment = calculate_commitment(packed_public_bytecode) -contract_class_id = pedersen([artifact_hash, private_functions_root, public_bytecode_commitment], GENERATOR__CLASS_IDENTIFIER_V1) + + + + +```rust +contract_class_id_crh( + artifact_hash: Field + private_functions: PrivateFunction[], + packed_public_bytecode: bytes[], +) -> Field { + let private_function_leaves: Field[] = private_functions.map(|f| private_function_leaf_crh(f)); + + // Illustrative function, not defined. TODO. + let private_function_tree_root: Field = merkleize(private_function_leaves); + + // Illustrative function, not defined. TODO. + let public_bytecode_commitment: Point = calculate_commitment(packed_public_bytecode); + + let contract_class_id = poseidon2( + be_string_to_field("az_contract_class_id"), + + artifact_hash, + private_function_tree_root, + public_bytecode_commitment.x, + public_bytecode_commitment.y, + ); + + contract_class_id +} ``` -Private Functions are sorted in ascending order by their selector and then hashed into Function Leaves before being merkleized into a tree of height [`FUNCTION_TREE_HEIGHT`](../constants.md#tree-constants). Empty leaves have value `0`. A poseidon hash is used. The AVM public bytecode commitment is calculated as [defined in the Public VM section](../public-vm/bytecode-validation-circuit.md#committed-representation). +> See below for `private_function_leaf_crh`. +> Private Functions are sorted in ascending order by their selector, and then hashed into Function Leaves, before being merkleized into a tree of height [`PRIVATE_FUNCTION_TREE_HEIGHT`](../constants.md#tree-constants). +> Empty leaves have value `0`. +> The AVM public bytecode commitment is calculated as [defined in the Public VM section](../public-vm/bytecode-validation-circuit.md#committed-representation). -### Private Function +### `PrivateFunction` The structure of each private function within the protocol is the following: @@ -59,26 +86,70 @@ Note the lack of visibility modifiers. Internal functions are specified as a mac Also note the lack of commitment to the function compilation artifact. Even though a commitment to a function is required so that the PXE can verify the execution of correct unconstrained Brillig code embedded within private functions, this is handled entirely out of protocol. As such, PXEs are expected to verify it against the `artifact_hash` in the containing contract class. +#### Private Function Leaf Hash + + + +```rust +private_function_leaf_crh( + f: PrivateFunction +) -> Field { + let private_function_leaf = poseidon2( + be_string_to_field("az_private_function_leaf"), + + be_bits_to_field(f.function_selector), + f.vk_hash + ); + + private_function_leaf +} +``` + ### Artifact Hash Even though not enforced by the protocol, it is suggested for the `artifact_hash` to follow this general structure, in order to be compatible with the definition of the [`broadcast` function below](#broadcast). -``` -private_functions_artifact_leaves = artifact.private_functions.map(fn => - sha256(fn.selector, fn.metadata_hash, sha256(fn.private_bytecode)) -) -private_functions_artifact_tree_root = merkleize(private_functions_artifact_leaves) + -unconstrained_functions_artifact_leaves = artifact.unconstrained_functions.map(fn => - sha256(fn.selector, fn.metadata_hash, sha256(fn.unconstrained_bytecode)) -) -unconstrained_functions_artifact_tree_root = merkleize(unconstrained_functions_artifact_leaves) +```rust +artifact_crh( + artifact // This type is out of protocol, e.g. the format output by Nargo +) -> Field { -artifact_hash = sha256( - private_functions_artifact_tree_root, - unconstrained_functions_artifact_tree_root, - artifact_metadata_hash, -) + let private_functions_artifact_leaves: [u8; 32][] = artifact.private_functions.map(|f| + sha256( + be_string_to_bits("az_artifact_private_function_leaf"), + + f.selector, // 32-bits + f.metadata_hash, // 256-bits + sha256(f.private_bytecode) + ) + ); + let private_functions_artifact_tree_root: [u8; 32] = merkleize(private_functions_artifact_leaves); + + let unconstrained_functions_artifact_leaves: [u8; 32][] = artifact.unconstrained_functions.map(|f| + sha256( + be_string_to_bits("az_artifact_unconstrained_function_leaf"), + + f.selector, // 32-bits + f.metadata_hash, // 256-bits + sha256(f.unconstrained_bytecode) + ) + ); + let unconstrained_functions_artifact_tree_root: [u8; 32] = merkleize(unconstrained_functions_artifact_leaves); + + let artifact_hash_256_bit: [u8; 32] = sha256( + be_string_to_field("az_artifact"), + + private_functions_artifact_tree_root, // 256-bits + unconstrained_functions_artifact_tree_root, // 256-bits + artifact_metadata + ); + + let artifact_hash: Field = artifact_hash_256_bit % FIELD_MODULUS; + + artifact_hash +} ``` For the artifact hash merkleization and hashing is done using sha256, since it is computed and verified outside of circuits and does not need to be SNARK friendly, and then wrapped around the field's maximum value. Fields are left-padded with zeros to 256 bits before being hashed. Function leaves are sorted in ascending order before being merkleized, according to their function selectors. Note that a tree with dynamic height is built instead of having a tree with a fixed height, since the merkleization is done out of a circuit. @@ -89,16 +160,40 @@ Bytecode for private functions is a mix of ACIR and Brillig, whereas unconstrain The metadata hash for each function is suggested to be computed as the sha256 of all JSON-serialized fields in the function struct of the compilation artifact, except for bytecode and debug symbols. The metadata is JSON-serialized using no spaces, and sorting ascending all keys in objects before serializing them. -``` -function_metadata = omit(function, "bytecode", "debug_symbols") -function_metadata_hash = sha256(json_serialize(function_metadata)) + + +```rust +function_metadata_crh( + function // This type is out of protocol, e.g. the format output by Nargo +) -> [u8; 32] { + let function_metadata = omit(function, "bytecode", "debug_symbols"); + + let function_metadata_hash: [u8; 32] = sha256( + be_string_to_bits("az_function_metadata"), + + json_serialize(function_metadata) + ); + + function_metadata_hash +} ``` The artifact metadata stores all data that is not contained within the contract functions and is not debug specific. This includes the compiler version identifier, events interface, and name. Metadata is JSON-serialized in the same fashion as the function metadata. -``` -artifact_metadata = omit(artifact, "functions", "file_map") -artifact_metadata_hash = sha256(json_serialize(artifact_metadata)) +```rust +artifact_metadata_crh( + artifact // This type is out of protocol, e.g. the format output by Nargo +) -> [u8; 32] { + let artifact_metadata = omit(artifact, "functions", "file_map"); + + let artifact_metadata_hash: [u8; 32] = sha256( + be_string_to_bits("az_artifact_metadata"), + + json_serialize(artifact_metadata) + ); + + artifact_metadata_hash +} ``` ### Versioning @@ -124,23 +219,32 @@ The `register` function receives the artifact hash, private functions tree root, In pseudocode: -``` -function register( +```rust +fn register( artifact_hash: Field, private_functions_root: Field, public_bytecode_commitment: Point, packed_public_bytecode: Field[], -) - version = 1 +) { + assert(is_valid_packed_public_bytecode(packed_public_bytecode)); - assert is_valid_packed_public_bytecode(packed_public_bytecode) - computed_bytecode_commitment = calculate_commitment(packed_public_bytecode) - assert public_bytecode_commitment == computed_bytecode_commitment + let computed_bytecode_commitment: Point = calculate_commitment(packed_public_bytecode); - contract_class_id = pedersen([version, artifact_hash, private_functions_root, computed_bytecode_commitment], GENERATOR__CLASS_IDENTIFIER) + assert(public_bytecode_commitment == computed_bytecode_commitment); - emit_nullifier contract_class_id - emit_unencrypted_event ContractClassRegistered(contract_class_id, version, artifact_hash, private_functions_root, packed_public_bytecode) + let version: Field = 1; + let contract_class_id = contract_class_id_crh(version, artifact_hash, private_functions_root, bytecode_commitment); + + emit_nullifier(contract_class_id); + + emit_unencrypted_event(ContractClassRegistered::new( + contract_class_id, + version, + artifact_hash, + private_functions_root, + packed_public_bytecode + )); +} ``` Upon seeing a `ContractClassRegistered` event in a mined transaction, nodes are expected to store the contract class, so they can retrieve it when executing a public function for that class. Note that a class may be used for deploying a contract within the same transaction in which it is registered. @@ -159,8 +263,8 @@ The `ContractClassRegisterer` has an additional private `broadcast` functions th Broadcasted function artifacts that do not match with their corresponding `artifact_hash`, or that reference a `contract_class_id` that has not been broadcasted, can be safely discarded. -``` -function broadcast_private_function( +```rust +fn broadcast_private_function( contract_class_id: Field, artifact_metadata_hash: Field, unconstrained_functions_artifact_tree_root: Field, @@ -182,8 +286,8 @@ function broadcast_private_function( ) ``` -``` -function broadcast_unconstrained_function( +```rust +fn broadcast_unconstrained_function( contract_class_id: Field, artifact_metadata_hash: Field, private_functions_artifact_tree_root: Field, diff --git a/yellow-paper/docs/contract-deployment/instances.md b/yellow-paper/docs/contract-deployment/instances.md index 6d90f33b62d5..69e836924c55 100644 --- a/yellow-paper/docs/contract-deployment/instances.md +++ b/yellow-paper/docs/contract-deployment/instances.md @@ -26,6 +26,8 @@ The structure of a contract instance is defined as: | `portal_contract_address` | `EthereumAddress` | Optional address of the L1 portal contract. | | `public_keys_hash` | `Field` | Optional hash of the struct of public keys used for encryption and nullifying by this contract. | + + ### Versioning @@ -101,8 +103,10 @@ A new contract instance can be _Publicly Deployed_ by calling a `deploy` functio The pseudocode for the process described above is the following: -``` -function deploy ( + + +```rust +fn deploy ( salt: Field, contract_class_id: Field, initialization_hash: Field, @@ -110,18 +114,32 @@ function deploy ( public_keys_hash: Field, universal_deploy?: boolean, ) - assert nullifier_exists silo(contract_class_id, ContractClassRegisterer) - assert is_valid_eth_address(portal_contract_address) + let contract_class_registerer: Contract = ContractClassRegisterer::at(CONTRACT_CLASS_REGISTERER_ADDRESS); + + assert(nullifier_exists(silo(contract_class_id, contract_class_registerer.address))); - deployer = if universal_deploy then zero else msg_sender - version = 1 - address = compute_address(version, salt, deployer, contract_class_id, initialization_hash, portal_contract_address, public_keys_hash) + assert(is_valid_eth_address(portal_contract_address)); - emit_nullifier(address) + let deployer: Address = if universal_deploy { 0 } else { msg_sender }; + let version: Field = 1; - emit_unencrypted_event ContractInstanceDeployed(address, version, salt, contract_class_id, initialization_hash, portal_contract_address, public_keys_hash) + let address = address_crh( + version, + salt, + deployer, + contract_class_id, + initialization_hash, + portal_contract_address, + public_keys_hash + ); + + emit_nullifier(address); + + emit_unencrypted_event(ContractInstanceDeployed::new(address, version, salt, contract_class_id, initialization_hash, portal_contract_address, public_keys_hash)); ``` +> See [address](../addresses-and-keys/address.md) for `address_crh`. + Upon seeing a `ContractInstanceDeployed` event from the canonical `ContractInstanceDeployer` contract, nodes are expected to store the address and preimage, so they can verify executed code during public code execution as described in the next section. The `ContractInstanceDeployer` contract provides two implementations of the `deploy` function: a private and a public one. diff --git a/yellow-paper/docs/cryptography/hashing/hashing.md b/yellow-paper/docs/cryptography/hashing/hashing.md index e69de29bb2d1..a3e4cb5f5e73 100644 --- a/yellow-paper/docs/cryptography/hashing/hashing.md +++ b/yellow-paper/docs/cryptography/hashing/hashing.md @@ -0,0 +1,29 @@ +## Desirable properties + +There are two main properties we might desire from the various hash functions defined in aztec: + +### Collision Resistance + +Poseidon2 is the predominant hash function in the Aztec protocol. It is assumed to be collision resistant. + +#### Domain Separators + +To minimize the potential for collisions between distinct hashing contexts, all hashes are domain-separated by passing a `string` which describes the context of the hash. Each such string in this spec begins with the prefix `az_`, to domain-separate all hashes specifically to the Aztec protocol. + +> The strings provided in this spec are mere suggestions at this stage; we might find that the strings should be smaller bit-lengths, to reduce in-circuit constraints. + +In the case of using Poseidon2 for hashing (which is the case for most hashing in the Aztec protocol), the string is converted from a big-endian byte representation into a `Field` element, and passed as a first argument into the hash. In the case of using non-algebraic hash functions (such as sha256), the string is converted from a big-endian byte representation into bits, and passed as the first bits into the hash. These details are conveyed more clearly as pseudocode in the relevant sections of the spec. + +For some hashes there is further domain-separation. For example, [Merkle tree hashing](#merkle-trees) includes the [layer](../merkle-trees.md#layers) of the tree. + +### Pseudo-randomness + +Sometimes we desire the output of a hash function to be pseudo-random. Throughout the Aztec protocol, it is assumed that Poseidon2 can be used as a pseudo-random function. + +Pseudo-randomness is required in cases such as: + +- Fiat-Shamir challenge generation. +- Expanding a random seed to generate additional randomness. + - See the derivation of [master secret keys](../../addresses-and-keys/keys.md#master-keys). +- Deriving a nullifier, and siloing a nullifier. + - See [deriving a nullifier](../../addresses-and-keys/keys.md#deriving-a-nullifier-within-an-app-contract). diff --git a/yellow-paper/docs/cryptography/hashing/poseidon2.md b/yellow-paper/docs/cryptography/hashing/poseidon2.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/yellow-paper/docs/cryptography/merkle-trees.md b/yellow-paper/docs/cryptography/merkle-trees.md index e69de29bb2d1..61de0b56d9d5 100644 --- a/yellow-paper/docs/cryptography/merkle-trees.md +++ b/yellow-paper/docs/cryptography/merkle-trees.md @@ -0,0 +1,120 @@ +# Merkle Trees + + + +## Notation + +### Orientation + +A tree is always visualised as a "triangle" with its point at the top (the root) and its base at the bottom (the leaves). Like this: $\Delta$. + +Hopefully this gives a clear intuition whenever the terms "left", "right", "up", "down", "above", "below" are used when describing trees. + +### Arity + +Trees in Aztec are currently all binary Merkle trees (2-ary). + + + +### Height + +The `height` of a tree with $l$ leaves is $\lceil \log_2(l) \rceil$. + +### Layers + +The `layers` of a tree are an enumerated from `0`. The leaves are at layer `0`; the root is at layer [`height`](#height). + +### Levels + +Synonymous with [layers](#layers). + +### Rows + +Synonymous with [layers](#layers) and [levels](#levels). + +### Leaf Index + +The leaves of a tree are indexed from `0`. The first, [left-most](#orientation) leaf is at `leaf_index = 0`. + +### Node Index + +All nodes of the tree (including the leaves) can be indexed. The method of indexing might depend on the algorithm being applied to the tree. + +### Relationships + +The terms "parent", "child", "sibling", "ancestor", "descendant", are the well-known definitions. + +### Path + +The path from (or "of") a particular node is a vector of that node's ancestors. That is, the node's parent, then its parent's parent, and so on, all the way up to and including the root. + +### Sibling Path + +The sibling path of a particular node is, loosely, a vector of the siblings of the nodes in its [path](#path), except it also includes the node's sibling, and excludes the root (which has no sibling). +The first element in the sibling path is the node's sibling. Then, the node's parent's sibling, then its parent's parent's sibling, and so on. + +## Hashing + +Used for computing the parent nodes of all merkle trees. + + + +```rust +enum TreeId { + Archive, + NoteHash, + Nullifier, + PrivateFunction, + L1ToL2Msgs, + PublicData +} + +fn merkle_crh( + tree_id: TreeId, + layer: u64, + left: Field, + right: Field +) -> Field { + let tree_id_domain_separator: string = match tree_id { + TreeId::Archive => "archive", + TreeId::NoteHash => "note_hash", + TreeId::Nullifier => "nullifier", + TreeId::PrivateFunction => "private_function", + TreeId::L2ToL2Msgs => "l1_to_l2_msgs", + TreeId::PublicData => "public_data", + }; + + let merkle_domain_separator: string = "az_merkle" + tree_id_domain_separator; + + let parent = poseidon2( + be_string_to_field(merkle_domain_separator), + int_to_field(layer), + + left, + right + ); + + parent +} +``` + +> `tree_id` reflects the various [trees in the protocol](../../state/index.md). The `PrivateFunction` tree is discussed in the [contract classes](../../contract-deployment/classes.md) section. +> `layer` is the [layer](../merkle-trees.md#layers) of the `left` and `right` children being hashed. For example, when hashing two leaves, `layer = 0`. + +:::danger + +- Q: Do we need the domain separator "az_merkle" + tree_id, for each of the trees? +- Q: do we need domain separation between different layers of the tree? +- Q: Can we optimise the two domain separators to take up 1 Field, instead of 2, or does squashing them together add too many constraints? +- Note: if it helps with optimisation, we can reduce the bit-length of the domain separator strings. +- Q: Can we specify the arguments to Poseidon as Fields, or do we need to specify them as bit-sequences? + +::: + +## Append-only Merkle Tree + +TODO + +## Indexed Merkle Tree + +TODO diff --git a/yellow-paper/docs/intro.md b/yellow-paper/docs/intro.md index 04279ac270f6..8a7c2fc331ba 100644 --- a/yellow-paper/docs/intro.md +++ b/yellow-paper/docs/intro.md @@ -6,7 +6,7 @@ It'll be more 'beige' than a yellow paper, initially. For example, we haven't se ## Target audience -In Q4, the target audience is _ourselves_ (people at Aztec Labs). Can we explain the protocol requirements and the protocol itself _to ourselves_, without gaps? +Initially, the target audience is people at Aztec Labs. Can we explain the protocol requirements and the protocol itself _to ourselves_, without gaps? In particular, can we explain the protocol to the wider cryptography team, whose help we'll need to ensure the protocol is secure. @@ -100,198 +100,3 @@ Provide links to discourse (if applicable), so that people can get context on pr Flag any requirements that are not-yet being met by the protocol described within this doc. Discuss what we've considered (or link to a discourse discussion). - -# ToC - -Names of 'owners' are in brackets (they don't necessarily need to write the sections). - -The draft subsections are mere suggestions (and serve as a helpful reminder of topics). The actual layout can be adjusted. - -- Abstract [Mike] -- High-level overview [Mike] -- Constants [all] -- Cryptography [Dont do yet] - - - Fields & Curves - - Hash functions - - Properties of different hash functions / pseudo-random functions - - Pedersen - - Poseidon - - Blake - - Sha256 - - Domain separation - - zk-SNARKs - - - The Data Bus - -- Addresses & Keys [Palla/Mike] - - Requirements - - Addresses - - - Keys - - Master keys - - Authorization key abstraction - - Nullifier keys - - Incoming viewing keys - - Outgoing viewing keys - - Randomizing/tagging/ - - App-siloed keys - - Security assumptions of each key - - - Example flows, to validate (and illustrate usage of) the keys: - - - BIP32 - - Normal key derivation - - Hardened key derivation - - Deviations from BIP32 - - Stealth keys vs Diversified keys - - Updating keys -- State - - L2 State - - Trees - - Note Hash Tree - - Indexed merkle trees - - Nullifier tree(s) - - Public Data Tree - - Slow Updates Tree (if enshrined) [Lasse] - - Contract Tree (if still around) - - Transactions / Receipts Trees? - - Block hashes tree - - Tree Epochs - - L1 State [Lasse] - - Contract State -- Transactions -- Bytecode [Alvaro] - - ACIR - - Encodings - - Brillig - - Encodings -- Contract Creation (Deployment) [???] - - Computing a contract address - - Nullifying a contract address - - Broadcasting Bytecode (under various scenarios) and other contract info - - Committing to Brillig bytecode -- Calls [???] - - - Private function call - - Public function call - - Enqueued public function call - - Delegatecall (if decided) - - Staticcall (if decided) - - Protocol Function Call (if decided) - - Inter-layer messaging - - L1->L2 messaging - - L2->L1 messaging - - Public->Private messaging -- Notes & Nullifiers [Leila?] - - Custom Notes - - Encoding - - Custom Nullifiers - - Siloing - - Uniqueness -- Logs [Jan?] - - Unencrypted - - Encrypted - - Optimization considerations - -- Private Message Delivery [Phil] - - To constrain or not to constrain? - - Encryption & decryption [Palla/Mike] - - - Which encryption scheme(s)? - - Types of message - - Initial Handshakes - - Incoming messages - - Internal incoming messages - - Outgoing messages - - Algorithms - - Encryption & decryption algorithms, for each type of message (incoming/outgoing/etc). - - Note Discovery [Phil] - - Abstracting note identification to a 'tag' of bytes - - etc. -- Gas & Fees [Phil] - - Gas Metering - - Categories of 'things' that contribute to compute effort - - Benchmarking compute effort - - Measuring 'gas used' at the protocol level - - Fees - - L2 fees vs L1 fees - - What currency is gas measured in / fee abstraction? - - Paying fees, and receiving rebates - - Paying fees privately - - Etc. -- Decentralization [Cooper] - - P2P network [Phil] - - Data that is sent between parties - - Users to the tx pool - - Sequencers to provers (and back) - - Sequencers to L1 - - Sequencer selection protocol [Palla] - - Prover selection protocol [Cooper?] - - Upgrading the protocol [Cooper] - - Training Wheels [Lasse?] - - Economics [Cooper] -- Chaining Transactions [Leila] -- EIP-4844 [Lasse?] -- Protocol Statements [Mike] - - Describe the logic of the protocol. - - Describe the assertions that must be in place. - - The constraints (assertions) of the system are spread across many circuits and L1 contracts, but they should be described as a whole, in one place. _Where_ the assertions are located (i.e. in which circuit/contract) is a matter of optimization, which might change over time. This can then serve as a checklist, to ensure those assertions are included in at least one of the circuits/contracts. - - In particular, exhaustively explain what _shouldn't_ be possible. - - Sub-protocols - - - Circuits - - - High-level topology [David/Leila?] - - Private Circuits [David/Leila?] - - General features a private circuit must adhere-to. - - Why not a private VM? - - Public VM Cicuit [David] <-- big section - - Kernel Circuits - - Private [David/Leila?] - - Initial Private Kernel Circuit - - Inner Private Kernel Circuit - - Ordering Private Kernel Circuit - - Public [David/Leila?] - - Initial Public Kernel Circuit - - Inner Public Kernel Circuit - - (Ordering Public Kernel Circuit???) - - Future Kernel Optimizations - - Delegating chunks of compute to 'gadget' circuits - - A Merkle Tree of Private Kernel iterations - - Rollup Circuits [Lasse/Leila?] - - Base Rollup Circuit - - Merge Rollup Circuit - - Root Rollup Circuit - - Squisher Circuits [???] - - Honk -> UltraPlonk - - UltraPlonk -> Standard Plonk / Fflonk - - Circuits for sequencer/prover selection? [Palla] - - EIP-4844 circuit [???] - - Bytecode commitment circuit [???] - - Smart Contracts [Lasse] - - ... -- Acknowledgements diff --git a/yellow-paper/docs/pre-compiled-contracts/registry.md b/yellow-paper/docs/pre-compiled-contracts/registry.md index d2302aa5c83a..2516a97389f9 100644 --- a/yellow-paper/docs/pre-compiled-contracts/registry.md +++ b/yellow-paper/docs/pre-compiled-contracts/registry.md @@ -30,7 +30,7 @@ The registry contract exposes functions for setting public keys and encryption m -``` +```rust contract Registry public mapping(address => { keys, precompile_address }) registry @@ -73,7 +73,7 @@ The registry limits multi-recipient registrations to no more than `MAX_ENTRIES_P Contracts that intend to register multiple recipients should account for those recipients eventually rotating their keys. To support this, contracts should include a method to refresh the registered addresses: -``` +```rust contract Sample private address[] owners diff --git a/yellow-paper/docs/private-message-delivery/send-note-guidelines.md b/yellow-paper/docs/private-message-delivery/send-note-guidelines.md index 707118f943c6..aa0b0a67edad 100644 --- a/yellow-paper/docs/private-message-delivery/send-note-guidelines.md +++ b/yellow-paper/docs/private-message-delivery/send-note-guidelines.md @@ -14,7 +14,7 @@ If the user is not in the registry and the sender cannot provide the address pre Execution of the precompile that implements the recipient's choice for encryption and tagging should be done using a batched delegated call, to amortize the cost of sending multiple notes using the same method, and to ensure the notes are broadcasted from the application contract's address. -## Pseudocode +### Pseudocode The following pseudocode covers how to provably send a note to a recipient, given an `encryption_type` (incoming, outgoing, or internal incoming). Should the registry support [multiple entries for a given recipient](../pre-compiled-contracts/registry.md#multiple-recipients-per-address), this method must execute a batched call per each entry recovered from the registry. diff --git a/yellow-paper/docs/todo.md b/yellow-paper/docs/todo.md new file mode 100644 index 000000000000..1722f08b3ab2 --- /dev/null +++ b/yellow-paper/docs/todo.md @@ -0,0 +1,55 @@ +TODO: + +- See cryptography/todo.md +- Describe what we _desire_ the protocol to be; not what it is today. +- Define/describe: + - an oracle +- More detail and precisely-named definitions relating to note hashes (inner, unique, siloed, ...) and nullifiers. +- Clear descriptions of the precompiles for handshaking & note tagging. Including the encoding of logs (header, body, ephemeral keys, etc.) +- Poseidon hash for all trees? +- Maybe describe all hashes on one page? +- For all hashes: Describe exact ordering of preimage values. Describe the encoding of each preimage value. State the hash to use. Include a domain separator. Give the hash a unique name. Is there anything weird like truncating an output to fit in a field? + If possible, describe the property(or properties) we want from the hash (collision resistance?, 2nd preimage resistance? pseudo-random function? hash to curve?). +- Consistently use the same name for each concept throughout. Link to it the first time it’s mentioned on a page. When introducing a ‘thing’, try to put it in a section with a subtitle which matches that thing’s name. +- Structs (or tables) (clearly named and typed, in a subsection) for everything whose layout we should know! +- Who is going to write the specs for: + - Data bus? + - RAM/ROM? + - Circuit arithmetisation + - Custom Gates + - The proving system (honk, goblin, protogalaxy, etc.) +- Spec logs better! + - Use data bus? + - Allow several (4 or 8 or something) fields to be emitted by a function, before needing to sha256 the data? + - Have a 'reset' circuit to sha256 logs? + - Will there be space in the data bus for logs? + - Resolve 'impersonation' discussions (see the forum) + +Contents: + +- Custom types +- Constants + - Subdivided into categories: + - Circuit constants + - Tree constants + - Seq Selection constants + - P2P constants + - Block constants +- Serialization & Deserialization + - aztec.nr +- Encodings +- Hashing +- Merkleization +- Key derivation algorithms + +Layout: highlight out-of-protocol information in a box. + +Abstraction & Standardisation: + +- Account abstraction +- Constructor abstraction +- Nonce abstraction +- Fee abstraction +- Tx Hash abstraction + +Every struct, constant, other definition, needs a corresponding subheading with that exact name, somewhere in the docs? Might get ugly... diff --git a/yellow-paper/sidebars.js b/yellow-paper/sidebars.js index a3d758a1ffee..08d7a2b3e5d4 100644 --- a/yellow-paper/sidebars.js +++ b/yellow-paper/sidebars.js @@ -44,6 +44,7 @@ const sidebars = { link: { type: "doc", id: "cryptography/hashing/hashing" }, items: [ "cryptography/hashing/hashing", + "cryptography/hashing/poseidon2", "cryptography/hashing/pedersen", ], }, @@ -58,6 +59,16 @@ const sidebars = { "addresses-and-keys/address", "addresses-and-keys/keys-requirements", "addresses-and-keys/keys", + { + label: "Example Usage of Keys", + type: "category", + items: [ + "addresses-and-keys/example-usage/nullifier", + "addresses-and-keys/example-usage/diversified-and-stealth-keys", + "addresses-and-keys/example-usage/tag-sequence-derivation", + "addresses-and-keys/example-usage/encrypt-and-tag", + ], + }, "addresses-and-keys/precompiles", "addresses-and-keys/diversified-and-stealth", ], From 028fc47b7d17e345f7f88832dba335b354b6c390 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Wed, 27 Mar 2024 13:38:29 +0000 Subject: [PATCH 2/5] fix: links --- yellow-paper/docs/cryptography/merkle-trees.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yellow-paper/docs/cryptography/merkle-trees.md b/yellow-paper/docs/cryptography/merkle-trees.md index 61de0b56d9d5..7af9de871972 100644 --- a/yellow-paper/docs/cryptography/merkle-trees.md +++ b/yellow-paper/docs/cryptography/merkle-trees.md @@ -98,8 +98,8 @@ fn merkle_crh( } ``` -> `tree_id` reflects the various [trees in the protocol](../../state/index.md). The `PrivateFunction` tree is discussed in the [contract classes](../../contract-deployment/classes.md) section. -> `layer` is the [layer](../merkle-trees.md#layers) of the `left` and `right` children being hashed. For example, when hashing two leaves, `layer = 0`. +> `tree_id` reflects the various [trees in the protocol](../state/index.md). The `PrivateFunction` tree is discussed in the [contract classes](../contract-deployment/classes.md) section. +> `layer` is the [layer](#layers) of the `left` and `right` children being hashed. For example, when hashing two leaves, `layer = 0`. :::danger From 2cfe519e9b43b2803dd9e1ae2e13321127b5cf2b Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Thu, 4 Apr 2024 08:21:08 +0000 Subject: [PATCH 3/5] chore: import latex preamble --- .../docs/addresses-and-keys/address.md | 2 +- .../diversified-and-stealth.md | 2 +- ...ys.md => diversified-and-stealth-keys.mdx} | 89 +----------------- ...encrypt-and-tag.md => encrypt-and-tag.mdx} | 90 +----------------- .../{nullifier.md => nullifier.mdx} | 93 +------------------ ...ivation.md => tag-sequence-derivation.mdx} | 89 +----------------- yellow-paper/docs/addresses-and-keys/index.md | 2 +- .../addresses-and-keys/keys-latex-preamble.md | 90 ++++++++++++++++++ .../addresses-and-keys/{keys.md => keys.mdx} | 92 +----------------- .../docs/cryptography/hashing/hashing.md | 4 +- .../docs/public-vm/gen/_instruction-set.mdx | 3 + yellow-paper/sidebars.js | 1 - 12 files changed, 115 insertions(+), 442 deletions(-) rename yellow-paper/docs/addresses-and-keys/example-usage/{diversified-and-stealth-keys.md => diversified-and-stealth-keys.mdx} (67%) rename yellow-paper/docs/addresses-and-keys/example-usage/{encrypt-and-tag.md => encrypt-and-tag.mdx} (77%) rename yellow-paper/docs/addresses-and-keys/example-usage/{nullifier.md => nullifier.mdx} (53%) rename yellow-paper/docs/addresses-and-keys/example-usage/{tag-sequence-derivation.md => tag-sequence-derivation.mdx} (67%) create mode 100644 yellow-paper/docs/addresses-and-keys/keys-latex-preamble.md rename yellow-paper/docs/addresses-and-keys/{keys.md => keys.mdx} (86%) diff --git a/yellow-paper/docs/addresses-and-keys/address.md b/yellow-paper/docs/addresses-and-keys/address.md index 5cd2e3ac20d4..e2fc1a1acff6 100644 --- a/yellow-paper/docs/addresses-and-keys/address.md +++ b/yellow-paper/docs/addresses-and-keys/address.md @@ -73,7 +73,7 @@ address_crh( } ``` -The `public_keys` array can vary depending on the format of keys used by the address, but it is suggested it includes the master keys defined in the [keys section](./keys.md). For example: +The `public_keys` array can vary depending on the format of keys used by the address, but it is suggested it includes the master keys defined in the [keys section](./keys.mdx). For example: ```rust let public_keys_hash: Field = poseidon2( diff --git a/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md b/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md index 51a274e12b44..f4fc8613f803 100644 --- a/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md +++ b/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md @@ -2,7 +2,7 @@ title: Diversified and Stealth Accounts --- -The [keys specification](./keys.md) describes derivation mechanisms for diversified and stealth public keys. However, the protocol requires users to interact with addresses. +The [keys specification](./keys.mdx) describes derivation mechanisms for diversified and stealth public keys. However, the protocol requires users to interact with addresses. ## Computing Addresses diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md b/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx similarity index 67% rename from yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md rename to yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx index 5f5516a2fc29..253d3ff0d501 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.md +++ b/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx @@ -1,90 +1,7 @@ -$$ -\gdef\sk{\color{red}{sk}} + -\gdef\nskm{\color{red}{nsk_m}} -\gdef\tskm{\color{red}{tsk_m}} -\gdef\ivskm{\color{red}{ivsk_m}} -\gdef\ovskm{\color{red}{ovsk_m}} - -\gdef\Npkm{\color{green}{Npk_m}} -\gdef\Tpkm{\color{green}{Tpk_m}} -\gdef\Ivpkm{\color{green}{Ivpk_m}} -\gdef\Ovpkm{\color{green}{Ovpk_m}} - - -\gdef\address{\color{green}{address}} -\gdef\codehash{\color{green}{code\_hash}} -\gdef\constructorhash{\color{green}{constructor\_hash}} -\gdef\classid{\color{green}{class\id}} - - -\gdef\nskapp{\color{red}{nsk_{app}}} -\gdef\tskapp{\color{red}{tsk_{app}}} -\gdef\ivskapp{\color{red}{ivsk_{app}}} -\gdef\ovskapp{\color{red}{ovsk_{app}}} - -\gdef\Nkapp{\color{orange}{Nk_{app}}} - -\gdef\Npkapp{\color{green}{Npk_{app}}} - - -\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} - - -\gdef\happL{\color{green}{h_{app}^L}} -\gdef\happn{\color{green}{h_{app}^n}} -\gdef\happiv{\color{green}{h_{app}^{iv}}} - - -\gdef\d{\color{green}{d}} -\gdef\Gd{\color{green}{G_d}} - -\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} -\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} -\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} -\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} - - -\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} -\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} -\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} -\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} -\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} -\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} - -\gdef\hstealth{\color{violet}{h_{stealth}}} - - -\gdef\esk{\color{red}{esk}} -\gdef\Epk{\color{green}{Epk}} -\gdef\Epkd{\color{green}{Epk_d}} -\gdef\eskheader{\color{red}{esk_{header}}} -\gdef\Epkheader{\color{green}{Epk_{header}}} -\gdef\Epkdheader{\color{green}{Epk_{d,header}}} - -\gdef\sharedsecret{\color{violet}{\text{S}}} -\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} -\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} - - -\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} -\gdef\happencheader{\color{violet}{h_{app,enc,header}}} -\gdef\hmenc{\color{violet}{h_{m,enc}}} -\gdef\happenc{\color{violet}{h_{app,enc}}} -\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} - - -\gdef\plaintext{\color{red}{\text{plaintext}}} -\gdef\ciphertext{\color{green}{\text{ciphertext}}} -\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} -\gdef\payload{\color{green}{\text{payload}}} - - -\gdef\tagg{\color{green}{\text{tag}}} -\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} - - -$$ +import LatexPreamble from "../keys-latex-preamble.md"; +; ## Deriving diversified public keys diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md b/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx similarity index 77% rename from yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md rename to yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx index ced34d223987..ec9594200801 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.md +++ b/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx @@ -1,91 +1,7 @@ -$$ + -\gdef\sk{\color{red}{sk}} - -\gdef\nskm{\color{red}{nsk_m}} -\gdef\tskm{\color{red}{tsk_m}} -\gdef\ivskm{\color{red}{ivsk_m}} -\gdef\ovskm{\color{red}{ovsk_m}} - -\gdef\Npkm{\color{green}{Npk_m}} -\gdef\Tpkm{\color{green}{Tpk_m}} -\gdef\Ivpkm{\color{green}{Ivpk_m}} -\gdef\Ovpkm{\color{green}{Ovpk_m}} - - -\gdef\address{\color{green}{address}} -\gdef\codehash{\color{green}{code\_hash}} -\gdef\constructorhash{\color{green}{constructor\_hash}} -\gdef\classid{\color{green}{class\id}} - - -\gdef\nskapp{\color{red}{nsk_{app}}} -\gdef\tskapp{\color{red}{tsk_{app}}} -\gdef\ivskapp{\color{red}{ivsk_{app}}} -\gdef\ovskapp{\color{red}{ovsk_{app}}} - -\gdef\Nkapp{\color{orange}{Nk_{app}}} - -\gdef\Npkapp{\color{green}{Npk_{app}}} - - -\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} - - -\gdef\happL{\color{green}{h_{app}^L}} -\gdef\happn{\color{green}{h_{app}^n}} -\gdef\happiv{\color{green}{h_{app}^{iv}}} - - -\gdef\d{\color{green}{d}} -\gdef\Gd{\color{green}{G_d}} - -\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} -\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} -\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} -\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} - - -\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} -\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} -\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} -\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} -\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} -\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} - -\gdef\hstealth{\color{violet}{h_{stealth}}} - - -\gdef\esk{\color{red}{esk}} -\gdef\Epk{\color{green}{Epk}} -\gdef\Epkd{\color{green}{Epk_d}} -\gdef\eskheader{\color{red}{esk_{header}}} -\gdef\Epkheader{\color{green}{Epk_{header}}} -\gdef\Epkdheader{\color{green}{Epk_{d,header}}} - -\gdef\sharedsecret{\color{violet}{\text{S}}} -\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} -\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} - - -\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} -\gdef\happencheader{\color{violet}{h_{app,enc,header}}} -\gdef\hmenc{\color{violet}{h_{m,enc}}} -\gdef\happenc{\color{violet}{h_{app,enc}}} -\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} - - -\gdef\plaintext{\color{red}{\text{plaintext}}} -\gdef\ciphertext{\color{green}{\text{ciphertext}}} -\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} -\gdef\payload{\color{green}{\text{payload}}} - - -\gdef\tagg{\color{green}{\text{tag}}} -\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} - - -$$ +import LatexPreamble from "../keys-latex-preamble.md"; +; ## Encrypt and tag an incoming message diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md b/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx similarity index 53% rename from yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md rename to yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx index 5bf59bff1b74..1634b870f937 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.md +++ b/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx @@ -1,90 +1,7 @@ -$$ -\gdef\sk{\color{red}{sk}} - -\gdef\nskm{\color{red}{nsk_m}} -\gdef\tskm{\color{red}{tsk_m}} -\gdef\ivskm{\color{red}{ivsk_m}} -\gdef\ovskm{\color{red}{ovsk_m}} - -\gdef\Npkm{\color{green}{Npk_m}} -\gdef\Tpkm{\color{green}{Tpk_m}} -\gdef\Ivpkm{\color{green}{Ivpk_m}} -\gdef\Ovpkm{\color{green}{Ovpk_m}} - - -\gdef\address{\color{green}{address}} -\gdef\codehash{\color{green}{code\_hash}} -\gdef\constructorhash{\color{green}{constructor\_hash}} -\gdef\classid{\color{green}{class\id}} - - -\gdef\nskapp{\color{red}{nsk_{app}}} -\gdef\tskapp{\color{red}{tsk_{app}}} -\gdef\ivskapp{\color{red}{ivsk_{app}}} -\gdef\ovskapp{\color{red}{ovsk_{app}}} - -\gdef\Nkapp{\color{orange}{Nk_{app}}} - -\gdef\Npkapp{\color{green}{Npk_{app}}} - - -\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} - - -\gdef\happL{\color{green}{h_{app}^L}} -\gdef\happn{\color{green}{h_{app}^n}} -\gdef\happiv{\color{green}{h_{app}^{iv}}} - - -\gdef\d{\color{green}{d}} -\gdef\Gd{\color{green}{G_d}} + -\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} -\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} -\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} -\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} - - -\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} -\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} -\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} -\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} -\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} -\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} - -\gdef\hstealth{\color{violet}{h_{stealth}}} - - -\gdef\esk{\color{red}{esk}} -\gdef\Epk{\color{green}{Epk}} -\gdef\Epkd{\color{green}{Epk_d}} -\gdef\eskheader{\color{red}{esk_{header}}} -\gdef\Epkheader{\color{green}{Epk_{header}}} -\gdef\Epkdheader{\color{green}{Epk_{d,header}}} - -\gdef\sharedsecret{\color{violet}{\text{S}}} -\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} -\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} - - -\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} -\gdef\happencheader{\color{violet}{h_{app,enc,header}}} -\gdef\hmenc{\color{violet}{h_{m,enc}}} -\gdef\happenc{\color{violet}{h_{app,enc}}} -\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} - - -\gdef\plaintext{\color{red}{\text{plaintext}}} -\gdef\ciphertext{\color{green}{\text{ciphertext}}} -\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} -\gdef\payload{\color{green}{\text{payload}}} - - -\gdef\tagg{\color{green}{\text{tag}}} -\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} - - -$$ +import LatexPreamble from "../keys-latex-preamble.md"; +; ## Deriving a nullifier within an app contract @@ -106,8 +23,8 @@ It's easiest to take a look at this first: Within the app, we can prove links between: -- the user's [$\nskapp$](../keys.md#app-siloed-nullifier-secret-key) and their [$\Nkapp$](../keys.md#app-siloed-nullifier-key); and between -- the user's [$\Npkm$](../keys.md#master-nullifier-public-key) and their [$\address$](../address.md). +- the user's [$\nskapp$](../keys.mdx#app-siloed-nullifier-secret-key) and their [$\Nkapp$](../keys.mdx#app-siloed-nullifier-key); and between +- the user's [$\Npkm$](../keys.mdx#master-nullifier-public-key) and their [$\address$](../address.md). The link that's missing is to prove that $\Npkm$ relates to $\nskapp$. To compute this missing link requires the $\nskm$, which MUST NOT be passed into an app circuit, and may only be passed into a kernel circuit. See the next ['Within the kernel circuit'](#within-the-kernel-circuit) section for details of this logic. diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md b/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx similarity index 67% rename from yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md rename to yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx index 198c15d7b047..a05338a77c08 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.md +++ b/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx @@ -1,90 +1,7 @@ -$$ -\gdef\sk{\color{red}{sk}} + -\gdef\nskm{\color{red}{nsk_m}} -\gdef\tskm{\color{red}{tsk_m}} -\gdef\ivskm{\color{red}{ivsk_m}} -\gdef\ovskm{\color{red}{ovsk_m}} - -\gdef\Npkm{\color{green}{Npk_m}} -\gdef\Tpkm{\color{green}{Tpk_m}} -\gdef\Ivpkm{\color{green}{Ivpk_m}} -\gdef\Ovpkm{\color{green}{Ovpk_m}} - - -\gdef\address{\color{green}{address}} -\gdef\codehash{\color{green}{code\_hash}} -\gdef\constructorhash{\color{green}{constructor\_hash}} -\gdef\classid{\color{green}{class\id}} - - -\gdef\nskapp{\color{red}{nsk_{app}}} -\gdef\tskapp{\color{red}{tsk_{app}}} -\gdef\ivskapp{\color{red}{ivsk_{app}}} -\gdef\ovskapp{\color{red}{ovsk_{app}}} - -\gdef\Nkapp{\color{orange}{Nk_{app}}} - -\gdef\Npkapp{\color{green}{Npk_{app}}} - - -\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} - - -\gdef\happL{\color{green}{h_{app}^L}} -\gdef\happn{\color{green}{h_{app}^n}} -\gdef\happiv{\color{green}{h_{app}^{iv}}} - - -\gdef\d{\color{green}{d}} -\gdef\Gd{\color{green}{G_d}} - -\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} -\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} -\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} -\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} - - -\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} -\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} -\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} -\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} -\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} -\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} - -\gdef\hstealth{\color{violet}{h_{stealth}}} - - -\gdef\esk{\color{red}{esk}} -\gdef\Epk{\color{green}{Epk}} -\gdef\Epkd{\color{green}{Epk_d}} -\gdef\eskheader{\color{red}{esk_{header}}} -\gdef\Epkheader{\color{green}{Epk_{header}}} -\gdef\Epkdheader{\color{green}{Epk_{d,header}}} - -\gdef\sharedsecret{\color{violet}{\text{S}}} -\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} -\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} - - -\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} -\gdef\happencheader{\color{violet}{h_{app,enc,header}}} -\gdef\hmenc{\color{violet}{h_{m,enc}}} -\gdef\happenc{\color{violet}{h_{app,enc}}} -\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} - - -\gdef\plaintext{\color{red}{\text{plaintext}}} -\gdef\ciphertext{\color{green}{\text{ciphertext}}} -\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} -\gdef\payload{\color{green}{\text{payload}}} - - -\gdef\tagg{\color{green}{\text{tag}}} -\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} - - -$$ +import LatexPreamble from "../keys-latex-preamble.md"; +; # Handshaking for tag-hopping diff --git a/yellow-paper/docs/addresses-and-keys/index.md b/yellow-paper/docs/addresses-and-keys/index.md index d9c2730d8fd0..bc0f6f27ab55 100644 --- a/yellow-paper/docs/addresses-and-keys/index.md +++ b/yellow-paper/docs/addresses-and-keys/index.md @@ -14,7 +14,7 @@ Keys in Aztec are used both for authorization and privacy. Authorization keys ar Privacy keys are used for note encryption, tagging, and nullifying. These are also not enforced by the protocol. However, for facilitating composability, the protocol enshrines a set of enshrined encryption and tagging mechanisms, that can be leveraged by applications as they interact with accounts. -The [requirements](./keys-requirements.md) section outlines the features that were sought when designing Aztec's addresses and keys. We then specify how [addresses](./address.md) are derived, as well as the default way in which [keys](./keys.md) will be derived. The [precompiles](./precompiles.md) section describes enshrined contract addresses, with implementations defined by the protocol, used for note encryption and tagging. +The [requirements](./keys-requirements.md) section outlines the features that were sought when designing Aztec's addresses and keys. We then specify how [addresses](./address.md) are derived, as well as the default way in which [keys](./keys.mdx) will be derived. The [precompiles](./precompiles.md) section describes enshrined contract addresses, with implementations defined by the protocol, used for note encryption and tagging. Last, the [diversified and stealth accounts](./diversified-and-stealth.md) sections describe application-level recommendations for diversified and stealth accounts. diff --git a/yellow-paper/docs/addresses-and-keys/keys-latex-preamble.md b/yellow-paper/docs/addresses-and-keys/keys-latex-preamble.md new file mode 100644 index 000000000000..c0a5816a5723 --- /dev/null +++ b/yellow-paper/docs/addresses-and-keys/keys-latex-preamble.md @@ -0,0 +1,90 @@ +$$ + + +\gdef\sk{\color{red}{sk}\color{black}{}} +\gdef\seed{\color{red}\text{{seed}}\color{black}{}} + +\gdef\nskm{\color{red}{nsk_m}\color{black}{}} +\gdef\tskm{\color{red}{tsk_m}\color{black}{}} +\gdef\ivskm{\color{red}{ivsk_m}\color{black}{}} +\gdef\ovskm{\color{red}{ovsk_m}\color{black}{}} + +\gdef\Npkm{\color{green}{Npk_m}\color{black}{}} +\gdef\Tpkm{\color{green}{Tpk_m}\color{black}{}} +\gdef\Ivpkm{\color{green}{Ivpk_m}\color{black}{}} +\gdef\Ovpkm{\color{green}{Ovpk_m}\color{black}{}} + + +\gdef\address{\color{green}{address}\color{black}{}} +\gdef\codehash{\color{green}{code\_hash}\color{black}{}} +\gdef\constructorhash{\color{green}{constructor\_hash}\color{black}{}} +\gdef\classid{\color{green}{class\id}\color{black}{}} + + +\gdef\nskapp{\color{red}{nsk_{app}}\color{black}{}} +\gdef\tskapp{\color{red}{tsk_{app}}\color{black}{}} +\gdef\ivskapp{\color{red}{ivsk_{app}}\color{black}{}} +\gdef\ovskapp{\color{red}{ovsk_{app}}\color{black}{}} + +\gdef\Nkapp{\color{orange}{Nk_{app}}\color{black}{}} + +\gdef\Npkapp{\color{green}{Npk_{app}}\color{black}{}} + + +\gdef\Ivpkapp{\color{green}{Ivpk_{app}}\color{black}{}} + + +\gdef\happL{\color{green}{h_{app}^L}\color{black}{}} +\gdef\happn{\color{green}{h_{app}^n}\color{black}{}} +\gdef\happiv{\color{green}{h_{app}^{iv}}\color{black}{}} + + +\gdef\d{\color{green}{d}\color{black}{}} +\gdef\Gd{\color{green}{G_d}\color{black}{}} + +\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}\color{black}{}} +\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}\color{black}{}} +\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}\color{black}{}} +\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}\color{black}{}} + + +\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}\color{black}{}} +\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}\color{black}{}} +\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}\color{black}{}} +\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}\color{black}{}} +\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}\color{black}{}} +\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}\color{black}{}} + +\gdef\hstealth{\color{violet}{h_{stealth}}\color{black}{}} + + +\gdef\esk{\color{red}{esk}\color{black}{}} +\gdef\Epk{\color{green}{Epk}\color{black}{}} +\gdef\Epkd{\color{green}{Epk_d}\color{black}{}} +\gdef\eskheader{\color{red}{esk_{header}}\color{black}{}} +\gdef\Epkheader{\color{green}{Epk_{header}}\color{black}{}} +\gdef\Epkdheader{\color{green}{Epk_{d,header}}\color{black}{}} + +\gdef\sharedsecret{\color{violet}{\text{S}}\color{black}{}} +\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}\color{black}{}} +\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}\color{black}{}} + + +\gdef\hmencheader{\color{violet}{h_{m,enc,header}}\color{black}{}} +\gdef\happencheader{\color{violet}{h_{app,enc,header}}\color{black}{}} +\gdef\hmenc{\color{violet}{h_{m,enc}}\color{black}{}} +\gdef\happenc{\color{violet}{h_{app,enc}}\color{black}{}} +\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}\color{black}{}} + + +\gdef\plaintext{\color{red}{\text{plaintext}}\color{black}{}} +\gdef\ciphertext{\color{green}{\text{ciphertext}}\color{black}{}} +\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}\color{black}{}} +\gdef\payload{\color{green}{\text{payload}}\color{black}{}} + + +\gdef\tagg{\color{green}{\text{tag}}\color{black}{}} +\gdef\Taghs{\color{green}{\text{Tag}_{hs}}\color{black}{}} + + +$$ diff --git a/yellow-paper/docs/addresses-and-keys/keys.md b/yellow-paper/docs/addresses-and-keys/keys.mdx similarity index 86% rename from yellow-paper/docs/addresses-and-keys/keys.md rename to yellow-paper/docs/addresses-and-keys/keys.mdx index 3a5f1daf4f55..489740b6a696 100644 --- a/yellow-paper/docs/addresses-and-keys/keys.md +++ b/yellow-paper/docs/addresses-and-keys/keys.mdx @@ -5,96 +5,10 @@ description: Specification for default privacy keys format and derivation, and n -$$ - - -\gdef\sk{\color{red}{sk}} -\gdef\seed{\color{red}\text{{seed}}} - -\gdef\nskm{\color{red}{nsk_m}} -\gdef\tskm{\color{red}{tsk_m}} -\gdef\ivskm{\color{red}{ivsk_m}} -\gdef\ovskm{\color{red}{ovsk_m}} - -\gdef\Npkm{\color{green}{Npk_m}} -\gdef\Tpkm{\color{green}{Tpk_m}} -\gdef\Ivpkm{\color{green}{Ivpk_m}} -\gdef\Ovpkm{\color{green}{Ovpk_m}} - - -\gdef\address{\color{green}{address}} -\gdef\codehash{\color{green}{code\_hash}} -\gdef\constructorhash{\color{green}{constructor\_hash}} -\gdef\classid{\color{green}{class\id}} - - -\gdef\nskapp{\color{red}{nsk_{app}}} -\gdef\tskapp{\color{red}{tsk_{app}}} -\gdef\ivskapp{\color{red}{ivsk_{app}}} -\gdef\ovskapp{\color{red}{ovsk_{app}}} - -\gdef\Nkapp{\color{orange}{Nk_{app}}} - -\gdef\Npkapp{\color{green}{Npk_{app}}} - - -\gdef\Ivpkapp{\color{green}{Ivpk_{app}}} - - -\gdef\happL{\color{green}{h_{app}^L}} -\gdef\happn{\color{green}{h_{app}^n}} -\gdef\happiv{\color{green}{h_{app}^{iv}}} - + -\gdef\d{\color{green}{d}} -\gdef\Gd{\color{green}{G_d}} - -\gdef\Ivpkappd{\color{violet}{Ivpk_{app,d}}} -\gdef\shareableIvpkappd{\color{violet}{\widetilde{Ivpk_{app,d}}}} -\gdef\Ivpkmd{\color{violet}{Ivpk_{m,d}}} -\gdef\shareableIvpkmd{\color{violet}{\widetilde{Ivpk_{m,d}}}} - - -\gdef\ivskappstealth{\color{red}{ivsk_{app,stealth}}} -\gdef\Ivpkappdstealth{\color{violet}{Ivpk_{app,d,stealth}}} -\gdef\Pkappdstealth{\color{violet}{Pk_{app,d,stealth}}} -\gdef\ivskmstealth{\color{red}{ivsk_{m,stealth}}} -\gdef\Ivpkmdstealth{\color{violet}{Ivpk_{m,d,stealth}}} -\gdef\Pkmdstealth{\color{violet}{Pk_{m,d,stealth}}} - -\gdef\hstealth{\color{violet}{h_{stealth}}} - - -\gdef\esk{\color{red}{esk}} -\gdef\Epk{\color{green}{Epk}} -\gdef\Epkd{\color{green}{Epk_d}} -\gdef\eskheader{\color{red}{esk_{header}}} -\gdef\Epkheader{\color{green}{Epk_{header}}} -\gdef\Epkdheader{\color{green}{Epk_{d,header}}} - -\gdef\sharedsecret{\color{violet}{\text{S}}} -\gdef\sharedsecretmheader{\color{violet}{\text{S_{m,header}}}} -\gdef\sharedsecretappheader{\color{violet}{\text{S_{app,header}}}} - - -\gdef\hmencheader{\color{violet}{h_{m,enc,header}}} -\gdef\happencheader{\color{violet}{h_{app,enc,header}}} -\gdef\hmenc{\color{violet}{h_{m,enc}}} -\gdef\happenc{\color{violet}{h_{app,enc}}} -\gdef\incomingenckey{\color{violet}{h_{incoming\_enc\_key}}} - - -\gdef\plaintext{\color{red}{\text{plaintext}}} -\gdef\ciphertext{\color{green}{\text{ciphertext}}} -\gdef\ciphertextheader{\color{green}{\text{ciphertext\_header}}} -\gdef\payload{\color{green}{\text{payload}}} - - -\gdef\tagg{\color{green}{\text{tag}}} -\gdef\Taghs{\color{green}{\text{Tag}_{hs}}} - - -$$ +import LatexPreamble from "./keys-latex-preamble.md"; +; ## Cheat Sheet diff --git a/yellow-paper/docs/cryptography/hashing/hashing.md b/yellow-paper/docs/cryptography/hashing/hashing.md index a3e4cb5f5e73..d720c250cc96 100644 --- a/yellow-paper/docs/cryptography/hashing/hashing.md +++ b/yellow-paper/docs/cryptography/hashing/hashing.md @@ -24,6 +24,6 @@ Pseudo-randomness is required in cases such as: - Fiat-Shamir challenge generation. - Expanding a random seed to generate additional randomness. - - See the derivation of [master secret keys](../../addresses-and-keys/keys.md#master-keys). + - See the derivation of [master secret keys](../../addresses-and-keys/keys.mdx#master-keys). - Deriving a nullifier, and siloing a nullifier. - - See [deriving a nullifier](../../addresses-and-keys/keys.md#deriving-a-nullifier-within-an-app-contract). + - See [deriving a nullifier](../../addresses-and-keys/keys.mdx#deriving-a-nullifier-within-an-app-contract). diff --git a/yellow-paper/docs/public-vm/gen/_instruction-set.mdx b/yellow-paper/docs/public-vm/gen/_instruction-set.mdx index 73902c179efd..08a13d9618d6 100644 --- a/yellow-paper/docs/public-vm/gen/_instruction-set.mdx +++ b/yellow-paper/docs/public-vm/gen/_instruction-set.mdx @@ -499,6 +499,7 @@ Addition (a + b) - **bOffset**: memory offset of the operation's right input - **dstOffset**: memory offset specifying where to store operation's result - **Expression**: `M[dstOffset] = M[aOffset] + M[bOffset] mod 2^k` +- **Details**: Wraps on overflow - **Tag checks**: `T[aOffset] == T[bOffset] == inTag` - **Tag updates**: `T[dstOffset] = inTag` - **Bit-size**: 128 @@ -520,6 +521,7 @@ Subtraction (a - b) - **bOffset**: memory offset of the operation's right input - **dstOffset**: memory offset specifying where to store operation's result - **Expression**: `M[dstOffset] = M[aOffset] - M[bOffset] mod 2^k` +- **Details**: Wraps on undeflow - **Tag checks**: `T[aOffset] == T[bOffset] == inTag` - **Tag updates**: `T[dstOffset] = inTag` - **Bit-size**: 128 @@ -541,6 +543,7 @@ Multiplication (a * b) - **bOffset**: memory offset of the operation's right input - **dstOffset**: memory offset specifying where to store operation's result - **Expression**: `M[dstOffset] = M[aOffset] * M[bOffset] mod 2^k` +- **Details**: Wraps on overflow - **Tag checks**: `T[aOffset] == T[bOffset] == inTag` - **Tag updates**: `T[dstOffset] = inTag` - **Bit-size**: 128 diff --git a/yellow-paper/sidebars.js b/yellow-paper/sidebars.js index 08d7a2b3e5d4..72e5a35c7dfd 100644 --- a/yellow-paper/sidebars.js +++ b/yellow-paper/sidebars.js @@ -178,7 +178,6 @@ const sidebars = { "decentralization/p2p-network", ], }, - // Protocol Statements? { label: "Circuits", type: "category", From 45ed9271f71896805f532d4fd79af09951d93988 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Thu, 4 Apr 2024 08:50:27 +0000 Subject: [PATCH 4/5] doc: addressing review comments --- .../docs/contract-deployment/classes.md | 24 ++++++++++--------- .../docs/cryptography/merkle-trees.md | 13 ++++++---- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/yellow-paper/docs/contract-deployment/classes.md b/yellow-paper/docs/contract-deployment/classes.md index e431b9bdedba..0131e7458084 100644 --- a/yellow-paper/docs/contract-deployment/classes.md +++ b/yellow-paper/docs/contract-deployment/classes.md @@ -109,6 +109,8 @@ private_function_leaf_crh( Even though not enforced by the protocol, it is suggested for the `artifact_hash` to follow this general structure, in order to be compatible with the definition of the [`broadcast` function below](#broadcast). +Note: below, `sha256_modulo(x) = sha256(x) % FIELD_MODULUS`. This approach must not be used if seeking pseudo-randomness, but can be used for collision resistance. + ```rust @@ -116,8 +118,8 @@ artifact_crh( artifact // This type is out of protocol, e.g. the format output by Nargo ) -> Field { - let private_functions_artifact_leaves: [u8; 32][] = artifact.private_functions.map(|f| - sha256( + let private_functions_artifact_leaves: Field[] = artifact.private_functions.map(|f| + sha256_modulo( be_string_to_bits("az_artifact_private_function_leaf"), f.selector, // 32-bits @@ -125,10 +127,10 @@ artifact_crh( sha256(f.private_bytecode) ) ); - let private_functions_artifact_tree_root: [u8; 32] = merkleize(private_functions_artifact_leaves); + let private_functions_artifact_tree_root: Field = merkleize(private_functions_artifact_leaves); - let unconstrained_functions_artifact_leaves: [u8; 32][] = artifact.unconstrained_functions.map(|f| - sha256( + let unconstrained_functions_artifact_leaves: Field[] = artifact.unconstrained_functions.map(|f| + sha256_modulo( be_string_to_bits("az_artifact_unconstrained_function_leaf"), f.selector, // 32-bits @@ -136,9 +138,9 @@ artifact_crh( sha256(f.unconstrained_bytecode) ) ); - let unconstrained_functions_artifact_tree_root: [u8; 32] = merkleize(unconstrained_functions_artifact_leaves); + let unconstrained_functions_artifact_tree_root: Field = merkleize(unconstrained_functions_artifact_leaves); - let artifact_hash_256_bit: [u8; 32] = sha256( + let artifact_hash: Field = sha256_modulo( be_string_to_field("az_artifact"), private_functions_artifact_tree_root, // 256-bits @@ -165,10 +167,10 @@ The metadata hash for each function is suggested to be computed as the sha256 of ```rust function_metadata_crh( function // This type is out of protocol, e.g. the format output by Nargo -) -> [u8; 32] { +) -> Field { let function_metadata = omit(function, "bytecode", "debug_symbols"); - let function_metadata_hash: [u8; 32] = sha256( + let function_metadata_hash: Field = sha256_modulo( be_string_to_bits("az_function_metadata"), json_serialize(function_metadata) @@ -183,10 +185,10 @@ The artifact metadata stores all data that is not contained within the contract ```rust artifact_metadata_crh( artifact // This type is out of protocol, e.g. the format output by Nargo -) -> [u8; 32] { +) -> Field { let artifact_metadata = omit(artifact, "functions", "file_map"); - let artifact_metadata_hash: [u8; 32] = sha256( + let artifact_metadata_hash: Field = sha256_modulo( be_string_to_bits("az_artifact_metadata"), json_serialize(artifact_metadata) diff --git a/yellow-paper/docs/cryptography/merkle-trees.md b/yellow-paper/docs/cryptography/merkle-trees.md index 7af9de871972..1fbf38e89a32 100644 --- a/yellow-paper/docs/cryptography/merkle-trees.md +++ b/yellow-paper/docs/cryptography/merkle-trees.md @@ -40,10 +40,6 @@ The leaves of a tree are indexed from `0`. The first, [left-most](#orientation) All nodes of the tree (including the leaves) can be indexed. The method of indexing might depend on the algorithm being applied to the tree. -### Relationships - -The terms "parent", "child", "sibling", "ancestor", "descendant", are the well-known definitions. - ### Path The path from (or "of") a particular node is a vector of that node's ancestors. That is, the node's parent, then its parent's parent, and so on, all the way up to and including the root. @@ -53,6 +49,15 @@ The path from (or "of") a particular node is a vector of that node's ancestors. The sibling path of a particular node is, loosely, a vector of the siblings of the nodes in its [path](#path), except it also includes the node's sibling, and excludes the root (which has no sibling). The first element in the sibling path is the node's sibling. Then, the node's parent's sibling, then its parent's parent's sibling, and so on. +### Membership Witness + +The membership witness for a particular leaf, is the minimum data needed to prove that leaf value's existence in the tree. That is: + +- The leaf's [leaf index](#leaf-index) +- The leaf's [sibling path](#sibling-path) + +(and the leaf value itself, of course, but we don't include that in this "membership witness" definition). + ## Hashing Used for computing the parent nodes of all merkle trees. From 889cc407b786392e15a7bfced3677bf0329ed29a Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Thu, 4 Apr 2024 09:06:30 +0000 Subject: [PATCH 5/5] chore: bodge to make latex gdef imports work --- .../{keys-latex-preamble.md => 0-keys-latex-preamble.md} | 0 .../example-usage/diversified-and-stealth-keys.mdx | 2 +- .../docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx | 2 +- .../docs/addresses-and-keys/example-usage/nullifier.mdx | 2 +- .../example-usage/tag-sequence-derivation.mdx | 2 +- yellow-paper/docs/addresses-and-keys/keys.mdx | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename yellow-paper/docs/addresses-and-keys/{keys-latex-preamble.md => 0-keys-latex-preamble.md} (100%) diff --git a/yellow-paper/docs/addresses-and-keys/keys-latex-preamble.md b/yellow-paper/docs/addresses-and-keys/0-keys-latex-preamble.md similarity index 100% rename from yellow-paper/docs/addresses-and-keys/keys-latex-preamble.md rename to yellow-paper/docs/addresses-and-keys/0-keys-latex-preamble.md diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx b/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx index 253d3ff0d501..b973e567ff5a 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx +++ b/yellow-paper/docs/addresses-and-keys/example-usage/diversified-and-stealth-keys.mdx @@ -1,6 +1,6 @@ -import LatexPreamble from "../keys-latex-preamble.md"; +import LatexPreamble from "../0-keys-latex-preamble.md"; ; ## Deriving diversified public keys diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx b/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx index ec9594200801..92835a848e93 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx +++ b/yellow-paper/docs/addresses-and-keys/example-usage/encrypt-and-tag.mdx @@ -1,6 +1,6 @@ -import LatexPreamble from "../keys-latex-preamble.md"; +import LatexPreamble from "../0-keys-latex-preamble.md"; ; ## Encrypt and tag an incoming message diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx b/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx index 1634b870f937..6504066b7ce0 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx +++ b/yellow-paper/docs/addresses-and-keys/example-usage/nullifier.mdx @@ -1,6 +1,6 @@ -import LatexPreamble from "../keys-latex-preamble.md"; +import LatexPreamble from "../0-keys-latex-preamble.md"; ; ## Deriving a nullifier within an app contract diff --git a/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx b/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx index a05338a77c08..4919c665fce3 100644 --- a/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx +++ b/yellow-paper/docs/addresses-and-keys/example-usage/tag-sequence-derivation.mdx @@ -1,6 +1,6 @@ -import LatexPreamble from "../keys-latex-preamble.md"; +import LatexPreamble from "../0-keys-latex-preamble.md"; ; # Handshaking for tag-hopping diff --git a/yellow-paper/docs/addresses-and-keys/keys.mdx b/yellow-paper/docs/addresses-and-keys/keys.mdx index 489740b6a696..03eb55d15441 100644 --- a/yellow-paper/docs/addresses-and-keys/keys.mdx +++ b/yellow-paper/docs/addresses-and-keys/keys.mdx @@ -7,7 +7,7 @@ description: Specification for default privacy keys format and derivation, and n -import LatexPreamble from "./keys-latex-preamble.md"; +import LatexPreamble from "./0-keys-latex-preamble.md"; ; ## Cheat Sheet