2022-05-29 23:52:27 +00:00
|
|
|
use core::fmt;
|
2022-05-25 01:41:14 +00:00
|
|
|
use std::collections::HashMap;
|
2022-04-22 01:36:18 +00:00
|
|
|
|
|
|
|
use rand_core::{RngCore, CryptoRng};
|
|
|
|
|
|
|
|
use ff::{Field, PrimeField};
|
|
|
|
|
2022-05-27 04:52:44 +00:00
|
|
|
use multiexp::{multiexp_vartime, BatchVerifier};
|
|
|
|
|
2022-05-25 04:22:00 +00:00
|
|
|
use crate::{
|
|
|
|
Curve, MultisigParams, MultisigKeys, FrostError,
|
|
|
|
schnorr::{self, SchnorrSignature},
|
|
|
|
validate_map
|
|
|
|
};
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-04-23 07:49:30 +00:00
|
|
|
#[allow(non_snake_case)]
|
2022-05-29 00:34:44 +00:00
|
|
|
fn challenge<C: Curve>(context: &str, l: u16, R: &[u8], Am: &[u8]) -> C::F {
|
|
|
|
const DST: &'static [u8] = b"FROST Schnorr Proof of Knowledge";
|
2022-06-03 05:25:46 +00:00
|
|
|
|
2022-05-29 00:34:44 +00:00
|
|
|
// Uses hash_msg to get a fixed size value out of the context string
|
2022-06-03 05:25:46 +00:00
|
|
|
let mut transcript = C::hash_msg(context.as_bytes());
|
|
|
|
transcript.extend(l.to_be_bytes());
|
|
|
|
transcript.extend(R);
|
|
|
|
transcript.extend(Am);
|
|
|
|
C::hash_to_F(DST, &transcript)
|
2022-04-23 07:49:30 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 01:36:18 +00:00
|
|
|
// Implements steps 1 through 3 of round 1 of FROST DKG. Returns the coefficients, commitments, and
|
|
|
|
// the serialized commitments to be broadcasted over an authenticated channel to all parties
|
|
|
|
fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
|
|
|
|
rng: &mut R,
|
|
|
|
params: &MultisigParams,
|
|
|
|
context: &str,
|
2022-05-25 01:41:14 +00:00
|
|
|
) -> (Vec<C::F>, Vec<u8>) {
|
|
|
|
let t = usize::from(params.t);
|
|
|
|
let mut coefficients = Vec::with_capacity(t);
|
|
|
|
let mut commitments = Vec::with_capacity(t);
|
|
|
|
let mut serialized = Vec::with_capacity((C::G_len() * t) + C::G_len() + C::F_len());
|
|
|
|
|
|
|
|
for i in 0 .. t {
|
2022-04-22 01:36:18 +00:00
|
|
|
// Step 1: Generate t random values to form a polynomial with
|
|
|
|
coefficients.push(C::F::random(&mut *rng));
|
|
|
|
// Step 3: Generate public commitments
|
2022-06-04 03:22:08 +00:00
|
|
|
commitments.push(C::GENERATOR_TABLE * coefficients[i]);
|
2022-04-22 01:36:18 +00:00
|
|
|
// Serialize them for publication
|
2022-05-25 01:41:14 +00:00
|
|
|
serialized.extend(&C::G_to_bytes(&commitments[i]));
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Step 2: Provide a proof of knowledge
|
2022-05-25 01:41:14 +00:00
|
|
|
let r = C::F::random(rng);
|
2022-05-25 04:22:00 +00:00
|
|
|
serialized.extend(
|
|
|
|
schnorr::sign::<C>(
|
|
|
|
coefficients[0],
|
|
|
|
// This could be deterministic as the PoK is a singleton never opened up to cooperative
|
|
|
|
// discussion
|
|
|
|
// There's no reason to spend the time and effort to make this deterministic besides a
|
|
|
|
// general obsession with canonicity and determinism though
|
|
|
|
r,
|
|
|
|
challenge::<C>(
|
|
|
|
context,
|
2022-05-29 00:34:44 +00:00
|
|
|
params.i(),
|
2022-06-04 03:22:08 +00:00
|
|
|
&C::G_to_bytes(&(C::GENERATOR_TABLE * r)),
|
2022-05-25 04:22:00 +00:00
|
|
|
&serialized
|
|
|
|
)
|
|
|
|
).serialize()
|
2022-05-25 01:41:14 +00:00
|
|
|
);
|
2022-04-22 01:36:18 +00:00
|
|
|
|
|
|
|
// Step 4: Broadcast
|
2022-05-25 01:41:14 +00:00
|
|
|
(coefficients, serialized)
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the received data from the first round of key generation
|
|
|
|
fn verify_r1<R: RngCore + CryptoRng, C: Curve>(
|
|
|
|
rng: &mut R,
|
|
|
|
params: &MultisigParams,
|
|
|
|
context: &str,
|
2022-05-25 01:41:14 +00:00
|
|
|
our_commitments: Vec<u8>,
|
|
|
|
mut serialized: HashMap<u16, Vec<u8>>,
|
|
|
|
) -> Result<HashMap<u16, Vec<C::G>>, FrostError> {
|
|
|
|
validate_map(
|
|
|
|
&mut serialized,
|
|
|
|
&(1 ..= params.n()).into_iter().collect::<Vec<_>>(),
|
|
|
|
(params.i(), our_commitments)
|
|
|
|
)?;
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
let commitments_len = usize::from(params.t()) * C::G_len();
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
let mut commitments = HashMap::new();
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let R_bytes = |l| &serialized[&l][commitments_len .. commitments_len + C::G_len()];
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let R = |l| C::G_from_slice(R_bytes(l)).map_err(|_| FrostError::InvalidProofOfKnowledge(l));
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let Am = |l| &serialized[&l][0 .. commitments_len];
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
let s = |l| C::F_from_slice(
|
|
|
|
&serialized[&l][commitments_len + C::G_len() ..]
|
|
|
|
).map_err(|_| FrostError::InvalidProofOfKnowledge(l));
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 04:22:00 +00:00
|
|
|
let mut signatures = Vec::with_capacity(usize::from(params.n() - 1));
|
2022-05-25 01:41:14 +00:00
|
|
|
for l in 1 ..= params.n() {
|
|
|
|
let mut these_commitments = vec![];
|
|
|
|
for c in 0 .. usize::from(params.t()) {
|
|
|
|
these_commitments.push(
|
2022-04-22 01:36:18 +00:00
|
|
|
C::G_from_slice(
|
2022-05-25 01:41:14 +00:00
|
|
|
&serialized[&l][(c * C::G_len()) .. ((c + 1) * C::G_len())]
|
|
|
|
).map_err(|_| FrostError::InvalidCommitment(l.try_into().unwrap()))?
|
2022-04-22 01:36:18 +00:00
|
|
|
);
|
|
|
|
}
|
2022-05-25 01:41:14 +00:00
|
|
|
|
|
|
|
// Don't bother validating our own proof of knowledge
|
2022-05-25 04:22:00 +00:00
|
|
|
if l != params.i() {
|
|
|
|
// Step 5: Validate each proof of knowledge
|
|
|
|
// This is solely the prep step for the latter batch verification
|
|
|
|
signatures.push((
|
|
|
|
l,
|
|
|
|
these_commitments[0],
|
2022-05-29 00:34:44 +00:00
|
|
|
challenge::<C>(context, l, R_bytes(l), Am(l)),
|
2022-05-25 04:22:00 +00:00
|
|
|
SchnorrSignature::<C> { R: R(l)?, s: s(l)? }
|
|
|
|
));
|
2022-05-25 01:41:14 +00:00
|
|
|
}
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 04:22:00 +00:00
|
|
|
commitments.insert(l, these_commitments);
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-27 04:52:44 +00:00
|
|
|
schnorr::batch_verify(rng, &signatures).map_err(|l| FrostError::InvalidProofOfKnowledge(l))?;
|
2022-04-22 01:36:18 +00:00
|
|
|
|
|
|
|
Ok(commitments)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn polynomial<F: PrimeField>(
|
|
|
|
coefficients: &[F],
|
2022-05-25 01:41:14 +00:00
|
|
|
l: u16
|
2022-04-22 01:36:18 +00:00
|
|
|
) -> F {
|
2022-05-25 01:41:14 +00:00
|
|
|
let l = F::from(u64::from(l));
|
2022-04-22 01:36:18 +00:00
|
|
|
let mut share = F::zero();
|
|
|
|
for (idx, coefficient) in coefficients.iter().rev().enumerate() {
|
|
|
|
share += coefficient;
|
|
|
|
if idx != (coefficients.len() - 1) {
|
2022-05-25 01:41:14 +00:00
|
|
|
share *= l;
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
share
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements round 1, step 5 and round 2, step 1 of FROST key generation
|
|
|
|
// Returns our secret share part, commitments for the next step, and a vector for each
|
|
|
|
// counterparty to receive
|
|
|
|
fn generate_key_r2<R: RngCore + CryptoRng, C: Curve>(
|
|
|
|
rng: &mut R,
|
|
|
|
params: &MultisigParams,
|
|
|
|
context: &str,
|
|
|
|
coefficients: Vec<C::F>,
|
2022-05-25 01:41:14 +00:00
|
|
|
our_commitments: Vec<u8>,
|
|
|
|
commitments: HashMap<u16, Vec<u8>>,
|
|
|
|
) -> Result<(C::F, HashMap<u16, Vec<C::G>>, HashMap<u16, Vec<u8>>), FrostError> {
|
2022-04-22 01:36:18 +00:00
|
|
|
let commitments = verify_r1::<R, C>(rng, params, context, our_commitments, commitments)?;
|
|
|
|
|
|
|
|
// Step 1: Generate secret shares for all other parties
|
2022-05-25 01:41:14 +00:00
|
|
|
let mut res = HashMap::new();
|
|
|
|
for l in 1 ..= params.n() {
|
|
|
|
// Don't insert our own shares to the byte buffer which is meant to be sent around
|
2022-04-22 01:36:18 +00:00
|
|
|
// An app developer could accidentally send it. Best to keep this black boxed
|
2022-05-25 01:41:14 +00:00
|
|
|
if l == params.i() {
|
|
|
|
continue;
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
res.insert(l, C::F_to_bytes(&polynomial(&coefficients, l)));
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate our own share
|
2022-05-25 01:41:14 +00:00
|
|
|
let share = polynomial(&coefficients, params.i());
|
2022-04-22 01:36:18 +00:00
|
|
|
|
|
|
|
// The secret shares are discarded here, not cleared. While any system which leaves its memory
|
|
|
|
// accessible is likely totally lost already, making the distinction meaningless when the key gen
|
|
|
|
// system acts as the signer system and therefore actively holds the signing key anyways, it
|
|
|
|
// should be overwritten with /dev/urandom in the name of security (which still doesn't meet
|
|
|
|
// requirements for secure data deletion yet those requirements expect hardware access which is
|
|
|
|
// far past what this library can reasonably counter)
|
|
|
|
// TODO: Zero out the coefficients
|
|
|
|
|
|
|
|
Ok((share, commitments, res))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Finishes round 2 and returns both the secret share and the serialized public key.
|
|
|
|
/// This key is not usable until all parties confirm they have completed the protocol without
|
|
|
|
/// issue, yet simply confirming protocol completion without issue is enough to confirm the same
|
|
|
|
/// key was generated as long as a lack of duplicated commitments was also confirmed when they were
|
|
|
|
/// broadcasted initially
|
2022-05-27 04:52:44 +00:00
|
|
|
fn complete_r2<R: RngCore + CryptoRng, C: Curve>(
|
|
|
|
rng: &mut R,
|
2022-04-22 01:36:18 +00:00
|
|
|
params: MultisigParams,
|
2022-05-30 05:46:30 +00:00
|
|
|
mut secret_share: C::F,
|
2022-05-25 01:41:14 +00:00
|
|
|
commitments: HashMap<u16, Vec<C::G>>,
|
2022-04-22 01:36:18 +00:00
|
|
|
// Vec to preserve ownership
|
2022-05-25 01:41:14 +00:00
|
|
|
mut serialized: HashMap<u16, Vec<u8>>,
|
2022-04-22 01:36:18 +00:00
|
|
|
) -> Result<MultisigKeys<C>, FrostError> {
|
2022-05-25 01:41:14 +00:00
|
|
|
validate_map(
|
|
|
|
&mut serialized,
|
|
|
|
&(1 ..= params.n()).into_iter().collect::<Vec<_>>(),
|
2022-05-30 05:46:30 +00:00
|
|
|
(params.i(), C::F_to_bytes(&secret_share))
|
2022-05-25 01:41:14 +00:00
|
|
|
)?;
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
// Step 2. Verify each share
|
|
|
|
let mut shares = HashMap::new();
|
|
|
|
for (l, share) in serialized {
|
2022-05-30 06:14:34 +00:00
|
|
|
shares.insert(l, C::F_from_slice(&share).map_err(|_| FrostError::InvalidShare(l))?);
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-30 05:46:30 +00:00
|
|
|
// Calculate the exponent for a given participant and apply it to a series of commitments
|
|
|
|
// Initially used with the actual commitments to verify the secret share, later used with stripes
|
|
|
|
// to generate the verification shares
|
|
|
|
let exponential = |i: u16, values: &[_]| {
|
|
|
|
let i = C::F::from(i.into());
|
|
|
|
let mut res = Vec::with_capacity(params.t().into());
|
|
|
|
(0 .. usize::from(params.t())).into_iter().fold(
|
|
|
|
C::F::one(),
|
|
|
|
|exp, l| {
|
|
|
|
res.push((exp, values[l]));
|
|
|
|
exp * i
|
|
|
|
}
|
|
|
|
);
|
|
|
|
res
|
|
|
|
};
|
|
|
|
|
2022-06-04 03:22:08 +00:00
|
|
|
let mut batch = BatchVerifier::new(shares.len(), C::LITTLE_ENDIAN);
|
2022-05-25 01:41:14 +00:00
|
|
|
for (l, share) in &shares {
|
|
|
|
if *l == params.i() {
|
2022-04-22 01:36:18 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-05-30 05:46:30 +00:00
|
|
|
secret_share += share;
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-30 05:46:30 +00:00
|
|
|
// This can be insecurely linearized from n * t to just n using the below sums for a given
|
|
|
|
// stripe. Doing so uses naive addition which is subject to malleability. The only way to
|
|
|
|
// ensure that malleability isn't present is to use this n * t algorithm, which runs
|
|
|
|
// per sender and not as an aggregate of all senders, which also enables blame
|
|
|
|
let mut values = exponential(params.i, &commitments[l]);
|
2022-06-04 03:22:08 +00:00
|
|
|
values.push((-*share, C::GENERATOR));
|
2022-05-27 04:52:44 +00:00
|
|
|
batch.queue(rng, *l, values);
|
|
|
|
}
|
2022-05-27 06:01:01 +00:00
|
|
|
batch.verify_with_vartime_blame().map_err(|l| FrostError::InvalidCommitment(l))?;
|
2022-04-22 01:36:18 +00:00
|
|
|
|
2022-05-30 05:46:30 +00:00
|
|
|
// Stripe commitments per t and sum them in advance. Calculating verification shares relies on
|
|
|
|
// these sums so preprocessing them is a massive speedup
|
|
|
|
// If these weren't just sums, yet the tables used in multiexp, this would be further optimized
|
2022-05-30 06:14:34 +00:00
|
|
|
// As of right now, each multiexp will regenerate them
|
2022-05-30 05:46:30 +00:00
|
|
|
let mut stripes = Vec::with_capacity(usize::from(params.t()));
|
|
|
|
for t in 0 .. usize::from(params.t()) {
|
|
|
|
stripes.push(commitments.values().map(|commitments| commitments[t]).sum());
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-30 05:46:30 +00:00
|
|
|
// Calculate each user's verification share
|
2022-05-25 01:41:14 +00:00
|
|
|
let mut verification_shares = HashMap::new();
|
2022-05-29 23:52:27 +00:00
|
|
|
for i in 1 ..= params.n() {
|
2022-06-04 03:22:08 +00:00
|
|
|
verification_shares.insert(i, multiexp_vartime(exponential(i, &stripes), C::LITTLE_ENDIAN));
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
2022-06-04 03:22:08 +00:00
|
|
|
debug_assert_eq!(C::GENERATOR_TABLE * secret_share, verification_shares[¶ms.i()]);
|
2022-04-22 01:36:18 +00:00
|
|
|
|
|
|
|
// TODO: Clear serialized and shares
|
|
|
|
|
2022-05-30 05:46:30 +00:00
|
|
|
Ok(
|
|
|
|
MultisigKeys {
|
|
|
|
params,
|
|
|
|
secret_share,
|
|
|
|
group_key: stripes[0],
|
|
|
|
verification_shares,
|
|
|
|
offset: None
|
|
|
|
}
|
|
|
|
)
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// State of a Key Generation machine
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
|
|
pub enum State {
|
|
|
|
Fresh,
|
|
|
|
GeneratedCoefficients,
|
|
|
|
GeneratedSecretShares,
|
|
|
|
Complete,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for State {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "{:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// State machine which manages key generation
|
2022-04-23 07:49:30 +00:00
|
|
|
#[allow(non_snake_case)]
|
2022-04-22 01:36:18 +00:00
|
|
|
pub struct StateMachine<C: Curve> {
|
|
|
|
params: MultisigParams,
|
|
|
|
context: String,
|
|
|
|
state: State,
|
|
|
|
coefficients: Option<Vec<C::F>>,
|
2022-05-25 01:41:14 +00:00
|
|
|
our_commitments: Option<Vec<u8>>,
|
2022-04-22 01:36:18 +00:00
|
|
|
secret: Option<C::F>,
|
2022-05-25 01:41:14 +00:00
|
|
|
commitments: Option<HashMap<u16, Vec<C::G>>>
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: Curve> StateMachine<C> {
|
|
|
|
/// Creates a new machine to generate a key for the specified curve in the specified multisig
|
|
|
|
// The context string must be unique among multisigs
|
|
|
|
pub fn new(params: MultisigParams, context: String) -> StateMachine<C> {
|
|
|
|
StateMachine {
|
|
|
|
params,
|
|
|
|
context,
|
|
|
|
state: State::Fresh,
|
|
|
|
coefficients: None,
|
|
|
|
our_commitments: None,
|
|
|
|
secret: None,
|
2022-04-23 07:49:30 +00:00
|
|
|
commitments: None
|
2022-04-22 01:36:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Start generating a key according to the FROST DKG spec
|
|
|
|
/// Returns a serialized list of commitments to be sent to all parties over an authenticated
|
|
|
|
/// channel. If any party submits multiple sets of commitments, they MUST be treated as malicious
|
|
|
|
pub fn generate_coefficients<R: RngCore + CryptoRng>(
|
|
|
|
&mut self,
|
|
|
|
rng: &mut R
|
|
|
|
) -> Result<Vec<u8>, FrostError> {
|
|
|
|
if self.state != State::Fresh {
|
|
|
|
Err(FrostError::InvalidKeyGenTransition(State::Fresh, self.state))?;
|
|
|
|
}
|
|
|
|
|
2022-05-25 01:41:14 +00:00
|
|
|
let (coefficients, serialized) = generate_key_r1::<R, C>(
|
2022-04-22 01:36:18 +00:00
|
|
|
rng,
|
|
|
|
&self.params,
|
|
|
|
&self.context,
|
|
|
|
);
|
|
|
|
|
|
|
|
self.coefficients = Some(coefficients);
|
2022-05-25 01:41:14 +00:00
|
|
|
self.our_commitments = Some(serialized.clone());
|
2022-04-22 01:36:18 +00:00
|
|
|
self.state = State::GeneratedCoefficients;
|
|
|
|
Ok(serialized)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Continue generating a key
|
|
|
|
/// Takes in everyone else's commitments, which are expected to be in a Vec where participant
|
|
|
|
/// index = Vec index. An empty vector is expected at index 0 to allow for this. An empty vector
|
|
|
|
/// is also expected at index i which is locally handled. Returns a byte vector representing a
|
|
|
|
/// secret share for each other participant which should be encrypted before sending
|
|
|
|
pub fn generate_secret_shares<R: RngCore + CryptoRng>(
|
|
|
|
&mut self,
|
|
|
|
rng: &mut R,
|
2022-05-25 01:41:14 +00:00
|
|
|
commitments: HashMap<u16, Vec<u8>>,
|
|
|
|
) -> Result<HashMap<u16, Vec<u8>>, FrostError> {
|
2022-04-22 01:36:18 +00:00
|
|
|
if self.state != State::GeneratedCoefficients {
|
|
|
|
Err(FrostError::InvalidKeyGenTransition(State::GeneratedCoefficients, self.state))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let (secret, commitments, shares) = generate_key_r2::<R, C>(
|
|
|
|
rng,
|
|
|
|
&self.params,
|
|
|
|
&self.context,
|
|
|
|
self.coefficients.take().unwrap(),
|
|
|
|
self.our_commitments.take().unwrap(),
|
2022-05-25 01:41:14 +00:00
|
|
|
commitments,
|
2022-04-22 01:36:18 +00:00
|
|
|
)?;
|
|
|
|
|
|
|
|
self.secret = Some(secret);
|
|
|
|
self.commitments = Some(commitments);
|
|
|
|
self.state = State::GeneratedSecretShares;
|
|
|
|
Ok(shares)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Complete key generation
|
|
|
|
/// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index =
|
|
|
|
/// Vec index with an empty vector at index 0 and index i. Returns a byte vector representing the
|
|
|
|
/// group's public key, while setting a valid secret share inside the machine. > t participants
|
|
|
|
/// must report completion without issue before this key can be considered usable, yet you should
|
|
|
|
/// wait for all participants to report as such
|
2022-05-27 04:52:44 +00:00
|
|
|
pub fn complete<R: RngCore + CryptoRng>(
|
2022-04-22 01:36:18 +00:00
|
|
|
&mut self,
|
2022-05-27 04:52:44 +00:00
|
|
|
rng: &mut R,
|
2022-05-25 01:41:14 +00:00
|
|
|
shares: HashMap<u16, Vec<u8>>,
|
|
|
|
) -> Result<MultisigKeys<C>, FrostError> {
|
2022-04-22 01:36:18 +00:00
|
|
|
if self.state != State::GeneratedSecretShares {
|
|
|
|
Err(FrostError::InvalidKeyGenTransition(State::GeneratedSecretShares, self.state))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let keys = complete_r2(
|
2022-05-27 04:52:44 +00:00
|
|
|
rng,
|
2022-04-22 01:36:18 +00:00
|
|
|
self.params,
|
|
|
|
self.secret.take().unwrap(),
|
2022-05-25 01:41:14 +00:00
|
|
|
self.commitments.take().unwrap(),
|
2022-04-22 01:36:18 +00:00
|
|
|
shares,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
self.state = State::Complete;
|
|
|
|
Ok(keys)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn params(&self) -> MultisigParams {
|
|
|
|
self.params.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn state(&self) -> State {
|
|
|
|
self.state
|
|
|
|
}
|
|
|
|
}
|