use rand::SeedableRng; use rand_core::CryptoRngCore; use frost_core::{Field, Group, keys::SigningShare}; use crate::crypto::polynomials::Polynomial; use crate::participants::Participant; #[cfg(test)] use crate::test_utils::participants::generate_participants_with_random_ids; use crate::test_utils::{GenOutput, GenProtocol, run_protocol}; use crate::thresholds::ReconstructionThreshold; use crate::{Ciphersuite, KeygenOutput, Scalar, VerifyingKey, keygen, refresh, reshare}; // +++++++++++++++++ DKG Functions +++++++++++++++++ // type DKGGenProtocol = GenProtocol>; /// Runs distributed keygen /// If the protocol succeeds, returns a sorted vector based on participants id /// Runs distributed keygen /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_keygen( participants: &[Participant], threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> GenOutput { let mut protocols: DKGGenProtocol = Vec::with_capacity(participants.len()); for p in participants { let rng_p = R::seed_from_u64(rng.next_u64()); let protocol = keygen::(participants, *p, threshold, rng_p).unwrap(); protocols.push((*p, Box::new(protocol))); } run_protocol(protocols).unwrap() } /// Deals key shares for a *supplied* master secret, acting as a trusted dealer. pub fn deal_keygen_outputs( secret: Scalar, participants: &[Participant], threshold: impl Into, rng: &mut impl CryptoRngCore, ) -> GenOutput { let degree = threshold.into().value() - 1; let (f, pk) = generate_test_keys_with_secret(secret, degree, rng); participants .iter() .map(|p| (*p, make_keygen_output(&f, &pk, *p))) .collect() } /// Runs distributed refresh /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_refresh( participants: &[Participant], keys: &[(Participant, KeygenOutput)], threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> GenOutput { let mut protocols: DKGGenProtocol = Vec::with_capacity(participants.len()); for (p, out) in keys { let rng_p = R::seed_from_u64(rng.next_u64()); let protocol = refresh::( Some(out.private_share), out.public_key, participants, threshold, *p, rng_p, ) .unwrap(); protocols.push((*p, Box::new(protocol))); } run_protocol(protocols).unwrap() } /// Runs distributed reshare /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_reshare( participants: &[Participant], pub_key: &VerifyingKey, keys: &[(Participant, KeygenOutput)], old_threshold: impl Into + Copy + Send + 'static, new_threshold: impl Into + Copy + Send + 'static, new_participants: &[Participant], rng: &mut R, ) -> GenOutput { assert!(!new_participants.is_empty()); let mut setup = vec![]; for new_participant in new_participants { let mut is_break = false; for (p, k) in keys { if p == new_participant { setup.push((*p, (Some(k.private_share), k.public_key))); is_break = true; break; } } if !is_break { setup.push((*new_participant, (None, *pub_key))); } } let mut protocols: DKGGenProtocol = Vec::with_capacity(participants.len()); for (p, out) in &setup { let rng_p = R::seed_from_u64(rng.next_u64()); let protocol = reshare( participants, old_threshold, out.0, out.1, new_participants, new_threshold, *p, rng_p, ) .unwrap(); protocols.push((*p, Box::new(protocol))); } run_protocol(protocols).unwrap() } /// Assert that each participant has the same view of the public key pub fn assert_public_key_invariant( participants: &[(Participant, KeygenOutput)], ) { let vk = participants.first().unwrap().1.public_key; if participants .iter() .any(|(_, key_pair)| key_pair.public_key != vk) { panic!("public key package is not the same for all participants"); } } /// Generates a random polynomial of given degree and derives the corresponding /// public verifying key. Returns both the polynomial (for per-participant share /// derivation) and the verifying key. pub fn generate_test_keys( degree: usize, rng: &mut impl CryptoRngCore, ) -> (Polynomial, VerifyingKey) { let secret = ::Field::random(rng); generate_test_keys_with_secret(secret, degree, rng) } /// Like [`generate_test_keys`] but pins the polynomial's constant term to /// `secret`, so the reconstructed master secret is known in advance. pub fn generate_test_keys_with_secret( secret: Scalar, degree: usize, rng: &mut impl CryptoRngCore, ) -> (Polynomial, VerifyingKey) { let f = Polynomial::::generate_polynomial(Some(secret), degree, rng).unwrap(); ( f, VerifyingKey::new(::generator() * secret), ) } /// Constructs a [`KeygenOutput`] for a single participant from a shared /// polynomial and public verifying key. pub fn make_keygen_output( f: &Polynomial, pk: &VerifyingKey, p: Participant, ) -> KeygenOutput { KeygenOutput { private_share: SigningShare::new(f.eval_at_participant(p).unwrap().0), public_key: *pk, } } /// Centralized key generation for testing: generates random participant IDs /// and creates `KeygenOutput` for each using polynomial evaluation. #[cfg(test)] pub fn build_frost_key_packages_with_dealer( max_signers: u16, min_signers: u16, rng: &mut impl CryptoRngCore, ) -> GenOutput { let participants = generate_participants_with_random_ids(max_signers as usize, rng); let (f, pk) = generate_test_keys::((min_signers - 1) as usize, rng); participants .iter() .map(|p| (*p, make_keygen_output(&f, &pk, *p))) .collect() }