Skip to main content

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