//! FROST keys, keygen, key shares #![allow(clippy::type_complexity)] use std::{ collections::{BTreeSet, HashMap, HashSet}, convert::TryFrom, default::Default, fmt::{self, Debug}, iter, }; use derive_getters::Getters; #[cfg(any(test, feature = "test-impl"))] use hex::FromHex; use rand_core::{CryptoRng, RngCore}; use zeroize::{DefaultIsZeroes, Zeroize}; use crate::{ frost::Identifier, Ciphersuite, Element, Error, Field, Group, Scalar, SigningKey, VerifyingKey, }; #[cfg(feature = "serde")] use crate::{ElementSerialization, ScalarSerialization}; use super::compute_lagrange_coefficient; pub mod dkg; pub mod repairable; /// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s). pub(crate) fn generate_coefficients( size: usize, rng: &mut R, ) -> Vec> { iter::repeat_with(|| <::Field>::random(rng)) .take(size) .collect() } /// Return a list of default identifiers (1 to max_signers, inclusive). pub(crate) fn default_identifiers(max_signers: u16) -> Vec> { (1..=max_signers) .map(|i| Identifier::::try_from(i).expect("nonzero")) .collect::>() } /// A secret scalar value representing a signer's share of the group secret. #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization"))] #[cfg_attr(feature = "serde", serde(into = "ScalarSerialization"))] pub struct SigningShare(pub(crate) Scalar); impl SigningShare where C: Ciphersuite, { /// Deserialize from bytes pub fn deserialize( bytes: <::Field as Field>::Serialization, ) -> Result> { <::Field>::deserialize(&bytes) .map(|scalar| Self(scalar)) .map_err(|e| e.into()) } /// Serialize to bytes pub fn serialize(&self) -> <::Field as Field>::Serialization { <::Field>::serialize(&self.0) } } impl Debug for SigningShare where C: Ciphersuite, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("SigningShare").field(&"").finish() } } impl Default for SigningShare where C: Ciphersuite, { fn default() -> Self { Self(<::Field>::zero()) } } // Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value impl DefaultIsZeroes for SigningShare where C: Ciphersuite {} #[cfg(any(test, feature = "test-impl"))] impl FromHex for SigningShare where C: Ciphersuite, { type Error = &'static str; fn from_hex>(hex: T) -> Result { let v: Vec = FromHex::from_hex(hex).map_err(|_| "invalid hex")?; match v.try_into() { Ok(bytes) => Self::deserialize(bytes).map_err(|_| "malformed secret encoding"), Err(_) => Err("malformed secret encoding"), } } } #[cfg(feature = "serde")] impl TryFrom> for SigningShare where C: Ciphersuite, { type Error = Error; fn try_from(value: ScalarSerialization) -> Result { Self::deserialize(value.0) } } #[cfg(feature = "serde")] impl From> for ScalarSerialization where C: Ciphersuite, { fn from(value: SigningShare) -> Self { Self(value.serialize()) } } /// A public group element that represents a single signer's public verification share. #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization"))] #[cfg_attr(feature = "serde", serde(into = "ElementSerialization"))] pub struct VerifyingShare(pub(super) Element) where C: Ciphersuite; impl VerifyingShare where C: Ciphersuite, { /// Deserialize from bytes pub fn deserialize(bytes: ::Serialization) -> Result> { ::deserialize(&bytes) .map(|element| Self(element)) .map_err(|e| e.into()) } /// Serialize to bytes pub fn serialize(&self) -> ::Serialization { ::serialize(&self.0) } } impl Debug for VerifyingShare where C: Ciphersuite, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("VerifyingShare") .field(&hex::encode(self.serialize())) .finish() } } impl From> for VerifyingShare where C: Ciphersuite, { fn from(secret: SigningShare) -> VerifyingShare { VerifyingShare(::generator() * secret.0 as Scalar) } } #[cfg(feature = "serde")] impl TryFrom> for VerifyingShare where C: Ciphersuite, { type Error = Error; fn try_from(value: ElementSerialization) -> Result { Self::deserialize(value.0) } } #[cfg(feature = "serde")] impl From> for ElementSerialization where C: Ciphersuite, { fn from(value: VerifyingShare) -> Self { Self(value.serialize()) } } /// A [`Group::Element`] newtype that is a commitment to one coefficient of our secret polynomial. /// /// This is a (public) commitment to one coefficient of a secret polynomial used for performing /// verifiable secret sharing for a Shamir secret share. #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization"))] #[cfg_attr(feature = "serde", serde(into = "ElementSerialization"))] pub struct CoefficientCommitment(pub(crate) Element); impl CoefficientCommitment where C: Ciphersuite, { /// returns serialized element pub fn serialize(&self) -> ::Serialization { ::serialize(&self.0) } /// Creates a new commitment from a coefficient input pub fn deserialize( coefficient: ::Serialization, ) -> Result, Error> { Ok(Self(::deserialize(&coefficient)?)) } /// Returns inner element value pub fn value(&self) -> Element { self.0 } } impl Debug for CoefficientCommitment where C: Ciphersuite, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("CoefficientCommitment") .field(&hex::encode(self.serialize())) .finish() } } #[cfg(feature = "serde")] impl TryFrom> for CoefficientCommitment where C: Ciphersuite, { type Error = Error; fn try_from(value: ElementSerialization) -> Result { Self::deserialize(value.0) } } #[cfg(feature = "serde")] impl From> for ElementSerialization where C: Ciphersuite, { fn from(value: CoefficientCommitment) -> Self { Self(value.serialize()) } } /// Contains the commitments to the coefficients for our secret polynomial _f_, /// used to generate participants' key shares. /// /// [`VerifiableSecretSharingCommitment`] contains a set of commitments to the coefficients (which /// themselves are scalars) for a secret polynomial f, where f is used to /// generate each ith participant's key share f(i). Participants use this set of /// commitments to perform verifiable secret sharing. /// /// Note that participants MUST be assured that they have the *same* /// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using /// some agreed-upon public location for publication, where each participant can /// ensure that they received the correct (and same) value. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct VerifiableSecretSharingCommitment( pub(crate) Vec>, ); impl VerifiableSecretSharingCommitment where C: Ciphersuite, { /// Returns serialized coefficent commitments pub fn serialize(&self) -> Vec<::Serialization> { self.0 .iter() .map(|cc| <::Group as Group>::serialize(&cc.0)) .collect() } /// Returns VerifiableSecretSharingCommitment from a vector of serialized CoefficientCommitments pub fn deserialize( serialized_coefficient_commitments: Vec<::Serialization>, ) -> Result> { let mut coefficient_commitments = Vec::new(); for cc in serialized_coefficient_commitments { coefficient_commitments.push(CoefficientCommitment::::deserialize(cc)?); } Ok(Self(coefficient_commitments)) } } /// A secret share generated by performing a (t-out-of-n) secret sharing scheme, /// generated by a dealer performing [`generate_with_dealer`]. /// /// `n` is the total number of shares and `t` is the threshold required to reconstruct the secret; /// in this case we use Shamir's secret sharing. /// /// As a solution to the secret polynomial _f_ (a 'point'), the `identifier` is the x-coordinate, and the /// `value` is the y-coordinate. /// /// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call /// .into(), which under the hood also performs validation. #[derive(Clone, Debug, Zeroize, PartialEq, Eq, Getters)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct SecretShare { /// The participant identifier of this [`SecretShare`]. #[zeroize(skip)] pub(crate) identifier: Identifier, /// Secret Key. pub(crate) value: SigningShare, #[zeroize(skip)] /// The commitments to be distributed among signers. pub(crate) commitment: VerifiableSecretSharingCommitment, /// Ciphersuite ID for serialization #[cfg_attr( feature = "serde", serde(serialize_with = "crate::ciphersuite_serialize::<_, C>") )] #[cfg_attr( feature = "serde", serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>") )] #[getter(skip)] ciphersuite: (), } impl SecretShare where C: Ciphersuite, { /// Create a new [`SecretShare`] instance. pub fn new( identifier: Identifier, value: SigningShare, commitment: VerifiableSecretSharingCommitment, ) -> Self { SecretShare { identifier, value, commitment, ciphersuite: (), } } /// Gets the inner [`SigningShare`] value. pub fn secret(&self) -> &SigningShare { &self.value } /// Verifies that a secret share is consistent with a verifiable secret sharing commitment, /// and returns the derived group info for the participant (their public verification share, /// and the group public key) if successful. /// /// This ensures that this participant's share has been generated using the same /// mechanism as all other signing participants. Note that participants *MUST* /// ensure that they have the same view as all other participants of the /// commitment! /// /// An implementation of `vss_verify()` from the [spec]. /// This also implements `derive_group_info()` from the [spec] (which is very similar), /// but only for this participant. /// /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C.2-4 pub fn verify(&self) -> Result<(VerifyingShare, VerifyingKey), Error> { let f_result = ::generator() * self.value.0; let result = evaluate_vss(&self.commitment, self.identifier); if !(f_result == result) { return Err(Error::InvalidSecretShare); } let group_public = VerifyingKey { element: self.commitment.0[0].0, }; Ok((VerifyingShare(result), group_public)) } } /// The identifier list to use when generating key shares. pub enum IdentifierList<'a, C: Ciphersuite> { /// Use the default values (1 to max_signers, inclusive). Default, /// A user-provided list of identifiers. Custom(&'a [Identifier]), } /// Allows all participants' keys to be generated using a central, trusted /// dealer. /// /// Under the hood, this performs verifiable secret sharing, which itself uses /// Shamir secret sharing, from which each share becomes a participant's secret /// key. The output from this function is a set of shares along with one single /// commitment that participants use to verify the integrity of the share. The /// number of signers is limited to 255. /// /// Implements [`trusted_dealer_keygen`] from the spec. /// /// [`trusted_dealer_keygen`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C pub fn generate_with_dealer( max_signers: u16, min_signers: u16, identifiers: IdentifierList, rng: &mut R, ) -> Result<(HashMap, SecretShare>, PublicKeyPackage), Error> { let mut bytes = [0; 64]; rng.fill_bytes(&mut bytes); let key = SigningKey::new(rng); split(&key, max_signers, min_signers, identifiers, rng) } /// Splits an existing key into FROST shares. /// /// This is identical to [`generate_with_dealer`] but receives an existing key /// instead of generating a fresh one. This is useful in scenarios where /// the key needs to be generated externally or must be derived from e.g. a /// seed phrase. pub fn split( key: &SigningKey, max_signers: u16, min_signers: u16, identifiers: IdentifierList, rng: &mut R, ) -> Result<(HashMap, SecretShare>, PublicKeyPackage), Error> { let group_public = VerifyingKey::from(key); let coefficients = generate_coefficients::(min_signers as usize - 1, rng); let default_identifiers = default_identifiers(max_signers); let identifiers = match identifiers { IdentifierList::Custom(identifiers) => identifiers, IdentifierList::Default => &default_identifiers, }; let secret_shares = generate_secret_shares(key, max_signers, min_signers, coefficients, identifiers)?; let mut signer_pubkeys: HashMap, VerifyingShare> = HashMap::with_capacity(max_signers as usize); let mut secret_shares_by_id: HashMap, SecretShare> = HashMap::with_capacity(max_signers as usize); for secret_share in secret_shares { let signer_public = secret_share.value.into(); signer_pubkeys.insert(secret_share.identifier, signer_public); secret_shares_by_id.insert(secret_share.identifier, secret_share); } Ok(( secret_shares_by_id, PublicKeyPackage { signer_pubkeys, group_public, ciphersuite: (), }, )) } /// Evaluate the polynomial with the given coefficients (constant term first) /// at the point x=identifier using Horner's method. /// /// Implements [`polynomial_evaluate`] from the spec. /// /// [`polynomial_evaluate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-evaluation-of-a-polynomial fn evaluate_polynomial( identifier: Identifier, coefficients: &[Scalar], ) -> Scalar { let mut value = <::Field>::zero(); let ell_scalar = identifier; for coeff in coefficients.iter().skip(1).rev() { value = value + *coeff; value *= ell_scalar; } value = value + coefficients[0]; value } /// Evaluates the right-hand side of the VSS verification equation, namely /// ∏^{t−1}_{k=0} φ^{i^k mod q}_{ℓk} using `identifier` as `i` and the /// `commitment` as the commitment vector φ_ℓ fn evaluate_vss( commitment: &VerifiableSecretSharingCommitment, identifier: Identifier, ) -> Element { let i = identifier; let (_, result) = commitment.0.iter().fold( (<::Field>::one(), ::identity()), |(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k.0 * i_to_the_k), ); result } /// A FROST keypair, which can be generated either by a trusted dealer or using /// a DKG. /// /// When using a central dealer, [`SecretShare`]s are distributed to /// participants, who then perform verification, before deriving /// [`KeyPackage`]s, which they store to later use during signing. #[derive(Clone, Debug, PartialEq, Eq, Getters, Zeroize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct KeyPackage { /// Denotes the participant identifier each secret share key package is owned by. #[zeroize(skip)] pub(crate) identifier: Identifier, /// This participant's secret share. pub(crate) secret_share: SigningShare, /// This participant's public key. #[zeroize(skip)] pub(crate) public: VerifyingShare, /// The public verifying key that represents the entire group. #[zeroize(skip)] pub(crate) group_public: VerifyingKey, /// Ciphersuite ID for serialization #[cfg_attr( feature = "serde", serde(serialize_with = "crate::ciphersuite_serialize::<_, C>") )] #[cfg_attr( feature = "serde", serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>") )] #[getter(skip)] ciphersuite: (), } impl KeyPackage where C: Ciphersuite, { /// Create a new [`KeyPackage`] instance. pub fn new( identifier: Identifier, secret_share: SigningShare, public: VerifyingShare, group_public: VerifyingKey, ) -> Self { Self { identifier, secret_share, public, group_public, ciphersuite: (), } } } impl TryFrom> for KeyPackage where C: Ciphersuite, { type Error = Error; /// Tries to verify a share and construct a [`KeyPackage`] from it. /// /// When participants receive a [`SecretShare`] from the dealer, they /// *MUST* verify the integrity of the share before continuing on to /// transform it into a signing/verification keypair. Here, we assume that /// every participant has the same view of the commitment issued by the /// dealer, but implementations *MUST* make sure that all participants have /// a consistent view of this commitment in practice. fn try_from(secret_share: SecretShare) -> Result> { let (public, group_public) = secret_share.verify()?; Ok(KeyPackage { identifier: secret_share.identifier, secret_share: secret_share.value, public, group_public, ciphersuite: (), }) } } /// Public data that contains all the signers' public keys as well as the /// group public key. /// /// Used for verification purposes before publishing a signature. #[derive(Clone, Debug, PartialEq, Eq, Getters)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct PublicKeyPackage { /// When performing signing, the coordinator must ensure that they have the /// correct view of participants' public keys to perform verification before /// publishing a signature. `signer_pubkeys` represents all signers for a /// signing operation. pub(crate) signer_pubkeys: HashMap, VerifyingShare>, /// The joint public key for the entire group. pub(crate) group_public: VerifyingKey, /// Ciphersuite ID for serialization #[cfg_attr( feature = "serde", serde(serialize_with = "crate::ciphersuite_serialize::<_, C>") )] #[cfg_attr( feature = "serde", serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>") )] #[getter(skip)] ciphersuite: (), } impl PublicKeyPackage where C: Ciphersuite, { /// Create a new [`PublicKeyPackage`] instance. pub fn new( signer_pubkeys: HashMap, VerifyingShare>, group_public: VerifyingKey, ) -> Self { Self { signer_pubkeys, group_public, ciphersuite: (), } } } /// Generate a secret polynomial to use in secret sharing, for the given /// secret value. Also validates the given parameters. /// /// Returns the full vector of coefficients in little-endian order (including the /// given secret, which is the first element) and a [`VerifiableSecretSharingCommitment`] /// which contains commitments to those coefficients. /// /// Returns an error if the parameters (max_signers, min_signers) are inconsistent. pub(crate) fn generate_secret_polynomial( secret: &SigningKey, max_signers: u16, min_signers: u16, mut coefficients: Vec>, ) -> Result<(Vec>, VerifiableSecretSharingCommitment), Error> { if min_signers < 2 { return Err(Error::InvalidMinSigners); } if max_signers < 2 { return Err(Error::InvalidMaxSigners); } if min_signers > max_signers { return Err(Error::InvalidMinSigners); } if coefficients.len() != min_signers as usize - 1 { return Err(Error::InvalidCoefficients); } // Prepend the secret, which is the 0th coefficient coefficients.insert(0, secret.scalar); // Create the vector of commitments let commitment: Vec<_> = coefficients .iter() .map(|c| CoefficientCommitment(::generator() * *c)) .collect(); let commitment: VerifiableSecretSharingCommitment = VerifiableSecretSharingCommitment(commitment); Ok((coefficients, commitment)) } /// Creates secret shares for a given secret using the given coefficients. /// /// This function accepts a secret from which shares are generated, /// and a list of threshold-1 coefficients. While in FROST this secret /// and coefficients should always be generated randomly, we allow them /// to be specified for this internal function for testability. /// /// Internally, [`generate_secret_shares`] performs verifiable secret sharing, which /// generates shares via Shamir Secret Sharing, and then generates public /// commitments to those shares. /// /// More specifically, [`generate_secret_shares`]: /// - Interpret [secret, `coefficients[0]`, ...] as a secret polynomial f /// - For each participant i, their secret share is f(i) /// - The commitment to the secret polynomial f is [g^secret, `g^coefficients[0]`, ...] /// /// Implements [`secret_share_shard`] from the spec. /// /// [`secret_share_shard`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C.1 pub(crate) fn generate_secret_shares( secret: &SigningKey, max_signers: u16, min_signers: u16, coefficients: Vec>, identifiers: &[Identifier], ) -> Result>, Error> { let mut secret_shares: Vec> = Vec::with_capacity(max_signers as usize); let (coefficients, commitment) = generate_secret_polynomial(secret, max_signers, min_signers, coefficients)?; let identifiers_set: HashSet<_> = identifiers.iter().collect(); if identifiers_set.len() != identifiers.len() { return Err(Error::DuplicatedIdentifiers); } for id in identifiers { let value = evaluate_polynomial(*id, &coefficients); secret_shares.push(SecretShare { identifier: *id, value: SigningShare(value), commitment: commitment.clone(), ciphersuite: (), }); } Ok(secret_shares) } /// Recompute the secret from at least `min_signers` secret shares /// using Lagrange interpolation. /// /// This can be used if for some reason the original key must be restored; e.g. /// if threshold signing is not required anymore. /// /// This is NOT required to sign with FROST; the point of FROST is being /// able to generate signatures only using the shares, without having to /// reconstruct the original key. /// /// The caller is responsible for providing at least `min_signers` shares; /// if less than that is provided, a different key will be returned. pub fn reconstruct( secret_shares: &[SecretShare], ) -> Result, Error> { if secret_shares.is_empty() { return Err(Error::IncorrectNumberOfShares); } let mut secret = <::Field>::zero(); let identifiers: BTreeSet<_> = secret_shares .iter() .map(|s| s.identifier()) .cloned() .collect(); if identifiers.len() != secret_shares.len() { return Err(Error::DuplicatedIdentifiers); } // Compute the Lagrange coefficients for secret_share in secret_shares.iter() { let lagrange_coefficient = compute_lagrange_coefficient(&identifiers, None, secret_share.identifier)?; // Compute y = f(0) via polynomial interpolation of these t-of-n solutions ('points) of f secret = secret + (lagrange_coefficient * secret_share.value.0); } Ok(SigningKey { scalar: secret }) }