use std::{
marker::PhantomData,
io::{Read, Cursor},
collections::HashMap,
};
use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, ZeroizeOnDrop};
use group::{
ff::{Field, PrimeField},
GroupEncoding,
};
use multiexp::{multiexp_vartime, BatchVerifier};
use crate::{
curve::Curve,
FrostError, FrostParams, FrostCore,
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: &[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: &FrostParams,
context: &str,
) -> (Vec<C::F>, Vec<C::G>, 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() * coefficients[i]);
serialized.extend(commitments[i].to_bytes().as_ref());
}
let mut r = C::F::random(rng);
serialized.extend(
schnorr::sign::<C>(
coefficients[0],
r,
challenge::<C>(context, params.i(), (C::generator() * r).to_bytes().as_ref(), &serialized),
)
.serialize(),
);
r.zeroize();
(coefficients, commitments, serialized)
}
fn verify_r1<Re: Read, R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: &FrostParams,
context: &str,
our_commitments: Vec<C::G>,
mut serialized: HashMap<u16, Re>,
) -> Result<HashMap<u16, Vec<C::G>>, FrostError> {
validate_map(&serialized, &(1 ..= params.n()).collect::<Vec<_>>(), params.i())?;
let mut commitments = HashMap::new();
commitments.insert(params.i, our_commitments);
let mut signatures = Vec::with_capacity(usize::from(params.n() - 1));
for l in 1 ..= params.n() {
if l == params.i {
continue;
}
let invalid = FrostError::InvalidCommitment(l);
#[allow(non_snake_case)]
let mut Am = vec![0; usize::from(params.t()) * C::G_len()];
serialized.get_mut(&l).unwrap().read_exact(&mut Am).map_err(|_| invalid)?;
let mut these_commitments = vec![];
let mut cursor = Cursor::new(&Am);
for _ in 0 .. usize::from(params.t()) {
these_commitments.push(C::read_G(&mut cursor).map_err(|_| invalid)?);
}
if l != params.i() {
let cursor = serialized.get_mut(&l).unwrap();
#[allow(non_snake_case)]
let R = C::read_G(cursor).map_err(|_| FrostError::InvalidProofOfKnowledge(l))?;
let s = C::read_F(cursor).map_err(|_| FrostError::InvalidProofOfKnowledge(l))?;
signatures.push((
l,
these_commitments[0],
challenge::<C>(context, l, R.to_bytes().as_ref(), &Am),
SchnorrSignature::<C> { R, s },
));
}
commitments.insert(l, these_commitments);
}
schnorr::batch_verify(rng, &signatures).map_err(FrostError::InvalidProofOfKnowledge)?;
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<Re: Read, R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: &FrostParams,
context: &str,
coefficients: &mut Vec<C::F>,
our_commitments: Vec<C::G>,
commitments: HashMap<u16, Re>,
) -> Result<(C::F, HashMap<u16, Vec<C::G>>, HashMap<u16, Vec<u8>>), FrostError> {
let commitments = verify_r1::<_, _, 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, polynomial(coefficients, l).to_repr().as_ref().to_vec());
}
let share = polynomial(coefficients, params.i());
coefficients.zeroize();
Ok((share, commitments, res))
}
fn complete_r2<Re: Read, R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: FrostParams,
mut secret_share: C::F,
commitments: &mut HashMap<u16, Vec<C::G>>,
mut serialized: HashMap<u16, Re>,
) -> Result<FrostCore<C>, FrostError> {
validate_map(&serialized, &(1 ..= params.n()).collect::<Vec<_>>(), params.i())?;
let mut shares = HashMap::new();
for (l, share) in serialized.iter_mut() {
shares.insert(*l, C::read_F(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());
for (l, share) in shares.iter_mut() {
if *l == params.i() {
continue;
}
secret_share += *share;
let mut values = exponential(params.i, &commitments[l]);
values.push((-*share, C::generator()));
share.zeroize();
batch.queue(rng, *l, values);
}
batch.verify_with_vartime_blame().map_err(FrostError::InvalidCommitment)?;
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)));
}
debug_assert_eq!(C::generator() * secret_share, verification_shares[¶ms.i()]);
Ok(FrostCore { params, secret_share, group_key: stripes[0], verification_shares })
}
pub struct KeyGenMachine<C: Curve> {
params: FrostParams,
context: String,
_curve: PhantomData<C>,
}
#[derive(Zeroize)]
pub struct SecretShareMachine<C: Curve> {
#[zeroize(skip)]
params: FrostParams,
context: String,
coefficients: Vec<C::F>,
#[zeroize(skip)]
our_commitments: Vec<C::G>,
}
impl<C: Curve> Drop for SecretShareMachine<C> {
fn drop(&mut self) {
self.zeroize()
}
}
impl<C: Curve> ZeroizeOnDrop for SecretShareMachine<C> {}
#[derive(Zeroize)]
pub struct KeyMachine<C: Curve> {
#[zeroize(skip)]
params: FrostParams,
secret: C::F,
#[zeroize(skip)]
commitments: HashMap<u16, Vec<C::G>>,
}
impl<C: Curve> Drop for KeyMachine<C> {
fn drop(&mut self) {
self.zeroize()
}
}
impl<C: Curve> ZeroizeOnDrop for KeyMachine<C> {}
impl<C: Curve> KeyGenMachine<C> {
pub fn new(params: FrostParams, context: String) -> KeyGenMachine<C> {
KeyGenMachine { params, context, _curve: PhantomData }
}
pub fn generate_coefficients<R: RngCore + CryptoRng>(
self,
rng: &mut R,
) -> (SecretShareMachine<C>, Vec<u8>) {
let (coefficients, our_commitments, serialized) =
generate_key_r1::<_, C>(rng, &self.params, &self.context);
(
SecretShareMachine {
params: self.params,
context: self.context,
coefficients,
our_commitments,
},
serialized,
)
}
}
impl<C: Curve> SecretShareMachine<C> {
pub fn generate_secret_shares<Re: Read, R: RngCore + CryptoRng>(
mut self,
rng: &mut R,
commitments: HashMap<u16, Re>,
) -> Result<(KeyMachine<C>, HashMap<u16, Vec<u8>>), FrostError> {
let (secret, commitments, shares) = generate_key_r2::<_, _, C>(
rng,
&self.params,
&self.context,
&mut self.coefficients,
self.our_commitments.clone(),
commitments,
)?;
Ok((KeyMachine { params: self.params, secret, commitments }, shares))
}
}
impl<C: Curve> KeyMachine<C> {
pub fn complete<Re: Read, R: RngCore + CryptoRng>(
mut self,
rng: &mut R,
shares: HashMap<u16, Re>,
) -> Result<FrostCore<C>, FrostError> {
complete_r2(rng, self.params, self.secret, &mut self.commitments, shares)
}
}