2022-11-22 13:09:21 -08:00
|
|
|
#![allow(non_snake_case)]
|
|
|
|
#![deny(missing_docs)]
|
|
|
|
#![doc = include_str!("../README.md")]
|
|
|
|
|
2023-07-05 03:39:25 -07:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2022-11-22 13:09:21 -08:00
|
|
|
use k256::{
|
|
|
|
elliptic_curve::{
|
|
|
|
group::prime::PrimeCurveAffine,
|
2023-03-08 06:32:35 -08:00
|
|
|
hash2curve::{hash_to_field, ExpandMsgXmd},
|
2022-11-22 13:09:21 -08:00
|
|
|
sec1::{FromEncodedPoint, ToEncodedPoint},
|
|
|
|
Field as FFField, PrimeField,
|
|
|
|
},
|
|
|
|
AffinePoint, ProjectivePoint, Scalar,
|
|
|
|
};
|
|
|
|
use rand_core::{CryptoRng, RngCore};
|
2022-12-08 09:52:20 -08:00
|
|
|
use sha2::{Digest, Sha256};
|
2022-11-22 13:09:21 -08:00
|
|
|
|
2023-04-13 18:04:17 -07:00
|
|
|
use frost_core::frost;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
2023-06-23 07:22:33 -07:00
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
use frost_core::serde;
|
|
|
|
|
2022-11-22 13:09:21 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2023-04-13 18:04:17 -07:00
|
|
|
// Re-exports in our public API
|
|
|
|
pub use frost_core::{Ciphersuite, Field, FieldError, Group, GroupError};
|
|
|
|
pub use rand_core;
|
|
|
|
|
2022-12-12 14:04:10 -08:00
|
|
|
/// An error.
|
|
|
|
pub type Error = frost_core::Error<Secp256K1Sha256>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite scalar field.
|
2023-04-13 16:57:00 -07:00
|
|
|
#[derive(Clone, Copy)]
|
2022-11-22 13:09:21 -08:00
|
|
|
pub struct Secp256K1ScalarField;
|
|
|
|
|
|
|
|
impl Field for Secp256K1ScalarField {
|
|
|
|
type Scalar = Scalar;
|
|
|
|
|
|
|
|
type Serialization = [u8; 32];
|
|
|
|
|
|
|
|
fn zero() -> Self::Scalar {
|
|
|
|
Scalar::ZERO
|
|
|
|
}
|
|
|
|
|
|
|
|
fn one() -> Self::Scalar {
|
|
|
|
Scalar::ONE
|
|
|
|
}
|
|
|
|
|
2022-12-12 14:04:10 -08:00
|
|
|
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
|
2022-11-22 13:09:21 -08:00
|
|
|
// [`Scalar`]'s Eq/PartialEq does a constant-time comparison
|
|
|
|
if *scalar == <Self as Field>::zero() {
|
2022-12-12 14:04:10 -08:00
|
|
|
Err(FieldError::InvalidZeroScalar)
|
2022-11-22 13:09:21 -08:00
|
|
|
} else {
|
|
|
|
Ok(scalar.invert().unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
|
|
|
Scalar::random(rng)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
|
|
|
|
scalar.to_bytes().into()
|
|
|
|
}
|
|
|
|
|
2022-12-12 14:04:10 -08:00
|
|
|
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
|
2022-11-22 13:09:21 -08:00
|
|
|
let field_bytes: &k256::FieldBytes = buf.into();
|
|
|
|
match Scalar::from_repr(*field_bytes).into() {
|
|
|
|
Some(s) => Ok(s),
|
2022-12-12 14:04:10 -08:00
|
|
|
None => Err(FieldError::MalformedScalar),
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
|
|
|
|
let mut array = Self::serialize(scalar);
|
|
|
|
array.reverse();
|
|
|
|
array
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite group.
|
2023-04-13 16:57:00 -07:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
2022-11-22 13:09:21 -08:00
|
|
|
pub struct Secp256K1Group;
|
|
|
|
|
|
|
|
impl Group for Secp256K1Group {
|
|
|
|
type Field = Secp256K1ScalarField;
|
|
|
|
|
|
|
|
type Element = ProjectivePoint;
|
|
|
|
|
|
|
|
/// [SEC 1][1] serialization of a compressed point in secp256k1 takes 33 bytes
|
|
|
|
/// (1-byte prefix and 32 bytes for the coordinate).
|
|
|
|
///
|
|
|
|
/// Note that, in the SEC 1 spec, the identity is encoded as a single null byte;
|
|
|
|
/// but here we pad with zeroes. This is acceptable as the identity _should_ never
|
|
|
|
/// be serialized in FROST, else we error.
|
|
|
|
///
|
|
|
|
/// [1]: https://secg.org/sec1-v2.pdf
|
|
|
|
type Serialization = [u8; 33];
|
|
|
|
|
|
|
|
fn cofactor() -> <Self::Field as Field>::Scalar {
|
2023-03-08 06:32:35 -08:00
|
|
|
Scalar::ONE
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn identity() -> Self::Element {
|
|
|
|
ProjectivePoint::IDENTITY
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generator() -> Self::Element {
|
|
|
|
ProjectivePoint::GENERATOR
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize(element: &Self::Element) -> Self::Serialization {
|
|
|
|
let mut fixed_serialized = [0; 33];
|
|
|
|
let serialized_point = element.to_affine().to_encoded_point(true);
|
|
|
|
let serialized = serialized_point.as_bytes();
|
|
|
|
// Sanity check; either it takes all bytes or a single byte (identity).
|
|
|
|
assert!(serialized.len() == fixed_serialized.len() || serialized.len() == 1);
|
|
|
|
// Copy to the left of the buffer (i.e. pad the identity with zeroes).
|
2023-07-05 06:19:25 -07:00
|
|
|
// Note that identity elements shouldn't be serialized in FROST, but we
|
|
|
|
// do this padding so that this function doesn't have to return an error.
|
|
|
|
// If this encodes the identity, it will fail when deserializing.
|
2022-11-22 13:09:21 -08:00
|
|
|
{
|
|
|
|
let (left, _right) = fixed_serialized.split_at_mut(serialized.len());
|
|
|
|
left.copy_from_slice(serialized);
|
|
|
|
}
|
|
|
|
fixed_serialized
|
|
|
|
}
|
|
|
|
|
2022-12-12 14:04:10 -08:00
|
|
|
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
|
2022-11-22 13:09:21 -08:00
|
|
|
let encoded_point =
|
2022-12-12 14:04:10 -08:00
|
|
|
k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
match Option::<AffinePoint>::from(AffinePoint::from_encoded_point(&encoded_point)) {
|
|
|
|
Some(point) => {
|
|
|
|
if point.is_identity().into() {
|
|
|
|
// This is actually impossible since the identity is encoded a a single byte
|
|
|
|
// which will never happen since we receive a 33-byte buffer.
|
|
|
|
// We leave the check for consistency.
|
2022-12-12 14:04:10 -08:00
|
|
|
Err(GroupError::InvalidIdentityElement)
|
2022-11-22 13:09:21 -08:00
|
|
|
} else {
|
|
|
|
Ok(ProjectivePoint::from(point))
|
|
|
|
}
|
|
|
|
}
|
2022-12-12 14:04:10 -08:00
|
|
|
None => Err(GroupError::MalformedElement),
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 09:52:20 -08:00
|
|
|
fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] {
|
|
|
|
let mut h = Sha256::new();
|
|
|
|
for i in inputs {
|
|
|
|
h.update(i);
|
|
|
|
}
|
|
|
|
let mut output = [0u8; 32];
|
|
|
|
output.copy_from_slice(h.finalize().as_slice());
|
|
|
|
output
|
|
|
|
}
|
|
|
|
|
2023-03-08 06:32:35 -08:00
|
|
|
fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar {
|
|
|
|
let mut u = [Secp256K1ScalarField::zero()];
|
|
|
|
hash_to_field::<ExpandMsgXmd<Sha256>, Scalar>(&[msg], &[domain], &mut u)
|
|
|
|
.expect("should never return error according to error cases described in ExpandMsgXmd");
|
|
|
|
u[0]
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Context string from the ciphersuite in the [spec].
|
|
|
|
///
|
2023-08-14 03:39:15 -07:00
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-1
|
|
|
|
const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-v1";
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite.
|
2023-06-23 07:22:33 -07:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
|
|
#[cfg_attr(feature = "serde", serde(crate = "self::serde"))]
|
2022-11-22 13:09:21 -08:00
|
|
|
pub struct Secp256K1Sha256;
|
|
|
|
|
|
|
|
impl Ciphersuite for Secp256K1Sha256 {
|
2023-06-23 02:58:22 -07:00
|
|
|
const ID: &'static str = "FROST(secp256k1, SHA-256)";
|
|
|
|
|
2022-11-22 13:09:21 -08:00
|
|
|
type Group = Secp256K1Group;
|
|
|
|
|
|
|
|
type HashOutput = [u8; 32];
|
|
|
|
|
|
|
|
type SignatureSerialization = [u8; 65];
|
|
|
|
|
|
|
|
/// H1 for FROST(secp256k1, SHA-256)
|
|
|
|
///
|
2023-08-14 03:39:15 -07:00
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1
|
2022-11-22 13:09:21 -08:00
|
|
|
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
2023-03-08 06:32:35 -08:00
|
|
|
hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m)
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// H2 for FROST(secp256k1, SHA-256)
|
|
|
|
///
|
2023-08-14 03:39:15 -07:00
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.2
|
2022-11-22 13:09:21 -08:00
|
|
|
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
2023-03-08 06:32:35 -08:00
|
|
|
hash_to_scalar((CONTEXT_STRING.to_owned() + "chal").as_bytes(), m)
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// H3 for FROST(secp256k1, SHA-256)
|
|
|
|
///
|
2023-08-14 03:39:15 -07:00
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.3
|
2022-11-22 13:09:21 -08:00
|
|
|
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
2023-03-08 06:32:35 -08:00
|
|
|
hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m)
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// H4 for FROST(secp256k1, SHA-256)
|
|
|
|
///
|
2023-08-14 03:39:15 -07:00
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.4
|
2022-11-22 13:09:21 -08:00
|
|
|
fn H4(m: &[u8]) -> Self::HashOutput {
|
2022-12-08 09:52:20 -08:00
|
|
|
hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m])
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// H5 for FROST(secp256k1, SHA-256)
|
|
|
|
///
|
2023-08-14 03:39:15 -07:00
|
|
|
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.5
|
2022-11-22 13:09:21 -08:00
|
|
|
fn H5(m: &[u8]) -> Self::HashOutput {
|
2022-12-08 09:52:20 -08:00
|
|
|
hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m])
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// HDKG for FROST(secp256k1, SHA-256)
|
|
|
|
fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
2023-03-08 06:32:35 -08:00
|
|
|
Some(hash_to_scalar(
|
|
|
|
(CONTEXT_STRING.to_owned() + "dkg").as_bytes(),
|
|
|
|
m,
|
|
|
|
))
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
2023-06-30 08:45:46 -07:00
|
|
|
|
|
|
|
/// HID for FROST(secp256k1, SHA-256)
|
|
|
|
fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
|
|
|
Some(hash_to_scalar(
|
|
|
|
(CONTEXT_STRING.to_owned() + "id").as_bytes(),
|
|
|
|
m,
|
|
|
|
))
|
|
|
|
}
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
2022-11-24 16:36:34 -08:00
|
|
|
type S = Secp256K1Sha256;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// A FROST(secp256k1, SHA-256) participant identifier.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type Identifier = frost::Identifier<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// FROST(secp256k1, SHA-256) keys, key generation, key shares.
|
|
|
|
pub mod keys {
|
|
|
|
use super::*;
|
2023-03-23 15:24:33 -07:00
|
|
|
use std::collections::HashMap;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
2023-06-30 11:29:26 -07:00
|
|
|
/// The identifier list to use when generating key shares.
|
|
|
|
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, S>;
|
|
|
|
|
2022-11-22 13:09:21 -08:00
|
|
|
/// Allows all participants' keys to be generated using a central, trusted
|
|
|
|
/// dealer.
|
2023-05-19 02:54:52 -07:00
|
|
|
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
|
2022-11-22 13:09:21 -08:00
|
|
|
max_signers: u16,
|
|
|
|
min_signers: u16,
|
2023-06-30 11:29:26 -07:00
|
|
|
identifiers: IdentifierList,
|
2022-11-22 13:09:21 -08:00
|
|
|
mut rng: RNG,
|
2023-03-23 15:24:33 -07:00
|
|
|
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
2023-06-30 11:29:26 -07:00
|
|
|
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
|
2023-05-19 02:54:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<R: RngCore + CryptoRng>(
|
|
|
|
secret: &SigningKey,
|
|
|
|
max_signers: u16,
|
|
|
|
min_signers: u16,
|
2023-06-30 11:29:26 -07:00
|
|
|
identifiers: IdentifierList,
|
2023-05-19 02:54:52 -07:00
|
|
|
rng: &mut R,
|
|
|
|
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
2023-06-30 11:29:26 -07:00
|
|
|
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
|
2023-05-19 02:54:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Recompute the secret from t-of-n 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 whole 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<SigningKey, Error> {
|
|
|
|
frost::keys::reconstruct(secret_shares)
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Secret and public key material generated by a dealer performing
|
2023-05-19 02:54:52 -07:00
|
|
|
/// [`generate_with_dealer`].
|
2022-11-22 13:09:21 -08:00
|
|
|
///
|
|
|
|
/// # Security
|
|
|
|
///
|
|
|
|
/// To derive a FROST(secp256k1, SHA-256) keypair, the receiver of the [`SecretShare`] *must* call
|
|
|
|
/// .into(), which under the hood also performs validation.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type SecretShare = frost::keys::SecretShare<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
2023-06-21 09:51:50 -07:00
|
|
|
/// A secret scalar value representing a signer's share of the group secret.
|
|
|
|
pub type SigningShare = frost::keys::SigningShare<S>;
|
|
|
|
|
2023-06-22 02:26:56 -07:00
|
|
|
/// A public group element that represents a single signer's public verification share.
|
|
|
|
pub type VerifyingShare = frost::keys::VerifyingShare<S>;
|
|
|
|
|
2022-11-22 13:09:21 -08:00
|
|
|
/// A FROST(secp256k1, SHA-256) 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.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type KeyPackage = frost::keys::KeyPackage<S>;
|
2022-11-22 13:09:21 -08: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.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<S>;
|
|
|
|
|
2023-04-27 19:16:03 -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.
|
|
|
|
pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment<S>;
|
|
|
|
|
2022-12-15 01:38:48 -08:00
|
|
|
pub mod dkg;
|
2023-04-27 19:16:03 -07:00
|
|
|
pub mod repairable;
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// FROST(secp256k1, SHA-256) Round 1 functionality and types.
|
|
|
|
pub mod round1 {
|
2023-06-21 09:51:50 -07:00
|
|
|
use crate::keys::SigningShare;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// Comprised of FROST(secp256k1, SHA-256) hiding and binding nonces.
|
|
|
|
///
|
|
|
|
/// Note that [`SigningNonces`] must be used *only once* for a signing
|
|
|
|
/// operation; re-using nonces will result in leakage of a signer's long-lived
|
|
|
|
/// signing key.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type SigningNonces = frost::round1::SigningNonces<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// Published by each participant in the first round of the signing protocol.
|
|
|
|
///
|
|
|
|
/// This step can be batched if desired by the implementation. Each
|
|
|
|
/// SigningCommitment can be used for exactly *one* signature.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type SigningCommitments = frost::round1::SigningCommitments<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
2023-06-22 02:26:56 -07:00
|
|
|
/// A commitment to a signing nonce share.
|
|
|
|
pub type NonceCommitment = frost::round1::NonceCommitment<S>;
|
|
|
|
|
2022-11-22 13:09:21 -08:00
|
|
|
/// Performed once by each participant selected for the signing operation.
|
|
|
|
///
|
|
|
|
/// Generates the signing nonces and commitments to be used in the signing
|
|
|
|
/// operation.
|
2023-07-05 03:39:25 -07:00
|
|
|
pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
|
2022-11-22 13:09:21 -08:00
|
|
|
where
|
|
|
|
RNG: CryptoRng + RngCore,
|
|
|
|
{
|
2023-07-05 03:39:25 -07:00
|
|
|
frost::round1::commit::<S, RNG>(secret, rng)
|
2022-11-22 13:09:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generated by the coordinator of the signing operation and distributed to
|
|
|
|
/// each signing party.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type SigningPackage = frost::SigningPackage<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// FROST(secp256k1, SHA-256) Round 2 functionality and types, for signature share generation.
|
|
|
|
pub mod round2 {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// A FROST(secp256k1, SHA-256) participant's signature share, which the Coordinator will aggregate with all other signer's
|
|
|
|
/// shares into the joint signature.
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type SignatureShare = frost::round2::SignatureShare<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// Performed once by each participant selected for the signing operation.
|
|
|
|
///
|
|
|
|
/// Receives the message to be signed and a set of signing commitments and a set
|
|
|
|
/// of randomizing commitments to be used in that signing operation, including
|
|
|
|
/// that for this participant.
|
|
|
|
///
|
|
|
|
/// Assumes the participant has already determined which nonce corresponds with
|
|
|
|
/// the commitment that was assigned by the coordinator in the SigningPackage.
|
|
|
|
pub fn sign(
|
|
|
|
signing_package: &SigningPackage,
|
|
|
|
signer_nonces: &round1::SigningNonces,
|
|
|
|
key_package: &keys::KeyPackage,
|
|
|
|
) -> Result<SignatureShare, Error> {
|
|
|
|
frost::round2::sign(signing_package, signer_nonces, key_package)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A Schnorr signature on FROST(secp256k1, SHA-256).
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type Signature = frost_core::Signature<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// Verifies each FROST(secp256k1, SHA-256) participant's signature share, and if all are valid,
|
|
|
|
/// aggregates the shares into a signature to publish.
|
|
|
|
///
|
|
|
|
/// Resulting signature is compatible with verification of a plain Schnorr
|
|
|
|
/// signature.
|
|
|
|
///
|
|
|
|
/// This operation is performed by a coordinator that can communicate with all
|
|
|
|
/// the signing participants before publishing the final signature. The
|
|
|
|
/// coordinator can be one of the participants or a semi-trusted third party
|
|
|
|
/// (who is trusted to not perform denial of service attacks, but does not learn
|
|
|
|
/// any secret information). Note that because the coordinator is trusted to
|
|
|
|
/// report misbehaving parties in order to avoid publishing an invalid
|
|
|
|
/// signature, if the coordinator themselves is a signer and misbehaves, they
|
|
|
|
/// can avoid that step. However, at worst, this results in a denial of
|
|
|
|
/// service attack due to publishing an invalid signature.
|
|
|
|
pub fn aggregate(
|
2023-04-27 14:52:38 -07:00
|
|
|
signing_package: &SigningPackage,
|
2023-07-05 03:39:25 -07:00
|
|
|
signature_shares: &HashMap<Identifier, round2::SignatureShare>,
|
2022-11-22 13:09:21 -08:00
|
|
|
pubkeys: &keys::PublicKeyPackage,
|
|
|
|
) -> Result<Signature, Error> {
|
|
|
|
frost::aggregate(signing_package, signature_shares, pubkeys)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256).
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type SigningKey = frost_core::SigningKey<S>;
|
2022-11-22 13:09:21 -08:00
|
|
|
|
|
|
|
/// A valid verifying key for Schnorr signatures on FROST(secp256k1, SHA-256).
|
2022-11-24 16:36:34 -08:00
|
|
|
pub type VerifyingKey = frost_core::VerifyingKey<S>;
|