use core::{ops::Deref, fmt::Debug};
use std::{
io::{self, Read, Write},
collections::HashMap,
};
use rand_core::{RngCore, CryptoRng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use zeroize::{Zeroize, Zeroizing};
use transcript::Transcript;
use ciphersuite::group::{
ff::{Field, PrimeField},
GroupEncoding,
};
use multiexp::BatchVerifier;
use crate::{
curve::Curve,
Participant, FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
algorithm::{WriteAddendum, Addendum, Algorithm},
validate_map,
};
pub(crate) use crate::nonce::*;
pub trait Writable {
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()>;
fn serialize(&self) -> Vec<u8> {
let mut buf = vec![];
self.write(&mut buf).unwrap();
buf
}
}
impl<T: Writable> Writable for Vec<T> {
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
for w in self {
w.write(writer)?;
}
Ok(())
}
}
#[derive(Clone, Zeroize)]
struct Params<C: Curve, A: Algorithm<C>> {
#[zeroize(skip)]
algorithm: A,
keys: ThresholdKeys<C>,
}
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
fn new(algorithm: A, keys: ThresholdKeys<C>) -> Params<C, A> {
Params { algorithm, keys }
}
fn multisig_params(&self) -> ThresholdParams {
self.keys.params()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Preprocess<C: Curve, A: Addendum> {
pub(crate) commitments: Commitments<C>,
pub addendum: A,
}
impl<C: Curve, A: Addendum> Writable for Preprocess<C, A> {
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
self.commitments.write(writer)?;
self.addendum.write(writer)
}
}
#[derive(Zeroize)]
pub struct CachedPreprocess(pub Zeroizing<[u8; 32]>);
pub trait PreprocessMachine: Send {
type Preprocess: Clone + PartialEq + Writable;
type Signature: Clone + PartialEq + Debug;
type SignMachine: SignMachine<Self::Signature, Preprocess = Self::Preprocess>;
fn preprocess<R: RngCore + CryptoRng>(self, rng: &mut R)
-> (Self::SignMachine, Self::Preprocess);
}
pub struct AlgorithmMachine<C: Curve, A: Algorithm<C>> {
params: Params<C, A>,
}
impl<C: Curve, A: Algorithm<C>> AlgorithmMachine<C, A> {
pub fn new(algorithm: A, keys: ThresholdKeys<C>) -> AlgorithmMachine<C, A> {
AlgorithmMachine { params: Params::new(algorithm, keys) }
}
fn seeded_preprocess(
self,
seed: CachedPreprocess,
) -> (AlgorithmSignMachine<C, A>, Preprocess<C, A::Addendum>) {
let mut params = self.params;
let mut rng = ChaCha20Rng::from_seed(*seed.0);
let (nonces, commitments) =
Commitments::new::<_>(&mut rng, params.keys.secret_share(), ¶ms.algorithm.nonces());
let addendum = params.algorithm.preprocess_addendum(&mut rng, ¶ms.keys);
let preprocess = Preprocess { commitments, addendum };
let mut blame_entropy = [0; 32];
rng.fill_bytes(&mut blame_entropy);
(
AlgorithmSignMachine { params, seed, nonces, preprocess: preprocess.clone(), blame_entropy },
preprocess,
)
}
#[cfg(any(test, feature = "tests"))]
pub(crate) fn unsafe_override_preprocess(
self,
nonces: Vec<Nonce<C>>,
preprocess: Preprocess<C, A::Addendum>,
) -> AlgorithmSignMachine<C, A> {
AlgorithmSignMachine {
params: self.params,
seed: CachedPreprocess(Zeroizing::new([0; 32])),
nonces,
preprocess,
blame_entropy: [0; 32],
}
}
}
impl<C: Curve, A: Algorithm<C>> PreprocessMachine for AlgorithmMachine<C, A> {
type Preprocess = Preprocess<C, A::Addendum>;
type Signature = A::Signature;
type SignMachine = AlgorithmSignMachine<C, A>;
fn preprocess<R: RngCore + CryptoRng>(
self,
rng: &mut R,
) -> (Self::SignMachine, Preprocess<C, A::Addendum>) {
let mut seed = CachedPreprocess(Zeroizing::new([0; 32]));
rng.fill_bytes(seed.0.as_mut());
self.seeded_preprocess(seed)
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct SignatureShare<C: Curve>(C::F);
impl<C: Curve> Writable for SignatureShare<C> {
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(self.0.to_repr().as_ref())
}
}
#[cfg(any(test, feature = "tests"))]
impl<C: Curve> SignatureShare<C> {
pub(crate) fn invalidate(&mut self) {
self.0 += C::F::ONE;
}
}
pub trait SignMachine<S>: Send + Sync + Sized {
type Params: Clone;
type Keys;
type Preprocess: Clone + PartialEq + Writable;
type SignatureShare: Clone + PartialEq + Writable;
type SignatureMachine: SignatureMachine<S, SignatureShare = Self::SignatureShare>;
fn cache(self) -> CachedPreprocess;
fn from_cache(
params: Self::Params,
keys: Self::Keys,
cache: CachedPreprocess,
) -> (Self, Self::Preprocess);
fn read_preprocess<R: Read>(&self, reader: &mut R) -> io::Result<Self::Preprocess>;
fn sign(
self,
commitments: HashMap<Participant, Self::Preprocess>,
msg: &[u8],
) -> Result<(Self::SignatureMachine, Self::SignatureShare), FrostError>;
}
#[derive(Zeroize)]
pub struct AlgorithmSignMachine<C: Curve, A: Algorithm<C>> {
params: Params<C, A>,
seed: CachedPreprocess,
pub(crate) nonces: Vec<Nonce<C>>,
#[zeroize(skip)]
pub(crate) preprocess: Preprocess<C, A::Addendum>,
pub(crate) blame_entropy: [u8; 32],
}
impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachine<C, A> {
type Params = A;
type Keys = ThresholdKeys<C>;
type Preprocess = Preprocess<C, A::Addendum>;
type SignatureShare = SignatureShare<C>;
type SignatureMachine = AlgorithmSignatureMachine<C, A>;
fn cache(self) -> CachedPreprocess {
self.seed
}
fn from_cache(
algorithm: A,
keys: ThresholdKeys<C>,
cache: CachedPreprocess,
) -> (Self, Self::Preprocess) {
AlgorithmMachine::new(algorithm, keys).seeded_preprocess(cache)
}
fn read_preprocess<R: Read>(&self, reader: &mut R) -> io::Result<Self::Preprocess> {
Ok(Preprocess {
commitments: Commitments::read::<_>(reader, &self.params.algorithm.nonces())?,
addendum: self.params.algorithm.read_addendum(reader)?,
})
}
fn sign(
mut self,
mut preprocesses: HashMap<Participant, Preprocess<C, A::Addendum>>,
msg: &[u8],
) -> Result<(Self::SignatureMachine, SignatureShare<C>), FrostError> {
let multisig_params = self.params.multisig_params();
let mut included = Vec::with_capacity(preprocesses.len() + 1);
included.push(multisig_params.i());
for l in preprocesses.keys() {
included.push(*l);
}
included.sort_unstable();
if included.len() < usize::from(multisig_params.t()) {
Err(FrostError::InvalidSigningSet("not enough signers"))?;
}
if u16::from(included[included.len() - 1]) > multisig_params.n() {
Err(FrostError::InvalidParticipant(multisig_params.n(), included[included.len() - 1]))?;
}
for i in 0 .. (included.len() - 1) {
if included[i] == included[i + 1] {
Err(FrostError::DuplicatedParticipant(included[i]))?;
}
}
let view = self.params.keys.view(included.clone()).unwrap();
validate_map(&preprocesses, &included, multisig_params.i())?;
{
self.params.algorithm.transcript().domain_separate(b"FROST");
}
let nonces = self.params.algorithm.nonces();
#[allow(non_snake_case)]
let mut B = BindingFactor(HashMap::<Participant, _>::with_capacity(included.len()));
{
for l in &included {
{
self
.params
.algorithm
.transcript()
.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
}
if *l == self.params.keys.params().i() {
let commitments = self.preprocess.commitments.clone();
commitments.transcript(self.params.algorithm.transcript());
let addendum = self.preprocess.addendum.clone();
{
let mut buf = vec![];
addendum.write(&mut buf).unwrap();
self.params.algorithm.transcript().append_message(b"addendum", buf);
}
B.insert(*l, commitments);
self.params.algorithm.process_addendum(&view, *l, addendum)?;
} else {
let preprocess = preprocesses.remove(l).unwrap();
preprocess.commitments.transcript(self.params.algorithm.transcript());
{
let mut buf = vec![];
preprocess.addendum.write(&mut buf).unwrap();
self.params.algorithm.transcript().append_message(b"addendum", buf);
}
B.insert(*l, preprocess.commitments);
self.params.algorithm.process_addendum(&view, *l, preprocess.addendum)?;
}
}
let mut rho_transcript = A::Transcript::new(b"FROST_rho");
rho_transcript.append_message(
b"group_key",
(self.params.keys.group_key() +
(C::generator() * self.params.keys.current_offset().unwrap_or(C::F::ZERO)))
.to_bytes(),
);
rho_transcript.append_message(b"message", C::hash_msg(msg));
rho_transcript.append_message(
b"preprocesses",
C::hash_commitments(self.params.algorithm.transcript().challenge(b"preprocesses").as_ref()),
);
B.calculate_binding_factors(&rho_transcript);
self
.params
.algorithm
.transcript()
.append_message(b"rho_transcript", rho_transcript.challenge(b"merge"));
}
#[allow(non_snake_case)]
let Rs = B.nonces(&nonces);
let our_binding_factors = B.binding_factors(multisig_params.i());
let nonces = self
.nonces
.drain(..)
.enumerate()
.map(|(n, nonces)| {
let [base, mut actual] = nonces.0;
*actual *= our_binding_factors[n];
*actual += base.deref();
actual
})
.collect::<Vec<_>>();
let share = self.params.algorithm.sign_share(&view, &Rs, nonces, msg);
Ok((
AlgorithmSignatureMachine {
params: self.params.clone(),
view,
B,
Rs,
share,
blame_entropy: self.blame_entropy,
},
SignatureShare(share),
))
}
}
pub trait SignatureMachine<S>: Send + Sync {
type SignatureShare: Clone + PartialEq + Writable;
fn read_share<R: Read>(&self, reader: &mut R) -> io::Result<Self::SignatureShare>;
fn complete(self, shares: HashMap<Participant, Self::SignatureShare>) -> Result<S, FrostError>;
}
#[allow(non_snake_case)]
pub struct AlgorithmSignatureMachine<C: Curve, A: Algorithm<C>> {
params: Params<C, A>,
view: ThresholdView<C>,
B: BindingFactor<C>,
Rs: Vec<Vec<C::G>>,
share: C::F,
blame_entropy: [u8; 32],
}
impl<C: Curve, A: Algorithm<C>> SignatureMachine<A::Signature> for AlgorithmSignatureMachine<C, A> {
type SignatureShare = SignatureShare<C>;
fn read_share<R: Read>(&self, reader: &mut R) -> io::Result<SignatureShare<C>> {
Ok(SignatureShare(C::read_F(reader)?))
}
fn complete(
self,
mut shares: HashMap<Participant, SignatureShare<C>>,
) -> Result<A::Signature, FrostError> {
let params = self.params.multisig_params();
validate_map(&shares, self.view.included(), params.i())?;
let mut responses = HashMap::new();
responses.insert(params.i(), self.share);
let mut sum = self.share;
for (l, share) in shares.drain() {
responses.insert(l, share.0);
sum += share.0;
}
if let Some(sig) = self.params.algorithm.verify(self.view.group_key(), &self.Rs, sum) {
return Ok(sig);
}
let mut rng = ChaCha20Rng::from_seed(self.blame_entropy);
let mut batch = BatchVerifier::new(self.view.included().len());
for l in self.view.included() {
if let Ok(statements) = self.params.algorithm.verify_share(
self.view.verification_share(*l),
&self.B.bound(*l),
responses[l],
) {
batch.queue(&mut rng, *l, statements);
} else {
Err(FrostError::InvalidShare(*l))?;
}
}
if let Err(l) = batch.verify_vartime_with_vartime_blame() {
Err(FrostError::InvalidShare(l))?;
}
Err(FrostError::InternalError("everyone had a valid share yet the signature was still invalid"))
}
}