frost_core/
round2.rs

1//! FROST Round 2 functionality and types, for signature share generation
2
3use core::fmt::{self, Debug};
4
5use crate as frost;
6use crate::{
7    Challenge, Ciphersuite, Error, Field, Group, {round1, *},
8};
9
10/// A participant's signature share, which the coordinator will aggregate with all other signer's
11/// shares into the joint signature.
12#[derive(Clone, Copy, Eq, PartialEq, Getters)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
15#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
16pub struct SignatureShare<C: Ciphersuite> {
17    /// Serialization header
18    #[getter(skip)]
19    pub(crate) header: Header<C>,
20    /// This participant's signature over the message.
21    pub(crate) share: SerializableScalar<C>,
22}
23
24impl<C> SignatureShare<C>
25where
26    C: Ciphersuite,
27{
28    pub(crate) fn new(
29        scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
30    ) -> Self {
31        Self {
32            header: Header::default(),
33            share: SerializableScalar(scalar),
34        }
35    }
36
37    pub(crate) fn to_scalar(
38        self,
39    ) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
40        self.share.0
41    }
42
43    /// Deserialize [`SignatureShare`] from bytes
44    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
45        Ok(Self {
46            header: Header::default(),
47            share: SerializableScalar::deserialize(bytes)?,
48        })
49    }
50
51    /// Serialize [`SignatureShare`] to bytes
52    pub fn serialize(&self) -> Vec<u8> {
53        self.share.serialize()
54    }
55
56    /// Tests if a signature share issued by a participant is valid before
57    /// aggregating it into a final joint signature to publish.
58    ///
59    /// This is the final step of [`verify_signature_share`] from the spec.
60    ///
61    /// [`verify_signature_share`]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-share-aggregation
62    #[cfg_attr(feature = "internals", visibility::make(pub))]
63    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
64    pub(crate) fn verify(
65        &self,
66        identifier: Identifier<C>,
67        group_commitment_share: &round1::GroupCommitmentShare<C>,
68        verifying_share: &frost::keys::VerifyingShare<C>,
69        lambda_i: Scalar<C>,
70        challenge: &Challenge<C>,
71    ) -> Result<(), Error<C>> {
72        if (<C::Group>::generator() * self.to_scalar())
73            != (group_commitment_share.to_element()
74                + (verifying_share.to_element() * challenge.0 * lambda_i))
75        {
76            return Err(Error::InvalidSignatureShare {
77                culprit: identifier,
78            });
79        }
80
81        Ok(())
82    }
83}
84
85impl<C> Debug for SignatureShare<C>
86where
87    C: Ciphersuite,
88{
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        f.debug_struct("SignatureShare")
91            .field("share", &hex::encode(self.serialize()))
92            .finish()
93    }
94}
95
96/// Compute the signature share for a signing operation.
97#[cfg_attr(feature = "internals", visibility::make(pub))]
98#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
99pub(super) fn compute_signature_share<C: Ciphersuite>(
100    signer_nonces: &round1::SigningNonces<C>,
101    binding_factor: BindingFactor<C>,
102    lambda_i: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
103    key_package: &keys::KeyPackage<C>,
104    challenge: Challenge<C>,
105) -> SignatureShare<C> {
106    let z_share: <<C::Group as Group>::Field as Field>::Scalar = signer_nonces.hiding.to_scalar()
107        + (signer_nonces.binding.to_scalar() * binding_factor.0)
108        + (lambda_i * key_package.signing_share.to_scalar() * challenge.to_scalar());
109
110    SignatureShare::<C>::new(z_share)
111}
112
113/// Performed once by each participant selected for the signing operation.
114///
115/// Implements [`sign`] from the spec.
116///
117/// Receives the message to be signed and a set of signing commitments and a set
118/// of randomizing commitments to be used in that signing operation, including
119/// that for this participant.
120///
121/// Assumes the participant has already determined which nonce corresponds with
122/// the commitment that was assigned by the coordinator in the SigningPackage.
123///
124/// [`sign`]: https://datatracker.ietf.org/doc/html/rfc9591#name-round-two-signature-share-g
125pub fn sign<C: Ciphersuite>(
126    signing_package: &SigningPackage<C>,
127    signer_nonces: &round1::SigningNonces<C>,
128    key_package: &frost::keys::KeyPackage<C>,
129) -> Result<SignatureShare<C>, Error<C>> {
130    if signing_package.signing_commitments().len() < key_package.min_signers as usize {
131        return Err(Error::IncorrectNumberOfCommitments);
132    }
133
134    // Validate the signer's commitment is present in the signing package
135    let commitment = signing_package
136        .signing_commitments
137        .get(&key_package.identifier)
138        .ok_or(Error::MissingCommitment)?;
139
140    // Validate if the signer's commitment exists
141    if &signer_nonces.commitments != commitment {
142        return Err(Error::IncorrectCommitment);
143    }
144
145    let (signing_package, signer_nonces, key_package) =
146        <C>::pre_sign(signing_package, signer_nonces, key_package)?;
147
148    // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
149    // binding factor.
150    let binding_factor_list: BindingFactorList<C> =
151        compute_binding_factor_list(&signing_package, &key_package.verifying_key, &[])?;
152    let binding_factor: frost::BindingFactor<C> = binding_factor_list
153        .get(&key_package.identifier)
154        .ok_or(Error::UnknownIdentifier)?
155        .clone();
156
157    // Compute the group commitment from signing commitments produced in round one.
158    let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
159
160    // Compute Lagrange coefficient.
161    let lambda_i = frost::derive_interpolating_value(key_package.identifier(), &signing_package)?;
162
163    // Compute the per-message challenge.
164    let challenge = <C>::challenge(
165        &group_commitment.0,
166        &key_package.verifying_key,
167        signing_package.message(),
168    )?;
169
170    // Compute the Schnorr signature share.
171    let signature_share = <C>::compute_signature_share(
172        &group_commitment,
173        &signer_nonces,
174        binding_factor,
175        lambda_i,
176        &key_package,
177        challenge,
178    );
179
180    Ok(signature_share)
181}