Distributed Key Generation
The diagram below shows the distributed key generation process. Dashed lines represent data being sent through an authenticated and confidential communication channel. Note that the first dashed line requires a broadcast channel
Part 1
To start the DKG, each participant calls
dkg::part1()
passing its identifier, the desired threshold and total number of participants.
(Thus, they need to agree on those parameters via some mechanism which is up to
the application.) It returns a round1::SecretPackage
and a round1::Package
:
use rand::thread_rng;
use std::collections::BTreeMap;
use frost_ristretto255 as frost;
let mut rng = thread_rng();
let max_signers = 5;
let min_signers = 3;
// Ask the user which identifier they would like to use. You can create
// an identifier from a non-zero u16 or derive from an arbitrary string.
// Some fixed examples follow (each participant must choose a different identifier)
let participant_identifier = Identifier::try_from(7u16)?;
let participant_identifier = Identifier::derive("alice@example.com".as_bytes())?;
let (round1_secret_package, round1_package) = frost::keys::dkg::part1(
participant_identifier,
max_signers,
min_signers,
&mut rng,
)?;
Check the crate documentation for a full working example; keep in mind it's an artificial one since everything runs in the same program.
The round1::SecretPackage
must be kept in memory to use in the next round. The
round1::Package
must be sent to all other participants using a broadcast
channel to ensure
that all participants receive the same value.
A broadcast channel in this context is not simply broadcasting the value to all participants. It requires running a protocol to ensure that all participants have the same value or that the protocol is aborted. Check the linked Terminology section for more details.
Failure in using a proper broadcast channel will make the key generation insecure.
Part 2
Upon receiving the other participants' round1::Package
s, each participant then
calls
dkg::part2()
passing their own previously created round1::SecretPackage
and a map of the
received round1::Packages
, keyed by the Identifiers of the participant that
sent each one of them. (These identifiers must come from whatever mapping the
coordinator has between communication channels and participants, i.e. they must
have assurance that the round1::Package
came from the participant with that
identifier.) It returns a round2::SecretPackage
and a BTreeMap
mapping other
participants's Identifier
s to round2::Package
s:
let (round2_secret_package, round2_packages) =
frost::keys::dkg::part2(round1_secret_package, round1_packages)?;
The round2::SecretPackage
must be kept in memory for the next part; the
round1::SecretPackage
is consumed and is not required anymore.
The round2::Package
s must be sent to their respective participants with the
given Identifier
s, using an authenticated and confidential communication
channel.
Part 3
Finally, upon receiving the other participant's round2::Package
, the DKG is
concluded by calling
dkg::part3()
passing the same round1::Package
s received in Part 2, the round2::Package
s
just received (again keyed by the Identifier of the participant that sent each
one of them), and the previously stored round2::SecretPackage
for the
participant. It returns a KeyPackage
, with the participant's secret share, and
a PublicKeyPackage
containing the group verifying key:
let (key_package, pubkey_package) = frost::keys::dkg::part3(
round2_secret_package,
round1_packages,
round2_packages,
)?;