allow specifying identifiers in generate_with_dealer (#419)

This commit is contained in:
Conrado Gouvea 2023-06-30 15:29:26 -03:00 committed by GitHub
parent 78b5c44de0
commit 4e134f50d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 259 additions and 39 deletions

View File

@ -92,14 +92,24 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
|b, (max_signers, min_signers)| {
let mut rng = rng.clone();
b.iter(|| {
frost::keys::generate_with_dealer::<C, R>(*max_signers, *min_signers, &mut rng)
.unwrap();
frost::keys::generate_with_dealer::<C, R>(
*max_signers,
*min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)
.unwrap();
})
},
);
let (shares, pubkeys) =
frost::keys::generate_with_dealer::<C, R>(max_signers, min_signers, rng).unwrap();
let (shares, pubkeys) = frost::keys::generate_with_dealer::<C, R>(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
rng,
)
.unwrap();
// Verifies the secret shares from the dealer
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =

View File

@ -23,6 +23,9 @@ pub enum Error<C: Ciphersuite> {
/// This identifier is unserializable.
#[error("Malformed identifier is unserializable.")]
MalformedIdentifier,
/// This identifier is duplicated.
#[error("Duplicated identifier.")]
DuplicatedIdentifier,
/// The encoding of a signing key was malformed.
#[error("Malformed signing key encoding.")]
MalformedSigningKey,
@ -122,6 +125,7 @@ where
| Error::DKGNotSupported
| Error::FieldError(_)
| Error::GroupError(_)
| Error::DuplicatedIdentifier
| Error::InvalidCoefficient
| Error::IdentifierDerivationNotSupported => None,
}

View File

