diff --git a/Cargo.toml b/Cargo.toml index 7174f780f..e7c739774 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,4 @@ sha2 = { version = "0.10", default-features = false } sha3 = { version = "0.10", default-features = false } itertools = { version = "0.12", default-features = false } tagged-base64 = "0.4" +zeroize = { version = "^1.8" } diff --git a/elgamal/Cargo.toml b/elgamal/Cargo.toml index 25c7fbb31..a334dc207 100644 --- a/elgamal/Cargo.toml +++ b/elgamal/Cargo.toml @@ -20,7 +20,7 @@ displaydoc = { workspace = true } jf-relation = { version = "0.4.4", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", optional = true, default-features = false } jf-rescue = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false } rayon = { version = "1.5.0", optional = true } -zeroize = { version = "1.5", default-features = false } +zeroize = { workspace = true } [dev-dependencies] ark-ed-on-bls12-377 = "0.4.0" diff --git a/merkle_tree/Cargo.toml b/merkle_tree/Cargo.toml index 49ea399e3..1d6221085 100644 --- a/merkle_tree/Cargo.toml +++ b/merkle_tree/Cargo.toml @@ -24,7 +24,7 @@ displaydoc = { workspace = true } hashbrown = { workspace = true } hex = "0.4.3" itertools = { workspace = true, features = ["use_alloc"] } -jf-poseidon2 = { path = "../poseidon2" } +jf-poseidon2 = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish" } jf-relation = { version = "0.4.4", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", optional = true, default-features = false } jf-rescue = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false } jf-utils = { version = "0.4.4", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false } diff --git a/plonk/src/proof_system/structs.rs b/plonk/src/proof_system/structs.rs index 275e204bb..a817f8b34 100644 --- a/plonk/src/proof_system/structs.rs +++ b/plonk/src/proof_system/structs.rs @@ -289,7 +289,6 @@ impl ProofEvaluations { /// create variables for the ProofEvaluations who's field /// is smaller than plonk circuit field. /// The output wires are in the FpElemVar form. - pub(crate) fn create_variables( &self, circuit: &mut PlonkCircuit, diff --git a/poseidon2/Cargo.toml b/poseidon2/Cargo.toml index 3f88081ba..5e48f0e49 100644 --- a/poseidon2/Cargo.toml +++ b/poseidon2/Cargo.toml @@ -16,7 +16,11 @@ ark-bn254 = { workspace = true, optional = true } ark-ff = { workspace = true } ark-std = { workspace = true } hex = "0.4.3" +jf-crhf = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false } lazy_static = "1.5.0" +# TODO: fix this once upstream release a new tag +nimue = { git = "https://github.com/alxiong/nimue", branch = "ax/0.1.1", features = ["ark"] } +zeroize = { workspace = true } [dev-dependencies] criterion = "0.5.1" diff --git a/poseidon2/src/constants.rs b/poseidon2/src/constants.rs index 6cfe191bc..9e0375b5c 100644 --- a/poseidon2/src/constants.rs +++ b/poseidon2/src/constants.rs @@ -8,6 +8,7 @@ pub mod bls12_381; #[cfg(feature = "bn254")] pub mod bn254; +#[allow(dead_code)] #[inline] pub(crate) fn from_hex(s: &str) -> F { F::from_be_bytes_mod_order(&<[u8; 32]>::from_hex(s).expect("Invalid HexStr")) @@ -31,6 +32,7 @@ macro_rules! define_poseidon2_params { /// - sbox size = $sbox_size /// - external rounds = $ext_rounds /// - internal rounds = $int_rounds + #[derive(Clone, Debug)] pub struct $struct_name; impl Poseidon2Params for $struct_name { diff --git a/poseidon2/src/lib.rs b/poseidon2/src/lib.rs index 9670991fe..2686a8cd6 100644 --- a/poseidon2/src/lib.rs +++ b/poseidon2/src/lib.rs @@ -25,6 +25,7 @@ use ark_std::{borrow::ToOwned, marker::PhantomData}; pub mod constants; mod external; mod internal; +pub mod sponge; /// Parameters required for a Poseidon2 permutation instance. /// @@ -33,7 +34,7 @@ mod internal; /// - `T`: state size = rate + capacity, `T` is made generic for easy trait /// bound on `permute(input: [F; N])` and type safety on `external_rc()` /// return type. -pub trait Poseidon2Params { +pub trait Poseidon2Params: Clone { /// t: state size = rate + capacity const T: usize = T; /// d: sbox degree diff --git a/poseidon2/src/sponge.rs b/poseidon2/src/sponge.rs new file mode 100644 index 000000000..7184e5888 --- /dev/null +++ b/poseidon2/src/sponge.rs @@ -0,0 +1,197 @@ +//! Poseidon2-based Cryptographic Sponge + +use crate::{Poseidon2, Poseidon2Params}; +use ark_ff::PrimeField; +use ark_std::marker::PhantomData; +use nimue::{hash::sponge::Sponge, Unit}; +use zeroize::Zeroize; + +/// Poseidon2-based Cryptographic Sponge +/// +/// # Generic parameters: +/// - N: state size = rate (R) + capacity (C) +/// - R: rate (number of field abosrbed/squeezed) +/// +/// For security, for b=128-bit security, field size |F|, C*|F|>=2b: +/// i.e. 128-bit for 256-bit fields, C>=1. +/// This check is being down during `Poseidon2Sponge::new(&iv)` +/// (See Poseidon2 paper Page 7 Footnote 5) +/// +/// For BLS12-381, we choose C=1 for 128 security +/// For BN254, we choose C=1 for (100<*<128)-security +#[derive(Clone, Debug)] +pub struct Poseidon2Sponge> +{ + /// state of sponge + pub(crate) state: [F; N], + _rate: PhantomData<[(); R]>, + _p: PhantomData

, +} + +impl Sponge for Poseidon2Sponge +where + F: PrimeField + Unit, + P: Poseidon2Params, +{ + type U = F; + const N: usize = N; + const R: usize = R; + + fn new(iv: [u8; 32]) -> Self { + assert!(N >= 2 && R > 0 && N > R); + // For security, for b-bit security, field size |F|, C*|F|>=2b: + // at least 100 security required + assert!((N - R) as u32 * ::MODULUS_BIT_SIZE >= 200); + + // fill capacity portion with initial vector IV + let mut state = [F::default(); N]; + state[R] = F::from_be_bytes_mod_order(&iv); + Self { + state, + _rate: PhantomData, + _p: PhantomData, + } + } + + fn permute(&mut self) { + Poseidon2::permute_mut::(&mut self.state); + } +} +impl Default for Poseidon2Sponge +where + F: PrimeField, + P: Poseidon2Params, +{ + fn default() -> Self { + Self { + state: [F::default(); N], + _rate: PhantomData, + _p: PhantomData, + } + } +} + +impl AsRef<[F]> for Poseidon2Sponge +where + F: PrimeField, + P: Poseidon2Params, +{ + fn as_ref(&self) -> &[F] { + &self.state + } +} +impl AsMut<[F]> for Poseidon2Sponge +where + F: PrimeField, + P: Poseidon2Params, +{ + fn as_mut(&mut self) -> &mut [F] { + &mut self.state + } +} + +impl Zeroize for Poseidon2Sponge +where + F: PrimeField, + P: Poseidon2Params, +{ + fn zeroize(&mut self) { + self.state.zeroize(); + } +} + +#[cfg(feature = "bls12-381")] +mod bls12_381 { + #![allow(dead_code)] + use super::*; + use crate::constants::bls12_381::*; + use ark_bls12_381::Fr; + use nimue::hash::sponge::DuplexSponge; + /// A sponge over BLS12-381 scalar field, state_size=2, rate=1. + pub type Poseidon2SpongeBlsN2R1 = DuplexSponge>; + /// A sponge over BLS12-381 scalar field, state_size=3, rate=1. + pub type Poseidon2SpongeBlsN3R1 = DuplexSponge>; + /// A sponge over BLS12-381 scalar field, state_size=3, rate=2. + pub type Poseidon2SpongeBlsN3R2 = DuplexSponge>; + + #[test] + fn test_bls_sponge() { + use super::tests::test_sponge; + test_sponge::(); + test_sponge::(); + test_sponge::(); + } +} + +#[cfg(feature = "bn254")] +mod bn254 { + #![allow(dead_code)] + use super::*; + use crate::constants::bn254::*; + use ark_bn254::Fr; + use nimue::hash::sponge::DuplexSponge; + /// A sponge over BN254 scalar field, state_size=3, rate=1. + pub type Poseidon2SpongeBnN3R1 = DuplexSponge>; + /// A sponge over BN254 scalar field, state_size=3, rate=2. + pub type Poseidon2SpongeBnN3R2 = DuplexSponge>; + + #[test] + fn test_bn_sponge() { + use super::tests::test_sponge; + test_sponge::(); + test_sponge::(); + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use ark_ff::BigInteger; + use ark_std::vec::Vec; + use nimue::{DuplexHash, IOPattern, UnitTranscript}; + + // inspired by: + // + pub(crate) fn test_sponge>() { + let io = IOPattern::::new("test") + .absorb(1, "in") + .squeeze(2048, "out"); + + // prover transcript + let mut merlin = io.to_merlin(); + // prover first message (label: "in") + merlin.add_units(&[F::from(42u32)]).unwrap(); + + let mut merlin_challenges = [F::default(); 2048]; + merlin.fill_challenge_units(&mut merlin_challenges).unwrap(); + + // verifier transcript + let mut arthur = io.to_arthur(merlin.transcript()); + // reading prover's first message labelled "in", since we don't need it, read + // into a one-time throw-away array + arthur.fill_next_units(&mut [F::default()]).unwrap(); + let mut arthur_challenges = [F::default(); 2048]; + arthur.fill_challenge_units(&mut arthur_challenges).unwrap(); + + // challenge computed from both sides should be the same + assert_eq!(merlin_challenges, arthur_challenges); + + // Looking at byte distribution, whether it's close to uniform + let chal_bytes: Vec = merlin_challenges + .iter() + .flat_map(|c| c.into_bigint().to_bytes_le()) + .collect(); + let frequencies = (0u8..=255) + .map(|i| chal_bytes.iter().filter(|&&x| x == i).count()) + .collect::>(); + // the expected frequency if it's uniformly random + let expected_mean = (F::MODULUS_BIT_SIZE / 8 * 2048 / 256) as usize; + assert!( + frequencies + .iter() + .all(|&x| x < expected_mean * 2 && x > expected_mean / 2), + "Counts for each value shouldn't be too far away from mean: {:?}", + frequencies + ); + } +} diff --git a/signature/Cargo.toml b/signature/Cargo.toml index 1d25fe4ed..79e39dfe6 100644 --- a/signature/Cargo.toml +++ b/signature/Cargo.toml @@ -17,7 +17,7 @@ ark-ec = { workspace = true } ark-ff = { workspace = true } ark-serialize = { workspace = true } ark-std = { workspace = true } -blst = { version = "0.3.11", default-features = false } +blst = { version = "0.3.13", default-features = false } derivative = { workspace = true } digest = { workspace = true } displaydoc = { workspace = true } @@ -32,7 +32,12 @@ num-traits = { version = "0.2.15", default-features = false } serde = { workspace = true } sha3 = { workspace = true } tagged-base64 = { workspace = true } -zeroize = { version = "1.5", default-features = false } +zeroize = { workspace = true } + +[target.wasm32-unknown-unknown.dependencies] +# not directly used, but used by every member crate via ark-std +# since we can't specify [target] in workspace Cargo.toml, specify here +getrandom = { version = "^0.2", features = ["js"] } [dev-dependencies] ark-ed-on-bls12-377 = "0.4.0" diff --git a/vid/Cargo.toml b/vid/Cargo.toml index 9d03bcb3e..472819c8f 100644 --- a/vid/Cargo.toml +++ b/vid/Cargo.toml @@ -20,9 +20,9 @@ ark-std = { workspace = true } derivative = { workspace = true } digest = { workspace = true } displaydoc = { workspace = true } -generic-array = { version = "0", features = [ - "serde", -] } # not a direct dependency, but we need serde +# not a direct dependency, but we need serde; +# inherited from digest-v0.10.7->crypto_common->generic-array +generic-array = { version = "0.14.6", features = ["more_lengths", "serde"] } itertools = { workspace = true, features = ["use_alloc"] } jf-merkle-tree = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false } jf-pcs = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false } diff --git a/vrf/Cargo.toml b/vrf/Cargo.toml index 7996b5063..cc3a8a33c 100644 --- a/vrf/Cargo.toml +++ b/vrf/Cargo.toml @@ -17,7 +17,7 @@ displaydoc = { workspace = true } jf-signature = { git = "https://github.com/EspressoSystems/jellyfish", tag = "jf-signature-v0.2.0", default-features = false, features = [ "bls" ] } serde = { workspace = true } sha2 = { workspace = true } -zeroize = { version = "1.5", default-features = false } +zeroize = { workspace = true } [dev-dependencies] jf-utils = { version = "0.4.4", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", default-features = false }