2022-05-16 13:53:17 -07:00
|
|
|
|
//! An implementation of FROST (Flexible Round-Optimized Schnorr Threshold)
|
|
|
|
|
//! signatures.
|
|
|
|
|
//!
|
|
|
|
|
//! If you are interested in deploying FROST, please do not hesitate to consult the FROST authors.
|
|
|
|
|
//!
|
|
|
|
|
//! This implementation currently only supports key generation using a central
|
|
|
|
|
//! dealer. In the future, we will add support for key generation via a DKG,
|
|
|
|
|
//! as specified in the FROST paper.
|
|
|
|
|
//!
|
2023-05-19 02:54:52 -07:00
|
|
|
|
//! Internally, generate_with_dealer generates keys using Verifiable Secret
|
2022-05-16 13:53:17 -07:00
|
|
|
|
//! Sharing, where shares are generated using Shamir Secret Sharing.
|
|
|
|
|
|
|
|
|
|
use std::{
|
2022-10-26 07:41:19 -07:00
|
|
|
|
collections::{BTreeMap, HashMap},
|
2022-05-16 13:53:17 -07:00
|
|
|
|
fmt::{self, Debug},
|
2022-10-03 01:13:25 -07:00
|
|
|
|
ops::Index,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
};
|
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
#[cfg(any(test, feature = "test-impl"))]
|
2022-05-16 13:53:17 -07:00
|
|
|
|
use hex::FromHex;
|
|
|
|
|
|
|
|
|
|
mod identifier;
|
|
|
|
|
pub mod keys;
|
|
|
|
|
pub mod round1;
|
|
|
|
|
pub mod round2;
|
|
|
|
|
|
2023-05-16 17:06:22 -07:00
|
|
|
|
use crate::{
|
|
|
|
|
scalar_mul::VartimeMultiscalarMul, Ciphersuite, Element, Error, Field, Group, Scalar, Signature,
|
|
|
|
|
};
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
pub use self::identifier::Identifier;
|
|
|
|
|
|
|
|
|
|
/// The binding factor, also known as _rho_ (ρ)
|
|
|
|
|
///
|
|
|
|
|
/// Ensures each signature share is strongly bound to a signing set, specific set
|
|
|
|
|
/// of commitments, and a specific message.
|
|
|
|
|
///
|
|
|
|
|
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
|
2022-09-02 19:26:41 -07:00
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
2022-10-25 22:08:06 -07:00
|
|
|
|
pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-25 21:21:04 -07:00
|
|
|
|
impl<C> BindingFactor<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-10-25 21:21:04 -07:00
|
|
|
|
/// Deserializes [`BindingFactor`] from bytes.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn from_bytes(
|
|
|
|
|
bytes: <<C::Group as Group>::Field as Field>::Serialization,
|
2022-12-12 14:04:10 -08:00
|
|
|
|
) -> Result<Self, Error<C>> {
|
|
|
|
|
<<C::Group as Group>::Field>::deserialize(&bytes)
|
|
|
|
|
.map(|scalar| Self(scalar))
|
|
|
|
|
.map_err(|e| e.into())
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 21:21:04 -07:00
|
|
|
|
/// Serializes [`BindingFactor`] to bytes.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn to_bytes(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
<<C::Group as Group>::Field>::serialize(&self.0)
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 21:21:04 -07:00
|
|
|
|
impl<C> Debug for BindingFactor<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2022-10-25 21:21:04 -07:00
|
|
|
|
f.debug_tuple("BindingFactor")
|
2022-05-16 13:53:17 -07:00
|
|
|
|
.field(&hex::encode(self.to_bytes()))
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 01:13:25 -07:00
|
|
|
|
/// A list of binding factors and their associated identifiers.
|
|
|
|
|
#[derive(Clone)]
|
2022-10-26 07:41:19 -07:00
|
|
|
|
pub struct BindingFactorList<C: Ciphersuite>(BTreeMap<Identifier<C>, BindingFactor<C>>);
|
2022-10-03 01:13:25 -07:00
|
|
|
|
|
|
|
|
|
impl<C> BindingFactorList<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2023-01-05 20:26:13 -08:00
|
|
|
|
/// Create a new [`BindingFactorList`] from a vector of binding factors.
|
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
|
pub fn new(binding_factors: BTreeMap<Identifier<C>, BindingFactor<C>>) -> Self {
|
|
|
|
|
Self(binding_factors)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 01:13:25 -07:00
|
|
|
|
/// Return iterator through all factors.
|
2022-10-26 07:41:19 -07:00
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (&Identifier<C>, &BindingFactor<C>)> {
|
2022-10-03 01:13:25 -07:00
|
|
|
|
self.0.iter()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C> Index<Identifier<C>> for BindingFactorList<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-10-25 21:21:04 -07:00
|
|
|
|
type Output = BindingFactor<C>;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-03 01:13:25 -07:00
|
|
|
|
// Get the binding factor of a participant in the list.
|
|
|
|
|
//
|
|
|
|
|
// [`binding_factor_for_participant`] in the spec
|
|
|
|
|
//
|
2022-10-24 16:25:40 -07:00
|
|
|
|
// [`binding_factor_for_participant`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-4.3
|
2022-10-03 01:13:25 -07:00
|
|
|
|
fn index(&self, identifier: Identifier<C>) -> &Self::Output {
|
2022-10-26 07:41:19 -07:00
|
|
|
|
&self.0[&identifier]
|
2022-10-03 01:13:25 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
/// [`compute_binding_factors`] in the spec
|
|
|
|
|
///
|
|
|
|
|
/// [`compute_binding_factors`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-4.4
|
|
|
|
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
|
|
|
|
pub(crate) fn compute_binding_factor_list<C>(
|
|
|
|
|
signing_package: &SigningPackage<C>,
|
|
|
|
|
additional_prefix: &[u8],
|
|
|
|
|
) -> BindingFactorList<C>
|
2022-10-03 01:13:25 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2023-01-05 20:26:13 -08:00
|
|
|
|
let preimages = signing_package.binding_factor_preimages(additional_prefix);
|
|
|
|
|
|
|
|
|
|
BindingFactorList(
|
|
|
|
|
preimages
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(identifier, preimage)| {
|
|
|
|
|
let binding_factor = C::H1(preimage);
|
|
|
|
|
(*identifier, BindingFactor(binding_factor))
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
)
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 13:01:03 -07:00
|
|
|
|
#[cfg(any(test, feature = "test-impl"))]
|
2022-10-25 21:21:04 -07:00
|
|
|
|
impl<C> FromHex for BindingFactor<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
2022-06-27 14:47:27 -07:00
|
|
|
|
let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
|
|
|
|
|
match v.try_into() {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
Ok(bytes) => Self::from_bytes(bytes).map_err(|_| "malformed scalar encoding"),
|
2022-06-27 14:47:27 -07:00
|
|
|
|
Err(_) => Err("malformed scalar encoding"),
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: pub struct Lagrange<C: Ciphersuite>(Scalar);
|
|
|
|
|
|
|
|
|
|
/// Generates the lagrange coefficient for the i'th participant.
|
2023-01-05 20:26:13 -08:00
|
|
|
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
2023-02-06 07:51:16 -08:00
|
|
|
|
fn derive_interpolating_value<C: Ciphersuite>(
|
2022-09-01 13:07:50 -07:00
|
|
|
|
signer_id: &Identifier<C>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
signing_package: &SigningPackage<C>,
|
2022-12-12 14:04:10 -08:00
|
|
|
|
) -> Result<Scalar<C>, Error<C>> {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let zero = <<C::Group as Group>::Field>::zero();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let mut num = <<C::Group as Group>::Field>::one();
|
|
|
|
|
let mut den = <<C::Group as Group>::Field>::one();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-09-01 13:07:50 -07:00
|
|
|
|
// Ala the sorting of B, just always sort by identifier in ascending order
|
2022-05-16 13:53:17 -07:00
|
|
|
|
//
|
|
|
|
|
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
|
|
|
|
|
for commitment in signing_package.signing_commitments() {
|
2022-09-01 13:07:50 -07:00
|
|
|
|
if commitment.identifier == *signer_id {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 20:50:25 -07:00
|
|
|
|
num *= commitment.identifier;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-25 20:50:25 -07:00
|
|
|
|
den *= commitment.identifier - *signer_id;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if den == zero {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
return Err(Error::DuplicatedShares);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(dconnolly): return this error if the inversion result == zero
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let lagrange_coeff = num * <<C::Group as Group>::Field>::invert(&den).unwrap();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
Ok(lagrange_coeff)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Generated by the coordinator of the signing operation and distributed to
|
|
|
|
|
/// each signing party
|
2023-01-30 10:48:13 -08:00
|
|
|
|
#[derive(Clone)]
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub struct SigningPackage<C: Ciphersuite> {
|
|
|
|
|
/// The set of commitments participants published in the first round of the
|
|
|
|
|
/// protocol.
|
2022-09-01 13:07:50 -07:00
|
|
|
|
signing_commitments: HashMap<Identifier<C>, round1::SigningCommitments<C>>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// Message which each participant will sign.
|
|
|
|
|
///
|
|
|
|
|
/// Each signer should perform protocol-specific verification on the
|
|
|
|
|
/// message.
|
|
|
|
|
message: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C> SigningPackage<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
/// Create a new `SigingPackage`
|
|
|
|
|
///
|
2022-09-01 13:07:50 -07:00
|
|
|
|
/// The `signing_commitments` are sorted by participant `identifier`.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn new(
|
2022-09-01 13:07:50 -07:00
|
|
|
|
signing_commitments: Vec<round1::SigningCommitments<C>>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
message: Vec<u8>,
|
|
|
|
|
) -> SigningPackage<C> {
|
|
|
|
|
SigningPackage {
|
|
|
|
|
signing_commitments: signing_commitments
|
|
|
|
|
.into_iter()
|
2022-09-01 13:07:50 -07:00
|
|
|
|
.map(|s| (s.identifier, s))
|
2022-05-16 13:53:17 -07:00
|
|
|
|
.collect(),
|
|
|
|
|
message,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-01 13:07:50 -07:00
|
|
|
|
/// Get a signing commitment by its participant identifier.
|
|
|
|
|
pub fn signing_commitment(&self, identifier: &Identifier<C>) -> round1::SigningCommitments<C> {
|
|
|
|
|
self.signing_commitments[identifier]
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the signing commitments, sorted by the participant indices
|
|
|
|
|
pub fn signing_commitments(&self) -> Vec<round1::SigningCommitments<C>> {
|
|
|
|
|
let mut signing_commitments: Vec<round1::SigningCommitments<C>> =
|
|
|
|
|
self.signing_commitments.values().cloned().collect();
|
2022-09-01 13:07:50 -07:00
|
|
|
|
signing_commitments.sort_by_key(|a| a.identifier);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
signing_commitments
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the message to be signed
|
|
|
|
|
pub fn message(&self) -> &Vec<u8> {
|
|
|
|
|
&self.message
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
/// Compute the preimages to H1 to compute the per-signer binding factors
|
2022-05-16 13:53:17 -07:00
|
|
|
|
// We separate this out into its own method so it can be tested
|
2023-01-05 20:26:13 -08:00
|
|
|
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
|
|
|
|
pub fn binding_factor_preimages(
|
|
|
|
|
&self,
|
|
|
|
|
additional_prefix: &[u8],
|
|
|
|
|
) -> Vec<(Identifier<C>, Vec<u8>)> {
|
2022-10-25 21:21:04 -07:00
|
|
|
|
let mut binding_factor_input_prefix = vec![];
|
2022-10-03 01:13:25 -07:00
|
|
|
|
|
2022-10-25 21:21:04 -07:00
|
|
|
|
binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref());
|
|
|
|
|
binding_factor_input_prefix.extend_from_slice(
|
2022-10-03 01:31:58 -07:00
|
|
|
|
C::H5(&round1::encode_group_commitments(self.signing_commitments())[..]).as_ref(),
|
2022-10-03 01:13:25 -07:00
|
|
|
|
);
|
2023-01-05 20:26:13 -08:00
|
|
|
|
binding_factor_input_prefix.extend_from_slice(additional_prefix);
|
2022-10-03 01:13:25 -07:00
|
|
|
|
|
|
|
|
|
self.signing_commitments()
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|c| {
|
2022-10-25 21:21:04 -07:00
|
|
|
|
let mut binding_factor_input = vec![];
|
2022-10-03 01:13:25 -07:00
|
|
|
|
|
2022-10-25 21:21:04 -07:00
|
|
|
|
binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
|
|
|
|
|
binding_factor_input.extend_from_slice(c.identifier.serialize().as_ref());
|
|
|
|
|
(c.identifier, binding_factor_input)
|
2022-10-03 01:13:25 -07:00
|
|
|
|
})
|
|
|
|
|
.collect()
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The product of all signers' individual commitments, published as part of the
|
|
|
|
|
/// final signature.
|
2023-01-05 20:26:13 -08:00
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
2022-10-25 22:08:06 -07:00
|
|
|
|
pub struct GroupCommitment<C: Ciphersuite>(pub(super) Element<C>);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
impl<C> GroupCommitment<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
/// Return the underlying element.
|
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
|
pub fn to_element(self) -> <C::Group as Group>::Element {
|
|
|
|
|
self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 13:53:17 -07:00
|
|
|
|
// impl<C> Debug for GroupCommitment<C> where C: Ciphersuite {
|
|
|
|
|
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
// f.debug_tuple("GroupCommitment")
|
|
|
|
|
// .field(&hex::encode(self.0.compress().to_bytes()))
|
|
|
|
|
// .finish()
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
/// Generates the group commitment which is published as part of the joint
|
|
|
|
|
/// Schnorr signature.
|
|
|
|
|
///
|
|
|
|
|
/// Implements [`compute_group_commitment`] from the spec.
|
|
|
|
|
///
|
|
|
|
|
/// [`compute_group_commitment`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-4.5
|
|
|
|
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
|
|
|
|
fn compute_group_commitment<C>(
|
|
|
|
|
signing_package: &SigningPackage<C>,
|
|
|
|
|
binding_factor_list: &BindingFactorList<C>,
|
|
|
|
|
) -> Result<GroupCommitment<C>, Error<C>>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2023-01-05 20:26:13 -08:00
|
|
|
|
let identity = <C::Group as Group>::identity();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
let mut group_commitment = <C::Group as Group>::identity();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2023-05-16 17:06:22 -07:00
|
|
|
|
// Number of signing participants we are iterating over.
|
|
|
|
|
let n = signing_package.signing_commitments().len();
|
|
|
|
|
|
|
|
|
|
let mut binding_scalars = Vec::with_capacity(n);
|
|
|
|
|
|
|
|
|
|
let mut binding_elements = Vec::with_capacity(n);
|
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
// Ala the sorting of B, just always sort by identifier in ascending order
|
|
|
|
|
//
|
|
|
|
|
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
|
|
|
|
|
for commitment in signing_package.signing_commitments() {
|
|
|
|
|
// The following check prevents a party from accidentally revealing their share.
|
|
|
|
|
// Note that the '&&' operator would be sufficient.
|
|
|
|
|
if identity == commitment.binding.0 || identity == commitment.hiding.0 {
|
|
|
|
|
return Err(Error::IdentityCommitment);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
let binding_factor = binding_factor_list[commitment.identifier].clone();
|
|
|
|
|
|
2023-05-16 17:06:22 -07:00
|
|
|
|
// Collect the binding commitments and their binding factors for one big
|
|
|
|
|
// multiscalar multiplication at the end.
|
|
|
|
|
binding_elements.push(commitment.binding.0);
|
|
|
|
|
binding_scalars.push(binding_factor.0);
|
|
|
|
|
|
|
|
|
|
group_commitment = group_commitment + commitment.hiding.0;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
2023-01-05 20:26:13 -08:00
|
|
|
|
|
2023-05-16 17:06:22 -07:00
|
|
|
|
let accumulated_binding_commitment: Element<C> =
|
|
|
|
|
VartimeMultiscalarMul::<C>::vartime_multiscalar_mul(binding_scalars, binding_elements);
|
|
|
|
|
|
|
|
|
|
group_commitment = group_commitment + accumulated_binding_commitment;
|
|
|
|
|
|
2023-01-05 20:26:13 -08:00
|
|
|
|
Ok(GroupCommitment(group_commitment))
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Aggregation
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-01-11 16:16:06 -08:00
|
|
|
|
/// Aggregates the signature shares to produce a final signature that
|
|
|
|
|
/// can be verified with the group public key.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
|
|
|
|
/// 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
|
|
|
|
|
/// (who is trusted to not perform denial of service attacks, but does not learn
|
|
|
|
|
/// any secret information). Note that because the coordinator is trusted to
|
|
|
|
|
/// report misbehaving parties in order to avoid publishing an invalid
|
|
|
|
|
/// signature, if the coordinator themselves is a signer and misbehaves, they
|
|
|
|
|
/// can avoid that step. However, at worst, this results in a denial of
|
|
|
|
|
/// service attack due to publishing an invalid signature.
|
|
|
|
|
pub fn aggregate<C>(
|
|
|
|
|
signing_package: &SigningPackage<C>,
|
|
|
|
|
signature_shares: &[round2::SignatureShare<C>],
|
|
|
|
|
pubkeys: &keys::PublicKeyPackage<C>,
|
2022-12-12 14:04:10 -08:00
|
|
|
|
) -> Result<Signature<C>, Error<C>>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-10-25 21:21:04 -07:00
|
|
|
|
// Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
|
2022-05-16 13:53:17 -07:00
|
|
|
|
// binding factor.
|
2023-01-05 20:26:13 -08:00
|
|
|
|
let binding_factor_list: BindingFactorList<C> =
|
|
|
|
|
compute_binding_factor_list(signing_package, &[]);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
// Compute the group commitment from signing commitments produced in round one.
|
2023-01-05 20:26:13 -08:00
|
|
|
|
let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
// The aggregation of the signature shares by summing them up, resulting in
|
|
|
|
|
// a plain Schnorr signature.
|
|
|
|
|
//
|
2022-10-03 11:41:02 -07:00
|
|
|
|
// Implements [`aggregate`] from the spec.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
//
|
2022-12-08 09:42:47 -08:00
|
|
|
|
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-5.3
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let mut z = <<C::Group as Group>::Field>::zero();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
for signature_share in signature_shares {
|
|
|
|
|
z = z + signature_share.signature.z_share;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-11 16:16:06 -08:00
|
|
|
|
let signature = Signature {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
R: group_commitment.0,
|
|
|
|
|
z,
|
2023-01-11 16:16:06 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Verify the aggregate signature
|
|
|
|
|
let verification_result = pubkeys
|
|
|
|
|
.group_public
|
|
|
|
|
.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 {
|
|
|
|
|
// Compute the per-message challenge.
|
|
|
|
|
let challenge = crate::challenge::<C>(
|
|
|
|
|
&group_commitment.0,
|
|
|
|
|
&pubkeys.group_public.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.
|
2023-02-06 07:51:16 -08:00
|
|
|
|
let lambda_i =
|
|
|
|
|
derive_interpolating_value(&signature_share.identifier, signing_package)?;
|
2023-01-11 16:16:06 -08:00
|
|
|
|
|
|
|
|
|
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)
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|