use std::io::{self, Read}; use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroizing; use group::{ff::PrimeField, GroupEncoding}; use ciphersuite::{Ciphersuite, Ristretto}; use schnorr::SchnorrSignature; use frost::{ ThresholdKeys, ThresholdView, FrostError, algorithm::{IetfTranscript, Hram, Algorithm, Schnorr}, }; use schnorrkel::{PublicKey, Signature, context::SigningTranscript, signing_context}; type RistrettoPoint = ::G; type Scalar = ::F; #[cfg(test)] mod tests; #[derive(Clone)] struct SchnorrkelHram; impl Hram for SchnorrkelHram { #[allow(non_snake_case)] fn hram(R: &RistrettoPoint, A: &RistrettoPoint, m: &[u8]) -> Scalar { let ctx_len = usize::try_from(u32::from_le_bytes(m[0 .. 4].try_into().expect("malformed message"))) .unwrap(); let mut t = signing_context(&m[4 .. (4 + ctx_len)]).bytes(&m[(4 + ctx_len) ..]); t.proto_name(b"Schnorr-sig"); let convert = |point: &RistrettoPoint| PublicKey::from_bytes(&point.to_bytes()).unwrap().into_compressed(); t.commit_point(b"sign:pk", &convert(A)); t.commit_point(b"sign:R", &convert(R)); Scalar::from_repr(t.challenge_scalar(b"sign:c").to_bytes()).unwrap() } } #[derive(Clone)] pub struct Schnorrkel { context: &'static [u8], schnorr: Schnorr, msg: Option>, } impl Schnorrkel { pub fn new(context: &'static [u8]) -> Schnorrkel { Schnorrkel { context, schnorr: Schnorr::new(), msg: None } } } impl Algorithm for Schnorrkel { type Transcript = IetfTranscript; type Addendum = (); type Signature = Signature; fn transcript(&mut self) -> &mut Self::Transcript { self.schnorr.transcript() } fn nonces(&self) -> Vec::G>> { self.schnorr.nonces() } fn preprocess_addendum( &mut self, _: &mut R, _: &ThresholdKeys, ) { } fn read_addendum(&self, _: &mut R) -> io::Result { Ok(()) } fn process_addendum( &mut self, _: &ThresholdView, _: u16, _: (), ) -> Result<(), FrostError> { Ok(()) } fn sign_share( &mut self, params: &ThresholdView, nonce_sums: &[Vec<::G>], nonces: Vec::F>>, msg: &[u8], ) -> ::F { self.msg = Some(msg.to_vec()); self.schnorr.sign_share( params, nonce_sums, nonces, &[ &u32::try_from(self.context.len()).expect("context exceeded 2^32 bytes").to_le_bytes(), self.context, msg, ] .concat(), ) } #[must_use] fn verify( &self, group_key: ::G, nonces: &[Vec<::G>], sum: ::F, ) -> Option { let mut sig = (SchnorrSignature:: { R: nonces[0][0], s: sum }).serialize(); sig[63] |= 1 << 7; Some(Signature::from_bytes(&sig).unwrap()).filter(|sig| { PublicKey::from_bytes(&group_key.to_bytes()) .unwrap() .verify(&mut signing_context(self.context).bytes(self.msg.as_ref().unwrap()), sig) .is_ok() }) } fn verify_share( &self, verification_share: ::G, nonces: &[Vec<::G>], share: ::F, ) -> Result::F, ::G)>, ()> { self.schnorr.verify_share(verification_share, nonces, share) } }