mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-03 17:40:34 +00:00
Make Schnorr modular to its transcript
This commit is contained in:
parent
6bff3866ea
commit
837c776297
6 changed files with 43 additions and 27 deletions
|
@ -53,7 +53,7 @@ pub trait Ciphersuite:
|
||||||
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq;
|
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq;
|
||||||
/// Hash algorithm used with this curve.
|
/// Hash algorithm used with this curve.
|
||||||
// Requires BlockSizeUser so it can be used within Hkdf which requies that.
|
// 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.
|
/// ID for this curve.
|
||||||
const ID: &'static [u8];
|
const ID: &'static [u8];
|
||||||
|
|
|
@ -127,28 +127,42 @@ pub trait Hram<C: Curve>: Send + Clone {
|
||||||
fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F;
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct Schnorr<C: Curve, H: Hram<C>> {
|
pub struct Schnorr<C: Curve, T: Clone + Debug + Transcript, H: Hram<C>> {
|
||||||
transcript: IetfTranscript,
|
transcript: T,
|
||||||
c: Option<C::F>,
|
c: Option<C::F>,
|
||||||
_hram: PhantomData<H>,
|
_hram: PhantomData<H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Curve, H: Hram<C>> Default for Schnorr<C, H> {
|
/// IETF-compliant Schnorr signature algorithm.
|
||||||
fn default() -> Self {
|
///
|
||||||
Self::new()
|
/// 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<C, H> = Schnorr<C, IetfTranscript, H>;
|
||||||
|
|
||||||
|
impl<C: Curve, T: Clone + Debug + Transcript, H: Hram<C>> Schnorr<C, T, H> {
|
||||||
|
/// Construct a Schnorr algorithm continuing the specified transcript.
|
||||||
|
pub fn new(transcript: T) -> Schnorr<C, T, H> {
|
||||||
|
Schnorr { transcript, c: None, _hram: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Curve, H: Hram<C>> Schnorr<C, H> {
|
impl<C: Curve, H: Hram<C>> IetfSchnorr<C, H> {
|
||||||
pub fn new() -> Schnorr<C, H> {
|
/// Construct a IETF-compatible Schnorr algorithm.
|
||||||
Schnorr { transcript: IetfTranscript(vec![]), c: None, _hram: PhantomData }
|
///
|
||||||
|
/// Please see the IetfSchnorr documentation for the full details of this.
|
||||||
|
pub fn ietf() -> IetfSchnorr<C, H> {
|
||||||
|
Schnorr::new(IetfTranscript(vec![]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
|
impl<C: Curve, T: Clone + Debug + Transcript, H: Hram<C>> Algorithm<C> for Schnorr<C, T, H> {
|
||||||
type Transcript = IetfTranscript;
|
type Transcript = T;
|
||||||
type Addendum = ();
|
type Addendum = ();
|
||||||
type Signature = SchnorrSignature<C>;
|
type Signature = SchnorrSignature<C>;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub use dkg::tests::{key_gen, recover_key};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Curve, Participant, ThresholdKeys, FrostError,
|
Curve, Participant, ThresholdKeys, FrostError,
|
||||||
algorithm::{Algorithm, Hram, Schnorr},
|
algorithm::{Algorithm, Hram, IetfSchnorr},
|
||||||
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine},
|
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -197,8 +197,8 @@ pub fn test_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
|
||||||
const MSG: &[u8] = b"Hello, World!";
|
const MSG: &[u8] = b"Hello, World!";
|
||||||
|
|
||||||
let keys = key_gen(&mut *rng);
|
let keys = key_gen(&mut *rng);
|
||||||
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
let machines = algorithm_machines(&mut *rng, IetfSchnorr::<C, H>::ietf(), &keys);
|
||||||
let sig = sign(&mut *rng, Schnorr::<C, H>::new(), keys.clone(), machines, MSG);
|
let sig = sign(&mut *rng, IetfSchnorr::<C, H>::ietf(), keys.clone(), machines, MSG);
|
||||||
let group_key = keys[&Participant::new(1).unwrap()].group_key();
|
let group_key = keys[&Participant::new(1).unwrap()].group_key();
|
||||||
assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG)));
|
assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG)));
|
||||||
}
|
}
|
||||||
|
@ -217,8 +217,8 @@ pub fn test_offset_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &m
|
||||||
assert_eq!(keys.group_key(), offset_key);
|
assert_eq!(keys.group_key(), offset_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
let machines = algorithm_machines(&mut *rng, IetfSchnorr::<C, H>::ietf(), &keys);
|
||||||
let sig = sign(&mut *rng, Schnorr::<C, H>::new(), keys.clone(), machines, MSG);
|
let sig = sign(&mut *rng, IetfSchnorr::<C, H>::ietf(), keys.clone(), machines, MSG);
|
||||||
let group_key = keys[&Participant::new(1).unwrap()].group_key();
|
let group_key = keys[&Participant::new(1).unwrap()].group_key();
|
||||||
assert!(sig.verify(offset_key, H::hram(&sig.R, &group_key, MSG)));
|
assert!(sig.verify(offset_key, H::hram(&sig.R, &group_key, MSG)));
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ pub fn test_schnorr_blame<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mu
|
||||||
const MSG: &[u8] = b"Hello, World!";
|
const MSG: &[u8] = b"Hello, World!";
|
||||||
|
|
||||||
let keys = key_gen(&mut *rng);
|
let keys = key_gen(&mut *rng);
|
||||||
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
let machines = algorithm_machines(&mut *rng, IetfSchnorr::<C, H>::ietf(), &keys);
|
||||||
|
|
||||||
let (mut machines, shares) = preprocess_and_shares(&mut *rng, machines, |_, _| {}, MSG);
|
let (mut machines, shares) = preprocess_and_shares(&mut *rng, machines, |_, _| {}, MSG);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use ciphersuite::group::{ff::PrimeField, GroupEncoding};
|
||||||
use crate::{
|
use crate::{
|
||||||
curve::Curve,
|
curve::Curve,
|
||||||
Participant, ThresholdCore, ThresholdKeys,
|
Participant, ThresholdCore, ThresholdKeys,
|
||||||
algorithm::{IetfTranscript, Hram, Schnorr},
|
algorithm::{IetfTranscript, Hram, IetfSchnorr},
|
||||||
sign::{
|
sign::{
|
||||||
Writable, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Preprocess,
|
Writable, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Preprocess,
|
||||||
PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine,
|
PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine,
|
||||||
|
@ -160,7 +160,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
||||||
|
|
||||||
let mut machines = vec![];
|
let mut machines = vec![];
|
||||||
for i in &vectors.included {
|
for i in &vectors.included {
|
||||||
machines.push((i, AlgorithmMachine::new(Schnorr::<C, H>::new(), keys[i].clone()).unwrap()));
|
machines
|
||||||
|
.push((i, AlgorithmMachine::new(IetfSchnorr::<C, H>::ietf(), keys[i].clone()).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut commitments = HashMap::new();
|
let mut commitments = HashMap::new();
|
||||||
|
@ -342,7 +343,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
||||||
// Create the machines
|
// Create the machines
|
||||||
let mut machines = vec![];
|
let mut machines = vec![];
|
||||||
for i in &vectors.included {
|
for i in &vectors.included {
|
||||||
machines.push((i, AlgorithmMachine::new(Schnorr::<C, H>::new(), keys[i].clone()).unwrap()));
|
machines
|
||||||
|
.push((i, AlgorithmMachine::new(IetfSchnorr::<C, H>::ietf(), keys[i].clone()).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, machine) in machines.drain(..) {
|
for (i, machine) in machines.drain(..) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ use multiexp::multiexp_vartime;
|
||||||
use crate::SchnorrSignature;
|
use crate::SchnorrSignature;
|
||||||
|
|
||||||
// Returns a unbiased scalar weight to use on a signature in order to prevent malleability
|
// Returns a unbiased scalar weight to use on a signature in order to prevent malleability
|
||||||
fn weight<D: Clone + SecureDigest, F: PrimeField>(digest: &mut DigestTranscript<D>) -> F {
|
fn weight<D: Send + Clone + SecureDigest, F: PrimeField>(digest: &mut DigestTranscript<D>) -> F {
|
||||||
let mut bytes = digest.challenge(b"aggregation_weight");
|
let mut bytes = digest.challenge(b"aggregation_weight");
|
||||||
debug_assert_eq!(bytes.len() % 8, 0);
|
debug_assert_eq!(bytes.len() % 8, 0);
|
||||||
// This should be guaranteed thanks to SecureDigest
|
// This should be guaranteed thanks to SecureDigest
|
||||||
|
|
|
@ -16,8 +16,8 @@ use digest::{
|
||||||
Digest, Output, HashMarker,
|
Digest, Output, HashMarker,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Transcript {
|
pub trait Transcript: Send + Clone {
|
||||||
type Challenge: Clone + Send + Sync + AsRef<[u8]>;
|
type Challenge: Send + Sync + Clone + AsRef<[u8]>;
|
||||||
|
|
||||||
/// Create a new transcript with the specified name.
|
/// Create a new transcript with the specified name.
|
||||||
fn new(name: &'static [u8]) -> Self;
|
fn new(name: &'static [u8]) -> Self;
|
||||||
|
@ -83,9 +83,9 @@ where
|
||||||
|
|
||||||
/// A simple transcript format constructed around the specified hash algorithm.
|
/// A simple transcript format constructed around the specified hash algorithm.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DigestTranscript<D: Clone + SecureDigest>(D);
|
pub struct DigestTranscript<D: Send + Clone + SecureDigest>(D);
|
||||||
|
|
||||||
impl<D: Clone + SecureDigest> DigestTranscript<D> {
|
impl<D: Send + Clone + SecureDigest> DigestTranscript<D> {
|
||||||
fn append(&mut self, kind: DigestTranscriptMember, value: &[u8]) {
|
fn append(&mut self, kind: DigestTranscriptMember, value: &[u8]) {
|
||||||
self.0.update([kind.as_u8()]);
|
self.0.update([kind.as_u8()]);
|
||||||
// Assumes messages don't exceed 16 exabytes
|
// Assumes messages don't exceed 16 exabytes
|
||||||
|
@ -94,7 +94,7 @@ impl<D: Clone + SecureDigest> DigestTranscript<D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone + SecureDigest> Transcript for DigestTranscript<D> {
|
impl<D: Send + Clone + SecureDigest> Transcript for DigestTranscript<D> {
|
||||||
type Challenge = Output<D>;
|
type Challenge = Output<D>;
|
||||||
|
|
||||||
fn new(name: &'static [u8]) -> Self {
|
fn new(name: &'static [u8]) -> Self {
|
||||||
|
|
Loading…
Reference in a new issue