-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove schnorrkel, add generalized schnorr
- Loading branch information
1 parent
b6adef2
commit 9643b6d
Showing
12 changed files
with
307 additions
and
267 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,4 +34,4 @@ jobs: | |
-p dleq \ | ||
-p dkg \ | ||
-p modular-frost \ | ||
-p frost-schnorrkel | ||
-p generalized-schnorr |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
[package] | ||
name = "generalized-schnorr" | ||
version = "0.1.0" | ||
description = "Generalized Schnorr Protocols" | ||
license = "MIT" | ||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/schnorr" | ||
authors = ["Luke Parker <lukeparker5132@gmail.com>"] | ||
keywords = ["schnorr", "ff", "group"] | ||
edition = "2021" | ||
rust-version = "1.74" | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
rustdoc-args = ["--cfg", "docsrs"] | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[dependencies] | ||
std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-features = false } | ||
|
||
rand_core = { version = "0.6", default-features = false } | ||
|
||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } | ||
|
||
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false } | ||
|
||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] } | ||
multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["batch"] } | ||
|
||
[dev-dependencies] | ||
hex = "0.4" | ||
|
||
rand_core = { version = "0.6", features = ["std"] } | ||
|
||
transcript = { package = "flexible-transcript", path = "../transcript", features = ["recommended"] } | ||
|
||
ciphersuite = { path = "../ciphersuite", features = ["ed25519"] } | ||
|
||
[features] | ||
std = ["std-shims/std", "rand_core/std", "zeroize/std", "ciphersuite/std", "multiexp/std"] | ||
default = ["std"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Schnorr Signatures | ||
|
||
A challenge (and therefore HRAm) agnostic Schnorr signature library. This is | ||
intended to be used as a primitive by a variety of crates relying on Schnorr | ||
signatures, voiding the need to constantly define a Schnorr signature struct | ||
with associated functions. | ||
|
||
This library provides signatures of the `R, s` form. Batch verification is | ||
supported via the multiexp crate. Half-aggregation, as defined in | ||
<https://eprint.iacr.org/2021/350>, is also supported. | ||
|
||
This library was | ||
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf), | ||
culminating in commit | ||
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06). | ||
Any subsequent changes have not undergone auditing. | ||
|
||
This library is usable under no_std, via alloc, when the default features are | ||
disabled. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
#![cfg_attr(docsrs, feature(doc_auto_cfg))] | ||
#![doc = include_str!("../README.md")] | ||
#![cfg_attr(not(feature = "std"), no_std)] | ||
#![allow(non_snake_case)] | ||
|
||
use core::ops::Deref; | ||
#[cfg(not(feature = "std"))] | ||
#[macro_use] | ||
extern crate alloc; | ||
use std_shims::io::{self, Read, Write}; | ||
|
||
use rand_core::{RngCore, CryptoRng}; | ||
|
||
use zeroize::{Zeroize, Zeroizing}; | ||
|
||
use transcript::Transcript; | ||
|
||
use ciphersuite::{ | ||
group::{ | ||
ff::{Field, PrimeField}, | ||
Group, GroupEncoding, | ||
}, | ||
Ciphersuite, | ||
}; | ||
use multiexp::{multiexp_vartime, BatchVerifier}; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
/// A Generalized Schnorr Proof. | ||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)] | ||
pub struct GeneralizedSchnorr< | ||
C: Ciphersuite, | ||
const OUTPUTS: usize, | ||
const SCALARS: usize, | ||
const SCALARS_PLUS_TWO: usize, | ||
> { | ||
pub R: [C::G; OUTPUTS], | ||
pub s: [C::F; SCALARS], | ||
} | ||
|
||
impl<C: Ciphersuite, const OUTPUTS: usize, const SCALARS: usize, const SCALARS_PLUS_TWO: usize> | ||
GeneralizedSchnorr<C, OUTPUTS, SCALARS, SCALARS_PLUS_TWO> | ||
{ | ||
/// Read a GeneralizedSchnorr from something implementing Read. | ||
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> { | ||
let mut R = [C::G::identity(); OUTPUTS]; | ||
for R in &mut R { | ||
*R = C::read_G(reader)?; | ||
} | ||
let mut s = [C::F::ZERO; SCALARS]; | ||
for s in &mut s { | ||
*s = C::read_F(reader)?; | ||
} | ||
Ok(Self { R, s }) | ||
} | ||
|
||
/// Write a GeneralizedSchnorr to something implementing Read. | ||
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> { | ||
for R in self.R { | ||
writer.write_all(R.to_bytes().as_ref())?; | ||
} | ||
for s in self.s { | ||
writer.write_all(s.to_repr().as_ref())?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn challenge( | ||
transcript: &mut impl Transcript, | ||
matrix: [[C::G; SCALARS]; OUTPUTS], | ||
outputs: [C::G; OUTPUTS], | ||
nonces: [C::G; OUTPUTS], | ||
) -> C::F { | ||
transcript.domain_separate(b"generalized_schnorr"); | ||
transcript.append_message( | ||
b"scalars", | ||
u32::try_from(SCALARS).expect("passed 2**32 scalars").to_le_bytes(), | ||
); | ||
transcript.append_message( | ||
b"outputs", | ||
u32::try_from(OUTPUTS).expect("passed 2**32 outputs").to_le_bytes(), | ||
); | ||
for row in matrix { | ||
for generator in row { | ||
transcript.append_message(b"generator", generator.to_bytes()); | ||
} | ||
} | ||
for output in outputs { | ||
transcript.append_message(b"output", output.to_bytes()); | ||
} | ||
for nonce in nonces { | ||
transcript.append_message(b"nonce", nonce.to_bytes()); | ||
} | ||
C::hash_to_F(b"generalized_schnorr", transcript.challenge(b"c").as_ref()) | ||
} | ||
|
||
/// Serialize a GeneralizedSchnorr, returning a `Vec<u8>`. | ||
#[cfg(feature = "std")] | ||
pub fn serialize(&self) -> Vec<u8> { | ||
let mut buf = vec![]; | ||
self.write(&mut buf).unwrap(); | ||
buf | ||
} | ||
|
||
/// Prove a Generalized Schnorr statement. | ||
/// | ||
/// Returns the outputs and the proof for them. | ||
pub fn prove( | ||
rng: &mut (impl RngCore + CryptoRng), | ||
transcript: &mut impl Transcript, | ||
matrix: [[C::G; SCALARS]; OUTPUTS], | ||
scalars: [&Zeroizing<C::F>; SCALARS], | ||
) -> ([C::G; OUTPUTS], Self) { | ||
let outputs: [C::G; OUTPUTS] = core::array::from_fn(|i| { | ||
matrix[i].iter().zip(scalars.iter()).map(|(generator, scalar)| *generator * ***scalar).sum() | ||
}); | ||
|
||
let nonces: [Zeroizing<C::F>; SCALARS] = | ||
core::array::from_fn(|_| Zeroizing::new(C::F::random(&mut *rng))); | ||
let R = core::array::from_fn(|i| { | ||
matrix[i].iter().zip(nonces.iter()).map(|(generator, nonce)| *generator * **nonce).sum() | ||
}); | ||
|
||
let c = Self::challenge(transcript, matrix, outputs, R); | ||
(outputs, Self { R, s: core::array::from_fn(|i| c * scalars[i].deref() + nonces[i].deref()) }) | ||
} | ||
|
||
/// Return the series of pairs whose products sum to zero for a valid signature. | ||
/// | ||
/// This is intended to be used with a multiexp for efficient batch verification. | ||
fn batch_statements( | ||
&self, | ||
transcript: &mut impl Transcript, | ||
matrix: [[C::G; SCALARS]; OUTPUTS], | ||
outputs: [C::G; OUTPUTS], | ||
) -> [[(C::F, C::G); SCALARS_PLUS_TWO]; OUTPUTS] { | ||
assert_eq!(SCALARS_PLUS_TWO, SCALARS + 2); | ||
let c = Self::challenge(transcript, matrix, outputs, self.R); | ||
core::array::from_fn(|i| { | ||
core::array::from_fn(|j| { | ||
if j == SCALARS { | ||
(-C::F::ONE, self.R[i]) | ||
} else if j == (SCALARS + 1) { | ||
(-c, outputs[i]) | ||
} else { | ||
(self.s[j], matrix[i][j]) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
/// Verify a Generalized Schnorr proof. | ||
#[must_use] | ||
pub fn verify( | ||
&self, | ||
transcript: &mut impl Transcript, | ||
matrix: [[C::G; SCALARS]; OUTPUTS], | ||
outputs: [C::G; OUTPUTS], | ||
) -> bool { | ||
for statements in self.batch_statements(transcript, matrix, outputs) { | ||
if !bool::from(multiexp_vartime(statements.as_slice()).is_identity()) { | ||
return false; | ||
} | ||
} | ||
true | ||
} | ||
|
||
/// Queue a proof for batch verification. | ||
pub fn batch_verify<R: RngCore + CryptoRng, I: Copy + Zeroize>( | ||
&self, | ||
rng: &mut R, | ||
transcript: &mut impl Transcript, | ||
batch: &mut BatchVerifier<I, C::G>, | ||
id: I, | ||
matrix: [[C::G; SCALARS]; OUTPUTS], | ||
outputs: [C::G; OUTPUTS], | ||
) { | ||
for statements in self.batch_statements(transcript, matrix, outputs) { | ||
batch.queue(rng, id, statements); | ||
} | ||
} | ||
} |
Oops, something went wrong.