From 0a9b29bc86a8d5e08e7014cb52d4457bec906ebd Mon Sep 17 00:00:00 2001 From: Ben Savage Date: Thu, 28 Mar 2024 13:05:34 +0800 Subject: [PATCH] Alex's comments --- ipa-core/src/protocol/ipa_prf/.DS_Store | Bin 0 -> 6148 bytes .../ipa_prf/malicious_security/hashing.rs | 56 +-- .../ipa_prf/malicious_security/mod.rs | 2 +- .../ipa_prf/malicious_security/prover copy.rs | 324 ++++++++++++++++++ .../ipa_prf/malicious_security/prover.rs | 2 +- 5 files changed, 361 insertions(+), 23 deletions(-) create mode 100644 ipa-core/src/protocol/ipa_prf/.DS_Store create mode 100644 ipa-core/src/protocol/ipa_prf/malicious_security/prover copy.rs diff --git a/ipa-core/src/protocol/ipa_prf/.DS_Store b/ipa-core/src/protocol/ipa_prf/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a6fc5d4cf9032c0299ed902d4fd779aeb8b51c5f GIT binary patch literal 6148 zcmeHKOG*P#5UkcL0&apUm+uPRU=uR=XU1(|Azj<{C`Z+P6|kYf2Dv;7R$w)uT;Ht^m5*78~v8< uH6L_0u7ko5?U)$tm>X}$S5cI8&DVV16%L6(XFlje{S3G+GAZ!a3VZ?$PZ=2i literal 0 HcmV?d00001 diff --git a/ipa-core/src/protocol/ipa_prf/malicious_security/hashing.rs b/ipa-core/src/protocol/ipa_prf/malicious_security/hashing.rs index 0b82df05a..f91c881ac 100644 --- a/ipa-core/src/protocol/ipa_prf/malicious_security/hashing.rs +++ b/ipa-core/src/protocol/ipa_prf/malicious_security/hashing.rs @@ -1,35 +1,33 @@ use std::convert::Infallible; use generic_array::GenericArray; -use sha2::{Digest, Sha256}; -use typenum::U32; +use sha2::{ + digest::{Output, OutputSizeUser}, + Digest, Sha256, +}; use crate::{ ff::{Field, Serializable}, - helpers::Message, protocol::prss::FromRandomU128, }; -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Hash(pub(crate) GenericArray); +#[derive(Debug, PartialEq)] +pub struct Hash(Output); impl Serializable for Hash { - type Size = U32; + type Size = ::OutputSize; + type DeserializationError = Infallible; fn serialize(&self, buf: &mut GenericArray) { - *buf = self.0; + buf.copy_from_slice(&self.0); } fn deserialize(buf: &GenericArray) -> Result { - Ok(Hash(*buf)) + Ok(Hash(*Output::::from_slice(buf))) } } -impl Message for Hash {} - -/// This function allows to compute a hash of a slice of shared values. -/// The output is a single `Hash` struct that can be sent over the network channel to other helper parties. pub fn compute_hash<'a, I, S>(input: I) -> Hash where I: IntoIterator, @@ -37,20 +35,25 @@ where { // set up hash let mut sha = Sha256::new(); + let mut buf = GenericArray::default(); + let mut is_empty = true; + // set state for x in input { - let mut buf = GenericArray::default(); + is_empty = false; x.serialize(&mut buf); - sha.update(buf); + sha.update(&buf); } + + assert!(!is_empty, "must not provide an empty iterator"); // compute hash - Hash(*GenericArray::::from_slice(&sha.finalize()[0..32])) + Hash(sha.finalize()) } -/// This function allows to hash a vector of field elements into a single field element +/// This function takes two hash a vector of field elements into a single field element /// # Panics /// does not panic -pub fn hash_to_field(left: Hash, right: Hash) -> F +pub fn hash_to_field(left: &Hash, right: &Hash) -> F where F: Field + FromRandomU128, { @@ -78,13 +81,12 @@ where mod test { use rand::{thread_rng, Rng}; + use super::compute_hash; use crate::{ ff::{Fp31, Fp32BitPrime}, protocol::ipa_prf::malicious_security::hashing::hash_to_field, }; - use super::compute_hash; - #[test] fn hash_changes() { const LIST_LENGTH: usize = 5; @@ -111,6 +113,18 @@ mod test { hash_1, hash_2, "The hash should change if the input is different" ); + + // swapping two elements should change the hash + let index_1 = rng.gen_range(0..LIST_LENGTH); + let index_2 = (index_1 + rng.gen_range(1..LIST_LENGTH)) % LIST_LENGTH; + list.swap(index_1, index_2); + + let hash_3 = compute_hash(&list); + + assert_ne!( + hash_2, hash_3, + "The hash should change if two elements are swapped" + ); } #[test] @@ -125,7 +139,7 @@ mod test { left.push(rng.gen::()); right.push(rng.gen::()); } - let r1: Fp32BitPrime = hash_to_field(compute_hash(&left), compute_hash(&right)); + let r1: Fp32BitPrime = hash_to_field(&compute_hash(&left), &compute_hash(&right)); // modify one, randomly selected element in the list let random_index = rng.gen::() % LIST_LENGTH; @@ -137,7 +151,7 @@ mod test { right[random_index] = modified_value; } - let r2: Fp32BitPrime = hash_to_field(compute_hash(&left), compute_hash(&right)); + let r2: Fp32BitPrime = hash_to_field(&compute_hash(&left), &compute_hash(&right)); assert_ne!( r1, r2, diff --git a/ipa-core/src/protocol/ipa_prf/malicious_security/mod.rs b/ipa-core/src/protocol/ipa_prf/malicious_security/mod.rs index 89c576a22..9fd7a8b01 100644 --- a/ipa-core/src/protocol/ipa_prf/malicious_security/mod.rs +++ b/ipa-core/src/protocol/ipa_prf/malicious_security/mod.rs @@ -1,4 +1,4 @@ -pub mod hashing; +mod hashing; pub mod lagrange; pub mod prover; pub mod verifier; diff --git a/ipa-core/src/protocol/ipa_prf/malicious_security/prover copy.rs b/ipa-core/src/protocol/ipa_prf/malicious_security/prover copy.rs new file mode 100644 index 000000000..ef84742ea --- /dev/null +++ b/ipa-core/src/protocol/ipa_prf/malicious_security/prover copy.rs @@ -0,0 +1,324 @@ +use std::{ + iter::zip, + ops::{Add, Sub}, +}; + +use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}; +use typenum::{Diff, Sum, U1}; + +use super::hashing::{compute_hash, hash_to_field}; +use crate::{ + ff::PrimeField, + protocol::ipa_prf::malicious_security::lagrange::{ + CanonicalLagrangeDenominator, LagrangeTable, + }, +}; + +pub struct ZeroKnowledgeProof { + pub g: GenericArray, +} + +impl ZeroKnowledgeProof +where + F: PrimeField, + N: ArrayLength, +{ + pub fn new(g: I) -> Self + where + I: IntoIterator, + { + ZeroKnowledgeProof { + g: g.into_iter().collect(), + } + } +} + +#[derive(Debug)] +pub struct ProofGenerator { + u: Vec, + v: Vec, +} + +pub type TwoNMinusOne = Diff, U1>; +pub type TwoNPlusOne = Sum, U1>; + +/// +/// Distributed Zero Knowledge Proofs algorithm drawn from +/// `https://eprint.iacr.org/2023/909.pdf` +/// +#[allow(non_camel_case_types)] +impl ProofGenerator +where + F: PrimeField, +{ + pub fn new(u: Vec, v: Vec) -> Self { + debug_assert_eq!(u.len(), v.len(), "u and v must be of equal length"); + Self { u, v } + } + + pub fn gen_challenge_and_recurse<λ: ArrayLength, I, J>( + proof_left: GenericArray>, + proof_right: GenericArray>, + u: I, + v: J, + ) -> ProofGenerator + where + λ: ArrayLength + Add + Sub, + <λ as Add>::Output: Sub, + <<λ as Add>::Output as Sub>::Output: ArrayLength, + <λ as Sub>::Output: ArrayLength, + I: IntoIterator, + J: IntoIterator, + I::IntoIter: ExactSizeIterator, + J::IntoIter: ExactSizeIterator, + { + let mut u = u.into_iter(); + let mut v = v.into_iter(); + + debug_assert_eq!(u.len() % λ::USIZE, 0); // We should pad with zeroes eventually + + let s = u.len() / λ::USIZE; + + assert!( + s > 1, + "When the output is this small, you should validate the proof with a more straightforward reveal" + ); + + let r: F = hash_to_field(compute_hash(&proof_left), compute_hash(&proof_right)); + let mut p = GenericArray::::generate(|_| F::ZERO); + let mut q = GenericArray::::generate(|_| F::ZERO); + let denominator = CanonicalLagrangeDenominator::::new(); + let lagrange_table_r = LagrangeTable::::new(&denominator, &r); + + let pairs = (0..s).map(|_| { + for i in 0..λ::USIZE { + let x = u.next().unwrap_or(F::ZERO); + let y = v.next().unwrap_or(F::ZERO); + p[i] = x; + q[i] = y; + } + let p_r = lagrange_table_r.eval(&p)[0]; + let q_r = lagrange_table_r.eval(&q)[0]; + (p_r, q_r) + }); + let (u, v) = pairs.unzip(); + ProofGenerator::new(u, v) + } + + pub fn compute_proof<λ: ArrayLength, I, J>( + u: I, + v: J, + ) -> ZeroKnowledgeProof> + where + λ: ArrayLength + Add + Sub, + <λ as Add>::Output: Sub, + <<λ as Add>::Output as Sub>::Output: ArrayLength, + <λ as Sub>::Output: ArrayLength, + I: IntoIterator, + J: IntoIterator, + I::IntoIter: ExactSizeIterator, + J::IntoIter: ExactSizeIterator, + { + let mut u = u.into_iter(); + let mut v = v.into_iter(); + + debug_assert_eq!(u.len() % λ::USIZE, 0); // We should pad with zeroes eventually + + let s = u.len() / λ::USIZE; + + assert!( + s > 1, + "When the output is this small, you should call `compute_final_proof`" + ); + + let denominator = CanonicalLagrangeDenominator::::new(); + let lagrange_table = LagrangeTable::>::Output>::from(denominator); + let mut p = GenericArray::::generate(|_| F::ZERO); + let mut q = GenericArray::::generate(|_| F::ZERO); + let mut proof: GenericArray> = GenericArray::generate(|_| F::ZERO); + for _ in 0..s { + for i in 0..λ::USIZE { + let x = u.next().unwrap_or(F::ZERO); + let y = v.next().unwrap_or(F::ZERO); + p[i] = x; + q[i] = y; + proof[i] += x * y; + } + let p_extrapolated = lagrange_table.eval(&p); + let q_extrapolated = lagrange_table.eval(&q); + + for (i, (x, y)) in + zip(p_extrapolated.into_iter(), q_extrapolated.into_iter()).enumerate() + { + proof[λ::USIZE + i] += x * y; + } + } + ZeroKnowledgeProof::new(proof) + } + + pub fn compute_final_proof<λ: ArrayLength, I, J>( + u: I, + v: J, + p_0: F, + q_0: F, + ) -> ZeroKnowledgeProof> + where + λ: ArrayLength + Add + Add, + <λ as Add>::Output: Add, + <<λ as Add>::Output as Add>::Output: ArrayLength, + <λ as Add>::Output: ArrayLength, + I: IntoIterator, + J: IntoIterator, + I::IntoIter: ExactSizeIterator, + J::IntoIter: ExactSizeIterator, + { + let mut u = u.into_iter(); + let mut v = v.into_iter(); + + assert_eq!(u.len(), λ::USIZE); // We should pad with zeroes eventually + assert_eq!(v.len(), λ::USIZE); // We should pad with zeroes eventually + + // We need a table of size `λ + 1` since we add a random point at x=0 + let denominator = CanonicalLagrangeDenominator::>::new(); + let lagrange_table = LagrangeTable::, λ>::from(denominator); + + let mut p = GenericArray::>::generate(|_| F::ZERO); + let mut q = GenericArray::>::generate(|_| F::ZERO); + let mut proof: GenericArray> = GenericArray::generate(|_| F::ZERO); + p[0] = p_0; + q[0] = q_0; + proof[0] = p_0 * q_0; + + for i in 0..λ::USIZE { + let x = u.next().unwrap_or(F::ZERO); + let y = v.next().unwrap_or(F::ZERO); + p[i + 1] = x; + q[i + 1] = y; + proof[i + 1] += x * y; + } + let p_extrapolated = lagrange_table.eval(&p); + let q_extrapolated = lagrange_table.eval(&q); + + for (i, (x, y)) in zip(p_extrapolated.into_iter(), q_extrapolated.into_iter()).enumerate() { + proof[λ::USIZE + 1 + i] += x * y; + } + + ZeroKnowledgeProof::new(proof) + } +} + +impl PartialEq<(&[u128], &[u128])> for ProofGenerator +where + F: PrimeField + std::cmp::PartialEq, +{ + fn eq(&self, other: &(&[u128], &[u128])) -> bool { + let (cmp_a, cmp_b) = other; + for (i, elem) in cmp_a.iter().enumerate() { + if !self.u[i].eq(elem) { + return false; + } + } + for (i, elem) in cmp_b.iter().enumerate() { + if !self.v[i].eq(elem) { + return false; + } + } + true + } +} + +#[cfg(all(test, unit_test))] +mod test { + use generic_array::{sequence::GenericSequence, GenericArray}; + use typenum::{U2, U4, U7}; + + use super::ProofGenerator; + use crate::ff::{Fp31, U128Conversions}; + + #[test] + fn sample_proof() { + const U_1: [u128; 32] = [ + 0, 30, 0, 16, 0, 1, 0, 15, 0, 0, 0, 16, 0, 30, 0, 16, 29, 1, 1, 15, 0, 0, 1, 15, 2, 30, + 30, 16, 0, 0, 30, 16, + ]; + const V_1: [u128; 32] = [ + 0, 0, 0, 30, 0, 0, 0, 1, 30, 30, 30, 30, 0, 0, 30, 30, 0, 30, 0, 30, 0, 0, 0, 1, 0, 0, + 1, 1, 0, 0, 1, 1, + ]; + const PROOF_1: [u128; 7] = [0, 30, 29, 30, 5, 28, 13]; + const PROOF_LEFT_1: [u128; 7] = [1, 4, 10, 15, 12, 15, 29]; + const U_2: [u128; 8] = [0, 0, 26, 0, 7, 18, 24, 13]; + const V_2: [u128; 8] = [10, 21, 30, 28, 15, 21, 3, 3]; + + const PROOF_2: [u128; 7] = [12, 6, 15, 8, 29, 30, 6]; + const PROOF_LEFT_2: [u128; 7] = [30, 28, 10, 25, 12, 23, 29]; + const U_3: [u128; 2] = [3, 3]; + const V_3: [u128; 2] = [5, 24]; + + const PROOF_3: [u128; 5] = [12, 15, 10, 14, 17]; + const P_RANDOM_WEIGHT: u128 = 12; + const Q_RANDOM_WEIGHT: u128 = 1; + + // first iteration + let proof_1 = ProofGenerator::::compute_proof::( + U_1.into_iter().map(|x| Fp31::try_from(x).unwrap()), + V_1.into_iter().map(|x| Fp31::try_from(x).unwrap()), + ); + assert_eq!( + proof_1.g.iter().map(Fp31::as_u128).collect::>(), + PROOF_1, + ); + + // ZKP is secret-shared into two pieces + // proof_left comes from PRSS + let proof_left_1 = + GenericArray::::generate(|i| Fp31::try_from(PROOF_LEFT_1[i]).unwrap()); + let proof_right_1 = GenericArray::::generate(|i| proof_1.g[i] - proof_left_1[i]); + + // fiat-shamir + let pg_2 = ProofGenerator::gen_challenge_and_recurse::( + proof_left_1, + proof_right_1, + U_1.into_iter().map(|x| Fp31::try_from(x).unwrap()), + V_1.into_iter().map(|x| Fp31::try_from(x).unwrap()), + ); + assert_eq!(pg_2, (&U_2[..], &V_2[..])); + + // next iteration + let proof_2 = ProofGenerator::::compute_proof::( + U_2.into_iter().map(|x| Fp31::try_from(x).unwrap()), + V_2.into_iter().map(|x| Fp31::try_from(x).unwrap()), + ); + assert_eq!( + proof_2.g.iter().map(Fp31::as_u128).collect::>(), + PROOF_2, + ); + + // ZKP is secret-shared into two pieces + // proof_left comes from PRSS + let proof_left_2 = + GenericArray::::generate(|i| Fp31::try_from(PROOF_LEFT_2[i]).unwrap()); + let proof_right_2 = GenericArray::::generate(|i| proof_2.g[i] - proof_left_2[i]); + + // fiat-shamir + let pg_3 = ProofGenerator::gen_challenge_and_recurse::( + proof_left_2, + proof_right_2, + pg_2.u, + pg_2.v, + ); + assert_eq!(pg_3, (&U_3[..], &V_3[..])); + + // final iteration + let proof_3 = ProofGenerator::::compute_final_proof::( + pg_3.u, + pg_3.v, + Fp31::try_from(P_RANDOM_WEIGHT).unwrap(), + Fp31::try_from(Q_RANDOM_WEIGHT).unwrap(), + ); + assert_eq!( + proof_3.g.iter().map(Fp31::as_u128).collect::>(), + PROOF_3, + ); + } +} diff --git a/ipa-core/src/protocol/ipa_prf/malicious_security/prover.rs b/ipa-core/src/protocol/ipa_prf/malicious_security/prover.rs index ef84742ea..b8c566bbb 100644 --- a/ipa-core/src/protocol/ipa_prf/malicious_security/prover.rs +++ b/ipa-core/src/protocol/ipa_prf/malicious_security/prover.rs @@ -84,7 +84,7 @@ where "When the output is this small, you should validate the proof with a more straightforward reveal" ); - let r: F = hash_to_field(compute_hash(&proof_left), compute_hash(&proof_right)); + let r: F = hash_to_field(&compute_hash(&proof_left), &compute_hash(&proof_right)); let mut p = GenericArray::::generate(|_| F::ZERO); let mut q = GenericArray::::generate(|_| F::ZERO); let denominator = CanonicalLagrangeDenominator::::new();