From 26f6fe54f6c98c81e70cc99f84b9a0442c4ff36e Mon Sep 17 00:00:00 2001 From: David Rusu Date: Wed, 12 Jun 2024 16:28:49 -0400 Subject: [PATCH] cl: add nullifier module --- cl/Cargo.toml | 3 +- cl/src/main.rs | 1 + cl/src/nullifier.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 cl/src/nullifier.rs diff --git a/cl/Cargo.toml b/cl/Cargo.toml index eecb32a..13a5a73 100644 --- a/cl/Cargo.toml +++ b/cl/Cargo.toml @@ -11,4 +11,5 @@ jubjub = "0.10.0" group = "0.13.0" rand_core = "0.6.0" rand_chacha = "0.3.1" -lazy_static = "1.4.0" \ No newline at end of file +lazy_static = "1.4.0" +hex = "0.4.3" diff --git a/cl/src/main.rs b/cl/src/main.rs index 1bd1f87..878d5bc 100644 --- a/cl/src/main.rs +++ b/cl/src/main.rs @@ -1,5 +1,6 @@ mod crypto; mod note; +mod nullifier; fn main() { println!("Hello, world!"); diff --git a/cl/src/nullifier.rs b/cl/src/nullifier.rs new file mode 100644 index 0000000..bc7ff32 --- /dev/null +++ b/cl/src/nullifier.rs @@ -0,0 +1,107 @@ +// The Nullifier is used to detect if a note has +// already been consumed. + +// The same nullifier secret may be used across multiple +// notes to allow users to hold fewer secrets. A note +// nonce is used to disambiguate when the same nullifier +// secret is used for multiple notes. +use blake2::{Blake2s256, Digest}; +use hex; +use rand_core::{RngCore, SeedableRng}; + +// Maintained privately by note holder +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NullifierSecret([u8; 16]); + +// Nullifier commitment is public information that +// can be provided to anyone wishing to transfer +// you a note +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NullifierCommitment([u8; 32]); + +// To allow users to maintain fewer nullifier secrets, we +// provide a nonce to differentiate notes controlled by the same +// secret. Each note is assigned a unique nullifier nonce. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NullifierNonce([u8; 16]); + +// The nullifier attached to input notes to prove an input has not +// already been spent. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Nullifier([u8; 32]); + +impl NullifierSecret { + fn random(mut rng: impl RngCore) -> Self { + let mut sk = [0u8; 16]; + rng.fill_bytes(&mut sk); + Self(sk) + } + + fn commit(&self) -> NullifierCommitment { + let mut hasher = Blake2s256::new(); + hasher.update(b"NOMOS_CL_NULL_COMMIT"); + hasher.update(&self.0); + + let commit_bytes: [u8; 32] = hasher.finalize().into(); + NullifierCommitment(commit_bytes) + } +} + +impl NullifierCommitment { + pub fn to_hex(&self) -> String { + hex::encode(&self.0) + } +} + +impl NullifierNonce { + fn random(mut rng: impl RngCore) -> Self { + let mut nonce = [0u8; 16]; + rng.fill_bytes(&mut nonce); + Self(nonce) + } +} + +impl Nullifier { + fn new(sk: NullifierSecret, nonce: NullifierNonce) -> Self { + let mut hasher = Blake2s256::new(); + hasher.update(b"NOMOS_CL_NULLIFIER"); + hasher.update(&sk.0); + hasher.update(&nonce.0); + + let nf_bytes: [u8; 32] = hasher.finalize().into(); + Self(nf_bytes) + } +} + +fn seed_rng(seed: u64) -> impl rand_core::RngCore { + let mut bytes = [0u8; 32]; + (&mut bytes[..8]).copy_from_slice(&seed.to_le_bytes()); + rand_chacha::ChaCha12Rng::from_seed(bytes) +} + +#[test] +fn test_nullifier_commitment_vectors() { + assert_eq!( + NullifierSecret([0u8; 16]).commit().to_hex(), + "384318f9864fe57647bac344e2afdc500a672dedb29d2dc63b004e940e4b382a" + ); + assert_eq!( + NullifierSecret([1u8; 16]).commit().to_hex(), + "0fd667e6bb39fbdc35d6265726154b839638ea90bcf4e736953ccf27ca5f870b" + ); + assert_eq!( + NullifierSecret([u8::MAX; 16]).commit().to_hex(), + "1cb78e487eb0b3116389311fdde84cd3f619a4d7f487b29bf5a002eed3784d75" + ); +} + +#[test] +fn test_nullifier_same_sk_different_nonce() { + let sk = NullifierSecret::random(seed_rng(0)); + let nonce_1 = NullifierNonce::random(seed_rng(1)); + let nonce_2 = NullifierNonce::random(seed_rng(2)); + let nf_1 = Nullifier::new(sk, nonce_1); + let nf_2 = Nullifier::new(sk, nonce_2); + + assert_ne!(nf_1, nf_2); +}