use core::fmt;
use std::collections::HashMap;
use rand_core::{RngCore, CryptoRng};
use group::ff::{Field, PrimeField};
use multiexp::{multiexp_vartime, BatchVerifier};
use crate::{
Curve, MultisigParams, MultisigKeys, FrostError,
schnorr::{self, SchnorrSignature},
validate_map
};
#[allow(non_snake_case)]
fn challenge<C: Curve>(context: &str, l: u16, R: &[u8], Am: &[u8]) -> C::F {
const DST: &'static [u8] = b"FROST Schnorr Proof of Knowledge";
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)
}
fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: &MultisigParams,
context: &str,
) -> (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 {
coefficients.push(C::F::random(&mut *rng));
commitments.push(C::GENERATOR_TABLE * coefficients[i]);
serialized.extend(&C::G_to_bytes(&commitments[i]));
}
let r = C::F::random(rng);
serialized.extend(
schnorr::sign::<C>(
coefficients[0],
r,
challenge::<C>(
context,
params.i(),
&C::G_to_bytes(&(C::GENERATOR_TABLE * r)),
&serialized
)
).serialize()
);
(coefficients, serialized)
}
fn verify_r1<R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: &MultisigParams,
context: &str,
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)
)?;
let commitments_len = usize::from(params.t()) * C::G_len();
let mut commitments = HashMap::new();
#[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];
let s = |l| C::F_from_slice(
&serialized[&l][commitments_len + C::G_len() ..]
).map_err(|_| FrostError::InvalidProofOfKnowledge(l));
let mut signatures = Vec::with_capacity(usize::from(params.n() - 1));
for l in 1 ..= params.n() {
let mut these_commitments = vec![];
for c in 0 .. usize::from(params.t()) {
these_commitments.push(
C::G_from_slice(
&serialized[&l][(c * C::G_len()) .. ((c + 1) * C::G_len())]
).map_err(|_| FrostError::InvalidCommitment(l.try_into().unwrap()))?
);
}
if l != params.i() {
signatures.push((
l,
these_commitments[0],
challenge::<C>(context, l, R_bytes(l), Am(l)),
SchnorrSignature::<C> { R: R(l)?, s: s(l)? }
));
}
commitments.insert(l, these_commitments);
}
schnorr::batch_verify(rng, &signatures).map_err(|l| FrostError::InvalidProofOfKnowledge(l))?;
Ok(commitments)
}
fn polynomial<F: PrimeField>(
coefficients: &[F],
l: u16
) -> F {
let l = F::from(u64::from(l));
let mut share = F::zero();
for (idx, coefficient) in coefficients.iter().rev().enumerate() {
share += coefficient;
if idx != (coefficients.len() - 1) {
share *= l;
}
}
share
}
fn generate_key_r2<R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: &MultisigParams,
context: &str,
coefficients: Vec<C::F>,
our_commitments: Vec<u8>,
commitments: HashMap<u16, Vec<u8>>,
) -> Result<(C::F, HashMap<u16, Vec<C::G>>, HashMap<u16, Vec<u8>>), FrostError> {
let commitments = verify_r1::<R, C>(rng, params, context, our_commitments, commitments)?;
let mut res = HashMap::new();
for l in 1 ..= params.n() {
if l == params.i() {
continue;
}
res.insert(l, C::F_to_bytes(&polynomial(&coefficients, l)));
}
let share = polynomial(&coefficients, params.i());
Ok((share, commitments, res))
}
fn complete_r2<R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: MultisigParams,
mut secret_share: C::F,
commitments: HashMap<u16, Vec<C::G>>,
mut serialized: HashMap<u16, Vec<u8>>,
) -> Result<MultisigKeys<C>, FrostError> {
validate_map(
&mut serialized,
&(1 ..= params.n()).into_iter().collect::<Vec<_>>(),
(params.i(), C::F_to_bytes(&secret_share))
)?;
let mut shares = HashMap::new();
for (l, share) in serialized {
shares.insert(l, C::F_from_slice(&share).map_err(|_| FrostError::InvalidShare(l))?);
}
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
};
let mut batch = BatchVerifier::new(shares.len(), C::LITTLE_ENDIAN);
for (l, share) in &shares {
if *l == params.i() {
continue;
}
secret_share += share;
let mut values = exponential(params.i, &commitments[l]);
values.push((-*share, C::GENERATOR));
batch.queue(rng, *l, values);
}
batch.verify_with_vartime_blame().map_err(|l| FrostError::InvalidCommitment(l))?;
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());
}
let mut verification_shares = HashMap::new();
for i in 1 ..= params.n() {
verification_shares.insert(i, multiexp_vartime(&exponential(i, &stripes), C::LITTLE_ENDIAN));
}
debug_assert_eq!(C::GENERATOR_TABLE * secret_share, verification_shares[¶ms.i()]);
Ok(
MultisigKeys {
params,
secret_share,
group_key: stripes[0],
verification_shares,
offset: None
}
)
}
#[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)
}
}
#[allow(non_snake_case)]
pub struct StateMachine<C: Curve> {
params: MultisigParams,
context: String,
state: State,
coefficients: Option<Vec<C::F>>,
our_commitments: Option<Vec<u8>>,
secret: Option<C::F>,
commitments: Option<HashMap<u16, Vec<C::G>>>
}
impl<C: Curve> StateMachine<C> {
pub fn new(params: MultisigParams, context: String) -> StateMachine<C> {
StateMachine {
params,
context,
state: State::Fresh,
coefficients: None,
our_commitments: None,
secret: None,
commitments: None
}
}
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))?;
}
let (coefficients, serialized) = generate_key_r1::<R, C>(
rng,
&self.params,
&self.context,
);
self.coefficients = Some(coefficients);
self.our_commitments = Some(serialized.clone());
self.state = State::GeneratedCoefficients;
Ok(serialized)
}
pub fn generate_secret_shares<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R,
commitments: HashMap<u16, Vec<u8>>,
) -> Result<HashMap<u16, Vec<u8>>, FrostError> {
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(),
commitments,
)?;
self.secret = Some(secret);
self.commitments = Some(commitments);
self.state = State::GeneratedSecretShares;
Ok(shares)
}
pub fn complete<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R,
shares: HashMap<u16, Vec<u8>>,
) -> Result<MultisigKeys<C>, FrostError> {
if self.state != State::GeneratedSecretShares {
Err(FrostError::InvalidKeyGenTransition(State::GeneratedSecretShares, self.state))?;
}
let keys = complete_r2(
rng,
self.params,
self.secret.take().unwrap(),
self.commitments.take().unwrap(),
shares,
)?;
self.state = State::Complete;
Ok(keys)
}
pub fn params(&self) -> MultisigParams {
self.params.clone()
}
pub fn state(&self) -> State {
self.state
}
}