diff --git a/book/src/design/nullifiers.md b/book/src/design/nullifiers.md index 6fdf51a8..7471187c 100644 --- a/book/src/design/nullifiers.md +++ b/book/src/design/nullifiers.md @@ -2,28 +2,34 @@ The nullifier design we use for Orchard is -$$\mathsf{nf} = [Hash_{\mathsf{nk}}(\rho) + \psi \pmod{p}] \mathcal{G} + \mathsf{cm},$$ +$$\mathsf{nf} = [Hash_{\mathsf{nk}}(\psi)] H + [\mathsf{rnf}] \mathcal{I},$$ where: - $Hash$ is a keyed circuit-efficient hash (such as Rescue). -- $\rho$ is unique to this output. As with $\mathsf{h_{Sig}}$ in Sprout, $\rho$ includes - the nullifiers of any Orchard notes being spent. - - If spends and outputs are merged / combined, then we always have a nullifier - (internally derived from a real or dummy note), and can rely on the nullifier - derivation process to prevent an adversary from choosing dummy nullifiers arbitrarily. - - If spends and outputs are *not* merged, then $\rho$ should probably also include - unique information from other parts of the transaction as well. - - TODO: Decide which of the above two cases will be used, and update this. +- $GH$ is a cryptographic hash into the group (such as BLAKE2s with simplified SWU). +- $\mathcal{I}$ is a fixed base, independent of any others returned by $GH$. +- $H$ is a base unique to this output. + - For non-zero-valued notes, $H = GH(\rho)$. As with $\mathsf{h_{Sig}}$ in Sprout, + $\rho$ includes the nullifiers of any Orchard notes being spent in the same action. + Given that an action consists of a single spend and a single output, we set $\rho$ to + be the nullifier of the spent note. + - For zero-valued notes, $H$ is constrained by the circuit to a fixed base independent + of $\mathcal{I}$ and any others returned by $GH$. - $\psi$ is sender-controlled randomness. It is not required to be unique, and in practice - is derived from a sender-selected random value $\mathsf{rseed}$. -- $\mathcal{G}$ is an fixed independent base. + is derived from both $\rho$ and a sender-selected random value $\mathsf{rseed}$: + $\psi = KDF^\psi(\rho, \mathsf{rseed})$. +- $\mathsf{rnf}$ is a blinding scalar, similarly generated as + $\mathsf{rnf} = KDF^\mathsf{rnf}(\rho, \mathsf{rseed})$. This gives a note structure of -$$(addr, v, \rho, \psi, \mathsf{rcm}).$$ +$$(addr, v, H, \psi, \mathsf{rnf}, \mathsf{rcm}).$$ -The nullifier commits to the note value via $\mathsf{cm}$ in order to domain-separate -nullifiers for zero-valued notes from other notes. +The note plaintext includes $\mathsf{rseed}$ in place of $\psi$, $\mathsf{rnf}$, and +$\mathsf{rcm}$. $H$ is omitted entirely from the action: +- Consensus nodes directly derive $GH(\rho)$ and provide it as a public input to the + circuit (which ignores it for zero-valued notes, as with the commitment tree anchor). +- The recipient can recompute the correct $H$ given their additional knowledge of $v$. ## Security properties @@ -48,11 +54,12 @@ We care about several security properties for our nullifiers: - **Faerie Resistance:** can I perform a Faerie Gold attack (i.e. cause notes to be accepted that are unspendable)? + - We're giving the full viewing key to the attacker and allowing it to be the sender in + order to make this property as strong as possible: they will have *all* the notes sent + to that address, and be able to derive *every* nullifier. We assume (and instantiate elsewhere) the following primitives: -- $GH$ is a cryptographic hash into the group (such as BLAKE2s with simplified SWU), used - to derive all fixed independent bases. - $E$ is an elliptic curve (such as Pallas). - $KDF$ is the note encryption key derivation function. @@ -62,9 +69,9 @@ $$ \begin{array}{|l|l|} \text{Balance} & DL_E \\ \text{Note Privacy} & HashDH^{KDF}_E \\ -\text{Note Privacy (OOB)} & \text{Near perfect} \ddagger \\ +\text{Note Privacy (OOB)} & \text{Perfect} \\ \text{Spend Unlinkability} & DDH_E^\dagger \vee PRF_{Hash} \\ -\text{Faerie Resistance} & DL_E \\ +\text{Faerie Resistance} & (RO_{GH} \vee (Coll_{GH} \wedge RO_{Hash})) \wedge DL_E \\ \end{array} $$ @@ -72,15 +79,9 @@ $HashDH^{F}_E$ is computational Diffie-Hellman using $F$ for the key derivation, one-time ephemeral keys. This assumption is heuristically weaker than $DDH_E$ but stronger than $DL_E$. -We omit $RO_{GH}$ as a security assumption because we only rely on the random oracle -applied to fixed inputs defined by the protocol, i.e. to generate the fixed base -$\mathcal{G}$, not to attacker-specified inputs. - -> $\dagger$ We additionally assume that for any input $x$, $\{Hash_{\mathsf{nk}}(x) : -> \mathsf{nk} \in E\}$ gives a scalar in an adequate range for $DDH_E$. (Otherwise, $Hash$ -> could be trivial, e.g. independent of $\mathsf{nk}$.) -> -> $\ddagger$ Statistical distance $< 2^{-167.8}$ from perfect. +> $\dagger$ We additionally assume that for any input $x$, +> $\{Hash_{\mathsf{nk}}(x) : \mathsf{nk} \in E\}$ gives a scalar in an adequate range for +> $DDH_E$. (Otherwise, $Hash$ could be trivial, e.g. independent of $\mathsf{nk}$.) ## Considered alternatives @@ -89,35 +90,29 @@ problem(s) each security property depends on. They may not be accurate and are d not fully rigorous. $$ -\begin{array}{|c|l|c|c|c|c|} +\begin{array}{|c|l|c|c|c|c|c|} \hline -\mathsf{nf} & Note & \text{Balance} & \text{Note Privacy} & \text{Note Privacy (OOB)} & \text{Spend Unlinkability} & \text{Faerie Resistance} \\\hline -[\mathsf{nk}] [\theta] H & (addr, v, H, \theta, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E & RO_{GH} \wedge DL_E \\\hline -[\mathsf{nk}] H + [\mathsf{rnf}] \mathcal{I} & (addr, v, H, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E & RO_{GH} \wedge DL_E \\\hline -Hash([\mathsf{nk}] [\theta] H) & (addr, v, H, \theta, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E \vee Pre_{Hash} & Coll_{Hash} \wedge RO_{GH} \wedge DL_E \\\hline -Hash([\mathsf{nk}] H + [\mathsf{rnf}] \mathcal{I}) & (addr, v, H, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E \vee Pre_{Hash} & Coll_{Hash} \wedge RO_{GH} \wedge DL_E \\\hline -[Hash_{\mathsf{nk}}(\psi)] [\theta] H & (addr, v, H, \theta, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & RO_{GH} \wedge DL_E \\\hline -[Hash_{\mathsf{nk}}(\psi)] H + [\mathsf{rnf}] \mathcal{I} & (addr, v, H, \mathsf{rnf}, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & RO_{GH} \wedge DL_E \\\hline +\mathsf{nf} & Note & \text{Balance} & \text{Note Privacy} & \text{Note Privacy (OOB)} & \text{Spend Unlinkability} & \text{Faerie Resistance} & \text{Rejected because} \\\hline +[\mathsf{nk}] [\theta] H & (addr, v, H, \theta, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E & RO_{GH} \wedge DL_E & \text{No SU for DL-breaking} \\\hline +[\mathsf{nk}] H + [\mathsf{rnf}] \mathcal{I} & (addr, v, H, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E & RO_{GH} \wedge DL_E & \text{No SU for DL-breaking} \\\hline +Hash([\mathsf{nk}] [\theta] H) & (addr, v, H, \theta, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E \vee Pre_{Hash} & Coll_{Hash} \wedge RO_{GH} \wedge DL_E & Coll_{Hash} \text{ for FR} \\\hline +Hash([\mathsf{nk}] H + [\mathsf{rnf}] \mathcal{I}) & (addr, v, H, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E \vee Pre_{Hash} & Coll_{Hash} \wedge RO_{GH} \wedge DL_E & Coll_{Hash} \text{ for FR} \\\hline +[Hash_{\mathsf{nk}}(\psi)] [\theta] H & (addr, v, H, \theta, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & RO_{GH} \wedge DL_E & 2 \text{ variable-base scalar mults} \\\hline [Hash_{\mathsf{nk}}(\psi)] \mathcal{G} + [\theta] H & (addr, v, H, \theta, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & RO_{GH} \wedge DL_E \\\hline -[Hash_{\mathsf{nk}}(\psi)] H + \mathsf{cm} & (addr, v, H, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & PRF_{Hash} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline -[Hash_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + \mathsf{cm} & (addr, v, \rho, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & PRF_{Hash} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline -[Hash_{\mathsf{nk}}(\rho)] \mathcal{G} + \mathsf{cm} & (addr, v, \rho, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & PRF_{Hash} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline -[Hash_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + Commit^{\mathsf{nf}}_{\mathsf{rnf}}(v, \rho) & (addr, v, \rho, \mathsf{rnf}, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline -[Hash_{\mathsf{nk}}(\rho)] \mathcal{G} + Commit^{\mathsf{nf}}_{\mathsf{rnf}}(v, \rho) & (addr, v, \rho, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline -[Hash_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + [\mathsf{rnf}] \mathcal{I} + \mathsf{cm} & (addr, v, \rho, \mathsf{rnf}, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline -[Hash_{\mathsf{nk}}(\rho)] \mathcal{G} + [\mathsf{rnf}] \mathcal{I} + \mathsf{cm} & (addr, v, \rho, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & DL_E \\\hline +[Hash_{\mathsf{nk}}(\psi)] H + \mathsf{cm} & (addr, v, H, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & PRF_{Hash} & DDH_E^\dagger \vee PRF_{Hash} & (RO_{GH} \vee (Coll_{GH} \wedge RO_{Hash})) \wedge DL_E & PRF_{Hash} \text{ for NP(OOB)} \\\hline +[Hash_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + \mathsf{cm} & (addr, v, \rho, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & PRF_{Hash} & DDH_E^\dagger \vee PRF_{Hash} & Coll_{Hash} \wedge DL_E & PRF_{Hash} \text{ for NP(OOB)} \\\hline +[Hash_{\mathsf{nk}}(\rho)] \mathcal{G} + \mathsf{cm} & (addr, v, \rho, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & PRF_{Hash} & DDH_E^\dagger \vee PRF_{Hash} & Coll_{Hash} \wedge DL_E & PRF_{Hash} \text{ for NP(OOB)} \\\hline +[Hash_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + Commit^{\mathsf{nf}}_{\mathsf{rnf}}(v, \rho) & (addr, v, \rho, \mathsf{rnf}, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & Coll_{Hash} \wedge DL_E & Coll_{Hash} \text{ for FR} \\\hline +[Hash_{\mathsf{nk}}(\rho)] \mathcal{G} + Commit^{\mathsf{nf}}_{\mathsf{rnf}}(v, \rho) & (addr, v, \rho, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & Coll_{Hash} \wedge DL_E & Coll_{Hash} \text{ for FR} \\\hline +[Hash_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + [\mathsf{rnf}] \mathcal{I} + \mathsf{cm} & (addr, v, \rho, \mathsf{rnf}, \psi, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & Coll_{Hash} \wedge DL_E & Coll_{Hash} \text{ for FR} \\\hline +[Hash_{\mathsf{nk}}(\rho)] \mathcal{G} + [\mathsf{rnf}] \mathcal{I} + \mathsf{cm} & (addr, v, \rho, \mathsf{rnf}, \mathsf{rcm}) & DL_E & HashDH^{KDF}_E & \text{Perfect} & DDH_E^\dagger \vee PRF_{Hash} & Coll_{Hash} \wedge DL_E & Coll_{Hash} \text{ for FR} \\\hline \end{array} $$ In the above alternatives: -- $H$ is calculated by the sender as $H = GH(\rho)$, and would be provided in the action. -- $\mathcal{I}$ is an fixed independent base, independent of $\mathcal{G}$ and any others +- $\mathcal{G}$ is an fixed independent base, independent of $\mathcal{I}$ and any others returned by $GH$. -For the options that use $H$, when spending a note, -- if it's a real note, then $H$ is as computed for that note, so it is a unique RO output; -- if it's a dummy note, we enforce that it is some fixed base independent of other bases. - The $Commit^{\mathsf{nf}}$ variants enabled nullifier domain separation based on note value, without directly depending on $\mathsf{cm}$ (which in its native type is a base field element, not a group element). We decided instead to follow Sapling by defining an