Improve DKG API; add DKG example (#173)
* improve DKG API * add DKG example; add ciphersuite-specific DKG functions
This commit is contained in:
parent
41eda1fa80
commit
1815280576
|
@ -1,4 +1,10 @@
|
|||
//! Distributed Key Generation functions and structures.
|
||||
//!
|
||||
//! The DKG module supports generating FROST key shares in a distributed manner,
|
||||
//! without a trusted dealer.
|
||||
//!
|
||||
//! For more details and an example, see the ciphersuite-specific crates, e.g.
|
||||
//! [`frost_ristretto255::keys::dkg`](../../../../frost_ristretto255/keys/dkg).
|
||||
|
||||
use std::{collections::HashMap, iter};
|
||||
|
||||
|
@ -258,7 +264,7 @@ fn compute_verifying_keys<C: Ciphersuite>(
|
|||
///
|
||||
/// It returns the [`KeyPackage`] that has the long-lived key share for the
|
||||
/// participant, and the [`PublicKeyPackage`]s that has public information
|
||||
/// about other participants; both of which are required to compute FROST
|
||||
/// about all participants; both of which are required to compute FROST
|
||||
/// signatures.
|
||||
pub fn keygen_part3<C: Ciphersuite>(
|
||||
round2_secret_package: &Round2SecretPackage<C>,
|
||||
|
@ -339,9 +345,12 @@ pub fn keygen_part3<C: Ciphersuite>(
|
|||
//
|
||||
// > Any participant can compute the public verification share of any other participant
|
||||
// > by calculating Y_i = ∏_{j=1}^n ∏_{k=0}^{t−1} φ_{jk}^{i^k mod q}.
|
||||
let others_verifying_keys =
|
||||
let mut all_verifying_keys =
|
||||
compute_verifying_keys(round2_packages, round1_packages_map, round2_secret_package)?;
|
||||
|
||||
// Add the participant's own public verification share for consistency
|
||||
all_verifying_keys.insert(round2_secret_package.identifier, verifying_key);
|
||||
|
||||
let key_package = KeyPackage {
|
||||
identifier: round2_secret_package.identifier,
|
||||
secret_share: signing_share,
|
||||
|
@ -349,7 +358,7 @@ pub fn keygen_part3<C: Ciphersuite>(
|
|||
group_public,
|
||||
};
|
||||
let public_key_package = PublicKeyPackage {
|
||||
signer_pubkeys: others_verifying_keys,
|
||||
signer_pubkeys: all_verifying_keys,
|
||||
group_public,
|
||||
};
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ where
|
|||
// In practice each participant will store it in their own environment.
|
||||
round1_secret_packages.insert(participant_identifier, secret_package);
|
||||
|
||||
// Send the round 1 package to all other participants. In this
|
||||
// "Send" the round 1 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
for receiver_participant_index in 1..=max_signers {
|
||||
|
@ -263,20 +263,19 @@ where
|
|||
let mut verifying_keys = HashMap::new();
|
||||
// The group public key, used by the signing test that follows.
|
||||
let mut group_public = None;
|
||||
// For each participant, store the set of verifying keys of the other
|
||||
// participants. This is used to check if the set is correct for all
|
||||
// participants.
|
||||
// For each participant, store the set of verifying keys they have computed.
|
||||
// This is used to check if the set is correct (the same) for all participants.
|
||||
// In practice, if there is a Coordinator, only they need to store the set.
|
||||
// If there is not, then all candidates must store their own sets.
|
||||
// The verifying keys are used to verify the signature shares produced
|
||||
// for each signature before being aggregated.
|
||||
let mut others_verifying_keys_by_participant = HashMap::new();
|
||||
let mut pubkey_packages_by_participant = HashMap::new();
|
||||
|
||||
// For each participant, perform the third part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (key_package, others_verifying_keys) = frost::keys::dkg::keygen_part3(
|
||||
let (key_package, pubkey_package_for_participant) = frost::keys::dkg::keygen_part3(
|
||||
&round2_secret_packages[&participant_identifier],
|
||||
&received_round1_packages[&participant_identifier],
|
||||
&received_round2_packages[&participant_identifier],
|
||||
|
@ -289,24 +288,13 @@ where
|
|||
}
|
||||
group_public = Some(key_package.group_public);
|
||||
key_packages.insert(participant_identifier, key_package);
|
||||
others_verifying_keys_by_participant.insert(participant_identifier, others_verifying_keys);
|
||||
pubkey_packages_by_participant
|
||||
.insert(participant_identifier, pubkey_package_for_participant);
|
||||
}
|
||||
|
||||
// Test if the set of verifying keys is correct for all participants.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let public_key_package = others_verifying_keys_by_participant
|
||||
.get(&participant_identifier)
|
||||
.unwrap();
|
||||
for (identifier, verifying_key) in &public_key_package.signer_pubkeys {
|
||||
assert_eq!(
|
||||
verifying_keys.get(identifier).unwrap(),
|
||||
verifying_key,
|
||||
"the verifying key that participant {:?} computed for participant {:?} is not correct",
|
||||
participant_identifier,
|
||||
identifier
|
||||
);
|
||||
}
|
||||
for verifying_keys_for_participant in pubkey_packages_by_participant.values() {
|
||||
assert!(verifying_keys_for_participant.signer_pubkeys == verifying_keys);
|
||||
}
|
||||
|
||||
let pubkeys = frost::keys::PublicKeyPackage {
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
# Distributed Key Generation (DKG)
|
||||
|
||||
The DKG module supports generating FROST key shares in a distributed manner,
|
||||
without a trusted dealer.
|
||||
|
||||
Before starting, each participant needs an unique identifier, which can be built from
|
||||
a `u16`. The process in which these identifiers are allocated is up to the application.
|
||||
|
||||
The distributed key generation process has 3 parts, with 2 communication rounds
|
||||
between them, in which each participant needs to send a "package" to every other
|
||||
participant. In the first round, each participant sends the same package
|
||||
(a [`Round1Package`]) to every other. In the second round, each receiver gets
|
||||
their own package (a [`Round2Package`]).
|
||||
|
||||
Between part 1 and 2, each participant needs to hold onto a [`Round1SecretPackage`]
|
||||
that MUST be kept secret. Between part 2 and 3, each participant needs to hold
|
||||
onto a [`Round2SecretPackage`].
|
||||
|
||||
After the third part, each participant will get a [`KeyPackage`] with their
|
||||
long-term secret share that must be kept secret, and a [`PublicKeyPackage`]
|
||||
that is public (and will be the same between all participants). With those
|
||||
they can proceed to sign messages with FROST.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use rand::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use frost_ed25519 as frost;
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, Round 1
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
|
||||
// Keep track of each participant's round 1 secret package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut round1_secret_packages = HashMap::new();
|
||||
|
||||
// Keep track of all round 1 packages sent to the given participant.
|
||||
// This is used to simulate the broadcast; in practice the packages
|
||||
// will be sent through some communication channel.
|
||||
let mut received_round1_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the first part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (secret_package, round1_package) = frost::keys::dkg::keygen_part1(
|
||||
participant_identifier,
|
||||
max_signers,
|
||||
min_signers,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the participant's secret package for later use.
|
||||
// In practice each participant will store it in their own environment.
|
||||
round1_secret_packages.insert(participant_identifier, secret_package);
|
||||
|
||||
// "Send" the round 1 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
for receiver_participant_index in 1..=max_signers {
|
||||
if receiver_participant_index == participant_index {
|
||||
continue;
|
||||
}
|
||||
let receiver_participant_identifier: frost::Identifier = receiver_participant_index
|
||||
.try_into()
|
||||
.expect("should be nonzero");
|
||||
received_round1_packages
|
||||
.entry(receiver_participant_identifier)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(round1_package.clone());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, Round 2
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Keep track of each participant's round 2 secret package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut round2_secret_packages = HashMap::new();
|
||||
|
||||
// Keep track of all round 2 packages sent to the given participant.
|
||||
// This is used to simulate the broadcast; in practice the packages
|
||||
// will be sent through some communication channel.
|
||||
let mut received_round2_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the second part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (round2_secret_package, round2_packages) = frost::keys::dkg::keygen_part2(
|
||||
round1_secret_packages
|
||||
.remove(&participant_identifier)
|
||||
.unwrap(),
|
||||
received_round1_packages
|
||||
.get(&participant_identifier)
|
||||
.unwrap(),
|
||||
)
|
||||
.expect("should work");
|
||||
|
||||
// Store the participant's secret package for later use.
|
||||
// In practice each participant will store it in their own environment.
|
||||
round2_secret_packages.insert(participant_identifier, round2_secret_package);
|
||||
|
||||
// "Send" the round 2 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
// Note that, in contrast to the previous part, here each other participant
|
||||
// gets its own specific package.
|
||||
for round2_package in round2_packages {
|
||||
received_round2_packages
|
||||
.entry(round2_package.receiver_identifier)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(round2_package);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, final computation
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Keep track of each participant's long-lived key package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut key_packages = HashMap::new();
|
||||
|
||||
// Keep track of each participant's public key package.
|
||||
// In practice, if there is a Coordinator, only they need to store the set.
|
||||
// If there is not, then all candidates must store their own sets.
|
||||
// All participants will have the same exact public key package.
|
||||
let mut pubkey_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the third part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (key_package, pubkey_package_for_participant) = frost::keys::dkg::keygen_part3(
|
||||
&round2_secret_packages[&participant_identifier],
|
||||
&received_round1_packages[&participant_identifier],
|
||||
&received_round2_packages[&participant_identifier],
|
||||
)
|
||||
.unwrap();
|
||||
key_packages.insert(participant_identifier, key_package);
|
||||
pubkey_packages.insert(participant_identifier, pubkey_package_for_participant);
|
||||
}
|
||||
|
||||
// With its own key package and the pubkey package, each participant can now proceed
|
||||
// to sign with FROST.
|
||||
```
|
|
@ -201,6 +201,86 @@ pub mod keys {
|
|||
///
|
||||
/// Used for verification purposes before publishing a signature.
|
||||
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<E>;
|
||||
|
||||
pub mod dkg {
|
||||
#![doc = include_str!("../dkg.md")]
|
||||
use super::*;
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This package MUST NOT be sent to other participants!
|
||||
pub type Round1SecretPackage = frost::keys::dkg::Round1SecretPackage<E>;
|
||||
|
||||
/// The package that must be broadcast by each participant to all other participants
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
pub type Round1Package = frost::keys::dkg::Round1Package<E>;
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
/// between the second and third parts of the DKG protocol (round 2).
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This package MUST NOT be sent to other participants!
|
||||
pub type Round2SecretPackage = frost::keys::dkg::Round2SecretPackage<E>;
|
||||
|
||||
/// A package that must be sent by each participant to some other participants
|
||||
/// in Round 2 of the DKG protocol. Note that there is one specific package
|
||||
/// for each specific recipient, in contrast to Round 1.
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// The package must be sent on an *confidential* and *authenticated* channel.
|
||||
pub type Round2Package = frost::keys::dkg::Round2Package<E>;
|
||||
|
||||
/// Performs the first part of the distributed key generation protocol
|
||||
/// for the given participant.
|
||||
///
|
||||
/// It returns the [`Round1SecretPackage`] that must be kept in memory
|
||||
/// by the participant for the other steps, and the [`Round1Package`] that
|
||||
/// must be sent to other participants.
|
||||
pub fn keygen_part1<R: RngCore + CryptoRng>(
|
||||
identifier: Identifier,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: R,
|
||||
) -> Result<(Round1SecretPackage, Round1Package), Error> {
|
||||
frost::keys::dkg::keygen_part1(identifier, max_signers, min_signers, &mut rng)
|
||||
}
|
||||
|
||||
/// Performs the second part of the distributed key generation protocol
|
||||
/// for the participant holding the given [`Round1SecretPackage`],
|
||||
/// given the received [`Round1Package`]s received from the other participants.
|
||||
///
|
||||
/// It returns the [`Round2SecretPackage`] that must be kept in memory
|
||||
/// by the participant for the final step, and the [`Round2Package`]s that
|
||||
/// must be sent to other participants.
|
||||
pub fn keygen_part2(
|
||||
secret_package: Round1SecretPackage,
|
||||
round1_packages: &[Round1Package],
|
||||
) -> Result<(Round2SecretPackage, Vec<Round2Package>), Error> {
|
||||
frost::keys::dkg::keygen_part2(secret_package, round1_packages)
|
||||
}
|
||||
|
||||
/// Performs the third and final part of the distributed key generation protocol
|
||||
/// for the participant holding the given [`Round2SecretPackage`],
|
||||
/// given the received [`Round1Package`]s and [`Round2Package`]s received from
|
||||
/// the other participants.
|
||||
///
|
||||
/// It returns the [`KeyPackage`] that has the long-lived key share for the
|
||||
/// participant, and the [`PublicKeyPackage`]s that has public information
|
||||
/// about all participants; both of which are required to compute FROST
|
||||
/// signatures.
|
||||
pub fn keygen_part3(
|
||||
round2_secret_package: &Round2SecretPackage,
|
||||
round1_packages: &[Round1Package],
|
||||
round2_packages: &[Round2Package],
|
||||
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
|
||||
frost::keys::dkg::keygen_part3(round2_secret_package, round1_packages, round2_packages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FROST(Ed25519, SHA-512) Round 1 functionality and types.
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
# Distributed Key Generation (DKG)
|
||||
|
||||
The DKG module supports generating FROST key shares in a distributed manner,
|
||||
without a trusted dealer.
|
||||
|
||||
Before starting, each participant needs an unique identifier, which can be built from
|
||||
a `u16`. The process in which these identifiers are allocated is up to the application.
|
||||
|
||||
The distributed key generation process has 3 parts, with 2 communication rounds
|
||||
between them, in which each participant needs to send a "package" to every other
|
||||
participant. In the first round, each participant sends the same package
|
||||
(a [`Round1Package`]) to every other. In the second round, each receiver gets
|
||||
their own package (a [`Round2Package`]).
|
||||
|
||||
Between part 1 and 2, each participant needs to hold onto a [`Round1SecretPackage`]
|
||||
that MUST be kept secret. Between part 2 and 3, each participant needs to hold
|
||||
onto a [`Round2SecretPackage`].
|
||||
|
||||
After the third part, each participant will get a [`KeyPackage`] with their
|
||||
long-term secret share that must be kept secret, and a [`PublicKeyPackage`]
|
||||
that is public (and will be the same between all participants). With those
|
||||
they can proceed to sign messages with FROST.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use rand::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use frost_p256 as frost;
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, Round 1
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
|
||||
// Keep track of each participant's round 1 secret package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut round1_secret_packages = HashMap::new();
|
||||
|
||||
// Keep track of all round 1 packages sent to the given participant.
|
||||
// This is used to simulate the broadcast; in practice the packages
|
||||
// will be sent through some communication channel.
|
||||
let mut received_round1_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the first part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (secret_package, round1_package) = frost::keys::dkg::keygen_part1(
|
||||
participant_identifier,
|
||||
max_signers,
|
||||
min_signers,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Store the participant's secret package for later use.
|
||||
// In practice each participant will store it in their own environment.
|
||||
round1_secret_packages.insert(participant_identifier, secret_package);
|
||||
|
||||
// "Send" the round 1 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
for receiver_participant_index in 1..=max_signers {
|
||||
if receiver_participant_index == participant_index {
|
||||
continue;
|
||||
}
|
||||
let receiver_participant_identifier: frost::Identifier = receiver_participant_index
|
||||
.try_into()
|
||||
.expect("should be nonzero");
|
||||
received_round1_packages
|
||||
.entry(receiver_participant_identifier)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(round1_package.clone());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, Round 2
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Keep track of each participant's round 2 secret package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut round2_secret_packages = HashMap::new();
|
||||
|
||||
// Keep track of all round 2 packages sent to the given participant.
|
||||
// This is used to simulate the broadcast; in practice the packages
|
||||
// will be sent through some communication channel.
|
||||
let mut received_round2_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the second part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (round2_secret_package, round2_packages) = frost::keys::dkg::keygen_part2(
|
||||
round1_secret_packages
|
||||
.remove(&participant_identifier)
|
||||
.unwrap(),
|
||||
received_round1_packages
|
||||
.get(&participant_identifier)
|
||||
.unwrap(),
|
||||
)
|
||||
.expect("should work");
|
||||
|
||||
// Store the participant's secret package for later use.
|
||||
// In practice each participant will store it in their own environment.
|
||||
round2_secret_packages.insert(participant_identifier, round2_secret_package);
|
||||
|
||||
// "Send" the round 2 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
// Note that, in contrast to the previous part, here each other participant
|
||||
// gets its own specific package.
|
||||
for round2_package in round2_packages {
|
||||
received_round2_packages
|
||||
.entry(round2_package.receiver_identifier)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(round2_package);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, final computation
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Keep track of each participant's long-lived key package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut key_packages = HashMap::new();
|
||||
|
||||
// Keep track of each participant's public key package.
|
||||
// In practice, if there is a Coordinator, only they need to store the set.
|
||||
// If there is not, then all candidates must store their own sets.
|
||||
// All participants will have the same exact public key package.
|
||||
let mut pubkey_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the third part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (key_package, pubkey_package_for_participant) = frost::keys::dkg::keygen_part3(
|
||||
&round2_secret_packages[&participant_identifier],
|
||||
&received_round1_packages[&participant_identifier],
|
||||
&received_round2_packages[&participant_identifier],
|
||||
)
|
||||
.unwrap();
|
||||
key_packages.insert(participant_identifier, key_package);
|
||||
pubkey_packages.insert(participant_identifier, pubkey_package_for_participant);
|
||||
}
|
||||
|
||||
// With its own key package and the pubkey package, each participant can now proceed
|
||||
// to sign with FROST.
|
||||
```
|
|
@ -268,6 +268,86 @@ pub mod keys {
|
|||
///
|
||||
/// Used for verification purposes before publishing a signature.
|
||||
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<P>;
|
||||
|
||||
pub mod dkg {
|
||||
#![doc = include_str!("../dkg.md")]
|
||||
use super::*;
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This package MUST NOT be sent to other participants!
|
||||
pub type Round1SecretPackage = frost::keys::dkg::Round1SecretPackage<P>;
|
||||
|
||||
/// The package that must be broadcast by each participant to all other participants
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
pub type Round1Package = frost::keys::dkg::Round1Package<P>;
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
/// between the second and third parts of the DKG protocol (round 2).
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This package MUST NOT be sent to other participants!
|
||||
pub type Round2SecretPackage = frost::keys::dkg::Round2SecretPackage<P>;
|
||||
|
||||
/// A package that must be sent by each participant to some other participants
|
||||
/// in Round 2 of the DKG protocol. Note that there is one specific package
|
||||
/// for each specific recipient, in contrast to Round 1.
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// The package must be sent on an *confidential* and *authenticated* channel.
|
||||
pub type Round2Package = frost::keys::dkg::Round2Package<P>;
|
||||
|
||||
/// Performs the first part of the distributed key generation protocol
|
||||
/// for the given participant.
|
||||
///
|
||||
/// It returns the [`Round1SecretPackage`] that must be kept in memory
|
||||
/// by the participant for the other steps, and the [`Round1Package`] that
|
||||
/// must be sent to other participants.
|
||||
pub fn keygen_part1<R: RngCore + CryptoRng>(
|
||||
identifier: Identifier,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: R,
|
||||
) -> Result<(Round1SecretPackage, Round1Package), Error> {
|
||||
frost::keys::dkg::keygen_part1(identifier, max_signers, min_signers, &mut rng)
|
||||
}
|
||||
|
||||
/// Performs the second part of the distributed key generation protocol
|
||||
/// for the participant holding the given [`Round1SecretPackage`],
|
||||
/// given the received [`Round1Package`]s received from the other participants.
|
||||
///
|
||||
/// It returns the [`Round2SecretPackage`] that must be kept in memory
|
||||
/// by the participant for the final step, and the [`Round2Package`]s that
|
||||
/// must be sent to other participants.
|
||||
pub fn keygen_part2(
|
||||
secret_package: Round1SecretPackage,
|
||||
round1_packages: &[Round1Package],
|
||||
) -> Result<(Round2SecretPackage, Vec<Round2Package>), Error> {
|
||||
frost::keys::dkg::keygen_part2(secret_package, round1_packages)
|
||||
}
|
||||
|
||||
/// Performs the third and final part of the distributed key generation protocol
|
||||
/// for the participant holding the given [`Round2SecretPackage`],
|
||||
/// given the received [`Round1Package`]s and [`Round2Package`]s received from
|
||||
/// the other participants.
|
||||
///
|
||||
/// It returns the [`KeyPackage`] that has the long-lived key share for the
|
||||
/// participant, and the [`PublicKeyPackage`]s that has public information
|
||||
/// about all participants; both of which are required to compute FROST
|
||||
/// signatures.
|
||||
pub fn keygen_part3(
|
||||
round2_secret_package: &Round2SecretPackage,
|
||||
round1_packages: &[Round1Package],
|
||||
round2_packages: &[Round2Package],
|
||||
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
|
||||
frost::keys::dkg::keygen_part3(round2_secret_package, round1_packages, round2_packages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FROST(P-256, SHA-256) Round 1 functionality and types.
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
# Distributed Key Generation (DKG)
|
||||
|
||||
The DKG module supports generating FROST key shares in a distributed manner,
|
||||
without a trusted dealer.
|
||||
|
||||
Before starting, each participant needs an unique identifier, which can be built from
|
||||
a `u16`. The process in which these identifiers are allocated is up to the application.
|
||||
|
||||
The distributed key generation process has 3 parts, with 2 communication rounds
|
||||
between them, in which each participant needs to send a "package" to every other
|
||||
participant. In the first round, each participant sends the same package
|
||||
(a [`Round1Package`]) to every other. In the second round, each receiver gets
|
||||
their own package (a [`Round2Package`]).
|
||||
|
||||
Between part 1 and 2, each participant needs to hold onto a [`Round1SecretPackage`]
|
||||
that MUST be kept secret. Between part 2 and 3, each participant needs to hold
|
||||
onto a [`Round2SecretPackage`].
|
||||
|
||||
After the third part, each participant will get a [`KeyPackage`] with their
|
||||
long-term secret share that must be kept secret, and a [`PublicKeyPackage`]
|
||||
that is public (and will be the same between all participants). With those
|
||||
they can proceed to sign messages with FROST.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use rand::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use frost_ristretto255 as frost;
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, Round 1
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let max_signers = 5;
|
||||
let min_signers = 3;
|
||||
|
||||
// Keep track of each participant's round 1 secret package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut round1_secret_packages = HashMap::new();
|
||||
|
||||
// Keep track of all round 1 packages sent to the given participant.
|
||||
// This is used to simulate the broadcast; in practice the packages
|
||||
// will be sent through some communication channel.
|
||||
let mut received_round1_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the first part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (secret_package, round1_package) = frost::keys::dkg::keygen_part1(
|
||||
participant_identifier,
|
||||
max_signers,
|
||||
min_signers,
|
||||
&mut rng,
|
||||
)?;
|
||||
|
||||
// Store the participant's secret package for later use.
|
||||
// In practice each participant will store it in their own environment.
|
||||
round1_secret_packages.insert(participant_identifier, secret_package);
|
||||
|
||||
// "Send" the round 1 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
for receiver_participant_index in 1..=max_signers {
|
||||
if receiver_participant_index == participant_index {
|
||||
continue;
|
||||
}
|
||||
let receiver_participant_identifier: frost::Identifier = receiver_participant_index
|
||||
.try_into()
|
||||
.expect("should be nonzero");
|
||||
received_round1_packages
|
||||
.entry(receiver_participant_identifier)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(round1_package.clone());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, Round 2
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Keep track of each participant's round 2 secret package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut round2_secret_packages = HashMap::new();
|
||||
|
||||
// Keep track of all round 2 packages sent to the given participant.
|
||||
// This is used to simulate the broadcast; in practice the packages
|
||||
// will be sent through some communication channel.
|
||||
let mut received_round2_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the second part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (round2_secret_package, round2_packages) = frost::keys::dkg::keygen_part2(
|
||||
round1_secret_packages
|
||||
.remove(&participant_identifier)
|
||||
.unwrap(),
|
||||
&received_round1_packages[&participant_identifier],
|
||||
)?;
|
||||
|
||||
// Store the participant's secret package for later use.
|
||||
// In practice each participant will store it in their own environment.
|
||||
round2_secret_packages.insert(participant_identifier, round2_secret_package);
|
||||
|
||||
// "Send" the round 2 package to all other participants. In this
|
||||
// test this is simulated using a HashMap; in practice this will be
|
||||
// sent through some communication channel.
|
||||
// Note that, in contrast to the previous part, here each other participant
|
||||
// gets its own specific package.
|
||||
for round2_package in round2_packages {
|
||||
received_round2_packages
|
||||
.entry(round2_package.receiver_identifier)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(round2_package);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Key generation, final computation
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Keep track of each participant's long-lived key package.
|
||||
// In practice each participant will keep its copy; no one
|
||||
// will have all the participant's packages.
|
||||
let mut key_packages = HashMap::new();
|
||||
|
||||
// Keep track of each participant's public key package.
|
||||
// In practice, if there is a Coordinator, only they need to store the set.
|
||||
// If there is not, then all candidates must store their own sets.
|
||||
// All participants will have the same exact public key package.
|
||||
let mut pubkey_packages = HashMap::new();
|
||||
|
||||
// For each participant, perform the third part of the DKG protocol.
|
||||
// In practice, each participant will perform this on their own environments.
|
||||
for participant_index in 1..=max_signers {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
let (key_package, pubkey_package_for_participant) = frost::keys::dkg::keygen_part3(
|
||||
&round2_secret_packages[&participant_identifier],
|
||||
&received_round1_packages[&participant_identifier],
|
||||
&received_round2_packages[&participant_identifier],
|
||||
)?;
|
||||
key_packages.insert(participant_identifier, key_package);
|
||||
pubkey_packages.insert(participant_identifier, pubkey_package_for_participant);
|
||||
}
|
||||
|
||||
// With its own key package and the pubkey package, each participant can now proceed
|
||||
// to sign with FROST.
|
||||
# Ok::<(), frost::Error>(())
|
||||
```
|
|
@ -246,6 +246,86 @@ pub mod keys {
|
|||
///
|
||||
/// Used for verification purposes before publishing a signature.
|
||||
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<R>;
|
||||
|
||||
pub mod dkg {
|
||||
#![doc = include_str!("../dkg.md")]
|
||||
use super::*;
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This package MUST NOT be sent to other participants!
|
||||
pub type Round1SecretPackage = frost::keys::dkg::Round1SecretPackage<R>;
|
||||
|
||||
/// The package that must be broadcast by each participant to all other participants
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
pub type Round1Package = frost::keys::dkg::Round1Package<R>;
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
/// between the second and third parts of the DKG protocol (round 2).
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This package MUST NOT be sent to other participants!
|
||||
pub type Round2SecretPackage = frost::keys::dkg::Round2SecretPackage<R>;
|
||||
|
||||
/// A package that must be sent by each participant to some other participants
|
||||
/// in Round 2 of the DKG protocol. Note that there is one specific package
|
||||
/// for each specific recipient, in contrast to Round 1.
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// The package must be sent on an *confidential* and *authenticated* channel.
|
||||
pub type Round2Package = frost::keys::dkg::Round2Package<R>;
|
||||
|
||||
/// Performs the first part of the distributed key generation protocol
|
||||
/// for the given participant.
|
||||
///
|
||||
/// It returns the [`Round1SecretPackage`] that must be kept in memory
|
||||
/// by the participant for the other steps, and the [`Round1Package`] that
|
||||
/// must be sent to other participants.
|
||||
pub fn keygen_part1<R: RngCore + CryptoRng>(
|
||||
identifier: Identifier,
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: R,
|
||||
) -> Result<(Round1SecretPackage, Round1Package), Error> {
|
||||
frost::keys::dkg::keygen_part1(identifier, max_signers, min_signers, &mut rng)
|
||||
}
|
||||
|
||||
/// Performs the second part of the distributed key generation protocol
|
||||
/// for the participant holding the given [`Round1SecretPackage`],
|
||||
/// given the received [`Round1Package`]s received from the other participants.
|
||||
///
|
||||
/// It returns the [`Round2SecretPackage`] that must be kept in memory
|
||||
/// by the participant for the final step, and the [`Round2Package`]s that
|
||||
/// must be sent to other participants.
|
||||
pub fn keygen_part2(
|
||||
secret_package: Round1SecretPackage,
|
||||
round1_packages: &[Round1Package],
|
||||
) -> Result<(Round2SecretPackage, Vec<Round2Package>), Error> {
|
||||
frost::keys::dkg::keygen_part2(secret_package, round1_packages)
|
||||
}
|
||||
|
||||
/// Performs the third and final part of the distributed key generation protocol
|
||||
/// for the participant holding the given [`Round2SecretPackage`],
|
||||
/// given the received [`Round1Package`]s and [`Round2Package`]s received from
|
||||
/// the other participants.
|
||||
///
|
||||
/// It returns the [`KeyPackage`] that has the long-lived key share for the
|
||||
/// participant, and the [`PublicKeyPackage`]s that has public information
|
||||
/// about all participants; both of which are required to compute FROST
|
||||
/// signatures.
|
||||
pub fn keygen_part3(
|
||||
round2_secret_package: &Round2SecretPackage,
|
||||
round1_packages: &[Round1Package],
|
||||
round2_packages: &[Round2Package],
|
||||
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
|
||||
frost::keys::dkg::keygen_part3(round2_secret_package, round1_packages, round2_packages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FROST(ristretto255, SHA-512) Round 1 functionality and types.
|
||||
|
|
|
@ -132,7 +132,7 @@ fn main() {
|
|||
);
|
||||
let old_suite_names_doc = &["FROST(ristretto255, SHA-512)"];
|
||||
|
||||
let readme_filename = "frost-ristretto255/README.md";
|
||||
let original_basename = "frost-ristretto255/";
|
||||
let original_strings = &["frost_ristretto255", "Ristretto group"];
|
||||
|
||||
// To add a new ciphersuite, just copy this call and replace the required strings.
|
||||
|
@ -144,12 +144,14 @@ fn main() {
|
|||
old_suite_names_doc,
|
||||
&["FROST(P-256, SHA-256)"],
|
||||
);
|
||||
copy_and_replace(
|
||||
readme_filename,
|
||||
"frost-p256/README.md",
|
||||
original_strings,
|
||||
&["frost_p256", "P-256 curve"],
|
||||
);
|
||||
for filename in ["README.md", "dkg.md"] {
|
||||
copy_and_replace(
|
||||
format!("{}/{}", original_basename, filename).as_str(),
|
||||
format!("frost-p256/{}", filename).as_str(),
|
||||
original_strings,
|
||||
&["frost_p256", "P-256 curve"],
|
||||
);
|
||||
}
|
||||
|
||||
write_docs(
|
||||
&docs,
|
||||
|
@ -158,10 +160,12 @@ fn main() {
|
|||
old_suite_names_doc,
|
||||
&["FROST(Ed25519, SHA-512)"],
|
||||
);
|
||||
copy_and_replace(
|
||||
readme_filename,
|
||||
"frost-ed25519/README.md",
|
||||
original_strings,
|
||||
&["frost_ed25519", "Ed25519 curve"],
|
||||
);
|
||||
for filename in ["README.md", "dkg.md"] {
|
||||
copy_and_replace(
|
||||
format!("{}/{}", original_basename, filename).as_str(),
|
||||
format!("frost-ed25519/{}", filename).as_str(),
|
||||
original_strings,
|
||||
&["frost_ed25519", "Ed25519 curve"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue