From 6c6e284c9db6e2cb044ac91af7b3ae3279c42625 Mon Sep 17 00:00:00 2001 From: Hannah Davis Date: Wed, 30 Oct 2024 20:41:54 +0000 Subject: [PATCH] refactoring szk --- src/flp/szk.rs | 247 ++++++++++++++++++++++++++------------------- src/vdaf/mastic.rs | 61 ++++------- 2 files changed, 159 insertions(+), 149 deletions(-) diff --git a/src/flp/szk.rs b/src/flp/szk.rs index d48cd144..ac509f22 100644 --- a/src/flp/szk.rs +++ b/src/flp/szk.rs @@ -50,6 +50,11 @@ pub enum SzkError { /// can be properly read upon receipt. MissingVerifierLen(()), + #[error("Part of Szk query state not stored")] + /// Returned when a user fails to store the length of the verifier so it + /// can be properly read upon receipt. + InvalidState(String), + /// Returned if an FLP operation encountered an error. #[error("Flp error: {0}")] Flp(#[from] FlpError), @@ -255,85 +260,66 @@ impl ParameterizedDecode<(bool } } -impl SzkQueryShare { - pub(crate) fn merge_verifiers( - mut leader_share: SzkQueryShare, - helper_share: SzkQueryShare, - ) -> SzkVerifier { - for (x, y) in leader_share - .flp_verifier - .iter_mut() - .zip(helper_share.flp_verifier) - { - *x += y; - } - SzkVerifier { - flp_verifier: leader_share.flp_verifier, - leader_joint_rand_part_opt: leader_share.joint_rand_part_opt, - helper_joint_rand_part_opt: helper_share.joint_rand_part_opt, - } - } -} - /// Szk query state. /// /// The state that needs to be stored by an Szk verifier between query() and decide(). pub type SzkQueryState = Option>; /// Verifier type for the SZK proof. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SzkVerifier { - flp_verifier: Vec, - leader_joint_rand_part_opt: Option>, - helper_joint_rand_part_opt: Option>, +pub type SzkVerifier = Vec; + +impl Encode for SzkVerifier { + fn encode(&self, bytes: &mut Vec) -> Result<(), CodecError> { + encode_fieldvec(&self, bytes)?; + Ok(()) + } + + fn encoded_len(&self) -> Option { + Some(self.len() * F::ENCODED_SIZE) + } +} + +impl ParameterizedDecode for SzkVerifier { + fn decode_with_param( + verifier_len: &usize, + bytes: &mut Cursor<&[u8]>, + ) -> Result { + decode_fieldvec(*verifier_len, bytes) + } } -impl Encode for SzkVerifier { +/// Joint share type for the SZK proof. +pub type SzkJointShare = Option<[Seed; 2]>; + +impl Encode for SzkJointShare { fn encode(&self, bytes: &mut Vec) -> Result<(), CodecError> { - encode_fieldvec(&self.flp_verifier, bytes)?; - if let Some(ref part) = self.leader_joint_rand_part_opt { - part.encode(bytes)? - }; - if let Some(ref part) = self.helper_joint_rand_part_opt { - part.encode(bytes)? + if let Some([ref leader_part, ref helper_part]) = self { + leader_part.encode(bytes)?; + helper_part.encode(bytes)?; }; Ok(()) } fn encoded_len(&self) -> Option { - Some( - self.flp_verifier.len() * F::ENCODED_SIZE - + match self.leader_joint_rand_part_opt { - Some(ref part) => part.encoded_len()?, - None => 0, - } - + match self.helper_joint_rand_part_opt { - Some(ref part) => part.encoded_len()?, - None => 0, - }, - ) + Some(match self { + Some(ref part) => part[0].encoded_len()? * 2, + None => 0, + }) } } -impl ParameterizedDecode<(bool, usize)> - for SzkVerifier -{ +impl ParameterizedDecode for SzkJointShare { fn decode_with_param( - (requires_joint_rand, verifier_len): &(bool, usize), + requires_joint_rand: &bool, bytes: &mut Cursor<&[u8]>, ) -> Result { if *requires_joint_rand { - Ok(SzkVerifier { - flp_verifier: decode_fieldvec(*verifier_len, bytes)?, - leader_joint_rand_part_opt: Some(Seed::::decode(bytes)?), - helper_joint_rand_part_opt: Some(Seed::::decode(bytes)?), - }) + Ok(Some([ + Seed::::decode(bytes)?, + Seed::::decode(bytes)?, + ])) } else { - Ok(SzkVerifier { - flp_verifier: decode_fieldvec(*verifier_len, bytes)?, - leader_joint_rand_part_opt: None, - helper_joint_rand_part_opt: None, - }) + Ok(None) } } } @@ -629,34 +615,56 @@ where )) } - /// Returns true if the verifier message indicates that the input from which - /// it was generated is valid. + pub(crate) fn merge_verifiers( + &self, + mut leader_share: SzkQueryShare, + helper_share: SzkQueryShare, + ) -> Result, SzkError> { + for (x, y) in leader_share + .flp_verifier + .iter_mut() + .zip(helper_share.flp_verifier) + { + *x += y; + } + if self.typ.decide(&leader_share.flp_verifier)? { + match ( + leader_share.joint_rand_part_opt, + helper_share.joint_rand_part_opt, + ) { + (Some(leader_part), Some(helper_part)) => Ok(Some([leader_part, helper_part])), + (None, None) => Ok(None), + _ => Err(SzkError::InvalidState( + "at least one of the joint randomness parts is missing".to_string(), + )), + } + } else { + Err(SzkError::Decide("failed to verify FLP proof".to_string())) + } + } + /// Returns true if the leader and helper derive identical joint randomness + /// seeds pub fn decide( &self, - verifier: SzkVerifier, query_state: SzkQueryState, - ) -> Result { - // Check if underlying FLP proof validates - let check_flp_proof = self.typ.decide(&verifier.flp_verifier)?; - if !check_flp_proof { - return Ok(false); - } + joint_share: &[Seed; 2], + ) -> Result<(), SzkError> { // Check that joint randomness was properly derived from both // aggregators' parts - match ( - query_state, - verifier.leader_joint_rand_part_opt, - verifier.helper_joint_rand_part_opt, - ) { - (Some(joint_rand_seed), Some(leader_joint_rand_part), Some(helper_joint_rand_part)) => { + match (query_state, joint_share) { + (Some(joint_rand_seed), [leader_joint_rand_part, helper_joint_rand_part]) => { let expected_joint_rand_seed = - self.derive_joint_rand_seed(&leader_joint_rand_part, &helper_joint_rand_part); - Ok(joint_rand_seed == expected_joint_rand_seed) + self.derive_joint_rand_seed(leader_joint_rand_part, helper_joint_rand_part); + if joint_rand_seed == expected_joint_rand_seed { + Ok(()) + } else { + Err(SzkError::Decide( + "Aggregators failed to compute identical joint randomness seeds" + .to_string(), + )) + } } - (None, None, None) => Ok(true), - (_, _, _) => Err(SzkError::Decide( - "at least one of the input seeds is missing".to_string(), - )), + (None, _) => Ok(()), } } } @@ -744,29 +752,40 @@ mod tests { .query(&helper_input_share, h_proof_share, &verify_key, &nonce) .unwrap(); - let verifier = SzkQueryShare::merge_verifiers(l_query_share.clone(), h_query_share.clone()); - if let Ok(leader_decision) = szk_typ.decide(verifier.clone(), l_query_state.clone()) { - assert_eq!( - leader_decision, valid, - "Leader incorrectly determined validity", - ); - } else { - panic!("Leader failed during decision"); - }; - if let Ok(helper_decision) = szk_typ.decide(verifier.clone(), h_query_state.clone()) { - assert_eq!( - helper_decision, valid, - "Helper incorrectly determined validity", - ); - } else { - panic!("Helper failed during decision"); + let joint_share_result = + szk_typ.merge_verifiers(l_query_share.clone(), h_query_share.clone()); + match joint_share_result { + Ok(Some(ref joint_share)) => { + let leader_decision = match szk_typ.decide(l_query_state.clone(), joint_share) { + Ok(_) => true, + Err(_) => false, + }; + assert_eq!( + leader_decision, valid, + "Leader incorrectly determined validity", + ); + let helper_decision = match szk_typ.decide(h_query_state.clone(), joint_share) { + Ok(_) => true, + Err(_) => false, + }; + assert_eq!( + helper_decision, valid, + "Helper incorrectly determined validity", + ); + } + Ok(None) => assert_eq!(true, valid, "Aggregator incorrectly determined validity"), + Err(_) => { + assert_eq!(false, valid, "Aggregator incorrectly determined validity"); + } }; //test mutated jr seed if szk_typ.requires_joint_rand() { let joint_rand_seed_opt = Some(Seed::<16>::generate().unwrap()); - if let Ok(leader_decision) = szk_typ.decide(verifier, joint_rand_seed_opt.clone()) { - assert!(!leader_decision, "Leader accepted wrong jr seed"); + if let Ok(Some(ref joint_share)) = joint_share_result { + if let Ok(()) = szk_typ.decide(joint_rand_seed_opt.clone(), joint_share) { + panic!("Leader accepted wrong jr seed"); + }; }; }; @@ -778,9 +797,15 @@ mod tests { ); } - let verifier = SzkQueryShare::merge_verifiers(mutated_query_share, h_query_share.clone()); - - let leader_decision = szk_typ.decide(verifier, l_query_state.clone()).unwrap(); + let joint_share_res = szk_typ.merge_verifiers(mutated_query_share, h_query_share.clone()); + let leader_decision = match joint_share_res { + Ok(Some(ref joint_share)) => match szk_typ.decide(l_query_state.clone(), joint_share) { + Ok(_) => true, + Err(_) => false, + }, + Ok(None) => true, + Err(_) => false, + }; assert!(!leader_decision, "Leader validated after proof mutation"); // test mutated input share @@ -791,11 +816,17 @@ mod tests { .query(&mutated_input, l_proof_share.clone(), &verify_key, &nonce) .unwrap(); - let verifier = SzkQueryShare::merge_verifiers(mutated_query_share, h_query_share.clone()); + let joint_share_res = szk_typ.merge_verifiers(mutated_query_share, h_query_share.clone()); - if let Ok(leader_decision) = szk_typ.decide(verifier, mutated_query_state) { - assert!(!leader_decision, "Leader validated after input mutation"); + let leader_decision = match joint_share_res { + Ok(Some(ref joint_share)) => match szk_typ.decide(mutated_query_state, joint_share) { + Ok(_) => true, + Err(_) => false, + }, + Ok(None) => true, + Err(_) => false, }; + assert!(!leader_decision, "Leader validated after input mutation"); // test mutated proof share let (mut mutated_proof, leader_blind_and_helper_joint_rand_part_opt) = match l_proof_share { @@ -822,11 +853,17 @@ mod tests { &nonce, ) .unwrap(); - let verifier = SzkQueryShare::merge_verifiers(l_query_share, h_query_share.clone()); + let joint_share_res = szk_typ.merge_verifiers(l_query_share, h_query_share.clone()); - if let Ok(leader_decision) = szk_typ.decide(verifier, l_query_state) { - assert!(!leader_decision, "Leader validated after proof mutation"); + let leader_decision = match joint_share_res { + Ok(Some(ref joint_share)) => match szk_typ.decide(l_query_state.clone(), joint_share) { + Ok(_) => true, + Err(_) => false, + }, + Ok(None) => true, + Err(_) => false, }; + assert!(!leader_decision, "Leader validated after proof mutation"); } #[test] diff --git a/src/vdaf/mastic.rs b/src/vdaf/mastic.rs index 9148549f..7b23a957 100644 --- a/src/vdaf/mastic.rs +++ b/src/vdaf/mastic.rs @@ -9,7 +9,7 @@ use crate::{ codec::{CodecError, Decode, Encode, ParameterizedDecode}, field::{decode_fieldvec, FieldElement}, flp::{ - szk::{Szk, SzkProofShare, SzkQueryShare, SzkQueryState, SzkVerifier}, + szk::{Szk, SzkJointShare, SzkProofShare, SzkQueryShare, SzkQueryState}, Type, }, vdaf::{ @@ -459,40 +459,18 @@ impl ParameterizedDecode = Option>; - -impl Encode for MasticPrepareMessage { - fn encode(&self, bytes: &mut Vec) -> Result<(), CodecError> { - match self { - Some(verifier) => verifier.encode(bytes), - None => Ok(()), - } - } - - fn encoded_len(&self) -> Option { - match self { - Some(verifier) => verifier.encoded_len(), - None => Some(0), - } - } -} +pub type MasticPrepareMessage = SzkJointShare; impl ParameterizedDecode> - for MasticPrepareMessage + for MasticPrepareMessage { fn decode_with_param( prep_state: &MasticPrepareState, bytes: &mut Cursor<&[u8]>, ) -> Result { - match (&prep_state.szk_query_state, prep_state.verifier_len) { - (Some(_), Some(verifier_len)) => Ok(Some( - SzkVerifier::::decode_with_param(&(true, verifier_len), bytes)?, - )), - (None, Some(verifier_len)) => Ok(Some(SzkVerifier::::decode_with_param( - &(false, verifier_len), - bytes, - )?)), - (_, None) => Ok(None), + match prep_state.szk_query_state { + Some(_) => SzkJointShare::::decode_with_param(&true, bytes), + None => SzkJointShare::::decode_with_param(&false, bytes), } } } @@ -504,7 +482,7 @@ where { type PrepareState = MasticPrepareState; type PrepareShare = MasticPrepareShare; - type PrepareMessage = MasticPrepareMessage; + type PrepareMessage = MasticPrepareMessage; fn prepare_init( &self, @@ -614,7 +592,7 @@ where &self, _agg_param: &MasticAggregationParam, inputs: M, - ) -> Result, VdafError> { + ) -> Result, VdafError> { let mut inputs_iter = inputs.into_iter(); let leader_share = inputs_iter.next().ok_or(VdafError::Uncategorized( "No leader share received".to_string(), @@ -632,24 +610,24 @@ where "Vidpf proof verification failed".to_string(), )); }; - match ( + Ok(match ( leader_share.szk_query_share_opt, helper_share.szk_query_share_opt, ) { - (Some(leader_query_share), Some(helper_query_share)) => Ok(Some( - SzkQueryShare::merge_verifiers(leader_query_share, helper_query_share), - )), + (Some(leader_query_share), Some(helper_query_share)) => Ok(self + .szk + .merge_verifiers(leader_query_share, helper_query_share)?), (None, None) => Ok(None), (_, _) => Err(VdafError::Uncategorized( "Only one of leader and helper query shares is present".to_string(), )), - } + }?) } fn prepare_next( &self, state: MasticPrepareState, - input: MasticPrepareMessage, + input: MasticPrepareMessage, ) -> Result, VdafError> { match (state, input) { ( @@ -666,15 +644,10 @@ where szk_query_state, verifier_len: _, }, - Some(verifier), + Some(ref joint_share), ) => { - if self.szk.decide(verifier, szk_query_state)? { - Ok(PrepareTransition::Finish(output_shares)) - } else { - Err(VdafError::Uncategorized( - "Szk proof failed verification".to_string(), - )) - } + self.szk.decide(szk_query_state, joint_share)?; + Ok(PrepareTransition::Finish(output_shares)) } } }