add split_key; additional refactorings (#314)
* add split_key; additional refactorings * Update frost-core/src/frost/keys.rs Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com> * refactor reconstruct_key(); add tests for error cases * rename keys functions * leftover renames after sync with main --------- Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
parent
c1c3f2a11d
commit
6bca7a3918
|
@ -4,6 +4,15 @@ Entries are listed in reverse chronological order.
|
|||
|
||||
## Unreleased
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* add frost::keys::split()
|
||||
* rename reconstruct_secret() to reconstruct(), make it takes a slice instead
|
||||
of a Vector, make it return SigningKey, fix it to return Error instead of an
|
||||
error string
|
||||
* rename keygen_with_dealer() to generate_with_dealer()
|
||||
* change SigningKey::new() to take a reference instead of a value
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Implement Zeroize where needed or skip where not needed (fixes compiling error) (#301)
|
||||
|
|
|
@ -4,7 +4,7 @@ edition = "2021"
|
|||
# When releasing to crates.io:
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>", "Conrado Gouvea <conradoplg@gmail.com>"]
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
|
|
@ -92,14 +92,14 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
|
|||
|b, (max_signers, min_signers)| {
|
||||
let mut rng = rng.clone();
|
||||
b.iter(|| {
|
||||
frost::keys::keygen_with_dealer::<C, R>(*max_signers, *min_signers, &mut rng)
|
||||
frost::keys::generate_with_dealer::<C, R>(*max_signers, *min_signers, &mut rng)
|
||||
.unwrap();
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let (shares, pubkeys) =
|
||||
frost::keys::keygen_with_dealer::<C, R>(max_signers, min_signers, rng).unwrap();
|
||||
frost::keys::generate_with_dealer::<C, R>(max_signers, min_signers, rng).unwrap();
|
||||
|
||||
// Verifies the secret shares from the dealer
|
||||
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =
|
||||
|
|
|
@ -35,6 +35,9 @@ pub enum Error<C: Ciphersuite> {
|
|||
/// Duplicated shares provided
|
||||
#[error("Duplicated shares provided.")]
|
||||
DuplicatedShares,
|
||||
/// Incorrect number of shares.
|
||||
#[error("Incorrect number of shares.")]
|
||||
IncorrectNumberOfShares,
|
||||
/// Commitment equals the identity
|
||||
#[error("Commitment equals the identity.")]
|
||||
IdentityCommitment,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//! dealer. In the future, we will add support for key generation via a DKG,
|
||||
//! as specified in the FROST paper.
|
||||
//!
|
||||
//! Internally, keygen_with_dealer generates keys using Verifiable Secret
|
||||
//! Internally, generate_with_dealer generates keys using Verifiable Secret
|
||||
//! Sharing, where shares are generated using Shamir Secret Sharing.
|
||||
|
||||
use std::{
|
||||
|
|
|
@ -16,8 +16,7 @@ use rand_core::{CryptoRng, RngCore};
|
|||
use zeroize::{DefaultIsZeroes, Zeroize};
|
||||
|
||||
use crate::{
|
||||
frost::Identifier, random_nonzero, Ciphersuite, Element, Error, Field, Group, Scalar,
|
||||
VerifyingKey,
|
||||
frost::Identifier, Ciphersuite, Element, Error, Field, Group, Scalar, SigningKey, VerifyingKey,
|
||||
};
|
||||
|
||||
pub mod dkg;
|
||||
|
@ -33,92 +32,6 @@ pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SharedSecret<C: Ciphersuite>(pub(crate) Scalar<C>);
|
||||
|
||||
impl<C> SharedSecret<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(
|
||||
bytes: <<C::Group as Group>::Field as Field>::Serialization,
|
||||
) -> Result<Self, Error<C>> {
|
||||
<<C::Group as Group>::Field>::deserialize(&bytes)
|
||||
.map(|scalar| Self(scalar))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Serialize to bytes
|
||||
pub fn to_bytes(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
|
||||
<<C::Group as Group>::Field>::serialize(&self.0)
|
||||
}
|
||||
|
||||
/// Generates a new uniformly random secret value using the provided RNG.
|
||||
// TODO: should this only be behind test?
|
||||
pub fn random<R>(rng: &mut R) -> Self
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
Self(random_nonzero::<C, R>(rng))
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Debug for SharedSecret<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("SharedSecret")
|
||||
.field(&hex::encode(self.to_bytes()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Default for SharedSecret<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self(<<C::Group as Group>::Field>::zero())
|
||||
}
|
||||
}
|
||||
|
||||
// Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value
|
||||
impl<C> DefaultIsZeroes for SharedSecret<C> where C: Ciphersuite {}
|
||||
|
||||
impl<C> From<&SharedSecret<C>> for VerifyingKey<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(secret: &SharedSecret<C>) -> Self {
|
||||
let element = <C::Group>::generator() * secret.0;
|
||||
|
||||
VerifyingKey { element }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-impl"))]
|
||||
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>);
|
||||
|
@ -249,7 +162,7 @@ pub struct VerifiableSecretSharingCommitment<C: Ciphersuite>(
|
|||
);
|
||||
|
||||
/// A secret share generated by performing a (t-out-of-n) secret sharing scheme,
|
||||
/// generated by a dealer performing [`keygen_with_dealer`].
|
||||
/// 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.
|
||||
|
@ -324,7 +237,7 @@ where
|
|||
/// 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 keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
||||
pub fn generate_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
|
@ -332,12 +245,28 @@ pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
let mut bytes = [0; 64];
|
||||
rng.fill_bytes(&mut bytes);
|
||||
|
||||
let secret = SharedSecret::random(rng);
|
||||
let group_public = VerifyingKey::from(&secret);
|
||||
let key = SigningKey::new(rng);
|
||||
|
||||
split(&key, max_signers, min_signers, 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<C: Ciphersuite, R: RngCore + CryptoRng>(
|
||||
key: &SigningKey<C>,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
) -> Result<(HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
|
||||
let group_public = VerifyingKey::from(key);
|
||||
|
||||
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, rng);
|
||||
|
||||
let secret_shares = generate_secret_shares(&secret, max_signers, min_signers, coefficients)?;
|
||||
let secret_shares = generate_secret_shares(key, max_signers, min_signers, coefficients)?;
|
||||
let mut signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>> =
|
||||
HashMap::with_capacity(max_signers as usize);
|
||||
|
||||
|
@ -489,7 +418,7 @@ pub struct PublicKeyPackage<C: Ciphersuite> {
|
|||
///
|
||||
/// Returns an error if the parameters (max_signers, min_signers) are inconsistent.
|
||||
pub(crate) fn generate_secret_polynomial<C: Ciphersuite>(
|
||||
secret: &SharedSecret<C>,
|
||||
secret: &SigningKey<C>,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut coefficients: Vec<Scalar<C>>,
|
||||
|
@ -511,7 +440,7 @@ pub(crate) fn generate_secret_polynomial<C: Ciphersuite>(
|
|||
}
|
||||
|
||||
// Prepend the secret, which is the 0th coefficient
|
||||
coefficients.insert(0, secret.0);
|
||||
coefficients.insert(0, secret.scalar);
|
||||
|
||||
// Create the vector of commitments
|
||||
let commitment: Vec<_> = coefficients
|
||||
|
@ -544,7 +473,7 @@ pub(crate) fn generate_secret_polynomial<C: Ciphersuite>(
|
|||
///
|
||||
/// [`secret_share_shard`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C.1
|
||||
pub(crate) fn generate_secret_shares<C: Ciphersuite>(
|
||||
secret: &SharedSecret<C>,
|
||||
secret: &SigningKey<C>,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
coefficients: Vec<Scalar<C>>,
|
||||
|
@ -568,28 +497,42 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
|
|||
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>>,
|
||||
) -> Result<SharedSecret<C>, &'static str> {
|
||||
/// 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<C: Ciphersuite>(
|
||||
secret_shares: &[SecretShare<C>],
|
||||
) -> Result<SigningKey<C>, Error<C>> {
|
||||
if secret_shares.is_empty() {
|
||||
return Err("No secret_shares provided");
|
||||
return Err(Error::IncorrectNumberOfShares);
|
||||
}
|
||||
|
||||
let secret_share_map: HashMap<Identifier<C>, SecretShare<C>> = secret_shares
|
||||
.into_iter()
|
||||
.map(|share| (share.identifier, share))
|
||||
.collect();
|
||||
|
||||
let mut secret = <<C::Group as Group>::Field>::zero();
|
||||
|
||||
// Compute the Lagrange coefficients
|
||||
for (i, secret_share) in secret_share_map.clone() {
|
||||
for (i_idx, i, secret_share) in secret_shares
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, s)| (idx, s.identifier, s))
|
||||
{
|
||||
let mut num = <<C::Group as Group>::Field>::one();
|
||||
let mut den = <<C::Group as Group>::Field>::one();
|
||||
|
||||
for j in secret_share_map.clone().into_keys() {
|
||||
if j == i {
|
||||
for (j_idx, j) in secret_shares
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, s)| (idx, s.identifier))
|
||||
{
|
||||
if j_idx == i_idx {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -603,15 +546,15 @@ pub fn reconstruct_secret<C: Ciphersuite>(
|
|||
// If at this step, the denominator is zero in the scalar field, there must be a duplicate
|
||||
// secret share.
|
||||
if den == <<C::Group as Group>::Field>::zero() {
|
||||
return Err("Duplicate shares provided");
|
||||
return Err(Error::DuplicatedShares);
|
||||
}
|
||||
|
||||
// Save numerator * 1/denomintor in the scalar field
|
||||
// Save numerator * 1/denominator in the scalar field
|
||||
let lagrange_coefficient = num * <<C::Group as Group>::Field>::invert(&den).unwrap();
|
||||
|
||||
// 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(SharedSecret::from_bytes(<<C::Group as Group>::Field>::serialize(&secret)).unwrap())
|
||||
Ok(SigningKey { scalar: secret })
|
||||
}
|
||||
|
|
|
@ -36,13 +36,13 @@ use rand_core::{CryptoRng, RngCore};
|
|||
|
||||
use crate::{
|
||||
frost::Identifier, Challenge, Ciphersuite, Element, Error, Field, Group, Scalar, Signature,
|
||||
VerifyingKey,
|
||||
SigningKey, VerifyingKey,
|
||||
};
|
||||
|
||||
use super::{
|
||||
evaluate_polynomial, evaluate_vss, generate_coefficients, generate_secret_polynomial,
|
||||
KeyPackage, PublicKeyPackage, SecretShare, SharedSecret, SigningShare,
|
||||
VerifiableSecretSharingCommitment, VerifyingShare,
|
||||
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
|
||||
VerifyingShare,
|
||||
};
|
||||
|
||||
/// DKG Round 1 structures.
|
||||
|
@ -132,7 +132,7 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
min_signers: u16,
|
||||
mut rng: R,
|
||||
) -> Result<(round1::SecretPackage<C>, round1::Package<C>), Error<C>> {
|
||||
let secret: SharedSecret<C> = SharedSecret::random(&mut rng);
|
||||
let secret: SigningKey<C> = SigningKey::new(&mut rng);
|
||||
|
||||
// Round 1, Step 1
|
||||
//
|
||||
|
|
|
@ -18,8 +18,8 @@ where
|
|||
C: Ciphersuite,
|
||||
{
|
||||
/// Generate a new signing key.
|
||||
pub fn new<R: RngCore + CryptoRng>(mut rng: R) -> SigningKey<C> {
|
||||
let scalar = random_nonzero::<C, R>(&mut rng);
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> SigningKey<C> {
|
||||
let scalar = random_nonzero::<C, R>(rng);
|
||||
|
||||
SigningKey { scalar }
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
//! Ciphersuite-generic test functions.
|
||||
use std::{collections::HashMap, convert::TryFrom};
|
||||
|
||||
use crate::{frost, Signature, VerifyingKey};
|
||||
use crate::{frost, Error, Signature, VerifyingKey};
|
||||
use debugless_unwrap::DebuglessUnwrapErr;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::Ciphersuite;
|
||||
|
@ -13,7 +14,7 @@ pub mod vectors;
|
|||
|
||||
/// Test share generation with a Ciphersuite
|
||||
pub fn check_share_generation<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
|
||||
let secret = frost::keys::SharedSecret::<C>::random(&mut rng);
|
||||
let secret = crate::SigningKey::<C>::new(&mut rng);
|
||||
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
|
@ -30,9 +31,27 @@ pub fn check_share_generation<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R
|
|||
}
|
||||
|
||||
assert_eq!(
|
||||
frost::keys::reconstruct_secret::<C>(secret_shares).unwrap(),
|
||||
secret
|
||||
)
|
||||
frost::keys::reconstruct::<C>(&secret_shares)
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.as_ref(),
|
||||
secret.to_bytes().as_ref()
|
||||
);
|
||||
|
||||
// Test error cases
|
||||
|
||||
assert_eq!(
|
||||
frost::keys::reconstruct::<C>(&[]).debugless_unwrap_err(),
|
||||
Error::IncorrectNumberOfShares
|
||||
);
|
||||
|
||||
let mut secret_shares = secret_shares;
|
||||
secret_shares[0] = secret_shares[1].clone();
|
||||
|
||||
assert_eq!(
|
||||
frost::keys::reconstruct::<C>(&secret_shares).debugless_unwrap_err(),
|
||||
Error::DuplicatedShares
|
||||
);
|
||||
}
|
||||
|
||||
/// Test FROST signing with trusted dealer with a Ciphersuite.
|
||||
|
@ -46,7 +65,7 @@ pub fn check_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) =
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
|
||||
// Verifies the secret shares from the dealer
|
||||
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =
|
||||
|
|
|
@ -32,7 +32,7 @@ pub fn check_rts<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
|
|||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
|
||||
// Try to recover a share
|
||||
|
||||
|
@ -101,7 +101,7 @@ pub fn check_repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng
|
|||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
|
||||
// Signer 2 will lose their share
|
||||
// Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their share
|
||||
|
@ -161,7 +161,7 @@ pub fn check_repair_share_step_3<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
|
||||
let sigmas: &Value = &repair_share_helper_functions["sigma_generation"];
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@ use serde_json::Value;
|
|||
|
||||
use crate::{
|
||||
frost::{self, keys::*, round1::*, round2::*, *},
|
||||
Ciphersuite, Field, Group, Scalar, VerifyingKey,
|
||||
Ciphersuite, Field, Group, Scalar, SigningKey, VerifyingKey,
|
||||
};
|
||||
|
||||
/// Test vectors for a ciphersuite.
|
||||
pub struct TestVectors<C: Ciphersuite> {
|
||||
secret_key: SharedSecret<C>,
|
||||
secret_key: SigningKey<C>,
|
||||
group_public: VerifyingKey<C>,
|
||||
key_packages: HashMap<Identifier<C>, KeyPackage<C>>,
|
||||
message_bytes: Vec<u8>,
|
||||
|
@ -35,7 +35,7 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
|
|||
let secret_key_str = inputs["group_secret_key"].as_str().unwrap();
|
||||
let secret_key_bytes = hex::decode(secret_key_str).unwrap();
|
||||
let secret_key =
|
||||
SharedSecret::from_bytes(secret_key_bytes.try_into().debugless_unwrap()).unwrap();
|
||||
SigningKey::from_bytes(secret_key_bytes.try_into().debugless_unwrap()).unwrap();
|
||||
|
||||
let message = inputs["message"].as_str().unwrap();
|
||||
let message_bytes = hex::decode(message).unwrap();
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
# - Update html_root_url
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = [
|
||||
"Deirdre Connolly <durumcrustulum@gmail.com>",
|
||||
"Chelsea Komlo <me@chelseakomlo.com>",
|
||||
|
@ -23,7 +23,7 @@ features = ["nightly"]
|
|||
|
||||
[dependencies]
|
||||
curve25519-dalek = { version = "=4.0.0-rc.2", features = ["serde", "rand_core"] }
|
||||
frost-core = { path = "../frost-core", version = "0.2.0", features = ["test-impl"] }
|
||||
frost-core = { path = "../frost-core", version = "0.3.0", features = ["test-impl"] }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::collections::HashMap;
|
|||
let mut rng = thread_rng();
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
let (shares, pubkeys) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
|
||||
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||
// In practice, the KeyPackages must be sent to its respective participants
|
||||
|
|
|
@ -210,16 +210,46 @@ pub mod keys {
|
|||
|
||||
/// Allows all participants' keys to be generated using a central, trusted
|
||||
/// dealer.
|
||||
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: RNG,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut 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<R: RngCore + CryptoRng>(
|
||||
secret: &SigningKey,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::split(secret, max_signers, min_signers, rng)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Secret and public key material generated by a dealer performing
|
||||
/// [`keygen_with_dealer`].
|
||||
/// [`generate_with_dealer`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
|
|
|
@ -4,7 +4,7 @@ edition = "2021"
|
|||
# When releasing to crates.io:
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = [
|
||||
"Deirdre Connolly <durumcrustulum@gmail.com>",
|
||||
"Chelsea Komlo <me@chelseakomlo.com>",
|
||||
|
@ -22,7 +22,7 @@ features = ["nightly"]
|
|||
|
||||
[dependencies]
|
||||
ed448-goldilocks = { version = "0.9.0" }
|
||||
frost-core = { path = "../frost-core", version = "0.2.0", features = ["test-impl"] }
|
||||
frost-core = { path = "../frost-core", version = "0.3.0", features = ["test-impl"] }
|
||||
rand_core = "0.6"
|
||||
sha3 = "0.10.6"
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::collections::HashMap;
|
|||
let mut rng = thread_rng();
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
let (shares, pubkeys) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
|
||||
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||
// In practice, the KeyPackages must be sent to its respective participants
|
||||
|
|
|
@ -204,16 +204,46 @@ pub mod keys {
|
|||
|
||||
/// Allows all participants' keys to be generated using a central, trusted
|
||||
/// dealer.
|
||||
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: RNG,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut 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<R: RngCore + CryptoRng>(
|
||||
secret: &SigningKey,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::split(secret, max_signers, min_signers, rng)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Secret and public key material generated by a dealer performing
|
||||
/// [`keygen_with_dealer`].
|
||||
/// [`generate_with_dealer`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
# - Update html_root_url
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = [
|
||||
"Deirdre Connolly <durumcrustulum@gmail.com>",
|
||||
"Chelsea Komlo <me@chelseakomlo.com>",
|
||||
|
@ -23,7 +23,7 @@ features = ["nightly"]
|
|||
|
||||
[dependencies]
|
||||
p256 = { version = "0.13.0", features = ["hash2curve"] }
|
||||
frost-core = { path = "../frost-core", version = "0.2.0", features = ["test-impl"] }
|
||||
frost-core = { path = "../frost-core", version = "0.3.0", features = ["test-impl"] }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::collections::HashMap;
|
|||
let mut rng = thread_rng();
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
let (shares, pubkeys) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
|
||||
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||
// In practice, the KeyPackages must be sent to its respective participants
|
||||
|
|
|
@ -234,16 +234,46 @@ pub mod keys {
|
|||
|
||||
/// Allows all participants' keys to be generated using a central, trusted
|
||||
/// dealer.
|
||||
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: RNG,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut 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<R: RngCore + CryptoRng>(
|
||||
secret: &SigningKey,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::split(secret, max_signers, min_signers, rng)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Secret and public key material generated by a dealer performing
|
||||
/// [`keygen_with_dealer`].
|
||||
/// [`generate_with_dealer`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
# - Update html_root_url
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>",
|
||||
"Conrado Gouvea <conradoplg@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
@ -19,7 +19,7 @@ description = "Types and traits to support implementing a re-randomized variant
|
|||
features = ["nightly"]
|
||||
|
||||
[dependencies]
|
||||
frost-core = { path = "../frost-core", version = "0.2.0", features = ["internals"] }
|
||||
frost-core = { path = "../frost-core", version = "0.3.0", features = ["internals"] }
|
||||
rand_core = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
|
|||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) =
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
|
||||
|
||||
// Verifies the secret shares from the dealer
|
||||
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
# - Update html_root_url
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>", "Conrado Gouvea <conradoplg@gmail.com>"]
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -19,7 +19,7 @@ features = ["nightly"]
|
|||
|
||||
[dependencies]
|
||||
curve25519-dalek = { version = "=4.0.0-rc.2", features = ["serde", "rand_core"] }
|
||||
frost-core = { path = "../frost-core", version = "0.2.0", features = ["test-impl"] }
|
||||
frost-core = { path = "../frost-core", version = "0.3.0", features = ["test-impl"] }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::collections::HashMap;
|
|||
let mut rng = thread_rng();
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
let (shares, pubkeys) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
|
||||
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||
// In practice, the KeyPackages must be sent to its respective participants
|
||||
|
|
|
@ -198,16 +198,46 @@ pub mod keys {
|
|||
|
||||
/// Allows all participants' keys to be generated using a central, trusted
|
||||
/// dealer.
|
||||
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: RNG,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut 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<R: RngCore + CryptoRng>(
|
||||
secret: &SigningKey,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::split(secret, max_signers, min_signers, rng)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Secret and public key material generated by a dealer performing
|
||||
/// [`keygen_with_dealer`].
|
||||
/// [`generate_with_dealer`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
|
|
|
@ -4,7 +4,7 @@ edition = "2021"
|
|||
# When releasing to crates.io:
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = [
|
||||
"Deirdre Connolly <durumcrustulum@gmail.com>",
|
||||
"Chelsea Komlo <me@chelseakomlo.com>",
|
||||
|
@ -21,7 +21,7 @@ description = "A Schnorr signature scheme over the prime-order Ristretto group t
|
|||
features = ["nightly"]
|
||||
|
||||
[dependencies]
|
||||
frost-core = { path = "../frost-core", version = "0.2.0", features = ["test-impl"] }
|
||||
frost-core = { path = "../frost-core", version = "0.3.0", features = ["test-impl"] }
|
||||
k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"] }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::collections::HashMap;
|
|||
let mut rng = thread_rng();
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
let (shares, pubkeys) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||
|
||||
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||
// In practice, the KeyPackages must be sent to its respective participants
|
||||
|
|
|
@ -233,16 +233,46 @@ pub mod keys {
|
|||
|
||||
/// Allows all participants' keys to be generated using a central, trusted
|
||||
/// dealer.
|
||||
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: RNG,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
|
||||
frost::keys::generate_with_dealer(max_signers, min_signers, &mut 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<R: RngCore + CryptoRng>(
|
||||
secret: &SigningKey,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
rng: &mut R,
|
||||
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
|
||||
frost::keys::split(secret, max_signers, min_signers, rng)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Secret and public key material generated by a dealer performing
|
||||
/// [`keygen_with_dealer`].
|
||||
/// [`generate_with_dealer`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue