From 92481292ff89acdd80a0c9c556c8e82184a2e340 Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 28 Aug 2023 17:18:48 +0200 Subject: [PATCH 01/12] Refactor computing the public key package and expose it. --- frost-core/src/frost/keys.rs | 39 +++++++++++++ frost-core/src/frost/keys/dkg.rs | 94 ++++++-------------------------- 2 files changed, 57 insertions(+), 76 deletions(-) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index 820b9595..b0b18245 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -28,6 +28,45 @@ use super::compute_lagrange_coefficient; pub mod dkg; pub mod repairable; +/// Computes a verifying share for a peer given a list of commitments. +#[cfg_attr(feature = "internals", visibility::make(pub))] +pub(crate) fn compute_verifying_share( + peer: Identifier, + commitments: &HashMap, VerifiableSecretSharingCommitment>, +) -> VerifyingShare { + let mut y_i = ::identity(); + for commitment in commitments.values() { + y_i = y_i + evaluate_vss(commitment, peer); + } + VerifyingShare(y_i) +} + +/// Computes the group public key given a list of commitments. +#[cfg_attr(feature = "internals", visibility::make(pub))] +pub(crate) fn compute_public_key( + commitments: &HashMap, VerifiableSecretSharingCommitment>, +) -> VerifyingKey { + let mut group_public = ::identity(); + for commitment in commitments.values() { + group_public = group_public + commitment.first().unwrap().value(); + } + VerifyingKey { + element: group_public, + } +} + +/// Computes the public key package given a list of commitments. +#[cfg_attr(feature = "internals", visibility::make(pub))] +pub(crate) fn compute_public_key_package( + commitments: &HashMap, VerifiableSecretSharingCommitment>, +) -> PublicKeyPackage { + let mut verifying_keys = HashMap::new(); + for peer in commitments.keys() { + verifying_keys.insert(*peer, compute_verifying_share(*peer, commitments)); + } + PublicKeyPackage::new(verifying_keys, compute_public_key(commitments)) +} + /// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s). pub(crate) fn generate_coefficients( size: usize, diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index c0250e9a..1ae3ece7 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -36,13 +36,13 @@ use rand_core::{CryptoRng, RngCore}; use crate::{ frost::Identifier, Challenge, Ciphersuite, Element, Error, Field, Group, Scalar, Signature, - SigningKey, VerifyingKey, + SigningKey, }; use super::{ - evaluate_polynomial, evaluate_vss, generate_coefficients, generate_secret_polynomial, - validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, SigningShare, - VerifiableSecretSharingCommitment, VerifyingShare, + compute_public_key_package, evaluate_polynomial, generate_coefficients, + generate_secret_polynomial, validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, + SigningShare, VerifiableSecretSharingCommitment, }; /// DKG Round 1 structures. @@ -374,47 +374,6 @@ pub fn part2( )) } -/// Computes the verifying keys of the other participants for the third step -/// of the DKG protocol. -fn compute_verifying_keys( - round1_packages: &HashMap, round1::Package>, - round2_secret_package: &round2::SecretPackage, -) -> Result, VerifyingShare>, Error> { - // Round 2, Step 4 - // - // > Any participant can compute the public verification share of any other participant - // > by calculating Y_i = ∏_{j=1}^n ∏_{k=0}^{t−1} φ_{jk}^{i^k mod q}. - let mut others_verifying_keys = HashMap::new(); - - // Note that in this loop, "i" refers to the other participant whose public verification share - // we are computing, and not the current participant. - for i in round1_packages.keys().cloned() { - let mut y_i = ::identity(); - - // We need to iterate through all commitment vectors, including our own, - // so chain it manually - for commitment in round1_packages - .keys() - .map(|k| { - // Get the commitment vector for this participant - Ok::<&VerifiableSecretSharingCommitment, Error>( - &round1_packages - .get(k) - .ok_or(Error::PackageNotFound)? - .commitment, - ) - }) - // Chain our own commitment vector - .chain(iter::once(Ok(&round2_secret_package.commitment))) - { - y_i = y_i + evaluate_vss(commitment?, i); - } - let y_i = VerifyingShare(y_i); - others_verifying_keys.insert(i, y_i); - } - Ok(others_verifying_keys) -} - /// Performs the third and final part of the distributed key generation protocol /// for the participant holding the given [`round2::SecretPackage`], /// given the received [`round1::Package`]s and [`round2::Package`]s received from @@ -451,7 +410,6 @@ pub fn part3( } let mut signing_share = <::Field>::zero(); - let mut group_public = ::identity(); for (sender_identifier, round2_package) in round2_packages { // Round 2, Step 2 @@ -485,44 +443,28 @@ pub fn part3( // > Each P_i calculates their long-lived private signing share by computing // > s_i = ∑^n_{ℓ=1} f_ℓ(i), stores s_i securely, and deletes each f_ℓ(i). signing_share = signing_share + f_ell_i.0; - - // Round 2, Step 4 - // - // > Each P_i calculates [...] the group’s public key Y = ∏^n_{j=1} φ_{j0}. - group_public = group_public + commitment.first()?.0; } signing_share = signing_share + round2_secret_package.secret_share; - group_public = group_public + round2_secret_package.commitment.first()?.0; - let signing_share = SigningShare(signing_share); - // Round 2, Step 4 - // - // > Each P_i calculates their public verification share Y_i = g^{s_i}. - let verifying_key = signing_share.into(); - let group_public = VerifyingKey { - element: group_public, - }; - - // Round 2, Step 4 - // - // > Any participant can compute the public verification share of any other participant - // > by calculating Y_i = ∏_{j=1}^n ∏_{k=0}^{t−1} φ_{jk}^{i^k mod q}. - let mut all_verifying_keys = compute_verifying_keys(round1_packages, round2_secret_package)?; - - // Add the participant's own public verification share for consistency - all_verifying_keys.insert(round2_secret_package.identifier, verifying_key); + let commitments = round1_packages + .into_iter() + .map(|(peer, package)| (*peer, package.commitment.clone())) + .chain(iter::once(( + round2_secret_package.identifier, + round2_secret_package.commitment.clone(), + ))) + .collect(); + let public_key_package = compute_public_key_package(&commitments); let key_package = KeyPackage { identifier: round2_secret_package.identifier, secret_share: signing_share, - public: verifying_key, - group_public, - ciphersuite: (), - }; - let public_key_package = PublicKeyPackage { - signer_pubkeys: all_verifying_keys, - group_public, + public: *public_key_package + .signer_pubkeys + .get(&round2_secret_package.identifier) + .unwrap(), + group_public: public_key_package.group_public, ciphersuite: (), }; From 9f775fdc6101d541250b942bf3f0dcd2aba1d27c Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 28 Aug 2023 20:03:17 +0200 Subject: [PATCH 02/12] Fix clippy issue. --- frost-core/src/frost/keys.rs | 2 +- frost-core/src/frost/keys/dkg.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index b0b18245..c587c18b 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -48,7 +48,7 @@ pub(crate) fn compute_public_key( ) -> VerifyingKey { let mut group_public = ::identity(); for commitment in commitments.values() { - group_public = group_public + commitment.first().unwrap().value(); + group_public = group_public + commitment.first().expect("valid commitments").value(); } VerifyingKey { element: group_public, diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index 1ae3ece7..4148c817 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -449,7 +449,7 @@ pub fn part3( let signing_share = SigningShare(signing_share); let commitments = round1_packages - .into_iter() + .iter() .map(|(peer, package)| (*peer, package.commitment.clone())) .chain(iter::once(( round2_secret_package.identifier, @@ -463,7 +463,7 @@ pub fn part3( public: *public_key_package .signer_pubkeys .get(&round2_secret_package.identifier) - .unwrap(), + .expect("round2_secret_package.commitment is in the hashmap"), group_public: public_key_package.group_public, ciphersuite: (), }; From 4645b5e98dab74aaafea3c44415b4c5611bf69e7 Mon Sep 17 00:00:00 2001 From: David Craven Date: Sun, 3 Sep 2023 22:53:25 +0200 Subject: [PATCH 03/12] Add test. --- frost-core/src/tests/vss_commitment.rs | 13 +++++++++++++ frost-secp256k1/src/tests/vss_commitment.rs | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index e37efe19..edcbf9e9 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -11,6 +11,7 @@ use debugless_unwrap::DebuglessUnwrap; use rand_core::{CryptoRng, RngCore}; use serde_json::Value; +use crate::frost::keys::{compute_public_key_package, generate_with_dealer, IdentifierList}; use crate::Ciphersuite; /// Test serialize VerifiableSecretSharingCommitment @@ -107,3 +108,15 @@ pub fn check_deserialize_vss_commitment_error(mut rng: R) { + let (secret_shares, public_key) = + generate_with_dealer(3, 2, IdentifierList::Default, &mut rng).unwrap(); + let commitments = secret_shares + .iter() + .map(|(peer, secret_share)| (*peer, secret_share.commitment().clone())) + .collect(); + let public_key_package = compute_public_key_package::(&commitments); + assert_eq!(public_key, public_key_package); +} diff --git a/frost-secp256k1/src/tests/vss_commitment.rs b/frost-secp256k1/src/tests/vss_commitment.rs index 1434d7c3..1a09195a 100644 --- a/frost-secp256k1/src/tests/vss_commitment.rs +++ b/frost-secp256k1/src/tests/vss_commitment.rs @@ -30,3 +30,9 @@ fn check_deserialize_vss_commitment_error() { rng, &ELEMENTS, ); } + +#[test] +fn check_compute_public_key_package() { + let rng = thread_rng(); + frost_core::tests::vss_commitment::check_compute_public_key_package::(rng); +} From 7d321b159ba06919e8f14db950a4ff90ceae6782 Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 03:17:59 +0200 Subject: [PATCH 04/12] Fix test. --- frost-core/src/tests/vss_commitment.rs | 53 ++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index edcbf9e9..7d91e3ff 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -111,12 +111,51 @@ pub fn check_deserialize_vss_commitment_error(mut rng: R) { - let (secret_shares, public_key) = - generate_with_dealer(3, 2, IdentifierList::Default, &mut rng).unwrap(); - let commitments = secret_shares - .iter() - .map(|(peer, secret_share)| (*peer, secret_share.commitment().clone())) - .collect(); + const MAX_SIGNERS: u16 = 3; + const MIN_SIGNERS: u16 = 2; + let identifiers = crate::frost::keys::default_identifiers(MAX_SIGNERS); + let mut commitments = std::collections::HashMap::new(); + let mut verifying_shares = std::collections::HashMap::new(); + let mut public_key = ::identity(); + for identifier in &identifiers { + let (secret_shares_i, public_key_package_i) = generate_with_dealer::( + MAX_SIGNERS, + MIN_SIGNERS, + IdentifierList::Custom(&identifiers), + &mut rng, + ) + .unwrap(); + for identifier in &identifiers { + let entry = verifying_shares + .entry(*identifier) + .or_insert_with(|| ::identity()); + *entry = *entry + + public_key_package_i + .signer_pubkeys() + .get(identifier) + .unwrap() + .to_element(); + } + public_key = public_key + public_key_package_i.group_public().to_element(); + commitments.insert( + *identifier, + secret_shares_i + .get(identifier) + .unwrap() + .commitment() + .clone(), + ); + } let public_key_package = compute_public_key_package::(&commitments); - assert_eq!(public_key, public_key_package); + assert!(public_key == public_key_package.group_public().to_element()); + for identifier in &identifiers { + assert!( + *verifying_shares.get(identifier).unwrap() + == public_key_package + .signer_pubkeys() + .get(identifier) + .unwrap() + .to_element(), + ) + } } From afa6e2651d6718fb7ac1fbf374e7ffc9c8e0e4ff Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 03:46:24 +0200 Subject: [PATCH 05/12] Improve test. --- frost-core/src/tests/vss_commitment.rs | 98 ++++++++++++++------------ 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 7d91e3ff..e1219e9a 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -10,9 +10,13 @@ use crate::{ use debugless_unwrap::DebuglessUnwrap; use rand_core::{CryptoRng, RngCore}; use serde_json::Value; +use std::collections::HashMap; -use crate::frost::keys::{compute_public_key_package, generate_with_dealer, IdentifierList}; -use crate::Ciphersuite; +use crate::frost::keys::{ + compute_public_key_package, generate_with_dealer, reconstruct, IdentifierList, + PublicKeyPackage, SecretShare, SigningShare, VerifyingShare, +}; +use crate::{Ciphersuite, Field, VerifyingKey}; /// Test serialize VerifiableSecretSharingCommitment pub fn check_serialize_vss_commitment(mut rng: R) { @@ -111,51 +115,53 @@ pub fn check_deserialize_vss_commitment_error(mut rng: R) { - const MAX_SIGNERS: u16 = 3; - const MIN_SIGNERS: u16 = 2; - let identifiers = crate::frost::keys::default_identifiers(MAX_SIGNERS); - let mut commitments = std::collections::HashMap::new(); - let mut verifying_shares = std::collections::HashMap::new(); - let mut public_key = ::identity(); - for identifier in &identifiers { - let (secret_shares_i, public_key_package_i) = generate_with_dealer::( - MAX_SIGNERS, - MIN_SIGNERS, - IdentifierList::Custom(&identifiers), - &mut rng, - ) - .unwrap(); - for identifier in &identifiers { + let max_signers = 3; + let min_signers = 2; + let (secret_shares, public_key_package) = + generate_with_dealer::(max_signers, min_signers, IdentifierList::Default, &mut rng) + .unwrap(); + let commitments = secret_shares + .iter() + .map(|(id, secret_share)| (*id, secret_share.commitment().clone())) + .collect(); + let mut group_public = VerifyingKey::new(::identity()); + let mut signing_shares = HashMap::new(); + let mut verifying_shares = HashMap::new(); + for _ in 0..max_signers { + group_public = VerifyingKey::new( + group_public.to_element() + public_key_package.group_public().to_element(), + ); + for (id, verifying_share) in public_key_package.signer_pubkeys() { let entry = verifying_shares - .entry(*identifier) - .or_insert_with(|| ::identity()); - *entry = *entry - + public_key_package_i - .signer_pubkeys() - .get(identifier) - .unwrap() - .to_element(); + .entry(*id) + .or_insert_with(|| VerifyingShare::new(::identity())); + *entry = VerifyingShare::new(entry.to_element() + verifying_share.to_element()); + } + for (id, secret_share) in &secret_shares { + let entry = signing_shares + .entry(*id) + .or_insert_with(|| SigningShare::::new(::Field::zero())); + *entry = SigningShare::new(entry.to_scalar() + secret_share.value().to_scalar()); } - public_key = public_key + public_key_package_i.group_public().to_element(); - commitments.insert( - *identifier, - secret_shares_i - .get(identifier) - .unwrap() - .commitment() - .clone(), - ); - } - let public_key_package = compute_public_key_package::(&commitments); - assert!(public_key == public_key_package.group_public().to_element()); - for identifier in &identifiers { - assert!( - *verifying_shares.get(identifier).unwrap() - == public_key_package - .signer_pubkeys() - .get(identifier) - .unwrap() - .to_element(), - ) } + let public_key_package = PublicKeyPackage::new(verifying_shares, group_public); + assert_eq!(public_key_package, compute_public_key_package(&commitments)); + let signing_key = reconstruct( + &signing_shares + .iter() + .take(min_signers as _) + .map(|(id, signing_share)| { + SecretShare::new( + *id, + signing_share.clone(), + commitments.get(id).unwrap().clone(), + ) + }) + .collect::>(), + ) + .unwrap(); + assert_eq!( + *public_key_package.group_public(), + VerifyingKey::from(signing_key) + ); } From 6a34b37de34d2f2be7da661a9fde75a9e5af9bec Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 05:51:14 +0200 Subject: [PATCH 06/12] Split it up. --- frost-core/src/frost/keys.rs | 52 ++++++++++++++++---------- frost-core/src/frost/keys/dkg.rs | 22 +++++------ frost-core/src/tests/repairable.rs | 2 +- frost-core/src/tests/vss_commitment.rs | 41 ++++++++++---------- 4 files changed, 64 insertions(+), 53 deletions(-) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index c587c18b..d23aa97e 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -28,43 +28,50 @@ use super::compute_lagrange_coefficient; pub mod dkg; pub mod repairable; -/// Computes a verifying share for a peer given a list of commitments. +/// Sum the commitments from all peers into a group commitment. +#[cfg_attr(feature = "internals", visibility::make(pub))] +pub(crate) fn compute_group_commitment( + commitments: &[VerifiableSecretSharingCommitment], +) -> VerifiableSecretSharingCommitment { + let mut group_commitment = + vec![CoefficientCommitment(::identity()); commitments[0].0.len()]; + for commitment in commitments { + for i in 0..group_commitment.len() { + group_commitment[i] = + CoefficientCommitment(group_commitment[i].value() + commitment.0[i].value()); + } + } + VerifiableSecretSharingCommitment(group_commitment) +} + +/// Computes a verifying share for a peer given the group commitment. #[cfg_attr(feature = "internals", visibility::make(pub))] pub(crate) fn compute_verifying_share( + group_commitment: &VerifiableSecretSharingCommitment, peer: Identifier, - commitments: &HashMap, VerifiableSecretSharingCommitment>, ) -> VerifyingShare { - let mut y_i = ::identity(); - for commitment in commitments.values() { - y_i = y_i + evaluate_vss(commitment, peer); - } - VerifyingShare(y_i) + VerifyingShare::new(evaluate_vss(group_commitment, peer)) } -/// Computes the group public key given a list of commitments. +/// Computes the group public key given the group commitment. #[cfg_attr(feature = "internals", visibility::make(pub))] pub(crate) fn compute_public_key( - commitments: &HashMap, VerifiableSecretSharingCommitment>, + group_commitment: &VerifiableSecretSharingCommitment, ) -> VerifyingKey { - let mut group_public = ::identity(); - for commitment in commitments.values() { - group_public = group_public + commitment.first().expect("valid commitments").value(); - } - VerifyingKey { - element: group_public, - } + VerifyingKey::new(group_commitment.first().expect("valid commitments").value()) } /// Computes the public key package given a list of commitments. #[cfg_attr(feature = "internals", visibility::make(pub))] pub(crate) fn compute_public_key_package( - commitments: &HashMap, VerifiableSecretSharingCommitment>, + members: &BTreeSet>, + group_commitment: &VerifiableSecretSharingCommitment, ) -> PublicKeyPackage { let mut verifying_keys = HashMap::new(); - for peer in commitments.keys() { - verifying_keys.insert(*peer, compute_verifying_share(*peer, commitments)); + for peer in members { + verifying_keys.insert(*peer, compute_verifying_share(group_commitment, *peer)); } - PublicKeyPackage::new(verifying_keys, compute_public_key(commitments)) + PublicKeyPackage::new(verifying_keys, compute_public_key(group_commitment)) } /// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s). @@ -373,6 +380,11 @@ where pub(crate) fn first(&self) -> Result, Error> { self.0.get(0).ok_or(Error::MissingCommitment).copied() } + + /// Returns the coefficient commitments. + pub fn coefficients(&self) -> &[CoefficientCommitment] { + &self.0 + } } /// A secret share generated by performing a (t-out-of-n) secret sharing scheme, diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index 4148c817..5dd2ecb1 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -40,9 +40,9 @@ use crate::{ }; use super::{ - compute_public_key_package, evaluate_polynomial, generate_coefficients, - generate_secret_polynomial, validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, - SigningShare, VerifiableSecretSharingCommitment, + compute_group_commitment, compute_public_key_package, evaluate_polynomial, + generate_coefficients, generate_secret_polynomial, validate_num_of_signers, KeyPackage, + PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment, }; /// DKG Round 1 structures. @@ -447,16 +447,14 @@ pub fn part3( signing_share = signing_share + round2_secret_package.secret_share; let signing_share = SigningShare(signing_share); - - let commitments = round1_packages - .iter() - .map(|(peer, package)| (*peer, package.commitment.clone())) - .chain(iter::once(( - round2_secret_package.identifier, - round2_secret_package.commitment.clone(), - ))) + let members = round1_packages.keys().copied().collect(); + let commitments: Vec<_> = round1_packages + .values() + .map(|package| package.commitment.clone()) + .chain(iter::once(round2_secret_package.commitment.clone())) .collect(); - let public_key_package = compute_public_key_package(&commitments); + let group_commitment = compute_group_commitment(&commitments); + let public_key_package = compute_public_key_package(&members, &group_commitment); let key_package = KeyPackage { identifier: round2_secret_package.identifier, secret_share: signing_share, diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index c8a8f15f..a3e04755 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -11,7 +11,7 @@ use crate::{ self, compute_lagrange_coefficient, keys::{ repairable::{repair_share_step_1, repair_share_step_2, repair_share_step_3}, - PublicKeyPackage, SecretShare, SigningShare, + KeyPackage, PublicKeyPackage, SecretShare, SigningShare, }, Identifier, }, diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index e1219e9a..3a422330 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -13,8 +13,8 @@ use serde_json::Value; use std::collections::HashMap; use crate::frost::keys::{ - compute_public_key_package, generate_with_dealer, reconstruct, IdentifierList, - PublicKeyPackage, SecretShare, SigningShare, VerifyingShare, + compute_group_commitment, compute_public_key_package, generate_with_dealer, reconstruct, + IdentifierList, KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifyingShare, }; use crate::{Ciphersuite, Field, VerifyingKey}; @@ -120,10 +120,12 @@ pub fn check_compute_public_key_package( let (secret_shares, public_key_package) = generate_with_dealer::(max_signers, min_signers, IdentifierList::Default, &mut rng) .unwrap(); - let commitments = secret_shares - .iter() - .map(|(id, secret_share)| (*id, secret_share.commitment().clone())) + let commitments: Vec<_> = secret_shares + .values() + .map(|secret_share| secret_share.commitment().clone()) .collect(); + let members = secret_shares.keys().copied().collect(); + let group_commitment = compute_group_commitment(&commitments); let mut group_public = VerifyingKey::new(::identity()); let mut signing_shares = HashMap::new(); let mut verifying_shares = HashMap::new(); @@ -144,24 +146,23 @@ pub fn check_compute_public_key_package( *entry = SigningShare::new(entry.to_scalar() + secret_share.value().to_scalar()); } } + let secret_shares = signing_shares + .iter() + .map(|(id, signing_share)| { + SecretShare::new(*id, signing_share.clone(), group_commitment.clone()) + }) + .collect::>(); let public_key_package = PublicKeyPackage::new(verifying_shares, group_public); - assert_eq!(public_key_package, compute_public_key_package(&commitments)); - let signing_key = reconstruct( - &signing_shares - .iter() - .take(min_signers as _) - .map(|(id, signing_share)| { - SecretShare::new( - *id, - signing_share.clone(), - commitments.get(id).unwrap().clone(), - ) - }) - .collect::>(), - ) - .unwrap(); + assert_eq!( + public_key_package, + compute_public_key_package(&members, &group_commitment) + ); + let signing_key = reconstruct(&secret_shares[..min_signers as usize]).unwrap(); assert_eq!( *public_key_package.group_public(), VerifyingKey::from(signing_key) ); + for secret_share in secret_shares { + KeyPackage::try_from(secret_share).unwrap(); + } } From 352e7d9c15095658e0377e2d57d1e62c518111a6 Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 05:53:13 +0200 Subject: [PATCH 07/12] Fix build. --- frost-core/src/frost/keys.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index d23aa97e..6e422df5 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -50,7 +50,7 @@ pub(crate) fn compute_verifying_share( group_commitment: &VerifiableSecretSharingCommitment, peer: Identifier, ) -> VerifyingShare { - VerifyingShare::new(evaluate_vss(group_commitment, peer)) + VerifyingShare(evaluate_vss(group_commitment, peer)) } /// Computes the group public key given the group commitment. @@ -58,7 +58,9 @@ pub(crate) fn compute_verifying_share( pub(crate) fn compute_public_key( group_commitment: &VerifiableSecretSharingCommitment, ) -> VerifyingKey { - VerifyingKey::new(group_commitment.first().expect("valid commitments").value()) + VerifyingKey { + element: group_commitment.coefficients()[0].value(), + } } /// Computes the public key package given a list of commitments. From 297faafbcdd74b95da3596065f9b1a21a513dc6f Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 15:14:54 +0200 Subject: [PATCH 08/12] Improve api. --- frost-core/src/frost/keys.rs | 54 +++++++++++--------------- frost-core/src/frost/keys/dkg.rs | 8 ++-- frost-core/src/tests/vss_commitment.rs | 6 +-- frost-core/src/verifying_key.rs | 10 +++++ 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index 6e422df5..c8470f68 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -44,38 +44,6 @@ pub(crate) fn compute_group_commitment( VerifiableSecretSharingCommitment(group_commitment) } -/// Computes a verifying share for a peer given the group commitment. -#[cfg_attr(feature = "internals", visibility::make(pub))] -pub(crate) fn compute_verifying_share( - group_commitment: &VerifiableSecretSharingCommitment, - peer: Identifier, -) -> VerifyingShare { - VerifyingShare(evaluate_vss(group_commitment, peer)) -} - -/// Computes the group public key given the group commitment. -#[cfg_attr(feature = "internals", visibility::make(pub))] -pub(crate) fn compute_public_key( - group_commitment: &VerifiableSecretSharingCommitment, -) -> VerifyingKey { - VerifyingKey { - element: group_commitment.coefficients()[0].value(), - } -} - -/// Computes the public key package given a list of commitments. -#[cfg_attr(feature = "internals", visibility::make(pub))] -pub(crate) fn compute_public_key_package( - members: &BTreeSet>, - group_commitment: &VerifiableSecretSharingCommitment, -) -> PublicKeyPackage { - let mut verifying_keys = HashMap::new(); - for peer in members { - verifying_keys.insert(*peer, compute_verifying_share(group_commitment, *peer)); - } - PublicKeyPackage::new(verifying_keys, compute_public_key(group_commitment)) -} - /// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s). pub(crate) fn generate_coefficients( size: usize, @@ -226,6 +194,15 @@ where pub fn serialize(&self) -> ::Serialization { ::serialize(&self.0) } + + /// Computes a verifying share for a peer given the group commitment. + #[cfg_attr(feature = "internals", visibility::make(pub))] + pub(crate) fn from_commitment( + commitment: &VerifiableSecretSharingCommitment, + peer: Identifier, + ) -> VerifyingShare { + VerifyingShare(evaluate_vss(commitment, peer)) + } } impl Debug for VerifyingShare @@ -724,6 +701,19 @@ where ciphersuite: (), } } + + /// Computes the public key package given a list of commitments. + #[cfg_attr(feature = "internals", visibility::make(pub))] + pub(crate) fn from_commitment( + members: &BTreeSet>, + commitment: &VerifiableSecretSharingCommitment, + ) -> PublicKeyPackage { + let mut verifying_keys = HashMap::new(); + for peer in members { + verifying_keys.insert(*peer, VerifyingShare::from_commitment(commitment, *peer)); + } + PublicKeyPackage::new(verifying_keys, VerifyingKey::from_commitment(commitment)) + } } fn validate_num_of_signers( diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index 5dd2ecb1..650e8343 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -40,9 +40,9 @@ use crate::{ }; use super::{ - compute_group_commitment, compute_public_key_package, evaluate_polynomial, - generate_coefficients, generate_secret_polynomial, validate_num_of_signers, KeyPackage, - PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment, + compute_group_commitment, evaluate_polynomial, generate_coefficients, + generate_secret_polynomial, validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, + SigningShare, VerifiableSecretSharingCommitment, }; /// DKG Round 1 structures. @@ -454,7 +454,7 @@ pub fn part3( .chain(iter::once(round2_secret_package.commitment.clone())) .collect(); let group_commitment = compute_group_commitment(&commitments); - let public_key_package = compute_public_key_package(&members, &group_commitment); + let public_key_package = PublicKeyPackage::from_commitment(&members, &group_commitment); let key_package = KeyPackage { identifier: round2_secret_package.identifier, secret_share: signing_share, diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 3a422330..5a71855e 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -13,8 +13,8 @@ use serde_json::Value; use std::collections::HashMap; use crate::frost::keys::{ - compute_group_commitment, compute_public_key_package, generate_with_dealer, reconstruct, - IdentifierList, KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifyingShare, + compute_group_commitment, generate_with_dealer, reconstruct, IdentifierList, KeyPackage, + PublicKeyPackage, SecretShare, SigningShare, VerifyingShare, }; use crate::{Ciphersuite, Field, VerifyingKey}; @@ -155,7 +155,7 @@ pub fn check_compute_public_key_package( let public_key_package = PublicKeyPackage::new(verifying_shares, group_public); assert_eq!( public_key_package, - compute_public_key_package(&members, &group_commitment) + PublicKeyPackage::from_commitment(&members, &group_commitment) ); let signing_key = reconstruct(&secret_shares[..min_signers as usize]).unwrap(); assert_eq!( diff --git a/frost-core/src/verifying_key.rs b/frost-core/src/verifying_key.rs index 2932613e..273c594a 100644 --- a/frost-core/src/verifying_key.rs +++ b/frost-core/src/verifying_key.rs @@ -76,6 +76,16 @@ where pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { C::verify_signature(msg, signature, self) } + + /// Computes the group public key given the group commitment. + #[cfg_attr(feature = "internals", visibility::make(pub))] + pub(crate) fn from_commitment( + commitment: &crate::frost::keys::VerifiableSecretSharingCommitment, + ) -> VerifyingKey { + VerifyingKey { + element: commitment.coefficients()[0].value(), + } + } } impl Debug for VerifyingKey From c49edabb4bd8055ef49f6fd1b0f3109ee11a1951 Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 15:27:17 +0200 Subject: [PATCH 09/12] Expose more stuff. --- frost-core/src/frost/keys.rs | 6 ++++++ frost-core/src/frost/keys/dkg.rs | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index c8470f68..f8feea44 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -97,6 +97,12 @@ where pub fn serialize(&self) -> <::Field as Field>::Serialization { <::Field>::serialize(&self.0) } + + /// Computes the signing share from a list of coefficients. + #[cfg_attr(feature = "internals", visibility::make(pub))] + pub(crate) fn from_coefficients(coefficients: &[Scalar], peer: Identifier) -> Self { + Self(evaluate_polynomial(peer, coefficients)) + } } impl Debug for SigningShare diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index 650e8343..8442c8c3 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -111,6 +111,17 @@ pub mod round1 { pub(crate) max_signers: u16, } + impl SecretPackage + where + C: Ciphersuite, + { + #[cfg(feature = "internals")] + /// Returns the secret coefficients. + pub fn coefficients(&self) -> &[Scalar] { + &self.coefficients + } + } + impl std::fmt::Debug for SecretPackage where C: Ciphersuite, From 343c8ae928a5df14f0589a4d6de0b7dd6a67e46b Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 15:42:22 +0200 Subject: [PATCH 10/12] Expose more stuff. --- frost-core/src/frost/keys.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index f8feea44..d9245fac 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -55,6 +55,7 @@ pub(crate) fn generate_coefficients( } /// Return a list of default identifiers (1 to max_signers, inclusive). +#[cfg_attr(feature = "internals", visibility::make(pub))] pub(crate) fn default_identifiers(max_signers: u16) -> Vec> { (1..=max_signers) .map(|i| Identifier::::try_from(i).expect("nonzero")) From f810c254e301f2224ac95f31c63b2cb199d08269 Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 4 Sep 2023 15:53:29 +0200 Subject: [PATCH 11/12] Extract proof of knowledge verification. --- frost-core/src/frost/keys/dkg.rs | 42 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index 8442c8c3..5b7df338 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -313,6 +313,30 @@ where Some(Challenge(C::HDKG(&preimage[..])?)) } +/// Verifies the proof of knowledge of the secret coefficients used to generate the +/// public secret sharing commitment. +#[cfg_attr(feature = "internals", visibility::make(pub))] +pub(crate) fn verify_proof_of_knowledge( + identifier: Identifier, + commitment: &VerifiableSecretSharingCommitment, + proof_of_knowledge: Signature, +) -> Result<(), Error> { + // Round 1, Step 5 + // + // > Upon receiving C⃗_ℓ, σ_ℓ from participants 1 ≤ ℓ ≤ n, ℓ ≠ i, participant + // > P_i verifies σ_ℓ = (R_ℓ, μ_ℓ), aborting on failure, by checking + // > R_ℓ ? ≟ g^{μ_ℓ} · φ^{-c_ℓ}_{ℓ0}, where c_ℓ = H(ℓ, Φ, φ_{ℓ0}, R_ℓ). + let ell = identifier; + let R_ell = proof_of_knowledge.R; + let mu_ell = proof_of_knowledge.z; + let phi_ell0 = commitment.first()?.0; + let c_ell = challenge::(ell, &R_ell, &phi_ell0).ok_or(Error::DKGNotSupported)?; + if R_ell != ::generator() * mu_ell - phi_ell0 * c_ell.0 { + return Err(Error::InvalidProofOfKnowledge { culprit: ell }); + } + Ok(()) +} + /// Performs the second part of the distributed key generation protocol /// for the participant holding the given [`round1::SecretPackage`], /// given the received [`round1::Package`]s received from the other participants. @@ -344,19 +368,11 @@ pub fn part2( for (sender_identifier, round1_package) in round1_packages { let ell = *sender_identifier; - // Round 1, Step 5 - // - // > Upon receiving C⃗_ℓ, σ_ℓ from participants 1 ≤ ℓ ≤ n, ℓ ≠ i, participant - // > P_i verifies σ_ℓ = (R_ℓ, μ_ℓ), aborting on failure, by checking - // > R_ℓ ? ≟ g^{μ_ℓ} · φ^{-c_ℓ}_{ℓ0}, where c_ℓ = H(ℓ, Φ, φ_{ℓ0}, R_ℓ). - let R_ell = round1_package.proof_of_knowledge.R; - let mu_ell = round1_package.proof_of_knowledge.z; - let phi_ell0 = round1_package.commitment.first()?.0; - let c_ell = challenge::(ell, &R_ell, &phi_ell0).ok_or(Error::DKGNotSupported)?; - - if R_ell != ::generator() * mu_ell - phi_ell0 * c_ell.0 { - return Err(Error::InvalidProofOfKnowledge { culprit: ell }); - } + verify_proof_of_knowledge( + ell, + &round1_package.commitment, + round1_package.proof_of_knowledge, + )?; // Round 2, Step 1 // From 69a83a7d05f77d42f5e10ca5f853e4b5be2f006b Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 25 Sep 2023 13:42:37 +0200 Subject: [PATCH 12/12] Add construct_proof_of_knowledge. --- frost-core/src/frost/keys/dkg.rs | 46 ++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/frost-core/src/frost/keys/dkg.rs b/frost-core/src/frost/keys/dkg.rs index 5b7df338..239d4212 100644 --- a/frost-core/src/frost/keys/dkg.rs +++ b/frost-core/src/frost/keys/dkg.rs @@ -263,22 +263,8 @@ pub fn part1( let coefficients = generate_coefficients::(min_signers as usize - 1, &mut rng); let (coefficients, commitment) = generate_secret_polynomial(&secret, max_signers, min_signers, coefficients)?; - - // Round 1, Step 2 - // - // > Every P_i computes a proof of knowledge to the corresponding secret - // > a_{i0} by calculating σ_i = (R_i, μ_i), such that k ← Z_q, R_i = g^k, - // > c_i = H(i, Φ, g^{a_{i0}} , R_i), μ_i = k + a_{i0} · c_i, with Φ being - // > a context string to prevent replay attacks. - - let k = <::Field>::random(&mut rng); - let R_i = ::generator() * k; - let c_i = - challenge::(identifier, &R_i, &commitment.first()?.0).ok_or(Error::DKGNotSupported)?; - let a_i0 = *coefficients - .get(0) - .expect("coefficients must have at least one element"); - let mu_i = k + a_i0 * c_i.0; + let proof_of_knowledge = + construct_proof_of_knowledge(identifier, &coefficients, &commitment, &mut rng)?; let secret_package = round1::SecretPackage { identifier, @@ -288,7 +274,7 @@ pub fn part1( }; let package = round1::Package { commitment, - proof_of_knowledge: Signature { R: R_i, z: mu_i }, + proof_of_knowledge, ciphersuite: (), }; @@ -313,6 +299,32 @@ where Some(Challenge(C::HDKG(&preimage[..])?)) } +/// Constructs the proof of knowledge of the secret coefficients used to generate +/// the public secret sharing commitment. +#[cfg_attr(feature = "internals", visibility::make(pub))] +pub(crate) fn construct_proof_of_knowledge( + identifier: Identifier, + coefficients: &[Scalar], + commitment: &VerifiableSecretSharingCommitment, + mut rng: R, +) -> Result, Error> { + // Round 1, Step 2 + // + // > Every P_i computes a proof of knowledge to the corresponding secret + // > a_{i0} by calculating σ_i = (R_i, μ_i), such that k ← Z_q, R_i = g^k, + // > c_i = H(i, Φ, g^{a_{i0}} , R_i), μ_i = k + a_{i0} · c_i, with Φ being + // > a context string to prevent replay attacks. + let k = <::Field>::random(&mut rng); + let R_i = ::generator() * k; + let c_i = + challenge::(identifier, &R_i, &commitment.first()?.0).ok_or(Error::DKGNotSupported)?; + let a_i0 = *coefficients + .get(0) + .expect("coefficients must have at least one element"); + let mu_i = k + a_i0 * c_i.0; + Ok(Signature { R: R_i, z: mu_i }) +} + /// Verifies the proof of knowledge of the secret coefficients used to generate the /// public secret sharing commitment. #[cfg_attr(feature = "internals", visibility::make(pub))]