add computing PublicKeyPackage from commitments (#551)
* Refactor computing the public key package and expose it. * Fix clippy issue. * Add test. * Fix test. * Improve test. * Split it up. * Fix build. * Improve api. * Expose more stuff. * Expose more stuff. * Extract proof of knowledge verification. * Add construct_proof_of_knowledge. * fix dkg test * overall cleanup * change IncorrectCommitment to IncorrectNumberOfCommitments * clippy fixes * fix order of inputs for DKG commitment hashing * also change regular challenge() verifying key argument to VerifyingKey --------- Co-authored-by: David Craven <david@craven.ch>
This commit is contained in:
parent
e0db6151e0
commit
71c092532c
|
@ -14,6 +14,9 @@ Entries are listed in reverse chronological order.
|
|||
string was also changed for all ciphersuites: it is now equal to the
|
||||
`contextString` of each ciphersuite per the FROST spec.
|
||||
* An option to disable cheater detection during aggregation of signatures has been added.
|
||||
* 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
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ where
|
|||
{
|
||||
fn from((vk, sig, msg): (VerifyingKey<C>, Signature<C>, &'msg M)) -> Self {
|
||||
// Compute c now to avoid dependency on the msg lifetime.
|
||||
let c = crate::challenge(&sig.R, &vk.element, msg.as_ref());
|
||||
let c = crate::challenge(&sig.R, &vk, msg.as_ref());
|
||||
|
||||
Self { vk, sig, c }
|
||||
}
|
||||
|
|
|
@ -467,7 +467,7 @@ where
|
|||
// Compute the per-message challenge.
|
||||
let challenge = crate::challenge::<C>(
|
||||
&group_commitment.0,
|
||||
&pubkeys.verifying_key.element,
|
||||
&pubkeys.verifying_key,
|
||||
signing_package.message().as_slice(),
|
||||
);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -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"))
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -334,10 +390,18 @@ where
|
|||
Ok(Self(coefficient_commitments))
|
||||
}
|
||||
|
||||
/// Get the first commitment (which is equivalent to the VerifyingKey),
|
||||
/// or an error if the vector is empty.
|
||||
pub(crate) fn first(&self) -> Result<CoefficientCommitment<C>, Error<C>> {
|
||||
self.0.get(0).ok_or(Error::MissingCommitment).copied()
|
||||
/// Get the VerifyingKey matching this commitment vector (which is the first
|
||||
/// element in the vector), or an error if the vector is empty.
|
||||
pub(crate) fn verifying_key(&self) -> Result<VerifyingKey<C>, Error<C>> {
|
||||
Ok(VerifyingKey::new(
|
||||
self.0.get(0).ok_or(Error::MissingCommitment)?.0,
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the coefficient commitments.
|
||||
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||
pub(crate) fn coefficients(&self) -> &[CoefficientCommitment<C>] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,17 +468,13 @@ 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);
|
||||
}
|
||||
|
||||
let verifying_key = VerifyingKey {
|
||||
element: self.commitment.first()?.0,
|
||||
};
|
||||
|
||||
Ok((VerifyingShare(result), verifying_key))
|
||||
Ok((VerifyingShare(result), self.commitment.verifying_key()?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,11 +607,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;
|
||||
|
||||
|
@ -691,6 +753,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")]
|
||||
|
|
|
@ -40,9 +40,9 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
evaluate_polynomial, evaluate_vss, generate_coefficients, generate_secret_polynomial,
|
||||
evaluate_polynomial, generate_coefficients, generate_secret_polynomial,
|
||||
validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, SigningShare,
|
||||
VerifiableSecretSharingCommitment, VerifyingShare,
|
||||
VerifiableSecretSharingCommitment,
|
||||
};
|
||||
|
||||
/// DKG Round 1 structures.
|
||||
|
@ -124,6 +124,17 @@ pub mod round1 {
|
|||
pub(crate) max_signers: u16,
|
||||
}
|
||||
|
||||
impl<C> SecretPackage<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Returns the secret coefficients.
|
||||
#[cfg(feature = "internals")]
|
||||
pub fn coefficients(&self) -> &[Scalar<C>] {
|
||||
&self.coefficients
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> std::fmt::Debug for SecretPackage<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
|
@ -280,22 +291,8 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
let coefficients = generate_coefficients::<C, R>(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 = <<C::Group as Group>::Field>::random(&mut rng);
|
||||
let R_i = <C::Group>::generator() * k;
|
||||
let c_i =
|
||||
challenge::<C>(identifier, &commitment.first()?.0, &R_i).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 =
|
||||
compute_proof_of_knowledge(identifier, &coefficients, &commitment, &mut rng)?;
|
||||
|
||||
let secret_package = round1::SecretPackage {
|
||||
identifier,
|
||||
|
@ -307,7 +304,7 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
let package = round1::Package {
|
||||
header: Header::default(),
|
||||
commitment,
|
||||
proof_of_knowledge: Signature { R: R_i, z: mu_i },
|
||||
proof_of_knowledge,
|
||||
};
|
||||
|
||||
Ok((secret_package, package))
|
||||
|
@ -316,7 +313,7 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
/// Generates the challenge for the proof of knowledge to a secret for the DKG.
|
||||
fn challenge<C>(
|
||||
identifier: Identifier<C>,
|
||||
verifying_key: &Element<C>,
|
||||
verifying_key: &VerifyingKey<C>,
|
||||
R: &Element<C>,
|
||||
) -> Option<Challenge<C>>
|
||||
where
|
||||
|
@ -325,12 +322,62 @@ where
|
|||
let mut preimage = vec![];
|
||||
|
||||
preimage.extend_from_slice(identifier.serialize().as_ref());
|
||||
preimage.extend_from_slice(<C::Group>::serialize(verifying_key).as_ref());
|
||||
preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.element).as_ref());
|
||||
preimage.extend_from_slice(<C::Group>::serialize(R).as_ref());
|
||||
|
||||
Some(Challenge(C::HDKG(&preimage[..])?))
|
||||
}
|
||||
|
||||
/// Compute 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 compute_proof_of_knowledge<C: Ciphersuite, R: RngCore + CryptoRng>(
|
||||
identifier: Identifier<C>,
|
||||
coefficients: &[Scalar<C>],
|
||||
commitment: &VerifiableSecretSharingCommitment<C>,
|
||||
mut rng: R,
|
||||
) -> Result<Signature<C>, Error<C>> {
|
||||
// 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 = <<C::Group as Group>::Field>::random(&mut rng);
|
||||
let R_i = <C::Group>::generator() * k;
|
||||
let c_i = challenge::<C>(identifier, &commitment.verifying_key()?, &R_i)
|
||||
.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))]
|
||||
pub(crate) fn verify_proof_of_knowledge<C: Ciphersuite>(
|
||||
identifier: Identifier<C>,
|
||||
commitment: &VerifiableSecretSharingCommitment<C>,
|
||||
proof_of_knowledge: Signature<C>,
|
||||
) -> Result<(), Error<C>> {
|
||||
// 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.verifying_key()?;
|
||||
let c_ell = challenge::<C>(ell, &phi_ell0, &R_ell).ok_or(Error::DKGNotSupported)?;
|
||||
if R_ell != <C::Group>::generator() * mu_ell - phi_ell0.element * 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.
|
||||
|
@ -363,18 +410,11 @@ pub fn part2<C: Ciphersuite>(
|
|||
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::<C>(ell, &phi_ell0, &R_ell).ok_or(Error::DKGNotSupported)?;
|
||||
|
||||
if R_ell != <C::Group>::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
|
||||
//
|
||||
|
@ -404,47 +444,6 @@ pub fn part2<C: Ciphersuite>(
|
|||
))
|
||||
}
|
||||
|
||||
/// Computes the verifying shares of the other participants for the third step
|
||||
/// of the DKG protocol.
|
||||
fn compute_verifying_shares<C: Ciphersuite>(
|
||||
round1_packages: &BTreeMap<Identifier<C>, round1::Package<C>>,
|
||||
round2_secret_package: &round2::SecretPackage<C>,
|
||||
) -> Result<BTreeMap<Identifier<C>, VerifyingShare<C>>, Error<C>> {
|
||||
// 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_shares = BTreeMap::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 = <C::Group>::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<C>, Error<C>>(
|
||||
&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_shares.insert(i, y_i);
|
||||
}
|
||||
Ok(others_verifying_shares)
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -481,7 +480,6 @@ pub fn part3<C: Ciphersuite>(
|
|||
}
|
||||
|
||||
let mut signing_share = <<C::Group as Group>::Field>::zero();
|
||||
let mut verifying_key = <C::Group>::identity();
|
||||
|
||||
for (sender_identifier, round2_package) in round2_packages {
|
||||
// Round 2, Step 2
|
||||
|
@ -515,48 +513,33 @@ pub fn part3<C: Ciphersuite>(
|
|||
// > 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}.
|
||||
verifying_key = verifying_key + commitment.first()?.0;
|
||||
}
|
||||
|
||||
signing_share = signing_share + round2_secret_package.secret_share;
|
||||
verifying_key = verifying_key + 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_share = signing_share.into();
|
||||
let verifying_key = VerifyingKey {
|
||||
element: verifying_key,
|
||||
};
|
||||
|
||||
// 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_shares =
|
||||
compute_verifying_shares(round1_packages, round2_secret_package)?;
|
||||
|
||||
// Add the participant's own public verification share for consistency
|
||||
all_verifying_shares.insert(round2_secret_package.identifier, verifying_share);
|
||||
let commitments: BTreeMap<_, _> = round1_packages
|
||||
.iter()
|
||||
.map(|(id, package)| (*id, &package.commitment))
|
||||
.chain(iter::once((
|
||||
round2_secret_package.identifier,
|
||||
&round2_secret_package.commitment,
|
||||
)))
|
||||
.collect();
|
||||
let public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
|
||||
|
||||
let key_package = KeyPackage {
|
||||
header: Header::default(),
|
||||
identifier: round2_secret_package.identifier,
|
||||
signing_share,
|
||||
verifying_share,
|
||||
verifying_key,
|
||||
verifying_key: public_key_package.verifying_key,
|
||||
min_signers: round2_secret_package.min_signers,
|
||||
};
|
||||
let public_key_package = PublicKeyPackage {
|
||||
header: Header::default(),
|
||||
verifying_shares: all_verifying_shares,
|
||||
verifying_key,
|
||||
};
|
||||
|
||||
Ok((key_package, public_key_package))
|
||||
}
|
||||
|
|
|
@ -217,7 +217,7 @@ pub fn sign<C: Ciphersuite>(
|
|||
// Compute the per-message challenge.
|
||||
let challenge = challenge::<C>(
|
||||
&group_commitment.0,
|
||||
&key_package.verifying_key.element,
|
||||
&key_package.verifying_key,
|
||||
signing_package.message.as_slice(),
|
||||
);
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
|
|||
signature: &Signature<Self>,
|
||||
public_key: &VerifyingKey<Self>,
|
||||
) -> Result<(), Error<Self>> {
|
||||
let c = crate::challenge::<Self>(&signature.R, &public_key.element, msg);
|
||||
let c = crate::challenge::<Self>(&signature.R, public_key, msg);
|
||||
|
||||
public_key.verify_prehashed(c, signature)
|
||||
}
|
||||
|
@ -405,14 +405,14 @@ where
|
|||
/// [RFC]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.2
|
||||
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||
fn challenge<C>(R: &Element<C>, verifying_key: &Element<C>, msg: &[u8]) -> Challenge<C>
|
||||
fn challenge<C>(R: &Element<C>, verifying_key: &VerifyingKey<C>, msg: &[u8]) -> Challenge<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
let mut preimage = vec![];
|
||||
|
||||
preimage.extend_from_slice(<C::Group>::serialize(R).as_ref());
|
||||
preimage.extend_from_slice(<C::Group>::serialize(verifying_key).as_ref());
|
||||
preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.element).as_ref());
|
||||
preimage.extend_from_slice(msg);
|
||||
|
||||
Challenge(C::H2(&preimage[..]))
|
||||
|
|
|
@ -50,7 +50,7 @@ where
|
|||
let R = <C::Group>::generator() * k;
|
||||
|
||||
// Generate Schnorr challenge
|
||||
let c = crate::challenge::<C>(&R, &VerifyingKey::<C>::from(*self).element, msg);
|
||||
let c = crate::challenge::<C>(&R, &VerifyingKey::<C>::from(*self), msg);
|
||||
|
||||
let z = k + (c.0 * self.scalar);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use debugless_unwrap::DebuglessUnwrap;
|
|||
use rand_core::{CryptoRng, RngCore};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::frost::keys::{generate_with_dealer, IdentifierList, PublicKeyPackage};
|
||||
use crate::Ciphersuite;
|
||||
|
||||
/// Test serialize VerifiableSecretSharingCommitment
|
||||
|
@ -107,3 +108,20 @@ pub fn check_deserialize_vss_commitment_error<C: Ciphersuite, R: RngCore + Crypt
|
|||
|
||||
assert!(vss_value.is_err());
|
||||
}
|
||||
|
||||
/// Test computing the public key package from a list of commitments.
|
||||
pub fn check_compute_public_key_package<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
|
||||
let max_signers = 3;
|
||||
let min_signers = 2;
|
||||
let (secret_shares, public_key_package) =
|
||||
generate_with_dealer::<C, _>(max_signers, min_signers, IdentifierList::Default, &mut rng)
|
||||
.unwrap();
|
||||
|
||||
let members = secret_shares.keys().copied().collect();
|
||||
let group_commitment = secret_shares.values().next().unwrap().commitment().clone();
|
||||
|
||||
assert_eq!(
|
||||
public_key_package,
|
||||
PublicKeyPackage::from_commitment(&members, &group_commitment).unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,8 +26,9 @@ where
|
|||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new VerifyingKey from the given element.
|
||||
#[cfg(feature = "internals")]
|
||||
pub fn new(element: <C::Group as Group>::Element) -> Self {
|
||||
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||
pub(crate) fn new(element: <C::Group as Group>::Element) -> Self {
|
||||
Self { element }
|
||||
}
|
||||
|
||||
|
@ -77,6 +78,20 @@ where
|
|||
pub fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<(), Error<C>> {
|
||||
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<C>,
|
||||
) -> Result<VerifyingKey<C>, Error<C>> {
|
||||
Ok(VerifyingKey {
|
||||
element: commitment
|
||||
.coefficients()
|
||||
.first()
|
||||
.ok_or(Error::IncorrectCommitment)?
|
||||
.value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Debug for VerifyingKey<C>
|
||||
|
|
|
@ -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::<Ed25519Sha512, _>(rng);
|
||||
}
|
||||
|
|
|
@ -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::<Ed448Shake256, _>(rng);
|
||||
}
|
||||
|
|
|
@ -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::<P256Sha256, _>(rng);
|
||||
}
|
||||
|
|
|
@ -33,3 +33,11 @@ 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::<Ristretto255Sha512, _>(
|
||||
rng,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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::<Secp256K1Sha256, _>(rng);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue