2022-05-16 13:53:17 -07:00
|
|
|
|
//! FROST keys, keygen, key shares
|
|
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
|
collections::HashMap,
|
|
|
|
|
convert::TryFrom,
|
|
|
|
|
default::Default,
|
|
|
|
|
fmt::{self, Debug},
|
2022-10-03 11:41:02 -07:00
|
|
|
|
iter,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use hex::FromHex;
|
|
|
|
|
use rand_core::{CryptoRng, RngCore};
|
|
|
|
|
use zeroize::{DefaultIsZeroes, Zeroize};
|
|
|
|
|
|
2022-11-02 10:52:38 -07:00
|
|
|
|
use crate::{
|
|
|
|
|
frost::Identifier, random_nonzero, Ciphersuite, Element, Error, Field, Group, Scalar,
|
|
|
|
|
VerifyingKey,
|
|
|
|
|
};
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-18 15:11:05 -07:00
|
|
|
|
pub mod dkg;
|
|
|
|
|
|
2022-10-03 11:41:02 -07:00
|
|
|
|
/// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s).
|
|
|
|
|
pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|
|
|
|
size: usize,
|
2022-10-18 15:11:05 -07:00
|
|
|
|
rng: &mut R,
|
2022-10-25 22:08:06 -07:00
|
|
|
|
) -> Vec<Scalar<C>> {
|
|
|
|
|
iter::repeat_with(|| <<C::Group as Group>::Field>::random(rng))
|
2022-10-03 11:41:02 -07:00
|
|
|
|
.take(size)
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// A group secret to be split between participants.
|
|
|
|
|
///
|
|
|
|
|
/// This is similar to a [`crate::SigningKey`], but this secret is not intended to be used
|
|
|
|
|
/// on its own for signing, but split into shares that a threshold number of signers will use to
|
|
|
|
|
/// sign.
|
2022-09-02 19:26:41 -07:00
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
2022-09-15 09:15:53 -07:00
|
|
|
|
pub struct SharedSecret<C: Ciphersuite>(pub(crate) Scalar<C>);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> SharedSecret<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// Deserialize from bytes
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn from_bytes(
|
|
|
|
|
bytes: <<C::Group as Group>::Field as Field>::Serialization,
|
|
|
|
|
) -> Result<Self, Error> {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self(scalar))
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// Serialize to bytes
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn to_bytes(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
<<C::Group as Group>::Field>::serialize(&self.0)
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Generates a new uniformly random secret value using the provided RNG.
|
|
|
|
|
// TODO: should this only be behind test?
|
|
|
|
|
pub fn random<R>(mut rng: R) -> Self
|
|
|
|
|
where
|
|
|
|
|
R: CryptoRng + RngCore,
|
|
|
|
|
{
|
2022-11-02 10:52:38 -07:00
|
|
|
|
Self(random_nonzero::<C, R>(&mut rng))
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> Debug for SharedSecret<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
2022-09-15 09:15:53 -07:00
|
|
|
|
f.debug_tuple("SharedSecret")
|
2022-05-16 13:53:17 -07:00
|
|
|
|
.field(&hex::encode(self.to_bytes()))
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> Default for SharedSecret<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
fn default() -> Self {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
Self(<<C::Group as Group>::Field>::zero())
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> DefaultIsZeroes for SharedSecret<C> where C: Ciphersuite {}
|
|
|
|
|
|
|
|
|
|
impl<C> From<&SharedSecret<C>> for VerifyingKey<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-09-15 09:15:53 -07:00
|
|
|
|
fn from(secret: &SharedSecret<C>) -> Self {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let element = <C::Group>::generator() * secret.0;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
VerifyingKey { element }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 13:01:03 -07:00
|
|
|
|
#[cfg(any(test, feature = "test-impl"))]
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> FromHex for SharedSecret<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
|
|
|
|
let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
|
|
|
|
|
match v.try_into() {
|
|
|
|
|
Ok(bytes) => Self::from_bytes(bytes).map_err(|_| "malformed secret encoding"),
|
|
|
|
|
Err(_) => Err("malformed secret encoding"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A secret scalar value representing a signer's share of the group secret.
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub struct SigningShare<C: Ciphersuite>(pub(crate) Scalar<C>);
|
|
|
|
|
|
|
|
|
|
impl<C> SigningShare<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
/// Deserialize from bytes
|
|
|
|
|
pub fn from_bytes(
|
|
|
|
|
bytes: <<C::Group as Group>::Field as Field>::Serialization,
|
|
|
|
|
) -> Result<Self, Error> {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self(scalar))
|
2022-09-15 09:15:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Serialize to bytes
|
|
|
|
|
pub fn to_bytes(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
<<C::Group as Group>::Field>::serialize(&self.0)
|
2022-09-15 09:15:53 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C> Debug for SigningShare<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
f.debug_tuple("SigningShare")
|
|
|
|
|
.field(&hex::encode(self.to_bytes()))
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C> Default for SigningShare<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
fn default() -> Self {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
Self(<<C::Group as Group>::Field>::zero())
|
2022-09-15 09:15:53 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value
|
|
|
|
|
impl<C> DefaultIsZeroes for SigningShare<C> where C: Ciphersuite {}
|
|
|
|
|
|
2022-10-28 13:01:03 -07:00
|
|
|
|
#[cfg(any(test, feature = "test-impl"))]
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> FromHex for SigningShare<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
2022-06-27 14:47:27 -07:00
|
|
|
|
let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
|
|
|
|
|
match v.try_into() {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
Ok(bytes) => Self::from_bytes(bytes).map_err(|_| "malformed secret encoding"),
|
2022-06-27 14:47:27 -07:00
|
|
|
|
Err(_) => Err("malformed secret encoding"),
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// A public group element that represents a single signer's public verification share.
|
2022-09-02 19:26:41 -07:00
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
2022-10-25 22:08:06 -07:00
|
|
|
|
pub struct VerifyingShare<C>(pub(super) Element<C>)
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite;
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> VerifyingShare<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
/// Deserialize from bytes
|
|
|
|
|
pub fn from_bytes(bytes: <C::Group as Group>::Serialization) -> Result<Self, Error> {
|
|
|
|
|
<C::Group as Group>::deserialize(&bytes).map(|element| Self(element))
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// Serialize to bytes
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn to_bytes(&self) -> <C::Group as Group>::Serialization {
|
|
|
|
|
<C::Group as Group>::serialize(&self.0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> Debug for VerifyingShare<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2022-09-15 09:15:53 -07:00
|
|
|
|
f.debug_tuple("VerifyingShare")
|
2022-05-16 13:53:17 -07:00
|
|
|
|
.field(&hex::encode(self.to_bytes()))
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
impl<C> From<SigningShare<C>> for VerifyingShare<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-09-15 09:15:53 -07:00
|
|
|
|
fn from(secret: SigningShare<C>) -> VerifyingShare<C> {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
VerifyingShare(<C::Group>::generator() * secret.0 as Scalar<C>)
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// A [`Group::Element`] newtype that is a commitment to one coefficient of our secret polynomial.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
|
|
|
|
/// 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)]
|
2022-10-25 22:08:06 -07:00
|
|
|
|
pub(super) struct CoefficientCommitment<C: Ciphersuite>(pub(super) Element<C>);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
/// 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)]
|
|
|
|
|
pub struct VerifiableSecretSharingCommitment<C: Ciphersuite>(
|
|
|
|
|
pub(super) Vec<CoefficientCommitment<C>>,
|
|
|
|
|
);
|
|
|
|
|
|
2022-10-04 11:50:47 -07:00
|
|
|
|
/// A secret share generated by performing a (t-out-of-n) secret sharing scheme,
|
|
|
|
|
/// generated by a dealer performing [`keygen_with_dealer`].
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
|
|
|
|
/// `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.
|
|
|
|
|
///
|
2022-09-01 13:07:50 -07:00
|
|
|
|
/// As a solution to the secret polynomial _f_ (a 'point'), the `identifier` is the x-coordinate, and the
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// `value` is the y-coordinate.
|
2022-10-04 11:50:47 -07:00
|
|
|
|
///
|
|
|
|
|
/// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call
|
|
|
|
|
/// .into(), which under the hood also performs validation.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
#[derive(Clone, Zeroize)]
|
|
|
|
|
pub struct SecretShare<C: Ciphersuite> {
|
2022-09-01 13:07:50 -07:00
|
|
|
|
/// The participant identifier of this [`SecretShare`].
|
|
|
|
|
pub identifier: Identifier<C>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// Secret Key.
|
2022-09-15 09:15:53 -07:00
|
|
|
|
pub value: SigningShare<C>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// The commitments to be distributed among signers.
|
|
|
|
|
pub commitment: VerifiableSecretSharingCommitment<C>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C> SecretShare<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// Gets the inner [`SigningShare`] value.
|
|
|
|
|
pub fn secret(&self) -> &SigningShare<C> {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
&self.value
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 12:57:26 -07:00
|
|
|
|
/// 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.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
|
|
|
|
/// 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].
|
2022-07-29 12:57:26 -07:00
|
|
|
|
/// This also implements `derive_group_info()` from the [spec] (which is very similar),
|
|
|
|
|
/// but only for this participant.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
2022-10-24 16:25:40 -07:00
|
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C.2-4
|
2022-10-28 13:01:03 -07:00
|
|
|
|
pub fn verify(&self) -> Result<(VerifyingShare<C>, VerifyingKey<C>), Error> {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let f_result = <C::Group>::generator() * self.value.0;
|
2022-10-28 13:01:03 -07:00
|
|
|
|
let result = evaluate_vss(&self.commitment, self.identifier);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
if !(f_result == result) {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
return Err(Error::InvalidSecretShare);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 12:57:26 -07:00
|
|
|
|
let group_public = VerifyingKey {
|
|
|
|
|
element: self.commitment.0[0].0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok((VerifyingShare(result), group_public))
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
|
///
|
2022-10-24 16:25:40 -07:00
|
|
|
|
/// [`trusted_dealer_keygen`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C
|
2022-05-16 13:53:17 -07:00
|
|
|
|
pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
2022-10-26 21:35:16 -07:00
|
|
|
|
max_signers: u16,
|
|
|
|
|
min_signers: u16,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
mut rng: R,
|
2022-10-28 13:01:03 -07:00
|
|
|
|
) -> Result<(Vec<SecretShare<C>>, PublicKeyPackage<C>), Error> {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
let mut bytes = [0; 64];
|
|
|
|
|
rng.fill_bytes(&mut bytes);
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
let secret = SharedSecret::random(&mut rng);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
let group_public = VerifyingKey::from(&secret);
|
2022-10-03 11:41:02 -07:00
|
|
|
|
|
2022-10-26 21:35:16 -07:00
|
|
|
|
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, &mut rng);
|
2022-10-03 11:41:02 -07:00
|
|
|
|
|
2022-10-26 21:35:16 -07:00
|
|
|
|
let secret_shares = generate_secret_shares(&secret, max_signers, min_signers, coefficients)?;
|
2022-09-15 09:15:53 -07:00
|
|
|
|
let mut signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>> =
|
2022-10-26 21:35:16 -07:00
|
|
|
|
HashMap::with_capacity(max_signers as usize);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-04 11:50:47 -07:00
|
|
|
|
for secret_share in &secret_shares {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
let signer_public = secret_share.value.into();
|
2022-09-01 13:07:50 -07:00
|
|
|
|
signer_pubkeys.insert(secret_share.identifier, signer_public);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok((
|
2022-10-04 11:50:47 -07:00
|
|
|
|
secret_shares,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
PublicKeyPackage {
|
|
|
|
|
signer_pubkeys,
|
|
|
|
|
group_public,
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 15:11:05 -07:00
|
|
|
|
/// 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-10.html#name-evaluation-of-a-polynomial
|
|
|
|
|
fn evaluate_polynomial<C: Ciphersuite>(
|
|
|
|
|
identifier: Identifier<C>,
|
|
|
|
|
coefficients: &[Scalar<C>],
|
2022-10-28 13:01:03 -07:00
|
|
|
|
) -> Scalar<C> {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let mut value = <<C::Group as Group>::Field>::zero();
|
|
|
|
|
|
2022-10-25 20:50:25 -07:00
|
|
|
|
let ell_scalar = identifier;
|
2022-10-18 15:11:05 -07:00
|
|
|
|
for coeff in coefficients.iter().skip(1).rev() {
|
|
|
|
|
value = value + *coeff;
|
2022-10-25 20:50:25 -07:00
|
|
|
|
value *= ell_scalar;
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
value = value + coefficients[0];
|
2022-10-28 13:01:03 -07:00
|
|
|
|
value
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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<C: Ciphersuite>(
|
|
|
|
|
commitment: &VerifiableSecretSharingCommitment<C>,
|
|
|
|
|
identifier: Identifier<C>,
|
2022-10-28 13:01:03 -07:00
|
|
|
|
) -> Element<C> {
|
2022-10-25 20:50:25 -07:00
|
|
|
|
let i = identifier;
|
2022-10-18 15:11:05 -07:00
|
|
|
|
|
|
|
|
|
let (_, result) = commitment.0.iter().fold(
|
2022-10-25 22:08:06 -07:00
|
|
|
|
(<<C::Group as Group>::Field>::one(), <C::Group>::identity()),
|
2022-10-18 15:11:05 -07:00
|
|
|
|
|(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k.0 * i_to_the_k),
|
|
|
|
|
);
|
2022-10-28 13:01:03 -07:00
|
|
|
|
result
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// A FROST keypair, which can be generated either by a trusted dealer or using
|
|
|
|
|
/// a DKG.
|
|
|
|
|
///
|
2022-10-04 11:50:47 -07:00
|
|
|
|
/// When using a central dealer, [`SecretShare`]s are distributed to
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// participants, who then perform verification, before deriving
|
|
|
|
|
/// [`KeyPackage`]s, which they store to later use during signing.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct KeyPackage<C: Ciphersuite> {
|
2022-09-01 13:07:50 -07:00
|
|
|
|
/// Denotes the participant identifier each secret share key package is owned by.
|
|
|
|
|
pub identifier: Identifier<C>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// This participant's secret share.
|
2022-09-15 09:15:53 -07:00
|
|
|
|
pub secret_share: SigningShare<C>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// This participant's public key.
|
2022-09-15 09:15:53 -07:00
|
|
|
|
pub public: VerifyingShare<C>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// The public signing key that represents the entire group.
|
|
|
|
|
pub group_public: VerifyingKey<C>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C> KeyPackage<C>
|
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-09-01 13:07:50 -07:00
|
|
|
|
/// Gets the participant identifier associated with this [`KeyPackage`].
|
|
|
|
|
pub fn identifier(&self) -> &Identifier<C> {
|
|
|
|
|
&self.identifier
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// Gets the participant's [`SigningShare`] associated with this [`KeyPackage`].
|
|
|
|
|
pub fn secret_share(&self) -> &SigningShare<C> {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
&self.secret_share
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 09:15:53 -07:00
|
|
|
|
/// Gets the participant's [`VerifyingShare`] associated with the [`SigningShare`] in this [`KeyPackage`].
|
|
|
|
|
pub fn public(&self) -> &VerifyingShare<C> {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
&self.public
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets the group [`VerifyingKey`] associated with the entire group in this [`KeyPackage`].
|
|
|
|
|
pub fn group_public(&self) -> &VerifyingKey<C> {
|
|
|
|
|
&self.group_public
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-04 11:50:47 -07:00
|
|
|
|
impl<C> TryFrom<SecretShare<C>> for KeyPackage<C>
|
2022-05-16 13:53:17 -07:00
|
|
|
|
where
|
|
|
|
|
C: Ciphersuite,
|
|
|
|
|
{
|
2022-10-28 13:01:03 -07:00
|
|
|
|
type Error = Error;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
/// Tries to verify a share and construct a [`KeyPackage`] from it.
|
|
|
|
|
///
|
2022-10-04 11:50:47 -07:00
|
|
|
|
/// When participants receive a [`SecretShare`] from the dealer, they
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// *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.
|
2022-10-28 13:01:03 -07:00
|
|
|
|
fn try_from(secret_share: SecretShare<C>) -> Result<Self, Error> {
|
2022-10-04 11:50:47 -07:00
|
|
|
|
let (public, group_public) = secret_share.verify()?;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
Ok(KeyPackage {
|
2022-10-04 11:50:47 -07:00
|
|
|
|
identifier: secret_share.identifier,
|
|
|
|
|
secret_share: secret_share.value,
|
2022-07-29 12:57:26 -07:00
|
|
|
|
public,
|
|
|
|
|
group_public,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Public data that contains all the signers' public keys as well as the
|
|
|
|
|
/// group public key.
|
|
|
|
|
///
|
|
|
|
|
/// Used for verification purposes before publishing a signature.
|
|
|
|
|
pub struct PublicKeyPackage<C: Ciphersuite> {
|
|
|
|
|
/// 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.
|
2022-09-15 09:15:53 -07:00
|
|
|
|
pub signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>>,
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// The joint public key for the entire group.
|
|
|
|
|
pub group_public: VerifyingKey<C>,
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 15:11:05 -07:00
|
|
|
|
/// 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.
|
|
|
|
|
///
|
2022-10-26 21:35:16 -07:00
|
|
|
|
/// Returns an error if the parameters (max_signers, min_signers) are inconsistent.
|
2022-10-18 15:11:05 -07:00
|
|
|
|
pub(crate) fn generate_secret_polynomial<C: Ciphersuite>(
|
|
|
|
|
secret: &SharedSecret<C>,
|
2022-10-26 21:35:16 -07:00
|
|
|
|
max_signers: u16,
|
|
|
|
|
min_signers: u16,
|
2022-10-18 15:11:05 -07:00
|
|
|
|
mut coefficients: Vec<Scalar<C>>,
|
2022-10-28 13:01:03 -07:00
|
|
|
|
) -> Result<(Vec<Scalar<C>>, VerifiableSecretSharingCommitment<C>), Error> {
|
2022-10-26 21:35:16 -07:00
|
|
|
|
if min_signers < 2 {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
return Err(Error::InvalidMinSigners);
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-26 21:35:16 -07:00
|
|
|
|
if max_signers < 2 {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
return Err(Error::InvalidMaxSigners);
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-26 21:35:16 -07:00
|
|
|
|
if min_signers > max_signers {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
return Err(Error::InvalidMinSigners);
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-26 21:35:16 -07:00
|
|
|
|
if coefficients.len() != min_signers as usize - 1 {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
return Err(Error::InvalidCoefficients);
|
2022-10-18 15:11:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prepend the secret, which is the 0th coefficient
|
|
|
|
|
coefficients.insert(0, secret.0);
|
|
|
|
|
|
|
|
|
|
// Create the vector of commitments
|
|
|
|
|
let commitment: Vec<_> = coefficients
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|c| CoefficientCommitment(<C::Group as Group>::generator() * *c))
|
|
|
|
|
.collect();
|
|
|
|
|
let commitment: VerifiableSecretSharingCommitment<C> =
|
|
|
|
|
VerifiableSecretSharingCommitment(commitment);
|
|
|
|
|
|
|
|
|
|
Ok((coefficients, commitment))
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 11:41:02 -07:00
|
|
|
|
/// Creates secret shares for a given secret using the given coefficients.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
2022-10-03 11:41:02 -07:00
|
|
|
|
/// 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
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// 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`]:
|
2022-10-03 11:41:02 -07:00
|
|
|
|
/// - Interpret [secret, `coefficients[0]`, ...] as a secret polynomial f
|
2022-05-16 13:53:17 -07:00
|
|
|
|
/// - For each participant i, their secret share is f(i)
|
2022-10-03 11:41:02 -07:00
|
|
|
|
/// - The commitment to the secret polynomial f is [g^secret, `g^coefficients[0]`, ...]
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
2022-10-03 11:41:02 -07:00
|
|
|
|
/// Implements [`secret_share_shard`] from the spec.
|
2022-05-16 13:53:17 -07:00
|
|
|
|
///
|
2022-10-24 16:25:40 -07:00
|
|
|
|
/// [`secret_share_shard`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C.1
|
2022-10-03 11:41:02 -07:00
|
|
|
|
pub(crate) fn generate_secret_shares<C: Ciphersuite>(
|
2022-09-15 09:15:53 -07:00
|
|
|
|
secret: &SharedSecret<C>,
|
2022-10-26 21:35:16 -07:00
|
|
|
|
max_signers: u16,
|
|
|
|
|
min_signers: u16,
|
2022-10-03 11:41:02 -07:00
|
|
|
|
coefficients: Vec<Scalar<C>>,
|
2022-10-28 13:01:03 -07:00
|
|
|
|
) -> Result<Vec<SecretShare<C>>, Error> {
|
2022-10-26 21:35:16 -07:00
|
|
|
|
let mut secret_shares: Vec<SecretShare<C>> = Vec::with_capacity(max_signers as usize);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-18 15:11:05 -07:00
|
|
|
|
let (coefficients, commitment) =
|
2022-10-26 21:35:16 -07:00
|
|
|
|
generate_secret_polynomial(secret, max_signers, min_signers, coefficients)?;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
2022-10-26 21:35:16 -07:00
|
|
|
|
for id in (1..=max_signers as u16).map_while(|i| Identifier::<C>::try_from(i).ok()) {
|
2022-10-28 13:01:03 -07:00
|
|
|
|
let value = evaluate_polynomial(id, &coefficients);
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
secret_shares.push(SecretShare {
|
2022-09-01 13:07:50 -07:00
|
|
|
|
identifier: id,
|
2022-09-15 09:15:53 -07:00
|
|
|
|
value: SigningShare(value),
|
2022-05-16 13:53:17 -07:00
|
|
|
|
commitment: commitment.clone(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(secret_shares)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Recompute the secret from t-of-n secret shares using Lagrange interpolation.
|
|
|
|
|
pub fn reconstruct_secret<C: Ciphersuite>(
|
|
|
|
|
secret_shares: Vec<SecretShare<C>>,
|
2022-09-15 09:15:53 -07:00
|
|
|
|
) -> Result<SharedSecret<C>, &'static str> {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
if secret_shares.is_empty() {
|
|
|
|
|
return Err("No secret_shares provided");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let secret_share_map: HashMap<Identifier<C>, SecretShare<C>> = secret_shares
|
|
|
|
|
.into_iter()
|
2022-09-01 13:07:50 -07:00
|
|
|
|
.map(|share| (share.identifier, share))
|
2022-05-16 13:53:17 -07:00
|
|
|
|
.collect();
|
|
|
|
|
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let mut secret = <<C::Group as Group>::Field>::zero();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
// Compute the Lagrange coefficients
|
|
|
|
|
for (i, secret_share) in secret_share_map.clone() {
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let mut num = <<C::Group as Group>::Field>::one();
|
|
|
|
|
let mut den = <<C::Group as Group>::Field>::one();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
for j in secret_share_map.clone().into_keys() {
|
|
|
|
|
if j == i {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// numerator *= j
|
2022-10-25 20:50:25 -07:00
|
|
|
|
num *= j;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
// denominator *= j - i
|
2022-10-25 20:50:25 -07:00
|
|
|
|
den *= j - i;
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If at this step, the denominator is zero in the scalar field, there must be a duplicate
|
|
|
|
|
// secret share.
|
2022-10-25 22:08:06 -07:00
|
|
|
|
if den == <<C::Group as Group>::Field>::zero() {
|
2022-05-16 13:53:17 -07:00
|
|
|
|
return Err("Duplicate shares provided");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save numerator * 1/denomintor in the scalar field
|
2022-10-25 22:08:06 -07:00
|
|
|
|
let lagrange_coefficient = num * <<C::Group as Group>::Field>::invert(&den).unwrap();
|
2022-05-16 13:53:17 -07:00
|
|
|
|
|
|
|
|
|
// Compute y = f(0) via polynomial interpolation of these t-of-n solutions ('points) of f
|
|
|
|
|
secret = secret + (lagrange_coefficient * secret_share.value.0);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 22:08:06 -07:00
|
|
|
|
Ok(SharedSecret::from_bytes(<<C::Group as Group>::Field>::serialize(&secret)).unwrap())
|
2022-05-16 13:53:17 -07:00
|
|
|
|
}
|