mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Create a dedicated crate for the DKG (#141)
* Add dkg crate * Remove F_len and G_len They're generally no longer used. * Replace hash_to_vec with a provided method around associated type H: Digest Part of trying to minimize this trait so it can be moved elsewhere. Vec, which isn't std, may have been a blocker. * Encrypt secret shares within the FROST library Reduces requirements on callers in order to be correct. * Update usage of Zeroize within FROST * Inline functions in key_gen There was no reason to have them separated as they were. sign probably has the same statement available, yet that isn't the focus right now. * Add a ciphersuite package which provides hash_to_F * Set the Ciphersuite version to something valid * Have ed448 export Scalar/FieldElement/Point at the top level * Move FROST over to Ciphersuite * Correct usage of ff in ciphersuite * Correct documentation handling * Move Schnorr signatures to their own crate * Remove unused feature from schnorr * Fix Schnorr tests * Split DKG into a separate crate * Add serialize to Commitments and SecretShare Helper for buf = vec![]; .write(buf).unwrap(); buf * Move FROST over to the new dkg crate * Update Monero lib to latest FROST * Correct ethereum's usage of features * Add serialize to GeneratorProof * Add serialize helper function to FROST * Rename AddendumSerialize to WriteAddendum * Update processor * Slight fix to processor
This commit is contained in:
27
crypto/schnorr/Cargo.toml
Normal file
27
crypto/schnorr/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "schnorr-signatures"
|
||||
version = "0.1.0"
|
||||
description = "Minimal Schnorr signatures crate hosting common code"
|
||||
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"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rand_core = "0.6"
|
||||
|
||||
zeroize = { version = "1.5", features = ["zeroize_derive"] }
|
||||
|
||||
group = "0.12"
|
||||
ciphersuite = { path = "../ciphersuite", version = "0.1" }
|
||||
|
||||
multiexp = { path = "../multiexp", version = "0.2", features = ["batch"] }
|
||||
|
||||
[dev-dependencies]
|
||||
dalek-ff-group = { path = "../dalek-ff-group", version = "^0.1.2" }
|
||||
ciphersuite = { path = "../ciphersuite", version = "0.1", features = ["ristretto"] }
|
||||
21
crypto/schnorr/LICENSE
Normal file
21
crypto/schnorr/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2022 Luke Parker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
86
crypto/schnorr/src/lib.rs
Normal file
86
crypto/schnorr/src/lib.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use group::{
|
||||
ff::{Field, PrimeField},
|
||||
GroupEncoding,
|
||||
};
|
||||
|
||||
use multiexp::BatchVerifier;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// A Schnorr signature of the form (R, s) where s = r + cx.
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct SchnorrSignature<C: Ciphersuite> {
|
||||
pub R: C::G,
|
||||
pub s: C::F,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> SchnorrSignature<C> {
|
||||
/// Read a SchnorrSignature from something implementing Read.
|
||||
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
Ok(SchnorrSignature { R: C::read_G(reader)?, s: C::read_F(reader)? })
|
||||
}
|
||||
|
||||
/// Write a SchnorrSignature to something implementing Read.
|
||||
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(self.R.to_bytes().as_ref())?;
|
||||
writer.write_all(self.s.to_repr().as_ref())
|
||||
}
|
||||
|
||||
/// Serialize a SchnorrSignature, returning a Vec<u8>.
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut buf = vec![];
|
||||
self.write(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
/// Sign a Schnorr signature with the given nonce for the specified challenge.
|
||||
pub fn sign(mut private_key: C::F, mut nonce: C::F, challenge: C::F) -> SchnorrSignature<C> {
|
||||
let res = SchnorrSignature { R: C::generator() * nonce, s: nonce + (private_key * challenge) };
|
||||
private_key.zeroize();
|
||||
nonce.zeroize();
|
||||
res
|
||||
}
|
||||
|
||||
/// Verify a Schnorr signature for the given key with the specified challenge.
|
||||
#[must_use]
|
||||
pub fn verify(&self, public_key: C::G, challenge: C::F) -> bool {
|
||||
(C::generator() * self.s) == (self.R + (public_key * challenge))
|
||||
}
|
||||
|
||||
/// Queue a signature for batch verification.
|
||||
pub fn batch_verify<R: RngCore + CryptoRng, I: Copy + Zeroize>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
batch: &mut BatchVerifier<I, C::G>,
|
||||
id: I,
|
||||
public_key: C::G,
|
||||
challenge: C::F,
|
||||
) {
|
||||
// s = r + ca
|
||||
// sG == R + cA
|
||||
// R + cA - sG == 0
|
||||
|
||||
batch.queue(
|
||||
rng,
|
||||
id,
|
||||
[
|
||||
// R
|
||||
(C::F::one(), self.R),
|
||||
// cA
|
||||
(challenge, public_key),
|
||||
// -sG
|
||||
(-self.s, C::generator()),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
72
crypto/schnorr/src/tests.rs
Normal file
72
crypto/schnorr/src/tests.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use rand_core::OsRng;
|
||||
|
||||
use group::{ff::Field, Group};
|
||||
|
||||
use multiexp::BatchVerifier;
|
||||
|
||||
use ciphersuite::{Ciphersuite, Ristretto};
|
||||
use crate::SchnorrSignature;
|
||||
|
||||
pub(crate) fn core_sign<C: Ciphersuite>() {
|
||||
let private_key = C::random_nonzero_F(&mut OsRng);
|
||||
let nonce = C::random_nonzero_F(&mut OsRng);
|
||||
let challenge = C::random_nonzero_F(&mut OsRng); // Doesn't bother to craft an HRAm
|
||||
assert!(SchnorrSignature::<C>::sign(private_key, nonce, challenge)
|
||||
.verify(C::generator() * private_key, challenge));
|
||||
}
|
||||
|
||||
// The above sign function verifies signing works
|
||||
// This verifies invalid signatures don't pass, using zero signatures, which should effectively be
|
||||
// random
|
||||
pub(crate) fn core_verify<C: Ciphersuite>() {
|
||||
assert!(!SchnorrSignature::<C> { R: C::G::identity(), s: C::F::zero() }
|
||||
.verify(C::generator() * C::random_nonzero_F(&mut OsRng), C::random_nonzero_F(&mut OsRng)));
|
||||
}
|
||||
|
||||
pub(crate) fn core_batch_verify<C: Ciphersuite>() {
|
||||
// Create 5 signatures
|
||||
let mut keys = vec![];
|
||||
let mut challenges = vec![];
|
||||
let mut sigs = vec![];
|
||||
for i in 0 .. 5 {
|
||||
keys.push(C::random_nonzero_F(&mut OsRng));
|
||||
challenges.push(C::random_nonzero_F(&mut OsRng));
|
||||
sigs.push(SchnorrSignature::<C>::sign(keys[i], C::random_nonzero_F(&mut OsRng), challenges[i]));
|
||||
}
|
||||
|
||||
// Batch verify
|
||||
{
|
||||
let mut batch = BatchVerifier::new(5);
|
||||
for (i, sig) in sigs.iter().enumerate() {
|
||||
sig.batch_verify(&mut OsRng, &mut batch, i, C::generator() * keys[i], challenges[i]);
|
||||
}
|
||||
batch.verify_with_vartime_blame().unwrap();
|
||||
}
|
||||
|
||||
// Shift 1 from s from one to another and verify it fails
|
||||
// This test will fail if unique factors aren't used per-signature, hence its inclusion
|
||||
{
|
||||
let mut batch = BatchVerifier::new(5);
|
||||
for (i, mut sig) in sigs.clone().drain(..).enumerate() {
|
||||
if i == 1 {
|
||||
sig.s += C::F::one();
|
||||
}
|
||||
if i == 2 {
|
||||
sig.s -= C::F::one();
|
||||
}
|
||||
sig.batch_verify(&mut OsRng, &mut batch, i, C::generator() * keys[i], challenges[i]);
|
||||
}
|
||||
if let Err(blame) = batch.verify_with_vartime_blame() {
|
||||
assert!((blame == 1) || (blame == 2));
|
||||
} else {
|
||||
panic!("Batch verification considered malleated signatures valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
core_sign::<Ristretto>();
|
||||
core_verify::<Ristretto>();
|
||||
core_batch_verify::<Ristretto>();
|
||||
}
|
||||
Reference in New Issue
Block a user