Skip to main content

frost_core/
keys.rs

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