use core::{marker::PhantomData, fmt::Debug}; use std::io::Read; use rand_core::{RngCore, CryptoRng}; use transcript::Transcript; use crate::{Curve, FrostError, FrostView, schnorr}; pub use schnorr::SchnorrSignature; /// Algorithm to use FROST with pub trait Algorithm: Clone { type Transcript: Transcript + Clone + Debug; /// The resulting type of the signatures this algorithm will produce type Signature: Clone + PartialEq + Debug; /// Obtain a mutable borrow of the underlying transcript fn transcript(&mut self) -> &mut Self::Transcript; /// Obtain the list of nonces to generate, as specified by the basepoints to create commitments /// against per-nonce. These are not committed to by FROST on the underlying transcript fn nonces(&self) -> Vec>; /// Generate an addendum to FROST"s preprocessing stage fn preprocess_addendum( &mut self, rng: &mut R, params: &FrostView, ) -> Vec; /// Proccess the addendum for the specified participant. Guaranteed to be ordered fn process_addendum( &mut self, params: &FrostView, l: u16, reader: &mut Re, ) -> Result<(), FrostError>; /// Sign a share with the given secret/nonce /// The secret will already have been its lagrange coefficient applied so it is the necessary /// key share /// The nonce will already have been processed into the combined form d + (e * p) fn sign_share( &mut self, params: &FrostView, nonce_sums: &[Vec], nonces: &[C::F], msg: &[u8], ) -> C::F; /// Verify a signature #[must_use] fn verify(&self, group_key: C::G, nonces: &[Vec], sum: C::F) -> Option; /// Verify a specific share given as a response. Used to determine blame if signature /// verification fails #[must_use] fn verify_share(&self, verification_share: C::G, nonces: &[Vec], share: C::F) -> bool; } // Transcript which will create an IETF compliant serialization for the binding factor #[derive(Clone, Debug)] pub struct IetfTranscript(Vec); impl Transcript for IetfTranscript { type Challenge = Vec; fn new(_: &'static [u8]) -> IetfTranscript { IetfTranscript(vec![]) } fn domain_separate(&mut self, _: &[u8]) {} fn append_message(&mut self, _: &'static [u8], message: &[u8]) { self.0.extend(message); } fn challenge(&mut self, _: &'static [u8]) -> Vec { self.0.clone() } fn rng_seed(&mut self, _: &'static [u8]) -> [u8; 32] { unimplemented!() } } pub trait Hram: Clone { /// HRAM function to generate a challenge /// H2 from the IETF draft despite having a different argument set (not pre-formatted) #[allow(non_snake_case)] fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F; } #[derive(Clone)] pub struct Schnorr> { transcript: IetfTranscript, c: Option, _hram: PhantomData, } impl> Schnorr { pub fn new() -> Schnorr { Schnorr { transcript: IetfTranscript(vec![]), c: None, _hram: PhantomData } } } /// Implementation of Schnorr signatures for use with FROST impl> Algorithm for Schnorr { type Transcript = IetfTranscript; type Signature = SchnorrSignature; fn transcript(&mut self) -> &mut Self::Transcript { &mut self.transcript } fn nonces(&self) -> Vec> { vec![vec![C::GENERATOR]] } fn preprocess_addendum( &mut self, _: &mut R, _: &FrostView, ) -> Vec { vec![] } fn process_addendum( &mut self, _: &FrostView, _: u16, _: &mut Re, ) -> Result<(), FrostError> { Ok(()) } fn sign_share( &mut self, params: &FrostView, nonce_sums: &[Vec], nonces: &[C::F], msg: &[u8], ) -> C::F { let c = H::hram(&nonce_sums[0][0], ¶ms.group_key(), msg); self.c = Some(c); schnorr::sign::(params.secret_share(), nonces[0], c).s } #[must_use] fn verify(&self, group_key: C::G, nonces: &[Vec], sum: C::F) -> Option { let sig = SchnorrSignature { R: nonces[0][0], s: sum }; if schnorr::verify::(group_key, self.c.unwrap(), &sig) { Some(sig) } else { None } } #[must_use] fn verify_share(&self, verification_share: C::G, nonces: &[Vec], share: C::F) -> bool { schnorr::verify::( verification_share, self.c.unwrap(), &SchnorrSignature { R: nonces[0][0], s: share }, ) } }