frost-rerandomized: add aggregate optimization (#214)

* frost-rerandomized: add aggregate optimization

* Update frost-rerandomized/src/lib.rs

Co-authored-by: Marek <mail@marek.onl>

* Updated docs around aggregate()

* rustfmt

---------

Co-authored-by: Marek <mail@marek.onl>
Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
Conrado Gouvea 2023-02-06 20:49:37 -03:00 committed by GitHub
parent 984f421d47
commit 09f3f36aab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 48 additions and 31 deletions

View File

@ -68,12 +68,16 @@ pub fn sign<C: Ciphersuite>(
Ok(signature_share)
}
/// Verifies each participant's signature share, and if all are valid,
/// aggregates the shares into a signature to publish.
/// Aggregates the shares into a verified signature to publish.
///
/// Resulting signature is compatible with verification of a plain SpendAuth
/// signature.
///
/// If the aggegated signature does not verify, each participant's signature share
/// is validated, to find the cheater(s). This approach is more efficient and secure
/// as we don't need to verify all shares if the aggregate signature is verifiable
/// under the public group key and message (which should be the common case).
///
/// This operation is performed by a coordinator that can communicate with all
/// the signing participants before publishing the final signature. The
/// coordinator can be one of the participants or a semi-trusted third party
@ -92,7 +96,7 @@ pub fn aggregate<C>(
where
C: Ciphersuite,
{
let public_key = pubkeys.group_public.to_element() + *randomized_params.randomizer_point();
let public_key = randomized_params.randomized_group_public_key();
// Encodes the signing commitment list produced in round one as part of generating [`Rho`], the
// binding factor.
@ -107,49 +111,62 @@ where
// Compute the per-message challenge.
let challenge = frost_core::challenge::<C>(
&group_commitment.clone().to_element(),
&public_key,
&public_key.to_element(),
signing_package.message().as_slice(),
);
// Verify the signature shares.
for signature_share in signature_shares {
// Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_,
// and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
let signer_pubkey = pubkeys
.signer_pubkeys
.get(&signature_share.identifier)
.unwrap();
// Compute Lagrange coefficient.
let lambda_i =
frost::derive_interpolating_value(&signature_share.identifier, signing_package)?;
let rho = binding_factor_list[signature_share.identifier].clone();
// Compute the commitment share.
let R_share = signing_package
.signing_commitment(&signature_share.identifier)
.to_group_commitment_share(&rho);
// Compute relation values to verify this signature share.
signature_share.verify(&R_share, signer_pubkey, lambda_i, &challenge)?;
}
// The aggregation of the signature shares by summing them up, resulting in
// a plain Schnorr signature.
//
// Implements [`aggregate`] from the spec.
//
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-5.3
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-12.html#section-5.3
let mut z = <<C::Group as Group>::Field as Field>::zero();
for signature_share in signature_shares {
z = z + signature_share.signature.z_share;
}
z = z + challenge.to_scalar() * randomized_params.randomizer;
z = z + challenge.clone().to_scalar() * randomized_params.randomizer;
Ok(frost_core::Signature::new(group_commitment.to_element(), z))
let signature = frost_core::Signature::new(group_commitment.to_element(), z);
// Verify the aggregate signature
let verification_result = public_key.verify(signing_package.message(), &signature);
// Only if the verification of the aggregate signature failed; verify each share to find the cheater.
// This approach is more efficient since we don't need to verify all shares
// if the aggregate signature is valid (which should be the common case).
if let Err(err) = verification_result {
// Verify the signature shares.
for signature_share in signature_shares {
// Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_,
// and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
let signer_pubkey = pubkeys
.signer_pubkeys
.get(&signature_share.identifier)
.unwrap();
// Compute Lagrange coefficient.
let lambda_i =
frost::derive_interpolating_value(&signature_share.identifier, signing_package)?;
let binding_factor = binding_factor_list[signature_share.identifier].clone();
// Compute the commitment share.
let R_share = signing_package
.signing_commitment(&signature_share.identifier)
.to_group_commitment_share(&binding_factor);
// Compute relation values to verify this signature share.
signature_share.verify(&R_share, signer_pubkey, lambda_i, &challenge)?;
}
// We should never reach here; but we return the verification error to be safe.
return Err(err);
}
Ok(signature)
}
/// Randomized params for a signing instance of randomized FROST.