From 01d7e74cd910fbc7539fb25a13d526e921a67e5d Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Thu, 19 Sep 2024 23:17:56 -0700 Subject: [PATCH] Add an example of malicious behavior test (typed) --- synedrion/src/cggmp21/protocols.rs | 1 + synedrion/src/cggmp21/protocols/key_init.rs | 2 +- .../cggmp21/protocols/key_init_malicious.rs | 229 ++++++++++++++++++ synedrion/src/tools/bitvec.rs | 5 + 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 synedrion/src/cggmp21/protocols/key_init_malicious.rs diff --git a/synedrion/src/cggmp21/protocols.rs b/synedrion/src/cggmp21/protocols.rs index 3e0db5d..c47977d 100644 --- a/synedrion/src/cggmp21/protocols.rs +++ b/synedrion/src/cggmp21/protocols.rs @@ -3,6 +3,7 @@ pub(crate) mod interactive_signing; pub(crate) mod key_gen; pub(crate) mod key_init; pub(crate) mod key_init_errors; +pub(crate) mod key_init_malicious; pub(crate) mod key_refresh; pub(crate) mod presigning; pub(crate) mod signing; diff --git a/synedrion/src/cggmp21/protocols/key_init.rs b/synedrion/src/cggmp21/protocols/key_init.rs index 95b4055..bb620c1 100644 --- a/synedrion/src/cggmp21/protocols/key_init.rs +++ b/synedrion/src/cggmp21/protocols/key_init.rs @@ -39,7 +39,7 @@ pub(super) struct PublicData { pub(super) cap_x: Point, pub(super) cap_a: SchCommitment, pub(super) rid: BitVec, - u: BitVec, + pub(super) u: BitVec, phantom: PhantomData

, } diff --git a/synedrion/src/cggmp21/protocols/key_init_malicious.rs b/synedrion/src/cggmp21/protocols/key_init_malicious.rs new file mode 100644 index 0000000..91836ec --- /dev/null +++ b/synedrion/src/cggmp21/protocols/key_init_malicious.rs @@ -0,0 +1,229 @@ +#[cfg(test)] +mod tests { + use alloc::collections::{BTreeMap, BTreeSet}; + use rand_core::CryptoRngCore; + + use rand_core::{OsRng, RngCore}; + use secrecy::ExposeSecret; + + use super::super::key_init::{Round1, Round2, Round3}; + use crate::cggmp21::TestParams; + use crate::rounds::{ + test_utils::{step_next_round, step_result, step_round, Id, Without}, + FinalizableToNextRound, FinalizableToResult, FinalizationRequirement, FinalizeError, + FirstRound, Round, + }; + use crate::tools::bitvec::BitVec; + use crate::{PartyId, ProtocolResult, SchemeParams}; + + trait MaliciousRoundWrapper { + type InnerRound: Round; + fn inner_round(&self) -> &Self::InnerRound; + } + + trait MaliciousRound: MaliciousRoundWrapper { + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + destination: &I, + ) -> ( + >::DirectMessage, + >::Artifact, + ) { + self.inner_round().make_direct_message(rng, destination) + } + + fn make_broadcast_message( + &self, + rng: &mut impl CryptoRngCore, + ) -> Option<>::BroadcastMessage> { + self.inner_round().make_broadcast_message(rng) + } + } + + macro_rules! malicious_round { + ($round: ident, $inner_round: ident) => { + + impl MaliciousRoundWrapper for $round { + type InnerRound = $inner_round; + fn inner_round(&self) -> &Self::InnerRound { &self.0 } + } + + impl Round for $round { + type Type = <>::InnerRound as Round>::Type; + type Result = <>::InnerRound as Round>::Result; + const ROUND_NUM: u8 = <>::InnerRound as Round>::ROUND_NUM; + const NEXT_ROUND_NUM: Option = <>::InnerRound as Round>::NEXT_ROUND_NUM; + + fn other_ids(&self) -> &BTreeSet { + self.inner_round().other_ids() + } + + fn my_id(&self) -> &I { + self.inner_round().my_id() + } + + const REQUIRES_ECHO: bool = <>::InnerRound as Round>::REQUIRES_ECHO; + type BroadcastMessage = <>::InnerRound as Round>::BroadcastMessage; + type DirectMessage = <>::InnerRound as Round>::DirectMessage; + type Payload = <>::InnerRound as Round>::Payload; + type Artifact = <>::InnerRound as Round>::Artifact; + + fn message_destinations(&self) -> &BTreeSet { + self.inner_round().message_destinations() + } + + fn make_broadcast_message( + &self, + rng: &mut impl CryptoRngCore, + ) -> Option { + MaliciousRound::make_broadcast_message(self, rng) + } + + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + destination: &I, + ) -> (Self::DirectMessage, Self::Artifact) { + MaliciousRound::make_direct_message(self, rng, destination) + } + + fn verify_message( + &self, + rng: &mut impl CryptoRngCore, + from: &I, + broadcast_msg: Self::BroadcastMessage, + direct_msg: Self::DirectMessage, + ) -> Result>::ProvableError> { + self.inner_round() + .verify_message(rng, from, broadcast_msg, direct_msg) + } + + fn finalization_requirement() -> FinalizationRequirement { + <>::InnerRound as Round>::finalization_requirement() + } + } + }; + } + + macro_rules! malicious_to_next_round { + ($round: ident, $next_round: ident) => { + impl FinalizableToNextRound for $round { + type NextRound = $next_round; + fn finalize_to_next_round( + self, + rng: &mut impl CryptoRngCore, + payloads: BTreeMap>::Payload>, + artifacts: BTreeMap>::Artifact>, + ) -> Result> { + self.0 + .finalize_to_next_round(rng, payloads, artifacts) + .map($next_round) + } + } + }; + } + + macro_rules! malicious_to_result { + ($round: ident) => { + impl FinalizableToResult for $round { + fn finalize_to_result( + self, + rng: &mut impl CryptoRngCore, + payloads: BTreeMap>::Payload>, + artifacts: BTreeMap>::Artifact>, + ) -> Result< + >::Success, + FinalizeError, + > { + self.0.finalize_to_result(rng, payloads, artifacts) + } + } + }; + } + + struct MRound1(Round1); + struct MRound2(Round2); + struct MRound3(Round3); + + impl MaliciousRound for MRound1 { + // Here we do bad stuff + } + + impl MaliciousRound for MRound2 { + fn make_broadcast_message( + &self, + rng: &mut impl CryptoRngCore, + ) -> Option< as Round>::BroadcastMessage> { + let mut message = self.0.make_broadcast_message(rng)?; + + // Garbage `u` + message.data.u = BitVec::random(rng, message.data.u.len()); + + Some(message) + } + } + + impl MaliciousRound for MRound3 { + // Here we do bad stuff + } + + malicious_round!(MRound1, Round1); + malicious_round!(MRound2, Round2); + malicious_round!(MRound3, Round3); + + malicious_to_next_round!(MRound1, MRound2); + malicious_to_next_round!(MRound2, MRound3); + malicious_to_result!(MRound3); + + #[test] + fn execute_keygen() { + let mut shared_randomness = [0u8; 32]; + OsRng.fill_bytes(&mut shared_randomness); + + let ids = BTreeSet::from([Id(0), Id(1), Id(2)]); + + let r1 = ids + .iter() + .map(|id| { + let round = MRound1( + Round1::::new( + &mut OsRng, + &shared_randomness, + ids.clone().without(id), + *id, + (), + ) + .unwrap(), + ); + (*id, round) + }) + .collect(); + + let r1a = step_round(&mut OsRng, r1).unwrap(); + let r2 = step_next_round(&mut OsRng, r1a).unwrap(); + let r2a = step_round(&mut OsRng, r2).unwrap(); + let r3 = step_next_round(&mut OsRng, r2a).unwrap(); + let r3a = step_round(&mut OsRng, r3).unwrap(); + let shares = step_result(&mut OsRng, r3a).unwrap(); + + // Check that the sets of public keys are the same at each node + + let public_sets = shares + .iter() + .map(|(id, share)| (*id, share.public_shares.clone())) + .collect::>(); + + assert!(public_sets.values().all(|pk| pk == &public_sets[&Id(0)])); + + // Check that the public keys correspond to the secret key shares + let public_set = &public_sets[&Id(0)]; + + let public_from_secret = shares + .into_iter() + .map(|(id, share)| (id, share.secret_share.expose_secret().mul_by_generator())) + .collect(); + + assert!(public_set == &public_from_secret); + } +} diff --git a/synedrion/src/tools/bitvec.rs b/synedrion/src/tools/bitvec.rs index 7389899..ae3a9bc 100644 --- a/synedrion/src/tools/bitvec.rs +++ b/synedrion/src/tools/bitvec.rs @@ -26,6 +26,11 @@ impl BitVec { } result } + + // Test only + pub fn len(&self) -> usize { + self.0.len() + } } impl BitXorAssign<&BitVec> for BitVec {