frost_core/
lib.rs

1#![no_std]
2#![allow(non_snake_case)]
3// It's emitting false positives; see https://github.com/rust-lang/rust-clippy/issues/9413
4#![allow(clippy::derive_partial_eq_without_eq)]
5#![deny(missing_docs)]
6#![forbid(unsafe_code)]
7#![deny(clippy::indexing_slicing)]
8#![deny(clippy::unwrap_used)]
9#![cfg_attr(docsrs, feature(doc_auto_cfg))]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11#![doc = include_str!("../README.md")]
12#![doc = document_features::document_features!()]
13
14#[macro_use]
15extern crate alloc;
16
17use core::marker::PhantomData;
18
19use alloc::{
20    collections::{BTreeMap, BTreeSet},
21    fmt::{self, Debug},
22    vec::Vec,
23};
24
25use derive_getters::Getters;
26#[cfg(any(test, feature = "test-impl"))]
27use hex::FromHex;
28use keys::PublicKeyPackage;
29use rand_core::{CryptoRng, RngCore};
30use serialization::SerializableScalar;
31use zeroize::Zeroize;
32
33pub mod batch;
34#[cfg(any(test, feature = "test-impl"))]
35pub mod benches;
36mod error;
37mod identifier;
38pub mod keys;
39pub mod round1;
40pub mod round2;
41mod scalar_mul;
42// We'd like to make this conditionally pub but the attribute below does
43// not work yet (https://github.com/rust-lang/rust/issues/54727)
44// #[cfg_attr(feature = "internals", visibility::make(pub))]
45pub mod serialization;
46mod signature;
47mod signing_key;
48#[cfg(any(test, feature = "test-impl"))]
49pub mod tests;
50mod traits;
51mod verifying_key;
52
53pub use error::{Error, FieldError, GroupError};
54pub use identifier::Identifier;
55use scalar_mul::VartimeMultiscalarMul;
56// Re-export serde
57#[cfg(feature = "serde")]
58pub use serde;
59pub use signature::Signature;
60pub use signing_key::SigningKey;
61pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
62pub use verifying_key::VerifyingKey;
63
64/// A type refinement for the scalar field element representing the per-message _[challenge]_.
65///
66/// [challenge]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-challenge-computa
67#[derive(Copy, Clone)]
68pub struct Challenge<C: Ciphersuite>(pub(crate) <<C::Group as Group>::Field as Field>::Scalar);
69
70impl<C> Challenge<C>
71where
72    C: Ciphersuite,
73{
74    /// Creates a challenge from a scalar.
75    #[cfg_attr(feature = "internals", visibility::make(pub))]
76    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
77    #[allow(dead_code)]
78    pub(crate) fn from_scalar(
79        scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
80    ) -> Self {
81        Self(scalar)
82    }
83
84    /// Return the underlying scalar.
85    #[cfg_attr(feature = "internals", visibility::make(pub))]
86    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
87    pub(crate) fn to_scalar(
88        self,
89    ) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
90        self.0
91    }
92}
93
94impl<C> Debug for Challenge<C>
95where
96    C: Ciphersuite,
97{
98    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99        f.debug_tuple("Secret")
100            .field(&hex::encode(<<C::Group as Group>::Field>::serialize(
101                &self.0,
102            )))
103            .finish()
104    }
105}
106
107/// Generates the challenge as is required for Schnorr signatures.
108///
109/// Deals in bytes, so that [FROST] and singleton signing and verification can use it with different
110/// types.
111///
112/// This is the only invocation of the H2 hash function from the [RFC].
113///
114/// [FROST]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-challenge-computa
115/// [RFC]: https://datatracker.ietf.org/doc/html/rfc9591#name-cryptographic-hash-function
116#[cfg_attr(feature = "internals", visibility::make(pub))]
117#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
118fn challenge<C>(
119    R: &Element<C>,
120    verifying_key: &VerifyingKey<C>,
121    msg: &[u8],
122) -> Result<Challenge<C>, Error<C>>
123where
124    C: Ciphersuite,
125{
126    let mut preimage = Vec::new();
127
128    preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
129    preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
130    preimage.extend_from_slice(msg);
131
132    Ok(Challenge(C::H2(&preimage[..])))
133}
134
135/// Generates a random nonzero scalar.
136///
137/// It assumes that the Scalar Eq/PartialEq implementation is constant-time.
138#[cfg_attr(feature = "internals", visibility::make(pub))]
139#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
140pub(crate) fn random_nonzero<C: Ciphersuite, R: RngCore + CryptoRng>(rng: &mut R) -> Scalar<C> {
141    loop {
142        let scalar = <<C::Group as Group>::Field>::random(rng);
143
144        if scalar != <<C::Group as Group>::Field>::zero() {
145            return scalar;
146        }
147    }
148}
149
150#[derive(Copy, Clone, Debug, PartialEq, Eq, Zeroize)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
153struct Header<C: Ciphersuite> {
154    /// Format version
155    #[cfg_attr(
156        feature = "serde",
157        serde(deserialize_with = "crate::serialization::version_deserialize::<_>")
158    )]
159    version: u8,
160    /// Ciphersuite ID
161    #[cfg_attr(
162        feature = "serde",
163        serde(serialize_with = "crate::serialization::ciphersuite_serialize::<_, C>")
164    )]
165    #[cfg_attr(
166        feature = "serde",
167        serde(deserialize_with = "crate::serialization::ciphersuite_deserialize::<_, C>")
168    )]
169    ciphersuite: (),
170    #[cfg_attr(feature = "serde", serde(skip))]
171    phantom: PhantomData<C>,
172}
173
174impl<C> Default for Header<C>
175where
176    C: Ciphersuite,
177{
178    fn default() -> Self {
179        Self {
180            version: Default::default(),
181            ciphersuite: Default::default(),
182            phantom: Default::default(),
183        }
184    }
185}
186
187/// The binding factor, also known as _rho_ (ρ)
188///
189/// Ensures each signature share is strongly bound to a signing set, specific set
190/// of commitments, and a specific message.
191///
192/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
193#[derive(Clone, PartialEq, Eq)]
194pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);
195
196impl<C> BindingFactor<C>
197where
198    C: Ciphersuite,
199{
200    /// Serializes [`BindingFactor`] to bytes.
201    pub fn serialize(&self) -> Vec<u8> {
202        SerializableScalar::<C>(self.0).serialize()
203    }
204}
205
206impl<C> Debug for BindingFactor<C>
207where
208    C: Ciphersuite,
209{
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        f.debug_tuple("BindingFactor")
212            .field(&hex::encode(self.serialize()))
213            .finish()
214    }
215}
216
217/// A list of binding factors and their associated identifiers.
218#[derive(Clone)]
219#[cfg_attr(feature = "internals", visibility::make(pub))]
220#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
221pub(crate) struct BindingFactorList<C: Ciphersuite>(BTreeMap<Identifier<C>, BindingFactor<C>>);
222
223impl<C> BindingFactorList<C>
224where
225    C: Ciphersuite,
226{
227    /// Create a new [`BindingFactorList`] from a map of identifiers to binding factors.
228    #[cfg(feature = "internals")]
229    pub fn new(binding_factors: BTreeMap<Identifier<C>, BindingFactor<C>>) -> Self {
230        Self(binding_factors)
231    }
232
233    /// Get the [`BindingFactor`] for the given identifier, or None if not found.
234    pub fn get(&self, key: &Identifier<C>) -> Option<&BindingFactor<C>> {
235        self.0.get(key)
236    }
237}
238
239/// [`compute_binding_factors`] in the spec
240///
241/// [`compute_binding_factors`]: https://datatracker.ietf.org/doc/html/rfc9591#name-binding-factors-computation
242#[cfg_attr(feature = "internals", visibility::make(pub))]
243#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
244pub(crate) fn compute_binding_factor_list<C>(
245    signing_package: &SigningPackage<C>,
246    verifying_key: &VerifyingKey<C>,
247    additional_prefix: &[u8],
248) -> Result<BindingFactorList<C>, Error<C>>
249where
250    C: Ciphersuite,
251{
252    let preimages = signing_package.binding_factor_preimages(verifying_key, additional_prefix)?;
253
254    Ok(BindingFactorList(
255        preimages
256            .iter()
257            .map(|(identifier, preimage)| {
258                let binding_factor = C::H1(preimage);
259                (*identifier, BindingFactor(binding_factor))
260            })
261            .collect(),
262    ))
263}
264
265#[cfg(any(test, feature = "test-impl"))]
266impl<C> FromHex for BindingFactor<C>
267where
268    C: Ciphersuite,
269{
270    type Error = &'static str;
271
272    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
273        let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
274
275        match v.try_into() {
276            Ok(bytes) => <<C::Group as Group>::Field>::deserialize(&bytes)
277                .map(|scalar| Self(scalar))
278                .map_err(|_| "malformed scalar encoding"),
279            Err(_) => Err("malformed scalar encoding"),
280        }
281    }
282}
283
284/// Generates a lagrange coefficient.
285///
286/// The Lagrange polynomial for a set of points (x_j, y_j) for 0 <= j <= k
287/// is ∑_{i=0}^k y_i.ℓ_i(x), where ℓ_i(x) is the Lagrange basis polynomial:
288///
289/// ℓ_i(x) = ∏_{0≤j≤k; j≠i} (x - x_j) / (x_i - x_j).
290///
291/// This computes ℓ_j(x) for the set of points `xs` and for the j corresponding
292/// to the given xj.
293///
294/// If `x` is None, it uses 0 for it (since Identifiers can't be 0)
295#[cfg_attr(feature = "internals", visibility::make(pub))]
296#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
297fn compute_lagrange_coefficient<C: Ciphersuite>(
298    x_set: &BTreeSet<Identifier<C>>,
299    x: Option<Identifier<C>>,
300    x_i: Identifier<C>,
301) -> Result<Scalar<C>, Error<C>> {
302    if x_set.is_empty() {
303        return Err(Error::IncorrectNumberOfIdentifiers);
304    }
305    let mut num = <<C::Group as Group>::Field>::one();
306    let mut den = <<C::Group as Group>::Field>::one();
307
308    let mut x_i_found = false;
309
310    for x_j in x_set.iter() {
311        if x_i == *x_j {
312            x_i_found = true;
313            continue;
314        }
315
316        if let Some(x) = x {
317            num = num * (x.to_scalar() - x_j.to_scalar());
318            den = den * (x_i.to_scalar() - x_j.to_scalar());
319        } else {
320            // Both signs inverted just to avoid requiring Neg (-*xj)
321            num = num * x_j.to_scalar();
322            den = den * (x_j.to_scalar() - x_i.to_scalar());
323        }
324    }
325    if !x_i_found {
326        return Err(Error::UnknownIdentifier);
327    }
328
329    Ok(
330        num * <<C::Group as Group>::Field>::invert(&den)
331            .map_err(|_| Error::DuplicatedIdentifier)?,
332    )
333}
334
335/// Generates the lagrange coefficient for the i'th participant (for `signer_id`).
336///
337/// Implements [`derive_interpolating_value()`] from the spec.
338///
339/// [`derive_interpolating_value()`]: https://datatracker.ietf.org/doc/html/rfc9591#name-polynomials
340#[cfg_attr(feature = "internals", visibility::make(pub))]
341#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
342fn derive_interpolating_value<C: Ciphersuite>(
343    signer_id: &Identifier<C>,
344    signing_package: &SigningPackage<C>,
345) -> Result<Scalar<C>, Error<C>> {
346    compute_lagrange_coefficient(
347        &signing_package
348            .signing_commitments()
349            .keys()
350            .cloned()
351            .collect(),
352        None,
353        *signer_id,
354    )
355}
356
357/// Generated by the coordinator of the signing operation and distributed to
358/// each signing party
359#[derive(Clone, Debug, PartialEq, Eq, Getters)]
360#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
361#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
362#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
363pub struct SigningPackage<C: Ciphersuite> {
364    /// Serialization header
365    #[getter(skip)]
366    pub(crate) header: Header<C>,
367    /// The set of commitments participants published in the first round of the
368    /// protocol.
369    signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
370    /// Message which each participant will sign.
371    ///
372    /// Each signer should perform protocol-specific verification on the
373    /// message.
374    #[cfg_attr(
375        feature = "serde",
376        serde(
377            serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
378            deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
379        )
380    )]
381    message: Vec<u8>,
382}
383
384impl<C> SigningPackage<C>
385where
386    C: Ciphersuite,
387{
388    /// Create a new `SigningPackage`
389    ///
390    /// The `signing_commitments` are sorted by participant `identifier`.
391    pub fn new(
392        signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
393        message: &[u8],
394    ) -> SigningPackage<C> {
395        SigningPackage {
396            header: Header::default(),
397            signing_commitments,
398            message: message.to_vec(),
399        }
400    }
401
402    /// Get a signing commitment by its participant identifier, or None if not found.
403    pub fn signing_commitment(
404        &self,
405        identifier: &Identifier<C>,
406    ) -> Option<round1::SigningCommitments<C>> {
407        self.signing_commitments.get(identifier).copied()
408    }
409
410    /// Compute the preimages to H1 to compute the per-signer binding factors
411    // We separate this out into its own method so it can be tested
412    #[cfg_attr(feature = "internals", visibility::make(pub))]
413    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
414    #[allow(clippy::type_complexity)]
415    pub fn binding_factor_preimages(
416        &self,
417        verifying_key: &VerifyingKey<C>,
418        additional_prefix: &[u8],
419    ) -> Result<Vec<(Identifier<C>, Vec<u8>)>, Error<C>> {
420        let mut binding_factor_input_prefix = Vec::new();
421
422        // The length of a serialized verifying key of the same ciphersuite does
423        // not change between runs of the protocol, so we don't need to hash to
424        // get a fixed length.
425        binding_factor_input_prefix.extend_from_slice(verifying_key.serialize()?.as_ref());
426
427        // The message is hashed with H4 to force the variable-length message
428        // into a fixed-length byte string, same for hashing the variable-sized
429        // (between runs of the protocol) set of group commitments, but with H5.
430        binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref());
431        binding_factor_input_prefix.extend_from_slice(
432            C::H5(&round1::encode_group_commitments(self.signing_commitments())?[..]).as_ref(),
433        );
434        binding_factor_input_prefix.extend_from_slice(additional_prefix);
435
436        Ok(self
437            .signing_commitments()
438            .keys()
439            .map(|identifier| {
440                let mut binding_factor_input = Vec::new();
441
442                binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
443                binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
444                (*identifier, binding_factor_input)
445            })
446            .collect())
447    }
448}
449
450#[cfg(feature = "serialization")]
451impl<C> SigningPackage<C>
452where
453    C: Ciphersuite,
454{
455    /// Serialize the struct into a Vec.
456    pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
457        serialization::Serialize::serialize(&self)
458    }
459
460    /// Deserialize the struct from a slice of bytes.
461    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
462        serialization::Deserialize::deserialize(bytes)
463    }
464}
465
466/// The product of all signers' individual commitments, published as part of the
467/// final signature.
468#[derive(Clone, PartialEq, Eq)]
469pub struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
470
471impl<C> GroupCommitment<C>
472where
473    C: Ciphersuite,
474{
475    /// Return the underlying element.
476    #[cfg_attr(feature = "internals", visibility::make(pub))]
477    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
478    pub(crate) fn to_element(self) -> <C::Group as Group>::Element {
479        self.0
480    }
481
482    /// Return the underlying element.
483    #[cfg(feature = "internals")]
484    pub fn from_element(element: Element<C>) -> Self {
485        Self(element)
486    }
487}
488
489/// Generates the group commitment which is published as part of the joint
490/// Schnorr signature.
491///
492/// Implements [`compute_group_commitment`] from the spec.
493///
494/// [`compute_group_commitment`]: https://datatracker.ietf.org/doc/html/rfc9591#name-group-commitment-computatio
495#[cfg_attr(feature = "internals", visibility::make(pub))]
496#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
497fn compute_group_commitment<C>(
498    signing_package: &SigningPackage<C>,
499    binding_factor_list: &BindingFactorList<C>,
500) -> Result<GroupCommitment<C>, Error<C>>
501where
502    C: Ciphersuite,
503{
504    let identity = <C::Group as Group>::identity();
505
506    let mut group_commitment = <C::Group as Group>::identity();
507
508    // Number of signing participants we are iterating over.
509    let n = signing_package.signing_commitments().len();
510
511    let mut binding_scalars = Vec::with_capacity(n);
512
513    let mut binding_elements = Vec::with_capacity(n);
514
515    for (commitment_identifier, commitment) in signing_package.signing_commitments() {
516        // The following check prevents a party from accidentally revealing their share.
517        // Note that the '&&' operator would be sufficient.
518        if identity == commitment.binding.value() || identity == commitment.hiding.value() {
519            return Err(Error::IdentityCommitment);
520        }
521
522        let binding_factor = binding_factor_list
523            .get(commitment_identifier)
524            .ok_or(Error::UnknownIdentifier)?;
525
526        // Collect the binding commitments and their binding factors for one big
527        // multiscalar multiplication at the end.
528        binding_elements.push(commitment.binding.value());
529        binding_scalars.push(binding_factor.0);
530
531        group_commitment = group_commitment + commitment.hiding.value();
532    }
533
534    let accumulated_binding_commitment: Element<C> =
535        VartimeMultiscalarMul::<C>::vartime_multiscalar_mul(binding_scalars, binding_elements);
536
537    group_commitment = group_commitment + accumulated_binding_commitment;
538
539    Ok(GroupCommitment(group_commitment))
540}
541
542////////////////////////////////////////////////////////////////////////////////
543// Aggregation
544////////////////////////////////////////////////////////////////////////////////
545
546/// Aggregates the signature shares to produce a final signature that
547/// can be verified with the group public key.
548///
549/// `signature_shares` maps the identifier of each participant to the
550/// [`round2::SignatureShare`] they sent. These identifiers must come from whatever mapping
551/// the coordinator has between communication channels and participants, i.e.
552/// they must have assurance that the [`round2::SignatureShare`] came from
553/// the participant with that identifier.
554///
555/// This operation is performed by a coordinator that can communicate with all
556/// the signing participants before publishing the final signature. The
557/// coordinator can be one of the participants or a semi-trusted third party
558/// (who is trusted to not perform denial of service attacks, but does not learn
559/// any secret information). Note that because the coordinator is trusted to
560/// report misbehaving parties in order to avoid publishing an invalid
561/// signature, if the coordinator themselves is a signer and misbehaves, they
562/// can avoid that step. However, at worst, this results in a denial of
563/// service attack due to publishing an invalid signature.
564pub fn aggregate<C>(
565    signing_package: &SigningPackage<C>,
566    signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
567    pubkeys: &keys::PublicKeyPackage<C>,
568) -> Result<Signature<C>, Error<C>>
569where
570    C: Ciphersuite,
571{
572    // Check if signing_package.signing_commitments and signature_shares have
573    // the same set of identifiers, and if they are all in pubkeys.verifying_shares.
574    if signing_package.signing_commitments().len() != signature_shares.len() {
575        return Err(Error::UnknownIdentifier);
576    }
577
578    if !signing_package.signing_commitments().keys().all(|id| {
579        #[cfg(feature = "cheater-detection")]
580        return signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id);
581        #[cfg(not(feature = "cheater-detection"))]
582        return signature_shares.contains_key(id);
583    }) {
584        return Err(Error::UnknownIdentifier);
585    }
586
587    let (signing_package, signature_shares, pubkeys) =
588        <C>::pre_aggregate(signing_package, signature_shares, pubkeys)?;
589
590    // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
591    // binding factor.
592    let binding_factor_list: BindingFactorList<C> =
593        compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?;
594    // Compute the group commitment from signing commitments produced in round one.
595    let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
596
597    // The aggregation of the signature shares by summing them up, resulting in
598    // a plain Schnorr signature.
599    //
600    // Implements [`aggregate`] from the spec.
601    //
602    // [`aggregate`]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-share-aggregation
603    let mut z = <<C::Group as Group>::Field>::zero();
604
605    for signature_share in signature_shares.values() {
606        z = z + signature_share.to_scalar();
607    }
608
609    let signature = Signature {
610        R: group_commitment.0,
611        z,
612    };
613
614    // Verify the aggregate signature
615    let verification_result = pubkeys
616        .verifying_key
617        .verify(signing_package.message(), &signature);
618
619    // Only if the verification of the aggregate signature failed; verify each share to find the cheater.
620    // This approach is more efficient since we don't need to verify all shares
621    // if the aggregate signature is valid (which should be the common case).
622    #[cfg(feature = "cheater-detection")]
623    if verification_result.is_err() {
624        detect_cheater(
625            &group_commitment,
626            &pubkeys,
627            &signing_package,
628            &signature_shares,
629            &binding_factor_list,
630        )?;
631    }
632
633    #[cfg(not(feature = "cheater-detection"))]
634    verification_result?;
635
636    Ok(signature)
637}
638
639/// Optional cheater detection feature
640/// Each share is verified to find the cheater
641#[cfg(feature = "cheater-detection")]
642fn detect_cheater<C: Ciphersuite>(
643    group_commitment: &GroupCommitment<C>,
644    pubkeys: &keys::PublicKeyPackage<C>,
645    signing_package: &SigningPackage<C>,
646    signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
647    binding_factor_list: &BindingFactorList<C>,
648) -> Result<(), Error<C>> {
649    // Compute the per-message challenge.
650    let challenge = <C>::challenge(
651        &group_commitment.0,
652        &pubkeys.verifying_key,
653        signing_package.message(),
654    )?;
655
656    // Verify the signature shares.
657    for (identifier, signature_share) in signature_shares {
658        // Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_,
659        // and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
660        let verifying_share = pubkeys
661            .verifying_shares
662            .get(identifier)
663            .ok_or(Error::UnknownIdentifier)?;
664
665        verify_signature_share_precomputed(
666            *identifier,
667            signing_package,
668            binding_factor_list,
669            group_commitment,
670            signature_share,
671            verifying_share,
672            challenge,
673        )?;
674    }
675
676    // We should never reach here; but we return an error to be safe.
677    Err(Error::InvalidSignature)
678}
679
680/// Verify a signature share for the given participant `identifier`,
681/// `verifying_share` and `signature_share`; with the `signing_package`
682/// for which the signature share was produced and with the group's
683/// `verifying_key`.
684///
685/// This is not required for regular FROST usage but might be useful in certain
686/// situations where it is desired to verify each individual signature share
687/// before aggregating the signature.
688pub fn verify_signature_share<C: Ciphersuite>(
689    identifier: Identifier<C>,
690    verifying_share: &keys::VerifyingShare<C>,
691    signature_share: &round2::SignatureShare<C>,
692    signing_package: &SigningPackage<C>,
693    verifying_key: &VerifyingKey<C>,
694) -> Result<(), Error<C>> {
695    // In order to reuse `pre_aggregate()`, we need to create some "dummy" containers
696    let signature_shares = BTreeMap::from([(identifier, *signature_share)]);
697    let verifying_shares = BTreeMap::from([(identifier, *verifying_share)]);
698    let public_key_package = PublicKeyPackage::new(verifying_shares, *verifying_key);
699
700    let (signing_package, signature_shares, pubkeys) =
701        <C>::pre_aggregate(signing_package, &signature_shares, &public_key_package)?;
702
703    // Extract the processed values back from the "dummy" containers
704    let verifying_share = pubkeys
705        .verifying_shares()
706        .get(&identifier)
707        .expect("pre_aggregate() must keep the identifiers");
708    let verifying_key = pubkeys.verifying_key();
709    let signature_share = signature_shares
710        .get(&identifier)
711        .expect("pre_aggregate() must keep the identifiers");
712
713    // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
714    // binding factor.
715    let binding_factor_list: BindingFactorList<C> =
716        compute_binding_factor_list(&signing_package, verifying_key, &[])?;
717
718    // Compute the group commitment from signing commitments produced in round one.
719    let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
720
721    // Compute the per-message challenge.
722    let challenge = <C>::challenge(
723        &group_commitment.clone().to_element(),
724        verifying_key,
725        signing_package.message().as_slice(),
726    )?;
727
728    verify_signature_share_precomputed(
729        identifier,
730        &signing_package,
731        &binding_factor_list,
732        &group_commitment,
733        signature_share,
734        verifying_share,
735        challenge,
736    )
737}
738
739/// Similar to [`verify_signature_share()`] but using a precomputed
740/// `binding_factor_list` and `challenge`.
741#[cfg_attr(feature = "internals", visibility::make(pub))]
742#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
743fn verify_signature_share_precomputed<C: Ciphersuite>(
744    signature_share_identifier: Identifier<C>,
745    signing_package: &SigningPackage<C>,
746    binding_factor_list: &BindingFactorList<C>,
747    group_commitment: &GroupCommitment<C>,
748    signature_share: &round2::SignatureShare<C>,
749    verifying_share: &keys::VerifyingShare<C>,
750    challenge: Challenge<C>,
751) -> Result<(), Error<C>> {
752    let lambda_i = derive_interpolating_value(&signature_share_identifier, signing_package)?;
753
754    let binding_factor = binding_factor_list
755        .get(&signature_share_identifier)
756        .ok_or(Error::UnknownIdentifier)?;
757
758    let R_share = signing_package
759        .signing_commitment(&signature_share_identifier)
760        .ok_or(Error::UnknownIdentifier)?
761        .to_group_commitment_share(binding_factor);
762
763    // Compute relation values to verify this signature share.
764    <C>::verify_share(
765        group_commitment,
766        signature_share,
767        signature_share_identifier,
768        &R_share,
769        verifying_share,
770        lambda_i,
771        &challenge,
772    )?;
773
774    Ok(())
775}