Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add computing PublicKeyPackage from commitments #551

Merged
merged 21 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions frost-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Entries are listed in reverse chronological order.
struct, which affects self-describing formats like JSON. The ciphersuite ID
string was also changed for all ciphersuites: it is now equal to the
`contextString` of each ciphersuite per the FROST spec.
* Added `PublicKeyPackage::from_commitment()` and
`PublicKeyPackage::from_dkg_commitments` to create a `PublicKeyPackage` from
the commitments generated in trusted dealer or distributed key generation.

## Released

Expand Down
9 changes: 1 addition & 8 deletions frost-core/src/frost/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,7 @@ where
C: Ciphersuite,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
let serialized_self = <<C::Group as Group>::Field>::little_endian_serialize(&self.0);
let serialized_other = <<C::Group as Group>::Field>::little_endian_serialize(&other.0);
// The default cmp uses lexicographic order; so we need the elements in big endian
serialized_self
.as_ref()
.iter()
.rev()
.partial_cmp(serialized_other.as_ref().iter().rev())
Some(self.cmp(other))
}
}

Expand Down
104 changes: 100 additions & 4 deletions frost-core/src/frost/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,35 @@ use super::compute_lagrange_coefficient;
pub mod dkg;
pub mod repairable;

/// Sum the commitments from all participants in a distributed key generation
/// run into a single group commitment.
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn sum_commitments<C: Ciphersuite>(
commitments: &[&VerifiableSecretSharingCommitment<C>],
) -> Result<VerifiableSecretSharingCommitment<C>, Error<C>> {
let mut group_commitment = vec![
CoefficientCommitment(<C::Group>::identity());
commitments
.get(0)
.ok_or(Error::IncorrectNumberOfCommitments)?
.0
.len()
];
for commitment in commitments {
for (i, c) in group_commitment.iter_mut().enumerate() {
*c = CoefficientCommitment(
c.value()
+ commitment
.0
.get(i)
.ok_or(Error::IncorrectNumberOfCommitments)?
.value(),
);
}
}
Ok(VerifiableSecretSharingCommitment(group_commitment))
}

/// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s).
pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
size: usize,
Expand All @@ -40,6 +69,7 @@ pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
}

/// Return a list of default identifiers (1 to max_signers, inclusive).
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn default_identifiers<C: Ciphersuite>(max_signers: u16) -> Vec<Identifier<C>> {
(1..=max_signers)
.map(|i| Identifier::<C>::try_from(i).expect("nonzero"))
Expand Down Expand Up @@ -83,6 +113,12 @@ where
pub fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
<<C::Group as Group>::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<C>], peer: Identifier<C>) -> Self {
Self(evaluate_polynomial(peer, coefficients))
}
}

impl<C> Debug for SigningShare<C>
Expand Down Expand Up @@ -181,6 +217,26 @@ where
pub fn serialize(&self) -> <C::Group as Group>::Serialization {
<C::Group as Group>::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(
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> VerifyingShare<C> {
// DKG 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}.
//
// Rewriting the equation by moving the product over j to further inside
// the equation:
// Y_i = ∏_{k=0}^{t−1} (∏_{j=1}^n φ_{jk})^{i^k mod q}
// i.e. we can operate on the sum of all φ_j commitments, which is
// what is passed to the functions.
VerifyingShare(evaluate_vss(identifier, commitment))
}
}

impl<C> Debug for VerifyingShare<C>
Expand Down Expand Up @@ -339,6 +395,12 @@ where
pub(crate) fn first(&self) -> Result<CoefficientCommitment<C>, Error<C>> {
self.0.get(0).ok_or(Error::MissingCommitment).copied()
}

/// Returns the coefficient commitments.
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn coefficients(&self) -> &[CoefficientCommitment<C>] {
&self.0
}
}

/// A secret share generated by performing a (t-out-of-n) secret sharing scheme,
Expand Down Expand Up @@ -404,7 +466,7 @@ where
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#appendix-C.2-4
pub fn verify(&self) -> Result<(VerifyingShare<C>, VerifyingKey<C>), Error<C>> {
let f_result = <C::Group>::generator() * self.signing_share.0;
let result = evaluate_vss(&self.commitment, self.identifier);
let result = evaluate_vss(self.identifier, &self.commitment);

if !(f_result == result) {
return Err(Error::InvalidSecretShare);
Expand Down Expand Up @@ -547,11 +609,13 @@ fn evaluate_polynomial<C: Ciphersuite>(
}

/// Evaluates the right-hand side of the VSS verification equation, namely
/// ∏^{t−1}_{k=0} φ^{i^k mod q}_{ℓk} using `identifier` as `i` and the
/// `commitment` as the commitment vector φ_ℓ
/// ∏^{t−1}_{k=0} φ^{i^k mod q}_{ℓk} (multiplicative notation) using
/// `identifier` as `i` and the `commitment` as the commitment vector φ_ℓ.
///
/// This is also used in Round 2, Step 4 of the DKG.
fn evaluate_vss<C: Ciphersuite>(
commitment: &VerifiableSecretSharingCommitment<C>,
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> Element<C> {
let i = identifier;

Expand Down Expand Up @@ -691,6 +755,38 @@ where
verifying_key,
}
}

/// Computes the public key package given a list of participant identifiers
/// and a [`VerifiableSecretSharingCommitment`]. This is useful in scenarios
/// where the commitments are published somewhere and it's desirable to
/// recreate the public key package from them.
pub fn from_commitment(
identifiers: &BTreeSet<Identifier<C>>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> Result<PublicKeyPackage<C>, Error<C>> {
let verifying_keys: BTreeMap<_, _> = identifiers
.iter()
.map(|id| (*id, VerifyingShare::from_commitment(*id, commitment)))
.collect();
Ok(PublicKeyPackage::new(
verifying_keys,
VerifyingKey::from_commitment(commitment)?,
))
}

/// Computes the public key package given a map of participant identifiers
/// and their [`VerifiableSecretSharingCommitment`] from a distributed key
/// generation process. This is useful in scenarios where the commitments
/// are published somewhere and it's desirable to recreate the public key
/// package from them.
pub fn from_dkg_commitments(
commitments: &BTreeMap<Identifier<C>, &VerifiableSecretSharingCommitment<C>>,
) -> Result<PublicKeyPackage<C>, Error<C>> {
let identifiers: BTreeSet<_> = commitments.keys().copied().collect();
let commitments: Vec<_> = commitments.values().copied().collect();
let group_commitment = sum_commitments(&commitments)?;
Self::from_commitment(&identifiers, &group_commitment)
}
}

#[cfg(feature = "serialization")]
Expand Down
Loading
Loading