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:
Conrado Gouvea 2023-05-19 06:54:52 -03:00 committed by GitHub
parent c1c3f2a11d
commit 6bca7a3918
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 291 additions and 167 deletions

View File

@ -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)

View File

@ -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"

View File

@ -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>> =

View File

@ -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,

View File

@ -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::{

View File

@ -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 })
}

View File

@ -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
//

View File

@ -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 }
}

View File

@ -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>> =

View File

@ -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"];

View File

@ -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();

View File

@ -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"

View File

@ -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

View File

@ -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
///

View File

@ -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"

View File

@ -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

View File

@ -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
///

View File

@ -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"

View File

@ -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

View File

@ -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
///

View File

@ -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]

View File

@ -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>> =

View File

@ -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"

View File

@ -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

View File

@ -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
///

View File

@ -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"

View File

@ -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

View File

@ -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
///