diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 7c52eb0bc..c9b43e6e2 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -8,6 +8,7 @@ pub mod note_encryption; pub mod pedersen_hash; pub mod prover; pub mod redjubjub; +mod spec; mod tree; pub mod util; pub mod value; diff --git a/zcash_primitives/src/sapling/note.rs b/zcash_primitives/src/sapling/note.rs index 3e20c7bf4..fa652c4aa 100644 --- a/zcash_primitives/src/sapling/note.rs +++ b/zcash_primitives/src/sapling/note.rs @@ -1,4 +1,3 @@ -use blake2s_simd::Params as Blake2sParams; use byteorder::{LittleEndian, WriteBytesExt}; use group::{ ff::{Field, PrimeField}, @@ -84,22 +83,7 @@ impl Note { /// Computes the nullifier given the nullifier deriving key and /// note position pub fn nf(&self, nk: &NullifierDerivingKey, position: u64) -> Nullifier { - // Compute rho = cm + position.G - let rho = self.cm_full_point() - + (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position)); - - // Compute nf = BLAKE2s(nk | rho) - Nullifier::from_slice( - Blake2sParams::new() - .hash_length(32) - .personal(constants::PRF_NF_PERSONALIZATION) - .to_state() - .update(&nk.0.to_bytes()) - .update(&rho.to_bytes()) - .finalize() - .as_bytes(), - ) - .unwrap() + Nullifier::derive(nk, self.cm_full_point(), position) } /// Computes the note commitment diff --git a/zcash_primitives/src/sapling/note/nullifier.rs b/zcash_primitives/src/sapling/note/nullifier.rs index f9add0adf..bf84dd74b 100644 --- a/zcash_primitives/src/sapling/note/nullifier.rs +++ b/zcash_primitives/src/sapling/note/nullifier.rs @@ -2,6 +2,11 @@ use std::array::TryFromSliceError; use subtle::{Choice, ConstantTimeEq}; +use crate::sapling::{ + keys::NullifierDerivingKey, + spec::{mixing_pedersen_hash, prf_nf}, +}; + /// Typesafe wrapper for nullifier values. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Nullifier(pub [u8; 32]); @@ -14,6 +19,20 @@ impl Nullifier { pub fn to_vec(&self) -> Vec { self.0.to_vec() } + + /// $DeriveNullifier$. + /// + /// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers]. + /// + /// [commitmentsandnullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers + pub(super) fn derive( + nk: &NullifierDerivingKey, + cm: jubjub::SubgroupPoint, + position: u64, + ) -> Self { + let rho = mixing_pedersen_hash(cm, position); + Nullifier(prf_nf(&nk.0, &rho)) + } } impl AsRef<[u8]> for Nullifier { diff --git a/zcash_primitives/src/sapling/spec.rs b/zcash_primitives/src/sapling/spec.rs new file mode 100644 index 000000000..f3686a99a --- /dev/null +++ b/zcash_primitives/src/sapling/spec.rs @@ -0,0 +1,36 @@ +//! Helper functions defined in the Zcash Protocol Specification. + +use blake2s_simd::Params as Blake2sParams; +use group::GroupEncoding; + +use crate::constants::{NULLIFIER_POSITION_GENERATOR, PRF_NF_PERSONALIZATION}; + +/// $MixingPedersenHash$. +/// +/// Defined in [Zcash Protocol Spec § 5.4.1.8: Mixing Pedersen Hash Function][concretemixinghash]. +/// +/// [concretemixinghash]: https://zips.z.cash/protocol/protocol.pdf#concretemixinghash +pub(crate) fn mixing_pedersen_hash( + cm: jubjub::SubgroupPoint, + position: u64, +) -> jubjub::SubgroupPoint { + cm + (NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position)) +} + +/// $PRF^\mathsf{nfSapling}_{nk}(\rho)$ +/// +/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. +/// +/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs +pub(crate) fn prf_nf(nk: &jubjub::SubgroupPoint, rho: &jubjub::SubgroupPoint) -> [u8; 32] { + Blake2sParams::new() + .hash_length(32) + .personal(PRF_NF_PERSONALIZATION) + .to_state() + .update(&nk.to_bytes()) + .update(&rho.to_bytes()) + .finalize() + .as_bytes() + .try_into() + .expect("output length is correct") +}