@ -2,7 +2,7 @@
#![allow(clippy::type_complexity)]
use std::{
collections::HashMap,
collections::{HashMap, HashSet},
convert::TryFrom,
default::Default,
fmt::{self, Debug},
@ -36,6 +36,13 @@ pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
.collect()
}
/// Return a list of default identifiers (1 to max_signers, inclusive).
pub(crate) fn default_identifiers<C: Ciphersuite>(max_signers: u16) -> Vec<Identifier<C>> {
(1..=max_signers)
.map(|i| Identifier::<C>::try_from(i).expect("nonzero"))
.collect::<Vec<_>>()
}
/// A secret scalar value representing a signer's share of the group secret.
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -377,6 +384,14 @@ where
}
}
/// The identifier list to use when generating key shares.
pub enum IdentifierList<'a, C: Ciphersuite> {
/// Use the default values (1 to max_signers, inclusive).
Default,
/// A user-provided list of identifiers.
Custom(&'a [Identifier<C>]),
}
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
///
@ -392,6 +407,7 @@ where
pub fn generate_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList<C>,
rng: &mut R,
) -> Result<(HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
let mut bytes = [0; 64];
@ -399,7 +415,7 @@ pub fn generate_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
let key = SigningKey::new(rng);
split(&key, max_signers, min_signers, rng)
split(&key, max_signers, min_signers, identifiers, rng)
}
/// Splits an existing key into FROST shares.
@ -412,13 +428,21 @@ pub fn split<C: Ciphersuite, R: RngCore + CryptoRng>(
key: &SigningKey<C>,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList<C>,
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(key, max_signers, min_signers, coefficients)?;
let default_identifiers = default_identifiers(max_signers);
let identifiers = match identifiers {
IdentifierList::Custom(identifiers) => identifiers,
IdentifierList::Default => &default_identifiers,
};
let secret_shares =
generate_secret_shares(key, max_signers, min_signers, coefficients, identifiers)?;
let mut signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>> =
HashMap::with_capacity(max_signers as usize);
@ -671,18 +695,23 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
max_signers: u16,
min_signers: u16,
coefficients: Vec<Scalar<C>>,
identifiers: &[Identifier<C>],
) -> Result<Vec<SecretShare<C>>, Error<C>> {
let mut secret_shares: Vec<SecretShare<C>> = Vec::with_capacity(max_signers as usize);
let (coefficients, commitment) =
generate_secret_polynomial(secret, max_signers, min_signers, coefficients)?;
for idx in 1..=max_signers {
let id = Identifier::<C>::try_from(idx)?;
let value = evaluate_polynomial(id, &coefficients);
let identifiers_set: HashSet<_> = identifiers.iter().collect();
if identifiers_set.len() != identifiers.len() {
return Err(Error::DuplicatedIdentifier);
}
for id in identifiers {
let value = evaluate_polynomial(*id, &coefficients);
secret_shares.push(SecretShare {
identifier: id,
identifier: *id,
value: SigningShare(value),
commitment: commitment.clone(),
ciphersuite: (),

View File

@ -20,9 +20,14 @@ pub fn check_share_generation<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R
let coefficients =
frost::keys::generate_coefficients::<C, _>(min_signers as usize - 1, &mut rng);
let secret_shares =
frost::keys::generate_secret_shares(&secret, max_signers, min_signers, coefficients)
.unwrap();
let secret_shares = frost::keys::generate_secret_shares(
&secret,
max_signers,
min_signers,
coefficients,
&frost::keys::default_identifiers(max_signers),
)
.unwrap();
for secret_share in secret_shares.iter() {
assert!(secret_share.verify().is_ok());
@ -62,8 +67,13 @@ pub fn check_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
let max_signers = 5;
let min_signers = 3;
let (shares, pubkeys) =
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
let (shares, pubkeys) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)
.unwrap();
// Verifies the secret shares from the dealer
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =
@ -92,8 +102,7 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
// Round 1: generating nonces and signing commitments for each participant
////////////////////////////////////////////////////////////////////////////
for participant_index in 1..(min_signers + 1) {
let participant_identifier = participant_index.try_into().expect("should be nonzero");
for participant_identifier in key_packages.keys().take(min_signers as usize).cloned() {
// Generate one (1) nonce and one SigningCommitments instance for each
// participant, up to _min_signers_.
let (nonces, commitments) = frost::round1::commit(
@ -346,6 +355,62 @@ where
check_sign(min_signers, key_packages, rng, pubkeys)
}
/// Test FROST signing with trusted dealer with a Ciphersuite, using specified
/// Identifiers.
pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + CryptoRng>(
mut rng: R,
) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
// Check error case first (repeated identifiers)
let identifiers: Vec<frost::Identifier<C>> = [1u16, 42, 100, 257, 42]
.into_iter()
.map(|i| i.try_into().unwrap())
.collect();
let max_signers = 5;
let min_signers = 3;
let err = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Custom(&identifiers),
&mut rng,
)
.debugless_unwrap_err();
assert_eq!(err, Error::DuplicatedIdentifier);
// Check correct case
let identifiers: Vec<frost::Identifier<C>> = [1u16, 42, 100, 257, 65535]
.into_iter()
.map(|i| i.try_into().unwrap())
.collect();
let max_signers = 5;
let min_signers = 3;
let (shares, pubkeys) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Custom(&identifiers),
&mut rng,
)
.unwrap();
// Check if the specified identifiers were used
for id in identifiers {
assert!(shares.contains_key(&id));
}
// Do regular testing to make sure it works
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =
HashMap::new();
for (k, v) in shares {
let key_package = frost::keys::KeyPackage::try_from(v).unwrap();
key_packages.insert(k, key_package);
}
check_sign(min_signers, key_packages, rng, pubkeys)
}
fn check_part2_error<C: Ciphersuite>(
round1_secret_package: frost::keys::dkg::round1::SecretPackage<C>,
mut round1_packages: Vec<frost::keys::dkg::round1::Package<C>>,

View File

@ -32,7 +32,13 @@ 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::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)
.unwrap();
// Try to recover a share
@ -101,7 +107,13 @@ 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::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&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 +173,13 @@ 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::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)
.unwrap();
let sigmas: &Value = &repair_share_helpers["sigma_generation"];

View File

@ -210,6 +210,7 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
max_signers as u16,
min_signers as u16,
share_polynomial_coefficients,
&default_identifiers(max_signers as u16),
)
.unwrap();
let secret_shares: HashMap<_, _> = secret_shares

View File

@ -17,7 +17,12 @@ use std::collections::HashMap;
let mut rng = thread_rng();
let max_signers = 5;
let min_signers = 3;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
# // ANCHOR_END: tkg_gen
// Verifies the secret shares from the dealer and store them in a HashMap.

View File

@ -220,14 +220,18 @@ pub mod keys {
use super::*;
/// The identifier list to use when generating key shares.
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, E>;
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
mut rng: RNG,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
}
/// Splits an existing key into FROST shares.
@ -240,9 +244,10 @@ pub mod keys {
secret: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(secret, max_signers, min_signers, rng)
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
}
/// Recompute the secret from t-of-n secret shares using Lagrange interpolation.

View File

@ -17,7 +17,12 @@ use std::collections::HashMap;
let mut rng = thread_rng();
let max_signers = 5;
let min_signers = 3;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
# // ANCHOR_END: tkg_gen
// Verifies the secret shares from the dealer and store them in a HashMap.

View File

@ -214,14 +214,18 @@ pub mod keys {
use super::*;
use std::collections::HashMap;
/// The identifier list to use when generating key shares.
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, E>;
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
mut rng: RNG,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
}
/// Splits an existing key into FROST shares.
@ -234,9 +238,10 @@ pub mod keys {
secret: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(secret, max_signers, min_signers, rng)
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
}
/// Recompute the secret from t-of-n secret shares using Lagrange interpolation.

View File

@ -48,3 +48,13 @@ fn check_sign_with_test_vectors() {
&VECTORS_BIG_IDENTIFIER,
);
}
#[test]
fn check_sign_with_dealer_and_identifiers() {
let rng = thread_rng();
frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
Ed448Shake256,
_,
>(rng);
}

View File

@ -17,7 +17,12 @@ use std::collections::HashMap;
let mut rng = thread_rng();
let max_signers = 5;
let min_signers = 3;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
# // ANCHOR_END: tkg_gen
// Verifies the secret shares from the dealer and store them in a HashMap.

View File

@ -240,21 +240,24 @@ type P = P256Sha256;
/// A FROST(P-256, SHA-256) participant identifier.
pub type Identifier = frost::Identifier<P>;
/// FROST(P-256, SHA-256) keys, key generation, key shares.
pub mod keys {
use std::collections::HashMap;
use super::*;
/// The identifier list to use when generating key shares.
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, P>;
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
mut rng: RNG,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
}
/// Splits an existing key into FROST shares.
@ -267,9 +270,10 @@ pub mod keys {
secret: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(secret, max_signers, min_signers, rng)
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
}
/// Recompute the secret from t-of-n secret shares using Lagrange interpolation.

View File

@ -46,3 +46,12 @@ fn check_sign_with_test_vectors() {
frost_core::tests::vectors::check_sign_with_test_vectors::<P256Sha256>(&VECTORS);
frost_core::tests::vectors::check_sign_with_test_vectors::<P256Sha256>(&VECTORS_BIG_IDENTIFIER);
}
#[test]
fn check_sign_with_dealer_and_identifiers() {
let rng = thread_rng();
frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<P256Sha256, _>(
rng,
);
}

View File

@ -18,8 +18,13 @@ 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::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
let (shares, pubkeys) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)
.unwrap();
// Verifies the secret shares from the dealer
let mut key_packages: HashMap<frost::Identifier<C>, frost::keys::KeyPackage<C>> =

