From b59462b1b520439e4db38dafc51c2f97a1010191 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Mon, 13 Jan 2025 12:44:57 -0800 Subject: [PATCH 1/5] xof: Have `seed_stream()` take an array --- src/flp/szk.rs | 16 +++++++--------- src/prng.rs | 12 ++++++------ src/vdaf/mastic.rs | 33 ++++++++++++++++++--------------- src/vdaf/prio3.rs | 12 ++++++------ src/vdaf/xof.rs | 14 +++++++------- src/vidpf.rs | 10 +++++----- 6 files changed, 49 insertions(+), 48 deletions(-) diff --git a/src/flp/szk.rs b/src/flp/szk.rs index 72cee5c0..720112b9 100644 --- a/src/flp/szk.rs +++ b/src/flp/szk.rs @@ -303,7 +303,7 @@ impl Szk { /// prover. fn derive_prove_rand(&self, prove_rand_seed: &Seed<32>, ctx: &[u8]) -> Vec { XofTurboShake128::seed_stream( - prove_rand_seed, + prove_rand_seed.as_ref(), &[&mastic::dst_usage(mastic::USAGE_PROVE_RAND), &self.id, ctx], &[], ) @@ -365,7 +365,7 @@ impl Szk { let joint_rand_seed = self.derive_joint_rand_seed(leader_joint_rand_part, helper_joint_rand_part, ctx); let joint_rand = XofTurboShake128::seed_stream( - &joint_rand_seed, + joint_rand_seed.as_ref(), &[&mastic::dst_usage(mastic::USAGE_JOINT_RAND), &self.id, ctx], &[], ) @@ -376,7 +376,7 @@ impl Szk { fn derive_helper_proof_share(&self, proof_share_seed: &Seed<32>, ctx: &[u8]) -> Vec { XofTurboShake128::seed_stream( - proof_share_seed, + proof_share_seed.as_ref(), &[&mastic::dst_usage(USAGE_PROOF_SHARE), &self.id, ctx], &[], ) @@ -390,14 +390,12 @@ impl Szk { level: u16, ctx: &[u8], ) -> Vec { - let mut xof = XofTurboShake128::init( + XofTurboShake128::seed_stream( verify_key, &[&mastic::dst_usage(mastic::USAGE_QUERY_RAND), &self.id, ctx], - ); - xof.update(nonce); - xof.update(&level.to_le_bytes()); - xof.into_seed_stream() - .into_field_vec(self.typ.query_rand_len()) + &[nonce, &level.to_le_bytes()], + ) + .into_field_vec(self.typ.query_rand_len()) } pub(crate) fn requires_joint_rand(&self) -> bool { diff --git a/src/prng.rs b/src/prng.rs index 77c41657..5f23fadc 100644 --- a/src/prng.rs +++ b/src/prng.rs @@ -51,7 +51,7 @@ impl Prng { pub(crate) fn new() -> Result { let seed = Seed::generate()?; Ok(Prng::from_seed_stream(XofTurboShake128::seed_stream( - &seed, + seed.as_ref(), &[], &[], ))) @@ -234,14 +234,14 @@ mod tests { .unwrap(); let expected = Field64::from(4857131209231097247); - let seed_stream = XofTurboShake128::seed_stream(&seed, &[], &[]); + let seed_stream = XofTurboShake128::seed_stream(seed.as_ref(), &[], &[]); let mut prng = Prng::::from_seed_stream(seed_stream); let actual = prng.nth(13882).unwrap(); assert_eq!(actual, expected); #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] { - let mut seed_stream = XofTurboShake128::seed_stream(&seed, &[], &[]); + let mut seed_stream = XofTurboShake128::seed_stream(seed.as_ref(), &[], &[]); let mut actual = ::zero(); for _ in 0..=13882 { actual = ::generate(&mut seed_stream, &()); @@ -257,11 +257,11 @@ mod tests { let seed = Seed::generate().unwrap(); let mut prng: Prng = - Prng::from_seed_stream(XofTurboShake128::seed_stream(&seed, &[], &[])); + Prng::from_seed_stream(XofTurboShake128::seed_stream(seed.as_ref(), &[], &[])); // Construct a `Prng` with a longer-than-usual buffer. let mut prng_weird_buffer_size: Prng = - Prng::from_seed_stream(XofTurboShake128::seed_stream(&seed, &[], &[])); + Prng::from_seed_stream(XofTurboShake128::seed_stream(seed.as_ref(), &[], &[])); let mut extra = [0; 7]; prng_weird_buffer_size.seed_stream.fill_bytes(&mut extra); prng_weird_buffer_size.buffer.extend_from_slice(&extra); @@ -278,7 +278,7 @@ mod tests { fn into_different_field() { let seed = Seed::generate().unwrap(); let want: Prng = - Prng::from_seed_stream(XofTurboShake128::seed_stream(&seed, &[], &[])); + Prng::from_seed_stream(XofTurboShake128::seed_stream(seed.as_ref(), &[], &[])); let want_buffer = want.buffer.clone(); let got: Prng = want.into_new_field(); diff --git a/src/vdaf/mastic.rs b/src/vdaf/mastic.rs index 44f23b73..a5055742 100644 --- a/src/vdaf/mastic.rs +++ b/src/vdaf/mastic.rs @@ -24,6 +24,7 @@ use crate::{ }, }; +use rand::prelude::*; use std::io::{Cursor, Read}; use std::ops::BitAnd; use std::slice::from_ref; @@ -405,7 +406,7 @@ impl<'a, T: Type> ParameterizedDecode<(&'a Mastic, &'a MasticAggregationParam #[derive(Clone, Debug, PartialEq)] pub struct MasticPrepareShare { /// [`Vidpf`] evaluation proof, which guarantees one-hotness and payload consistency. - eval_proof: [u8; VIDPF_PROOF_SIZE], + vidpf_eval_proof: [u8; VIDPF_PROOF_SIZE], /// If [`Szk`]` verification of the root weight is needed, a verification message. szk_query_share_opt: Option>, @@ -413,7 +414,7 @@ pub struct MasticPrepareShare { impl Encode for MasticPrepareShare { fn encode(&self, bytes: &mut Vec) -> Result<(), CodecError> { - bytes.extend_from_slice(&self.eval_proof); + bytes.extend_from_slice(&self.vidpf_eval_proof); match &self.szk_query_share_opt { Some(query_share) => query_share.encode(bytes), None => Ok(()), @@ -436,8 +437,8 @@ impl ParameterizedDecode> for MasticPrepa prep_state: &MasticPrepareState, bytes: &mut Cursor<&[u8]>, ) -> Result { - let mut eval_proof = [0; VIDPF_PROOF_SIZE]; - bytes.read_exact(&mut eval_proof[..])?; + let mut vidpf_eval_proof = [0; VIDPF_PROOF_SIZE]; + bytes.read_exact(&mut vidpf_eval_proof[..])?; let requires_joint_rand = prep_state.szk_query_state.is_some(); let szk_query_share_opt = prep_state .verifier_len @@ -446,7 +447,7 @@ impl ParameterizedDecode> for MasticPrepa }) .transpose()?; Ok(Self { - eval_proof, + vidpf_eval_proof, szk_query_share_opt, }) } @@ -583,13 +584,15 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { c.get_encoded().unwrap() }; - let eval_proof = { - let mut eval_proof_xof = - XofTurboShake128::init(verify_key, &[&dst_usage(USAGE_EVAL_PROOF), &self.id, ctx]); - eval_proof_xof.update(&onehot_check); - eval_proof_xof.update(&counter_check); - eval_proof_xof.update(&payload_check); - eval_proof_xof.into_seed().0 + let vidpf_eval_proof = { + let mut vidpf_eval_proof = [0; VIDPF_PROOF_SIZE]; + XofTurboShake128::seed_stream( + verify_key, + &[&dst_usage(USAGE_EVAL_PROOF), &self.id, ctx], + &[&onehot_check, &counter_check, &payload_check], + ) + .fill(&mut vidpf_eval_proof); + vidpf_eval_proof }; let mut truncated_out_shares = @@ -626,7 +629,7 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { verifier_len: Some(verifier_len), }, MasticPrepareShare { - eval_proof, + vidpf_eval_proof, szk_query_share_opt: Some(szk_query_share), }, ) @@ -638,7 +641,7 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { verifier_len: None, }, MasticPrepareShare { - eval_proof, + vidpf_eval_proof, szk_query_share_opt: None, }, ) @@ -663,7 +666,7 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { "Received more than two prepare shares".to_string(), )); }; - if leader_share.eval_proof != helper_share.eval_proof { + if leader_share.vidpf_eval_proof != helper_share.vidpf_eval_proof { return Err(VdafError::Uncategorized( "Vidpf proof verification failed".to_string(), )); diff --git a/src/vdaf/prio3.rs b/src/vdaf/prio3.rs index 4457e708..1759af27 100644 --- a/src/vdaf/prio3.rs +++ b/src/vdaf/prio3.rs @@ -481,7 +481,7 @@ where fn derive_prove_rands(&self, ctx: &[u8], prove_rand_seed: &Seed) -> Vec { P::seed_stream( - prove_rand_seed, + prove_rand_seed.as_ref(), &[&self.domain_separation_tag(DST_PROVE_RANDOMNESS), ctx], &[&[self.num_proofs]], ) @@ -510,7 +510,7 @@ where ) -> (Seed, Vec) { let joint_rand_seed = self.derive_joint_rand_seed(ctx, joint_rand_parts); let joint_rands = P::seed_stream( - &joint_rand_seed, + joint_rand_seed.as_ref(), &[&self.domain_separation_tag(DST_JOINT_RANDOMNESS), ctx], &[&[self.num_proofs]], ) @@ -526,7 +526,7 @@ where agg_id: u8, ) -> Prng { Prng::from_seed_stream(P::seed_stream( - proofs_share_seed, + proofs_share_seed.as_ref(), &[&self.domain_separation_tag(DST_PROOF_SHARE), ctx], &[&[self.num_proofs, agg_id]], )) @@ -617,7 +617,7 @@ where // This seed is used for both the helper measurement share and the helper proof share let meas_and_proof_share_seed = random_seeds.next().unwrap().try_into().unwrap(); let measurement_share_prng: Prng = Prng::from_seed_stream(P::seed_stream( - &Seed(meas_and_proof_share_seed), + &meas_and_proof_share_seed, &[&self.domain_separation_tag(DST_MEASUREMENT_SHARE), ctx], &[&[agg_id]], )); @@ -1378,7 +1378,7 @@ where } => { let measurement_share = Cow::Owned( P::seed_stream( - meas_and_proofs_share, + meas_and_proofs_share.as_ref(), &[&self.domain_separation_tag(DST_MEASUREMENT_SHARE), ctx], &[&[agg_id]], ) @@ -1560,7 +1560,7 @@ where let measurement_share = match step.measurement_share { Share::Leader(data) => data, Share::Helper(seed) => P::seed_stream( - &seed, + seed.as_ref(), &[&self.domain_separation_tag(DST_MEASUREMENT_SHARE), ctx], &[&[step.agg_id]], ) diff --git a/src/vdaf/xof.rs b/src/vdaf/xof.rs index 5ed1f0fe..c59de144 100644 --- a/src/vdaf/xof.rs +++ b/src/vdaf/xof.rs @@ -143,11 +143,11 @@ pub trait Xof: Clone + Debug { /// Construct a seed stream from the given seed and info string. fn seed_stream( - seed: &Seed, + seed: &[u8; SEED_SIZE], dst_parts: &[&[u8]], binder_parts: &[&[u8]], ) -> Self::SeedStream { - let mut xof = Self::init(seed.as_ref(), dst_parts); + let mut xof = Self::init(seed, dst_parts); for binder_part in binder_parts { xof.update(binder_part); } @@ -586,7 +586,7 @@ mod tests { let mut want = [0; 45]; xof.clone().into_seed_stream().fill_bytes(&mut want); let mut got = [0; 45]; - P::seed_stream(&seed, &[dst], &[binder]).fill_bytes(&mut got); + P::seed_stream(seed.as_ref(), &[dst], &[binder]).fill_bytes(&mut got); assert_eq!(got, want); } @@ -666,11 +666,11 @@ mod tests { fn xof_fixed_key_aes128_incomplete_block() { let seed = Seed::generate().unwrap(); let mut expected = [0; 32]; - XofFixedKeyAes128::seed_stream(&seed, &[b"dst"], &[b"binder"]).fill(&mut expected); + XofFixedKeyAes128::seed_stream(seed.as_ref(), &[b"dst"], &[b"binder"]).fill(&mut expected); for len in 0..=32 { let mut buf = vec![0; len]; - XofFixedKeyAes128::seed_stream(&seed, &[b"dst"], &[b"binder"]).fill(&mut buf); + XofFixedKeyAes128::seed_stream(seed.as_ref(), &[b"dst"], &[b"binder"]).fill(&mut buf); assert_eq!(buf, &expected[..len]); } } @@ -685,11 +685,11 @@ mod tests { let seed_2 = Seed::generate().unwrap(); let mut stream_1_trait_api = - XofFixedKeyAes128::seed_stream(&seed_1, &[fixed_dst, ctx], &[binder]); + XofFixedKeyAes128::seed_stream(seed_1.as_ref(), &[fixed_dst, ctx], &[binder]); let mut output_1_trait_api = [0u8; 32]; stream_1_trait_api.fill(&mut output_1_trait_api); let mut stream_2_trait_api = - XofFixedKeyAes128::seed_stream(&seed_2, &[fixed_dst, ctx], &[binder]); + XofFixedKeyAes128::seed_stream(seed_2.as_ref(), &[fixed_dst, ctx], &[binder]); let mut output_2_trait_api = [0u8; 32]; stream_2_trait_api.fill(&mut output_2_trait_api); diff --git a/src/vidpf.rs b/src/vidpf.rs index 174f4669..8519cc17 100644 --- a/src/vidpf.rs +++ b/src/vidpf.rs @@ -361,7 +361,7 @@ impl Vidpf { fn extend(seed: VidpfSeed, ctx: &[u8], nonce: &[u8]) -> ExtendedSeed { let mut rng = XofFixedKeyAes128::seed_stream( - &Seed(seed), + &seed, &[&mastic::dst_usage(mastic::USAGE_EXTEND), ctx], &[nonce], ); @@ -388,7 +388,7 @@ impl Vidpf { fn convert(&self, seed: VidpfSeed, ctx: &[u8], nonce: &[u8]) -> (VidpfSeed, W) { let mut seed_stream = XofFixedKeyAes128::seed_stream( - &Seed(seed), + &seed, &[&mastic::dst_usage(mastic::USAGE_CONVERT), ctx], &[nonce], ); @@ -1015,7 +1015,7 @@ mod tests { use crate::{ codec::{Encode, ParameterizedDecode}, idpf::IdpfValue, - vdaf::xof::{Seed, Xof, XofTurboShake128}, + vdaf::xof::{Xof, XofTurboShake128}, }; use super::{TestWeight, TEST_WEIGHT_LEN}; @@ -1099,7 +1099,7 @@ mod tests { } fn compatible_weights() -> [TestWeight; 2] { - let mut xof = XofTurboShake128::seed_stream(&Seed(Default::default()), &[], &[]); + let mut xof = XofTurboShake128::seed_stream(&[0; 32], &[], &[]); [ TestWeight::generate(&mut xof, &TEST_WEIGHT_LEN), TestWeight::generate(&mut xof, &TEST_WEIGHT_LEN), @@ -1107,7 +1107,7 @@ mod tests { } fn incompatible_weights() -> [TestWeight; 2] { - let mut xof = XofTurboShake128::seed_stream(&Seed(Default::default()), &[], &[]); + let mut xof = XofTurboShake128::seed_stream(&[0; 32], &[], &[]); [ TestWeight::generate(&mut xof, &TEST_WEIGHT_LEN), TestWeight::generate(&mut xof, &(2 * TEST_WEIGHT_LEN)), From 47ea791c350c142cd20d1691ce703b24ddc9d350 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Mon, 13 Jan 2025 12:52:33 -0800 Subject: [PATCH 2/5] mastic: Define constants for nonce and seed sizes --- src/flp/szk.rs | 57 +++++++++++++++++++++++++--------------------- src/vdaf/mastic.rs | 19 ++++++++-------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/flp/szk.rs b/src/flp/szk.rs index 720112b9..870c788c 100644 --- a/src/flp/szk.rs +++ b/src/flp/szk.rs @@ -17,7 +17,7 @@ use crate::{ flp::{FlpError, Type}, prng::PrngError, vdaf::{ - mastic::{self, USAGE_PROOF_SHARE}, + mastic::{self, NONCE_SIZE, SEED_SIZE, USAGE_PROOF_SHARE}, xof::{IntoFieldVec, Seed, Xof, XofTurboShake128}, }, }; @@ -68,10 +68,11 @@ pub enum SzkProofShare { /// The Helper uses one seed for both its compressed proof share and as the blind for its joint /// randomness. Helper { - /// The Seed that acts both as the compressed proof share and, optionally, as the blind. - proof_share_seed_and_blind: Seed<32>, - /// The leader's joint randomness part, if needed. - leader_joint_rand_part_opt: Option>, + /// Used to derive the helper's input and proof shares and its blind for FLP joint + /// randomness computation. + proof_share_seed_and_blind: Seed, + /// Set only if joint randomness is needed for the FLP. + leader_joint_rand_part_opt: Option>, }, } @@ -199,7 +200,7 @@ impl ParameterizedDecode<(bool, usize, bool)> for SzkP /// A tuple containing the state and messages produced by an SZK query. #[derive(Clone, Debug, PartialEq)] pub struct SzkQueryShare { - joint_rand_part_opt: Option>, + joint_rand_part_opt: Option>, pub(crate) flp_verifier: Vec, } @@ -241,14 +242,14 @@ impl ParameterizedDecode<(bool, usize)> for SzkQuerySh /// Szk query state. /// /// The state that needs to be stored by an Szk verifier between query() and decide(). -pub(crate) type SzkQueryState = Option>; +pub(crate) type SzkQueryState = Option>; /// Joint share type for the SZK proof. /// /// This is produced as the result of combining two query shares. /// It contains the re-computed joint randomness seed, if applicable. It is consumed by [`Szk::decide`]. #[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct SzkJointShare(Option>); +pub struct SzkJointShare(Option>); impl Encode for SzkJointShare { fn encode(&self, bytes: &mut Vec) -> Result<(), CodecError> { @@ -301,7 +302,7 @@ impl Szk { /// Derive a vector of random field elements for consumption by the FLP /// prover. - fn derive_prove_rand(&self, prove_rand_seed: &Seed<32>, ctx: &[u8]) -> Vec { + fn derive_prove_rand(&self, prove_rand_seed: &Seed, ctx: &[u8]) -> Vec { XofTurboShake128::seed_stream( prove_rand_seed.as_ref(), &[&mastic::dst_usage(mastic::USAGE_PROVE_RAND), &self.id, ctx], @@ -312,11 +313,11 @@ impl Szk { fn derive_joint_rand_part( &self, - aggregator_blind: &Seed<32>, + aggregator_blind: &Seed, measurement_share: &[T::Field], - nonce: &[u8; 16], + nonce: &[u8; NONCE_SIZE], ctx: &[u8], - ) -> Result, SzkError> { + ) -> Result, SzkError> { let mut xof = XofTurboShake128::init( aggregator_blind.as_ref(), &[ @@ -339,10 +340,10 @@ impl Szk { fn derive_joint_rand_seed( &self, - leader_joint_rand_part: &Seed<32>, - helper_joint_rand_part: &Seed<32>, + leader_joint_rand_part: &Seed, + helper_joint_rand_part: &Seed, ctx: &[u8], - ) -> Seed<32> { + ) -> Seed { let mut xof = XofTurboShake128::from_seed_slice( &[], &[ @@ -358,10 +359,10 @@ impl Szk { fn derive_joint_rand_and_seed( &self, - leader_joint_rand_part: &Seed<32>, - helper_joint_rand_part: &Seed<32>, + leader_joint_rand_part: &Seed, + helper_joint_rand_part: &Seed, ctx: &[u8], - ) -> (Seed<32>, Vec) { + ) -> (Seed, Vec) { let joint_rand_seed = self.derive_joint_rand_seed(leader_joint_rand_part, helper_joint_rand_part, ctx); let joint_rand = XofTurboShake128::seed_stream( @@ -374,7 +375,11 @@ impl Szk { (joint_rand_seed, joint_rand) } - fn derive_helper_proof_share(&self, proof_share_seed: &Seed<32>, ctx: &[u8]) -> Vec { + fn derive_helper_proof_share( + &self, + proof_share_seed: &Seed, + ctx: &[u8], + ) -> Vec { XofTurboShake128::seed_stream( proof_share_seed.as_ref(), &[&mastic::dst_usage(USAGE_PROOF_SHARE), &self.id, ctx], @@ -385,8 +390,8 @@ impl Szk { fn derive_query_rand( &self, - verify_key: &[u8; 32], - nonce: &[u8; 16], + verify_key: &[u8; SEED_SIZE], + nonce: &[u8; NONCE_SIZE], level: u16, ctx: &[u8], ) -> Vec { @@ -417,9 +422,9 @@ impl Szk { leader_input_share: &[T::Field], helper_input_share: &[T::Field], encoded_measurement: &[T::Field], - rand_seeds: [Seed<32>; 2], - leader_seed_opt: Option>, - nonce: &[u8; 16], + rand_seeds: [Seed; 2], + leader_seed_opt: Option>, + nonce: &[u8; NONCE_SIZE], ) -> Result<[SzkProofShare; 2], SzkError> { let [prove_rand_seed, helper_seed] = rand_seeds; // If joint randomness is used, derive it from the two input shares, @@ -478,8 +483,8 @@ impl Szk { level: u16, // level of the prefix tree input_share: &[T::Field], proof_share: &SzkProofShare, - verify_key: &[u8; 32], - nonce: &[u8; 16], + verify_key: &[u8; SEED_SIZE], + nonce: &[u8; NONCE_SIZE], ) -> Result<(SzkQueryShare, SzkQueryState), SzkError> { let query_rand = self.derive_query_rand(verify_key, nonce, level, ctx); let flp_proof_share = match proof_share { diff --git a/src/vdaf/mastic.rs b/src/vdaf/mastic.rs index a5055742..a3ba2346 100644 --- a/src/vdaf/mastic.rs +++ b/src/vdaf/mastic.rs @@ -33,7 +33,8 @@ use subtle::{Choice, ConstantTimeEq}; use super::xof::XofTurboShake128; -const NONCE_SIZE: usize = 16; +pub(crate) const SEED_SIZE: usize = 32; +pub(crate) const NONCE_SIZE: usize = 16; pub(crate) const USAGE_PROVE_RAND: u8 = 0; pub(crate) const USAGE_PROOF_SHARE: u8 = 1; @@ -261,10 +262,10 @@ impl Mastic { &self, ctx: &[u8], (alpha, weight): &(VidpfInput, T::Measurement), - nonce: &[u8; 16], + nonce: &[u8; NONCE_SIZE], vidpf_keys: [VidpfKey; 2], - szk_random: [Seed<32>; 2], - joint_random_opt: Option>, + szk_random: [Seed; 2], + joint_random_opt: Option>, ) -> Result<(::PublicShare, Vec<::InputShare>), VdafError> { if alpha.len() != self.bits { return Err(VdafError::Vidpf(VidpfError::InvalidInputLength)); @@ -369,7 +370,7 @@ impl Encode for MasticPrepareState { fn encoded_len(&self) -> Option { Some( self.output_shares.as_ref().len() * F::ENCODED_SIZE - + self.szk_query_state.as_ref().map_or(0, |_| 32), + + self.szk_query_state.as_ref().map_or(0, |_| SEED_SIZE), ) } } @@ -471,7 +472,7 @@ impl ParameterizedDecode> for MasticPrepa } } -impl Aggregator<32, NONCE_SIZE> for Mastic { +impl Aggregator for Mastic { type PrepareState = MasticPrepareState; type PrepareShare = MasticPrepareShare; type PrepareMessage = MasticPrepareMessage; @@ -488,7 +489,7 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { // Unpack this agg param and the last one in the list let cur_poplar_agg_param = &cur.level_and_prefixes; let prev_poplar_agg_param = from_ref(&prev.last().as_ref().unwrap().level_and_prefixes); - Poplar1::::is_agg_param_valid( + Poplar1::::is_agg_param_valid( cur_poplar_agg_param, prev_poplar_agg_param, ) @@ -496,7 +497,7 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { fn prepare_init( &self, - verify_key: &[u8; 32], + verify_key: &[u8; SEED_SIZE], ctx: &[u8], agg_id: usize, agg_param: &MasticAggregationParam, @@ -691,7 +692,7 @@ impl Aggregator<32, NONCE_SIZE> for Mastic { _ctx: &[u8], state: MasticPrepareState, input: MasticPrepareMessage, - ) -> Result, VdafError> { + ) -> Result, VdafError> { let MasticPrepareState { output_shares, szk_query_state, From 94fa4ef535407a2ef5d2c6e16b9ff8b9d60fb71c Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Mon, 13 Jan 2025 12:58:59 -0800 Subject: [PATCH 3/5] mastic: Remove `bits` field from `Mastic` struct This is redundant since `Vidpf` now needs to know the bit length. --- src/vdaf/mastic.rs | 7 ++----- src/vidpf.rs | 26 ++++++++++---------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/vdaf/mastic.rs b/src/vdaf/mastic.rs index a3ba2346..aac208b5 100644 --- a/src/vdaf/mastic.rs +++ b/src/vdaf/mastic.rs @@ -62,8 +62,6 @@ pub struct Mastic { id: [u8; 4], pub(crate) szk: Szk, pub(crate) vidpf: Vidpf>, - /// The length of the private attribute associated with any input. - pub(crate) bits: usize, } impl Mastic { @@ -75,7 +73,6 @@ impl Mastic { id: algorithm_id.to_le_bytes(), szk, vidpf, - bits, }) } @@ -142,7 +139,7 @@ impl ParameterizedDecode> for MasticPublicShare, bytes: &mut Cursor<&[u8]>, ) -> Result { - VidpfPublicShare::decode_with_param(&(mastic.bits, mastic.vidpf.weight_len), bytes) + VidpfPublicShare::decode_with_param(&mastic.vidpf, bytes) } } @@ -267,7 +264,7 @@ impl Mastic { szk_random: [Seed; 2], joint_random_opt: Option>, ) -> Result<(::PublicShare, Vec<::InputShare>), VdafError> { - if alpha.len() != self.bits { + if alpha.len() != usize::from(self.vidpf.bits) { return Err(VdafError::Vidpf(VidpfError::InvalidInputLength)); } diff --git a/src/vidpf.rs b/src/vidpf.rs index 8519cc17..7222bf87 100644 --- a/src/vidpf.rs +++ b/src/vidpf.rs @@ -538,18 +538,16 @@ impl Encode for VidpfPublicShare { } } -impl ParameterizedDecode<(usize, W::ValueParameter)> for VidpfPublicShare { - fn decode_with_param( - (bits, weight_len): &(usize, W::ValueParameter), - bytes: &mut Cursor<&[u8]>, - ) -> Result { +impl ParameterizedDecode> for VidpfPublicShare { + fn decode_with_param(vidpf: &Vidpf, bytes: &mut Cursor<&[u8]>) -> Result { + let bits = usize::from(vidpf.bits); let packed_control_len = (bits + 3) / 4; let mut packed_control_bits = vec![0u8; packed_control_len]; bytes.read_exact(&mut packed_control_bits)?; let unpacked_control_bits: BitVec = BitVec::from_vec(packed_control_bits); // Control bits - let mut control_bits = Vec::with_capacity(*bits); + let mut control_bits = Vec::with_capacity(bits); for chunk in unpacked_control_bits[0..bits * 2].chunks(2) { control_bits.push([(chunk[0] as u8).into(), (chunk[1] as u8).into()]); } @@ -561,12 +559,12 @@ impl ParameterizedDecode<(usize, W::ValueParameter)> for VidpfPub // Seeds let seeds = std::iter::repeat_with(|| Seed::decode(bytes).map(|seed| seed.0)) - .take(*bits) + .take(bits) .collect::, _>>()?; // Weights - let weights = std::iter::repeat_with(|| W::decode_with_param(weight_len, bytes)) - .take(*bits) + let weights = std::iter::repeat_with(|| W::decode_with_param(&vidpf.weight_len, bytes)) + .take(bits) .collect::, _>>()?; let proofs = std::iter::repeat_with(|| { @@ -574,7 +572,7 @@ impl ParameterizedDecode<(usize, W::ValueParameter)> for VidpfPub bytes.read_exact(&mut proof)?; Ok::<_, CodecError>(proof) }) - .take(*bits) + .take(bits) .collect::, _>>()?; let cw = seeds @@ -878,16 +876,12 @@ mod tests { let ctx = b"appliction context"; let input = VidpfInput::from_bytes(&[0xFF]); let weight = TestWeight::from(vec![21.into(), 22.into(), 23.into()]); - let (_, public, _, _) = vidpf_gen_setup(ctx, &input, &weight); + let (vidpf, public, _, _) = vidpf_gen_setup(ctx, &input, &weight); let bytes = public.get_encoded().unwrap(); assert_eq!(public.encoded_len().unwrap(), bytes.len()); - let decoded = VidpfPublicShare::::get_decoded_with_param( - &(8, TEST_WEIGHT_LEN), - &bytes, - ) - .unwrap(); + let decoded = VidpfPublicShare::get_decoded_with_param(&vidpf, &bytes).unwrap(); assert_eq!(public, decoded); } From 98bc8283e5ba3231a68825f83e8f2f4ec1e7caa8 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Mon, 13 Jan 2025 13:03:26 -0800 Subject: [PATCH 4/5] Move `szk` module from `flp` to `vdaf::mastic` Some of the implementation details of SZK are, for the moment, specific to Mastic. For example, the domain separation tag for each XOF invocation is prefixed by `b"mastic"`. It therefore makes sense to move this module to `mastic` until such time we need to refactor in order to use SZK in multiple places. --- src/flp.rs | 2 -- src/vdaf.rs | 4 ++-- src/vdaf/mastic.rs | 15 ++++++++------- src/{flp => vdaf/mastic}/szk.rs | 0 4 files changed, 10 insertions(+), 11 deletions(-) rename src/{flp => vdaf/mastic}/szk.rs (100%) diff --git a/src/flp.rs b/src/flp.rs index a34f3cf0..d6e1bdf8 100644 --- a/src/flp.rs +++ b/src/flp.rs @@ -57,8 +57,6 @@ use std::convert::TryFrom; use std::fmt::Debug; pub mod gadgets; -#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] -pub mod szk; pub mod types; /// Errors propagated by methods in this module. diff --git a/src/vdaf.rs b/src/vdaf.rs index c992869b..c76bf456 100644 --- a/src/vdaf.rs +++ b/src/vdaf.rs @@ -8,10 +8,10 @@ #[cfg(feature = "experimental")] use crate::dp::DifferentialPrivacyStrategy; #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] -use crate::flp::szk::SzkError; -#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] use crate::idpf::IdpfError; #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] +use crate::vdaf::mastic::szk::SzkError; +#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] use crate::vidpf::VidpfError; use crate::{ codec::{CodecError, Decode, Encode, ParameterizedDecode}, diff --git a/src/vdaf/mastic.rs b/src/vdaf/mastic.rs index aac208b5..3819d077 100644 --- a/src/vdaf/mastic.rs +++ b/src/vdaf/mastic.rs @@ -8,10 +8,7 @@ use crate::{ bt::BinaryTree, codec::{CodecError, Decode, Encode, ParameterizedDecode}, field::{decode_fieldvec, FieldElement, FieldElementWithInteger}, - flp::{ - szk::{Szk, SzkJointShare, SzkProofShare, SzkQueryShare, SzkQueryState}, - Type, - }, + flp::Type, vdaf::{ poplar1::{Poplar1, Poplar1AggregationParam}, xof::{Seed, Xof}, @@ -24,6 +21,8 @@ use crate::{ }, }; +use szk::{Szk, SzkJointShare, SzkProofShare, SzkQueryShare, SzkQueryState}; + use rand::prelude::*; use std::io::{Cursor, Read}; use std::ops::BitAnd; @@ -33,6 +32,8 @@ use subtle::{Choice, ConstantTimeEq}; use super::xof::XofTurboShake128; +pub(crate) mod szk; + pub(crate) const SEED_SIZE: usize = 32; pub(crate) const NONCE_SIZE: usize = 16; @@ -343,9 +344,9 @@ impl Client<16> for Mastic { /// Mastic preparation state. /// -/// State held by an aggregator waiting for a message during Mastic preparation. Includes intermediate -/// state for [`Szk`] verification, the output shares currently being validated, and -/// parameters of Mastic used for encoding. +/// State held by an aggregator waiting for a message during Mastic preparation. Includes +/// intermediate state for the evaluation check, the range check (if applicable) verification, and +/// the output shares currently being validated. #[derive(Clone, Debug, Eq, PartialEq)] pub struct MasticPrepareState { /// The counter and truncated weight for each candidate prefix. diff --git a/src/flp/szk.rs b/src/vdaf/mastic/szk.rs similarity index 100% rename from src/flp/szk.rs rename to src/vdaf/mastic/szk.rs From 386d288054713e6a1973cb8368921f5b5aa95350 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Mon, 13 Jan 2025 13:11:09 -0800 Subject: [PATCH 5/5] mastic: Update reference --- src/vdaf/mastic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vdaf/mastic.rs b/src/vdaf/mastic.rs index 3819d077..b1048cd1 100644 --- a/src/vdaf/mastic.rs +++ b/src/vdaf/mastic.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 -//! Implementation of Mastic as specified in [[draft-mouris-cfrg-mastic-01]]. +//! Implementation of Mastic as specified in [[draft-mouris-cfrg-mastic-04]]. //! -//! [draft-mouris-cfrg-mastic-01]: https://www.ietf.org/archive/id/draft-mouris-cfrg-mastic-01.html +//! [draft-mouris-cfrg-mastic-04]: https://www.ietf.org/archive/id/draft-mouris-cfrg-mastic-04.html use crate::{ bt::BinaryTree,