From 837c77629740a2af4c266cac94ae321f10e93965 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Tue, 7 Mar 2023 05:30:21 -0500 Subject: [PATCH] Make Schnorr modular to its transcript --- crypto/ciphersuite/src/lib.rs | 2 +- crypto/frost/src/algorithm.rs | 36 +++++++++++++++++++++---------- crypto/frost/src/tests/mod.rs | 12 +++++------ crypto/frost/src/tests/vectors.rs | 8 ++++--- crypto/schnorr/src/aggregate.rs | 2 +- crypto/transcript/src/lib.rs | 10 ++++----- 6 files changed, 43 insertions(+), 27 deletions(-) diff --git a/crypto/ciphersuite/src/lib.rs b/crypto/ciphersuite/src/lib.rs index 11533a73..c4bbcf55 100644 --- a/crypto/ciphersuite/src/lib.rs +++ b/crypto/ciphersuite/src/lib.rs @@ -53,7 +53,7 @@ pub trait Ciphersuite: type G: Group + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq; /// Hash algorithm used with this curve. // Requires BlockSizeUser so it can be used within Hkdf which requies that. - type H: Clone + BlockSizeUser + Digest + HashMarker + SecureDigest; + type H: Send + Clone + BlockSizeUser + Digest + HashMarker + SecureDigest; /// ID for this curve. const ID: &'static [u8]; diff --git a/crypto/frost/src/algorithm.rs b/crypto/frost/src/algorithm.rs index 43a11a3d..a8222168 100644 --- a/crypto/frost/src/algorithm.rs +++ b/crypto/frost/src/algorithm.rs @@ -127,28 +127,42 @@ pub trait Hram: Send + Clone { fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F; } -/// IETF-compliant Schnorr signature algorithm ((R, s) where s = r + cx). +/// Schnorr signature algorithm ((R, s) where s = r + cx). #[derive(Clone)] -pub struct Schnorr> { - transcript: IetfTranscript, +pub struct Schnorr> { + transcript: T, c: Option, _hram: PhantomData, } -impl> Default for Schnorr { - fn default() -> Self { - Self::new() +/// IETF-compliant Schnorr signature algorithm. +/// +/// This algorithm specifically uses the transcript format defined in the FROST IETF draft. +/// It's a naive transcript format not viable for usage in larger protocols, yet is presented here +/// in order to provide compatibility. +/// +/// Usage of this with key offsets will break the intended compatibility as the IETF draft does not +/// specify a protocol for offsets. +pub type IetfSchnorr = Schnorr; + +impl> Schnorr { + /// Construct a Schnorr algorithm continuing the specified transcript. + pub fn new(transcript: T) -> Schnorr { + Schnorr { transcript, c: None, _hram: PhantomData } } } -impl> Schnorr { - pub fn new() -> Schnorr { - Schnorr { transcript: IetfTranscript(vec![]), c: None, _hram: PhantomData } +impl> IetfSchnorr { + /// Construct a IETF-compatible Schnorr algorithm. + /// + /// Please see the IetfSchnorr documentation for the full details of this. + pub fn ietf() -> IetfSchnorr { + Schnorr::new(IetfTranscript(vec![])) } } -impl> Algorithm for Schnorr { - type Transcript = IetfTranscript; +impl> Algorithm for Schnorr { + type Transcript = T; type Addendum = (); type Signature = SchnorrSignature; diff --git a/crypto/frost/src/tests/mod.rs b/crypto/frost/src/tests/mod.rs index a0d2e8fe..1297cdd9 100644 --- a/crypto/frost/src/tests/mod.rs +++ b/crypto/frost/src/tests/mod.rs @@ -6,7 +6,7 @@ pub use dkg::tests::{key_gen, recover_key}; use crate::{ Curve, Participant, ThresholdKeys, FrostError, - algorithm::{Algorithm, Hram, Schnorr}, + algorithm::{Algorithm, Hram, IetfSchnorr}, sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine}, }; @@ -197,8 +197,8 @@ pub fn test_schnorr>(rng: &mut R) { const MSG: &[u8] = b"Hello, World!"; let keys = key_gen(&mut *rng); - let machines = algorithm_machines(&mut *rng, Schnorr::::new(), &keys); - let sig = sign(&mut *rng, Schnorr::::new(), keys.clone(), machines, MSG); + let machines = algorithm_machines(&mut *rng, IetfSchnorr::::ietf(), &keys); + let sig = sign(&mut *rng, IetfSchnorr::::ietf(), keys.clone(), machines, MSG); let group_key = keys[&Participant::new(1).unwrap()].group_key(); assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG))); } @@ -217,8 +217,8 @@ pub fn test_offset_schnorr>(rng: &m assert_eq!(keys.group_key(), offset_key); } - let machines = algorithm_machines(&mut *rng, Schnorr::::new(), &keys); - let sig = sign(&mut *rng, Schnorr::::new(), keys.clone(), machines, MSG); + let machines = algorithm_machines(&mut *rng, IetfSchnorr::::ietf(), &keys); + let sig = sign(&mut *rng, IetfSchnorr::::ietf(), keys.clone(), machines, MSG); let group_key = keys[&Participant::new(1).unwrap()].group_key(); assert!(sig.verify(offset_key, H::hram(&sig.R, &group_key, MSG))); } @@ -228,7 +228,7 @@ pub fn test_schnorr_blame>(rng: &mu const MSG: &[u8] = b"Hello, World!"; let keys = key_gen(&mut *rng); - let machines = algorithm_machines(&mut *rng, Schnorr::::new(), &keys); + let machines = algorithm_machines(&mut *rng, IetfSchnorr::::ietf(), &keys); let (mut machines, shares) = preprocess_and_shares(&mut *rng, machines, |_, _| {}, MSG); diff --git a/crypto/frost/src/tests/vectors.rs b/crypto/frost/src/tests/vectors.rs index e92474b2..84d3676e 100644 --- a/crypto/frost/src/tests/vectors.rs +++ b/crypto/frost/src/tests/vectors.rs @@ -14,7 +14,7 @@ use ciphersuite::group::{ff::PrimeField, GroupEncoding}; use crate::{ curve::Curve, Participant, ThresholdCore, ThresholdKeys, - algorithm::{IetfTranscript, Hram, Schnorr}, + algorithm::{IetfTranscript, Hram, IetfSchnorr}, sign::{ Writable, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Preprocess, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine, @@ -160,7 +160,8 @@ pub fn test_with_vectors>( let mut machines = vec![]; for i in &vectors.included { - machines.push((i, AlgorithmMachine::new(Schnorr::::new(), keys[i].clone()).unwrap())); + machines + .push((i, AlgorithmMachine::new(IetfSchnorr::::ietf(), keys[i].clone()).unwrap())); } let mut commitments = HashMap::new(); @@ -342,7 +343,8 @@ pub fn test_with_vectors>( // Create the machines let mut machines = vec![]; for i in &vectors.included { - machines.push((i, AlgorithmMachine::new(Schnorr::::new(), keys[i].clone()).unwrap())); + machines + .push((i, AlgorithmMachine::new(IetfSchnorr::::ietf(), keys[i].clone()).unwrap())); } for (i, machine) in machines.drain(..) { diff --git a/crypto/schnorr/src/aggregate.rs b/crypto/schnorr/src/aggregate.rs index c45abff9..52a9554f 100644 --- a/crypto/schnorr/src/aggregate.rs +++ b/crypto/schnorr/src/aggregate.rs @@ -16,7 +16,7 @@ use multiexp::multiexp_vartime; use crate::SchnorrSignature; // Returns a unbiased scalar weight to use on a signature in order to prevent malleability -fn weight(digest: &mut DigestTranscript) -> F { +fn weight(digest: &mut DigestTranscript) -> F { let mut bytes = digest.challenge(b"aggregation_weight"); debug_assert_eq!(bytes.len() % 8, 0); // This should be guaranteed thanks to SecureDigest diff --git a/crypto/transcript/src/lib.rs b/crypto/transcript/src/lib.rs index 6cd6485c..13bb750a 100644 --- a/crypto/transcript/src/lib.rs +++ b/crypto/transcript/src/lib.rs @@ -16,8 +16,8 @@ use digest::{ Digest, Output, HashMarker, }; -pub trait Transcript { - type Challenge: Clone + Send + Sync + AsRef<[u8]>; +pub trait Transcript: Send + Clone { + type Challenge: Send + Sync + Clone + AsRef<[u8]>; /// Create a new transcript with the specified name. fn new(name: &'static [u8]) -> Self; @@ -83,9 +83,9 @@ where /// A simple transcript format constructed around the specified hash algorithm. #[derive(Clone, Debug)] -pub struct DigestTranscript(D); +pub struct DigestTranscript(D); -impl DigestTranscript { +impl DigestTranscript { fn append(&mut self, kind: DigestTranscriptMember, value: &[u8]) { self.0.update([kind.as_u8()]); // Assumes messages don't exceed 16 exabytes @@ -94,7 +94,7 @@ impl DigestTranscript { } } -impl Transcript for DigestTranscript { +impl Transcript for DigestTranscript { type Challenge = Output; fn new(name: &'static [u8]) -> Self {