View File

@ -17,7 +17,12 @@ use std::collections::HashMap;
let mut rng = thread_rng();
let max_signers = 5;
let min_signers = 3;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
# // ANCHOR_END: tkg_gen
// Verifies the secret shares from the dealer and store them in a HashMap.

View File

@ -208,14 +208,18 @@ pub mod keys {
use super::*;
use std::collections::HashMap;
/// The identifier list to use when generating key shares.
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, R>;
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
mut rng: RNG,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
}
/// Splits an existing key into FROST shares.
@ -228,9 +232,10 @@ pub mod keys {
secret: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(secret, max_signers, min_signers, rng)
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
}
/// Recompute the secret from t-of-n secret shares using Lagrange interpolation.

View File

@ -58,3 +58,13 @@ fn check_error_culprit() {
fn check_identifier_derivation() {
frost_core::tests::ciphersuite_generic::check_identifier_derivation::<Ristretto255Sha512>();
}
#[test]
fn check_sign_with_dealer_and_identifiers() {
let rng = thread_rng();
frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
Ristretto255Sha512,
_,
>(rng);
}

View File

@ -17,7 +17,12 @@ use std::collections::HashMap;
let mut rng = thread_rng();
let max_signers = 5;
let min_signers = 3;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)?;
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
# // ANCHOR_END: tkg_gen
// Verifies the secret shares from the dealer and store them in a HashMap.

View File

@ -246,14 +246,18 @@ pub mod keys {
use super::*;
use std::collections::HashMap;
/// The identifier list to use when generating key shares.
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, S>;
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
mut rng: RNG,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng)
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
}
/// Splits an existing key into FROST shares.
@ -266,9 +270,10 @@ pub mod keys {
secret: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(secret, max_signers, min_signers, rng)
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
}
/// Recompute the secret from t-of-n secret shares using Lagrange interpolation.

View File

@ -48,3 +48,13 @@ fn check_sign_with_test_vectors() {
&VECTORS_BIG_IDENTIFIER,
);
}
#[test]
fn check_sign_with_dealer_and_identifiers() {
let rng = thread_rng();
frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
Secp256K1Sha256,
_,
>(rng);
}