frost_core/
keys.rs

1//! FROST keys, keygen, key shares
2#![allow(clippy::type_complexity)]
3
4use core::iter;
5
6use alloc::{
7    collections::{BTreeMap, BTreeSet},
8    fmt::{self, Debug},
9    string::ToString,
10    vec::Vec,
11};
12
13use derive_getters::Getters;
14#[cfg(any(test, feature = "test-impl"))]
15use hex::FromHex;
16
17use rand_core::{CryptoRng, RngCore};
18use zeroize::{DefaultIsZeroes, Zeroize};
19
20use crate::{
21    serialization::{SerializableElement, SerializableScalar},
22    Ciphersuite, Element, Error, Field, Group, Header, Identifier, Scalar, SigningKey,
23    VerifyingKey,
24};
25
26#[cfg(feature = "serialization")]
27use crate::serialization::{Deserialize, Serialize};
28
29use super::compute_lagrange_coefficient;
30
31pub mod dkg;
32pub mod refresh;
33pub mod repairable;
34
35/// Sum the commitments from all participants in a distributed key generation
36/// run into a single group commitment.
37#[cfg_attr(feature = "internals", visibility::make(pub))]
38pub(crate) fn sum_commitments<C: Ciphersuite>(
39    commitments: &[&VerifiableSecretSharingCommitment<C>],
40) -> Result<VerifiableSecretSharingCommitment<C>, Error<C>> {
41    let mut group_commitment = vec![
42        CoefficientCommitment::new(<C::Group>::identity());
43        commitments
44            .first()
45            .ok_or(Error::IncorrectNumberOfCommitments)?
46            .0
47            .len()
48    ];
49    for commitment in commitments {
50        for (i, c) in group_commitment.iter_mut().enumerate() {
51            *c = CoefficientCommitment::new(
52                c.value()
53                    + commitment
54                        .0
55                        .get(i)
56                        .ok_or(Error::IncorrectNumberOfCommitments)?
57                        .value(),
58            );
59        }
60    }
61    Ok(VerifiableSecretSharingCommitment(group_commitment))
62}
63
64/// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s).
65pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
66    size: usize,
67    rng: &mut R,
68) -> Vec<Scalar<C>> {
69    iter::repeat_with(|| <<C::Group as Group>::Field>::random(rng))
70        .take(size)
71        .collect()
72}
73
74/// Return a list of default identifiers (1 to max_signers, inclusive).
75#[cfg_attr(feature = "internals", visibility::make(pub))]
76pub(crate) fn default_identifiers<C: Ciphersuite>(max_signers: u16) -> Vec<Identifier<C>> {
77    (1..=max_signers)
78        .map(|i| Identifier::<C>::try_from(i).expect("nonzero"))
79        .collect::<Vec<_>>()
80}
81
82/// A secret scalar value representing a signer's share of the group secret.
83#[derive(Clone, Copy, PartialEq, Eq)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
85#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
86#[cfg_attr(feature = "serde", serde(transparent))]
87pub struct SigningShare<C: Ciphersuite>(pub(crate) SerializableScalar<C>);
88
89impl<C> SigningShare<C>
90where
91    C: Ciphersuite,
92{
93    /// Create a new [`SigningShare`] from a scalar.
94    #[cfg_attr(feature = "internals", visibility::make(pub))]
95    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
96    pub(crate) fn new(scalar: Scalar<C>) -> Self {
97        Self(SerializableScalar(scalar))
98    }
99
100    /// Get the inner scalar.
101    #[cfg_attr(feature = "internals", visibility::make(pub))]
102    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
103    pub(crate) fn to_scalar(&self) -> Scalar<C> {
104        self.0 .0
105    }
106
107    /// Deserialize from bytes
108    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
109        Ok(Self(SerializableScalar::deserialize(bytes)?))
110    }
111
112    /// Serialize to bytes
113    pub fn serialize(&self) -> Vec<u8> {
114        self.0.serialize()
115    }
116
117    /// Computes the signing share from a list of coefficients.
118    #[cfg_attr(feature = "internals", visibility::make(pub))]
119    pub(crate) fn from_coefficients(coefficients: &[Scalar<C>], peer: Identifier<C>) -> Self {
120        Self::new(evaluate_polynomial(peer, coefficients))
121    }
122}
123
124impl<C> Debug for SigningShare<C>
125where
126    C: Ciphersuite,
127{
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
129        f.debug_tuple("SigningShare").field(&"<redacted>").finish()
130    }
131}
132
133impl<C> Default for SigningShare<C>
134where
135    C: Ciphersuite,
136{
137    fn default() -> Self {
138        Self::new(<<C::Group as Group>::Field>::zero())
139    }
140}
141
142// Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value
143impl<C> DefaultIsZeroes for SigningShare<C> where C: Ciphersuite {}
144
145#[cfg(any(test, feature = "test-impl"))]
146impl<C> FromHex for SigningShare<C>
147where
148    C: Ciphersuite,
149{
150    type Error = &'static str;
151
152    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
153        let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
154        Self::deserialize(&v).map_err(|_| "malformed scalar")
155    }
156}
157
158/// A public group element that represents a single signer's public verification share.
159#[derive(Copy, Clone, PartialEq, Eq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
162#[cfg_attr(feature = "serde", serde(transparent))]
163pub struct VerifyingShare<C>(pub(super) SerializableElement<C>)
164where
165    C: Ciphersuite;
166
167impl<C> VerifyingShare<C>
168where
169    C: Ciphersuite,
170{
171    /// Create a new [`VerifyingShare`] from a element.
172    #[cfg_attr(feature = "internals", visibility::make(pub))]
173    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
174    pub(crate) fn new(element: Element<C>) -> Self {
175        Self(SerializableElement(element))
176    }
177
178    /// Get the inner element.
179    #[cfg_attr(feature = "internals", visibility::make(pub))]
180    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
181    #[allow(dead_code)]
182    pub(crate) fn to_element(&self) -> Element<C> {
183        self.0 .0
184    }
185
186    /// Deserialize from bytes
187    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
188        Ok(Self(SerializableElement::deserialize(bytes)?))
189    }
190
191    /// Serialize to bytes
192    pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
193        self.0.serialize()
194    }
195
196    /// Computes a verifying share for a peer given the group commitment.
197    #[cfg_attr(feature = "internals", visibility::make(pub))]
198    pub(crate) fn from_commitment(
199        identifier: Identifier<C>,
200        commitment: &VerifiableSecretSharingCommitment<C>,
201    ) -> VerifyingShare<C> {
202        // DKG Round 2, Step 4
203        //
204        // > Any participant can compute the public verification share of any
205        // > other participant by calculating
206        // > Y_i = ∏_{j=1}^n ∏_{k=0}^{t−1} φ_{jk}^{i^k mod q}.
207        //
208        // Rewriting the equation by moving the product over j to further inside
209        // the equation:
210        // Y_i = ∏_{k=0}^{t−1} (∏_{j=1}^n φ_{jk})^{i^k mod q}
211        // i.e. we can operate on the sum of all φ_j commitments, which is
212        // what is passed to the functions.
213        VerifyingShare::new(evaluate_vss(identifier, commitment))
214    }
215}
216
217impl<C> Debug for VerifyingShare<C>
218where
219    C: Ciphersuite,
220{
221    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222        f.debug_tuple("VerifyingShare")
223            .field(
224                &self
225                    .serialize()
226                    .map(hex::encode)
227                    .unwrap_or("<invalid>".to_string()),
228            )
229            .finish()
230    }
231}
232
233impl<C> From<SigningShare<C>> for VerifyingShare<C>
234where
235    C: Ciphersuite,
236{
237    fn from(secret: SigningShare<C>) -> VerifyingShare<C> {
238        VerifyingShare::new(<C::Group>::generator() * secret.to_scalar())
239    }
240}
241
242/// A [`Group::Element`] newtype that is a commitment to one coefficient of our secret polynomial.
243///
244/// This is a (public) commitment to one coefficient of a secret polynomial used for performing
245/// verifiable secret sharing for a Shamir secret share.
246#[derive(Clone, Copy, PartialEq, Eq)]
247#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
248#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
249pub struct CoefficientCommitment<C: Ciphersuite>(pub(crate) SerializableElement<C>);
250
251impl<C> CoefficientCommitment<C>
252where
253    C: Ciphersuite,
254{
255    /// Create a new CoefficientCommitment.
256    #[cfg_attr(feature = "internals", visibility::make(pub))]
257    pub(crate) fn new(value: Element<C>) -> Self {
258        Self(SerializableElement(value))
259    }
260
261    /// Deserialize from bytes
262    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
263        Ok(Self(SerializableElement::deserialize(bytes)?))
264    }
265
266    /// Serialize to bytes
267    pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
268        self.0.serialize()
269    }
270
271    /// Returns inner element value
272    pub fn value(&self) -> Element<C> {
273        self.0 .0
274    }
275}
276
277impl<C> Debug for CoefficientCommitment<C>
278where
279    C: Ciphersuite,
280{
281    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
282        f.debug_tuple("CoefficientCommitment")
283            .field(
284                &self
285                    .serialize()
286                    .map(hex::encode)
287                    .unwrap_or("<invalid>".to_string()),
288            )
289            .finish()
290    }
291}
292
293/// Contains the commitments to the coefficients for our secret polynomial _f_,
294/// used to generate participants' key shares.
295///
296/// [`VerifiableSecretSharingCommitment`] contains a set of commitments to the coefficients (which
297/// themselves are scalars) for a secret polynomial f, where f is used to
298/// generate each ith participant's key share f(i). Participants use this set of
299/// commitments to perform verifiable secret sharing.
300///
301/// Note that participants MUST be assured that they have the *same*
302/// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using
303/// some agreed-upon public location for publication, where each participant can
304/// ensure that they received the correct (and same) value.
305#[derive(Clone, Debug, PartialEq, Eq)]
306#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
307#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
308pub struct VerifiableSecretSharingCommitment<C: Ciphersuite>(
309    pub(crate) Vec<CoefficientCommitment<C>>,
310);
311
312impl<C> VerifiableSecretSharingCommitment<C>
313where
314    C: Ciphersuite,
315{
316    /// Create a new VerifiableSecretSharingCommitment.
317    #[cfg_attr(feature = "internals", visibility::make(pub))]
318    pub(crate) fn new(coefficients: Vec<CoefficientCommitment<C>>) -> Self {
319        Self(coefficients)
320    }
321
322    /// Returns serialized coefficient commitments
323    pub fn serialize(&self) -> Result<Vec<Vec<u8>>, Error<C>> {
324        self.0
325            .iter()
326            .map(|cc| cc.serialize())
327            .collect::<Result<_, Error<C>>>()
328    }
329
330    /// Serialize the whole commitment vector as a single byte vector.
331    pub fn serialize_whole(&self) -> Result<Vec<u8>, Error<C>> {
332        self.serialize().map(|v| v.concat())
333    }
334
335    /// Returns VerifiableSecretSharingCommitment from an iterator of serialized
336    /// CoefficientCommitments (e.g. a [`Vec<Vec<u8>>`]).
337    pub fn deserialize<I, V>(serialized_coefficient_commitments: I) -> Result<Self, Error<C>>
338    where
339        I: IntoIterator<Item = V>,
340        V: AsRef<[u8]>,
341    {
342        let mut coefficient_commitments = Vec::new();
343        for cc in serialized_coefficient_commitments.into_iter() {
344            coefficient_commitments.push(CoefficientCommitment::<C>::deserialize(cc.as_ref())?);
345        }
346
347        Ok(Self::new(coefficient_commitments))
348    }
349
350    /// Deserialize a whole commitment vector from a single byte vector as returned by
351    /// [`VerifiableSecretSharingCommitment::serialize_whole()`].
352    pub fn deserialize_whole(bytes: &[u8]) -> Result<Self, Error<C>> {
353        // Get size from the size of the generator
354        let generator = <C::Group>::generator();
355        let len = <C::Group>::serialize(&generator)
356            .expect("serializing the generator always works")
357            .as_ref()
358            .len();
359
360        let serialized_coefficient_commitments = bytes.chunks_exact(len);
361
362        if !serialized_coefficient_commitments.remainder().is_empty() {
363            return Err(Error::InvalidCoefficient);
364        }
365
366        Self::deserialize(serialized_coefficient_commitments)
367    }
368
369    /// Get the VerifyingKey matching this commitment vector (which is the first
370    /// element in the vector), or an error if the vector is empty.
371    pub(crate) fn verifying_key(&self) -> Result<VerifyingKey<C>, Error<C>> {
372        Ok(VerifyingKey::new(
373            self.0.first().ok_or(Error::MissingCommitment)?.0 .0,
374        ))
375    }
376
377    /// Returns the coefficient commitments.
378    #[cfg_attr(feature = "internals", visibility::make(pub))]
379    pub(crate) fn coefficients(&self) -> &[CoefficientCommitment<C>] {
380        &self.0
381    }
382}
383
384/// A secret share generated by performing a (t-out-of-n) secret sharing scheme,
385/// generated by a dealer performing [`generate_with_dealer`].
386///
387/// `n` is the total number of shares and `t` is the threshold required to reconstruct the secret;
388/// in this case we use Shamir's secret sharing.
389///
390/// As a solution to the secret polynomial _f_ (a 'point'), the `identifier` is the x-coordinate, and the
391/// `value` is the y-coordinate.
392///
393/// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call
394/// .into(), which under the hood also performs validation.
395#[derive(Clone, Debug, Zeroize, PartialEq, Eq, Getters)]
396#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
397#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
398#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
399pub struct SecretShare<C: Ciphersuite> {
400    /// Serialization header
401    #[getter(skip)]
402    pub(crate) header: Header<C>,
403    /// The participant identifier of this [`SecretShare`].
404    #[zeroize(skip)]
405    pub(crate) identifier: Identifier<C>,
406    /// Secret Key.
407    pub(crate) signing_share: SigningShare<C>,
408    #[zeroize(skip)]
409    /// The commitments to be distributed among signers.
410    pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
411}
412
413impl<C> SecretShare<C>
414where
415    C: Ciphersuite,
416{
417    /// Create a new [`SecretShare`] instance.
418    pub fn new(
419        identifier: Identifier<C>,
420        signing_share: SigningShare<C>,
421        commitment: VerifiableSecretSharingCommitment<C>,
422    ) -> Self {
423        SecretShare {
424            header: Header::default(),
425            identifier,
426            signing_share,
427            commitment,
428        }
429    }
430
431    /// Verifies that a secret share is consistent with a verifiable secret sharing commitment,
432    /// and returns the derived group info for the participant (their public verification share,
433    /// and the group public key) if successful.
434    ///
435    /// This ensures that this participant's share has been generated using the same
436    /// mechanism as all other signing participants. Note that participants *MUST*
437    /// ensure that they have the same view as all other participants of the
438    /// commitment!
439    ///
440    /// An implementation of `vss_verify()` from the [spec].
441    /// This also implements `derive_group_info()` from the [spec] (which is very similar),
442    /// but only for this participant.
443    ///
444    /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#appendix-C.2-3
445    pub fn verify(&self) -> Result<(VerifyingShare<C>, VerifyingKey<C>), Error<C>> {
446        let f_result = <C::Group>::generator() * self.signing_share.to_scalar();
447        let result = evaluate_vss(self.identifier, &self.commitment);
448
449        if !(f_result == result) {
450            // The culprit needs to be identified by the caller if needed,
451            // because this function is called in two different contexts:
452            // - after trusted dealer key generation, by the participant who
453            //   receives the SecretShare. In that case it does not make sense
454            //   to identify themselves as the culprit, since the issue was with
455            //   the Coordinator or in the communication.
456            // - during DKG, where a "fake" SecretShare is built just to reuse
457            //   the verification logic and it does make sense to identify the
458            //   culprit. Note that in this case, self.identifier is the caller's
459            //   identifier and not the culprit's, so we couldn't identify
460            //   the culprit inside this function anyway.
461            return Err(Error::InvalidSecretShare { culprit: None });
462        }
463
464        Ok((
465            VerifyingShare::new(result),
466            self.commitment.verifying_key()?,
467        ))
468    }
469}
470
471#[cfg(feature = "serialization")]
472impl<C> SecretShare<C>
473where
474    C: Ciphersuite,
475{
476    /// Serialize the struct into a Vec.
477    pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
478        Serialize::serialize(&self)
479    }
480
481    /// Deserialize the struct from a slice of bytes.
482    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
483        Deserialize::deserialize(bytes)
484    }
485}
486
487/// The identifier list to use when generating key shares.
488pub enum IdentifierList<'a, C: Ciphersuite> {
489    /// Use the default values (1 to max_signers, inclusive).
490    Default,
491    /// A user-provided list of identifiers.
492    Custom(&'a [Identifier<C>]),
493}
494
495/// Allows all participants' keys to be generated using a central, trusted
496/// dealer.
497///
498/// Under the hood, this performs verifiable secret sharing, which itself uses
499/// Shamir secret sharing, from which each share becomes a participant's secret
500/// key. The output from this function is a set of shares along with one single
501/// commitment that participants use to verify the integrity of the share.
502///
503/// Implements [`trusted_dealer_keygen`] from the spec.
504///
505/// [`trusted_dealer_keygen`]: https://datatracker.ietf.org/doc/html/rfc9591#appendix-C
506pub fn generate_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
507    max_signers: u16,
508    min_signers: u16,
509    identifiers: IdentifierList<C>,
510    rng: &mut R,
511) -> Result<(BTreeMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
512    let key = SigningKey::new(rng);
513    split(&key, max_signers, min_signers, identifiers, rng)
514}
515
516/// Splits an existing key into FROST shares.
517///
518/// This is identical to [`generate_with_dealer`] but receives an existing key
519/// instead of generating a fresh one. This is useful in scenarios where
520/// the key needs to be generated externally or must be derived from e.g. a
521/// seed phrase.
522pub fn split<C: Ciphersuite, R: RngCore + CryptoRng>(
523    key: &SigningKey<C>,
524    max_signers: u16,
525    min_signers: u16,
526    identifiers: IdentifierList<C>,
527    rng: &mut R,
528) -> Result<(BTreeMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
529    validate_num_of_signers(min_signers, max_signers)?;
530
531    if let IdentifierList::Custom(identifiers) = &identifiers {
532        if identifiers.len() != max_signers as usize {
533            return Err(Error::IncorrectNumberOfIdentifiers);
534        }
535    }
536
537    let verifying_key = VerifyingKey::from(key);
538
539    let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, rng);
540
541    let secret_shares = match identifiers {
542        IdentifierList::Default => {
543            let identifiers = default_identifiers(max_signers);
544            generate_secret_shares(key, max_signers, min_signers, coefficients, &identifiers)?
545        }
546        IdentifierList::Custom(identifiers) => {
547            generate_secret_shares(key, max_signers, min_signers, coefficients, identifiers)?
548        }
549    };
550    let mut verifying_shares: BTreeMap<Identifier<C>, VerifyingShare<C>> = BTreeMap::new();
551    let mut secret_shares_by_id: BTreeMap<Identifier<C>, SecretShare<C>> = BTreeMap::new();
552
553    for secret_share in secret_shares {
554        let signer_public = secret_share.signing_share.into();
555        verifying_shares.insert(secret_share.identifier, signer_public);
556
557        secret_shares_by_id.insert(secret_share.identifier, secret_share);
558    }
559
560    let public_key_package = PublicKeyPackage {
561        header: Header::default(),
562        verifying_shares,
563        verifying_key,
564    };
565
566    // Apply post-processing
567    let (processed_secret_shares, processed_public_key_package) =
568        C::post_generate(secret_shares_by_id, public_key_package)?;
569
570    Ok((processed_secret_shares, processed_public_key_package))
571}
572
573/// Evaluate the polynomial with the given coefficients (constant term first)
574/// at the point x=identifier using Horner's method.
575///
576/// Implements [`polynomial_evaluate`] from the spec.
577///
578/// [`polynomial_evaluate`]: https://datatracker.ietf.org/doc/html/rfc9591#name-additional-polynomial-opera
579fn evaluate_polynomial<C: Ciphersuite>(
580    identifier: Identifier<C>,
581    coefficients: &[Scalar<C>],
582) -> Scalar<C> {
583    let mut value = <<C::Group as Group>::Field>::zero();
584
585    let ell = identifier;
586    for coeff in coefficients.iter().skip(1).rev() {
587        value = value + *coeff;
588        value = value * ell.to_scalar();
589    }
590    value = value
591        + *coefficients
592            .first()
593            .expect("coefficients must have at least one element");
594    value
595}
596
597/// Evaluates the right-hand side of the VSS verification equation, namely
598/// ∏^{t−1}_{k=0} φ^{i^k mod q}_{ℓk} (multiplicative notation) using
599/// `identifier` as `i` and the `commitment` as the commitment vector φ_ℓ.
600///
601/// This is also used in Round 2, Step 4 of the DKG.
602fn evaluate_vss<C: Ciphersuite>(
603    identifier: Identifier<C>,
604    commitment: &VerifiableSecretSharingCommitment<C>,
605) -> Element<C> {
606    let i = identifier.to_scalar();
607
608    let (_, result) = commitment.0.iter().fold(
609        (<<C::Group as Group>::Field>::one(), <C::Group>::identity()),
610        |(i_to_the_k, sum_so_far), comm_k| {
611            (i * i_to_the_k, sum_so_far + comm_k.value() * i_to_the_k)
612        },
613    );
614    result
615}
616
617/// A FROST keypair, which can be generated either by a trusted dealer or using
618/// a DKG.
619///
620/// When using a central dealer, [`SecretShare`]s are distributed to
621/// participants, who then perform verification, before deriving
622/// [`KeyPackage`]s, which they store to later use during signing.
623#[derive(Clone, Debug, PartialEq, Eq, Getters, Zeroize)]
624#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
625#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
626#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
627pub struct KeyPackage<C: Ciphersuite> {
628    /// Serialization header
629    #[getter(skip)]
630    pub(crate) header: Header<C>,
631    /// Denotes the participant identifier each secret share key package is owned by.
632    #[zeroize(skip)]
633    pub(crate) identifier: Identifier<C>,
634    /// This participant's signing share. This is secret.
635    pub(crate) signing_share: SigningShare<C>,
636    /// This participant's public key.
637    #[zeroize(skip)]
638    pub(crate) verifying_share: VerifyingShare<C>,
639    /// The public verifying key that represents the entire group.
640    #[zeroize(skip)]
641    pub(crate) verifying_key: VerifyingKey<C>,
642    pub(crate) min_signers: u16,
643}
644
645impl<C> KeyPackage<C>
646where
647    C: Ciphersuite,
648{
649    /// Create a new [`KeyPackage`] instance.
650    pub fn new(
651        identifier: Identifier<C>,
652        signing_share: SigningShare<C>,
653        verifying_share: VerifyingShare<C>,
654        verifying_key: VerifyingKey<C>,
655        min_signers: u16,
656    ) -> Self {
657        Self {
658            header: Header::default(),
659            identifier,
660            signing_share,
661            verifying_share,
662            verifying_key,
663            min_signers,
664        }
665    }
666}
667
668#[cfg(feature = "serialization")]
669impl<C> KeyPackage<C>
670where
671    C: Ciphersuite,
672{
673    /// Serialize the struct into a Vec.
674    pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
675        Serialize::serialize(&self)
676    }
677
678    /// Deserialize the struct from a slice of bytes.
679    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
680        Deserialize::deserialize(bytes)
681    }
682}
683
684impl<C> TryFrom<SecretShare<C>> for KeyPackage<C>
685where
686    C: Ciphersuite,
687{
688    type Error = Error<C>;
689
690    /// Tries to verify a share and construct a [`KeyPackage`] from it.
691    ///
692    /// When participants receive a [`SecretShare`] from the dealer, they
693    /// *MUST* verify the integrity of the share before continuing on to
694    /// transform it into a signing/verification keypair. Here, we assume that
695    /// every participant has the same view of the commitment issued by the
696    /// dealer, but implementations *MUST* make sure that all participants have
697    /// a consistent view of this commitment in practice.
698    fn try_from(secret_share: SecretShare<C>) -> Result<Self, Error<C>> {
699        let (verifying_share, verifying_key) = secret_share.verify()?;
700
701        Ok(KeyPackage {
702            header: Header::default(),
703            identifier: secret_share.identifier,
704            signing_share: secret_share.signing_share,
705            verifying_share,
706            verifying_key,
707            min_signers: secret_share.commitment.0.len() as u16,
708        })
709    }
710}
711
712/// Public data that contains all the signers' verifying shares as well as the
713/// group verifying key.
714///
715/// Used for verification purposes before publishing a signature.
716#[derive(Clone, Debug, PartialEq, Eq, Getters)]
717#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
718#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
719#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
720pub struct PublicKeyPackage<C: Ciphersuite> {
721    /// Serialization header
722    #[getter(skip)]
723    pub(crate) header: Header<C>,
724    /// The verifying shares for all participants. Used to validate signature
725    /// shares they generate.
726    pub(crate) verifying_shares: BTreeMap<Identifier<C>, VerifyingShare<C>>,
727    /// The joint public key for the entire group.
728    pub(crate) verifying_key: VerifyingKey<C>,
729}
730
731impl<C> PublicKeyPackage<C>
732where
733    C: Ciphersuite,
734{
735    /// Create a new [`PublicKeyPackage`] instance.
736    pub fn new(
737        verifying_shares: BTreeMap<Identifier<C>, VerifyingShare<C>>,
738        verifying_key: VerifyingKey<C>,
739    ) -> Self {
740        Self {
741            header: Header::default(),
742            verifying_shares,
743            verifying_key,
744        }
745    }
746
747    /// Computes the public key package given a list of participant identifiers
748    /// and a [`VerifiableSecretSharingCommitment`]. This is useful in scenarios
749    /// where the commitments are published somewhere and it's desirable to
750    /// recreate the public key package from them.
751    pub fn from_commitment(
752        identifiers: &BTreeSet<Identifier<C>>,
753        commitment: &VerifiableSecretSharingCommitment<C>,
754    ) -> Result<PublicKeyPackage<C>, Error<C>> {
755        let verifying_keys: BTreeMap<_, _> = identifiers
756            .iter()
757            .map(|id| (*id, VerifyingShare::from_commitment(*id, commitment)))
758            .collect();
759        Ok(PublicKeyPackage::new(
760            verifying_keys,
761            VerifyingKey::from_commitment(commitment)?,
762        ))
763    }
764
765    /// Computes the public key package given a map of participant identifiers
766    /// and their [`VerifiableSecretSharingCommitment`] from a distributed key
767    /// generation process. This is useful in scenarios where the commitments
768    /// are published somewhere and it's desirable to recreate the public key
769    /// package from them.
770    pub fn from_dkg_commitments(
771        commitments: &BTreeMap<Identifier<C>, &VerifiableSecretSharingCommitment<C>>,
772    ) -> Result<PublicKeyPackage<C>, Error<C>> {
773        let identifiers: BTreeSet<_> = commitments.keys().copied().collect();
774        let commitments: Vec<_> = commitments.values().copied().collect();
775        let group_commitment = sum_commitments(&commitments)?;
776        Self::from_commitment(&identifiers, &group_commitment)
777    }
778}
779
780#[cfg(feature = "serialization")]
781impl<C> PublicKeyPackage<C>
782where
783    C: Ciphersuite,
784{
785    /// Serialize the struct into a Vec.
786    pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
787        Serialize::serialize(&self)
788    }
789
790    /// Deserialize the struct from a slice of bytes.
791    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
792        Deserialize::deserialize(bytes)
793    }
794}
795
796/// Validates the number of signers.
797#[cfg_attr(feature = "internals", visibility::make(pub))]
798fn validate_num_of_signers<C: Ciphersuite>(
799    min_signers: u16,
800    max_signers: u16,
801) -> Result<(), Error<C>> {
802    if min_signers < 2 {
803        return Err(Error::InvalidMinSigners);
804    }
805
806    if max_signers < 2 {
807        return Err(Error::InvalidMaxSigners);
808    }
809
810    if min_signers > max_signers {
811        return Err(Error::InvalidMinSigners);
812    }
813
814    Ok(())
815}
816
817/// Generate a secret polynomial to use in secret sharing, for the given
818/// secret value. Also validates the given parameters.
819///
820/// Returns the full vector of coefficients in little-endian order (including the
821/// given secret, which is the first element) and a [`VerifiableSecretSharingCommitment`]
822/// which contains commitments to those coefficients.
823///
824/// Returns an error if the parameters (max_signers, min_signers) are inconsistent.
825pub(crate) fn generate_secret_polynomial<C: Ciphersuite>(
826    secret: &SigningKey<C>,
827    max_signers: u16,
828    min_signers: u16,
829    mut coefficients: Vec<Scalar<C>>,
830) -> Result<(Vec<Scalar<C>>, VerifiableSecretSharingCommitment<C>), Error<C>> {
831    validate_num_of_signers(min_signers, max_signers)?;
832
833    if coefficients.len() != min_signers as usize - 1 {
834        return Err(Error::InvalidCoefficients);
835    }
836
837    // Prepend the secret, which is the 0th coefficient
838    coefficients.insert(0, secret.scalar);
839
840    // Create the vector of commitments
841    let commitment: Vec<_> = coefficients
842        .iter()
843        .map(|c| CoefficientCommitment::new(<C::Group as Group>::generator() * *c))
844        .collect();
845    let commitment: VerifiableSecretSharingCommitment<C> =
846        VerifiableSecretSharingCommitment(commitment);
847
848    Ok((coefficients, commitment))
849}
850
851/// Creates secret shares for a given secret using the given coefficients.
852///
853/// This function accepts a secret from which shares are generated,
854/// and a list of threshold-1 coefficients. While in FROST this secret
855/// and coefficients should always be generated randomly, we allow them
856/// to be specified for this internal function for testability.
857///
858/// Internally, [`generate_secret_shares`] performs verifiable secret sharing, which
859/// generates shares via Shamir Secret Sharing, and then generates public
860/// commitments to those shares.
861///
862/// More specifically, [`generate_secret_shares`]:
863/// - Interpret [secret, `coefficients[0]`, ...] as a secret polynomial f
864/// - For each participant i, their secret share is f(i)
865/// - The commitment to the secret polynomial f is [g^secret, `g^coefficients[0]`, ...]
866///
867/// Implements [`secret_share_shard`] from the spec.
868///
869/// [`secret_share_shard`]: https://datatracker.ietf.org/doc/html/rfc9591#name-shamir-secret-sharing
870pub(crate) fn generate_secret_shares<C: Ciphersuite>(
871    secret: &SigningKey<C>,
872    max_signers: u16,
873    min_signers: u16,
874    coefficients: Vec<Scalar<C>>,
875    identifiers: &[Identifier<C>],
876) -> Result<Vec<SecretShare<C>>, Error<C>> {
877    let mut secret_shares: Vec<SecretShare<C>> = Vec::with_capacity(max_signers as usize);
878
879    let (coefficients, commitment) =
880        generate_secret_polynomial(secret, max_signers, min_signers, coefficients)?;
881
882    let identifiers_set: BTreeSet<_> = identifiers.iter().collect();
883    if identifiers_set.len() != identifiers.len() {
884        return Err(Error::DuplicatedIdentifier);
885    }
886
887    for id in identifiers {
888        let signing_share = SigningShare::from_coefficients(&coefficients, *id);
889
890        secret_shares.push(SecretShare {
891            header: Header::default(),
892            identifier: *id,
893            signing_share,
894            commitment: commitment.clone(),
895        });
896    }
897
898    Ok(secret_shares)
899}
900
901/// Recompute the secret from at least `min_signers` secret shares (inside
902/// [`KeyPackage`]s) using Lagrange interpolation.
903///
904/// This can be used if for some reason the original key must be restored; e.g.
905/// if threshold signing is not required anymore.
906///
907/// This is NOT required to sign with FROST; the point of FROST is being
908/// able to generate signatures only using the shares, without having to
909/// reconstruct the original key.
910///
911/// The caller is responsible for providing at least `min_signers` packages;
912/// if less than that is provided, a different key will be returned.
913pub fn reconstruct<C: Ciphersuite>(
914    key_packages: &[KeyPackage<C>],
915) -> Result<SigningKey<C>, Error<C>> {
916    if key_packages.is_empty() {
917        return Err(Error::IncorrectNumberOfShares);
918    }
919    // There is no obvious way to get `min_signers` in order to validate the
920    // size of `secret_shares`. Since that is just a best-effort validation,
921    // we don't need to worry too much about adversarial situations where people
922    // lie about min_signers, so just get the minimum value out of all of them.
923    let min_signers = key_packages
924        .iter()
925        .map(|k| k.min_signers)
926        .min()
927        .expect("should not be empty since that was just tested");
928    if key_packages.len() < min_signers as usize {
929        return Err(Error::IncorrectNumberOfShares);
930    }
931
932    let mut secret = <<C::Group as Group>::Field>::zero();
933
934    let identifiers: BTreeSet<_> = key_packages
935        .iter()
936        .map(|s| s.identifier())
937        .cloned()
938        .collect();
939
940    if identifiers.len() != key_packages.len() {
941        return Err(Error::DuplicatedIdentifier);
942    }
943
944    // Compute the Lagrange coefficients
945    for key_package in key_packages.iter() {
946        let lagrange_coefficient =
947            compute_lagrange_coefficient(&identifiers, None, key_package.identifier)?;
948
949        // Compute y = f(0) via polynomial interpolation of these t-of-n solutions ('points) of f
950        secret = secret + (lagrange_coefficient * key_package.signing_share().to_scalar());
951    }
952
953    Ok(SigningKey { scalar: secret })
954}