2022-06-05 07:33:15 -04:00
|
|
|
use std::{marker::PhantomData, sync::Arc, collections::HashMap};
|
2022-06-03 19:08:25 -04:00
|
|
|
|
2022-05-25 00:28:57 -04:00
|
|
|
use rand_core::{RngCore, CryptoRng};
|
|
|
|
|
|
|
|
|
|
use ff::Field;
|
|
|
|
|
|
2022-06-03 19:08:25 -04:00
|
|
|
use crate::{
|
|
|
|
|
Curve, MultisigKeys, schnorr::{self, SchnorrSignature}, algorithm::{Hram, Schnorr},
|
|
|
|
|
tests::{key_gen, algorithm_machines, sign as sign_test}
|
|
|
|
|
};
|
2022-05-25 00:28:57 -04:00
|
|
|
|
2022-06-03 19:08:25 -04:00
|
|
|
pub(crate) fn core_sign<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
2022-05-25 00:28:57 -04:00
|
|
|
let private_key = C::F::random(&mut *rng);
|
|
|
|
|
let nonce = C::F::random(&mut *rng);
|
|
|
|
|
let challenge = C::F::random(rng); // Doesn't bother to craft an HRAM
|
|
|
|
|
assert!(
|
|
|
|
|
schnorr::verify::<C>(
|
2022-06-03 23:22:08 -04:00
|
|
|
C::GENERATOR_TABLE * private_key,
|
2022-05-25 00:28:57 -04:00
|
|
|
challenge,
|
|
|
|
|
&schnorr::sign(private_key, nonce, challenge)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The above sign function verifies signing works
|
|
|
|
|
// This verifies invalid signatures don't pass, using zero signatures, which should effectively be
|
|
|
|
|
// random
|
2022-06-03 19:08:25 -04:00
|
|
|
pub(crate) fn core_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
2022-05-25 00:28:57 -04:00
|
|
|
assert!(
|
|
|
|
|
!schnorr::verify::<C>(
|
2022-06-03 23:22:08 -04:00
|
|
|
C::GENERATOR_TABLE * C::F::random(&mut *rng),
|
2022-05-25 00:28:57 -04:00
|
|
|
C::F::random(rng),
|
2022-06-03 23:22:08 -04:00
|
|
|
&SchnorrSignature { R: C::GENERATOR_TABLE * C::F::zero(), s: C::F::zero() }
|
2022-05-25 00:28:57 -04:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-03 19:08:25 -04:00
|
|
|
pub(crate) fn core_batch_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
2022-05-27 02:01:01 -04:00
|
|
|
// Create 5 signatures
|
2022-05-25 00:57:00 -04:00
|
|
|
let mut keys = vec![];
|
|
|
|
|
let mut challenges = vec![];
|
|
|
|
|
let mut sigs = vec![];
|
2022-05-27 02:01:01 -04:00
|
|
|
for i in 0 .. 5 {
|
2022-05-25 00:57:00 -04:00
|
|
|
keys.push(C::F::random(&mut *rng));
|
|
|
|
|
challenges.push(C::F::random(&mut *rng));
|
|
|
|
|
sigs.push(schnorr::sign::<C>(keys[i], C::F::random(&mut *rng), challenges[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Batch verify
|
2022-05-27 02:01:01 -04:00
|
|
|
let triplets = (0 .. 5).map(
|
2022-06-03 23:22:08 -04:00
|
|
|
|i| (u16::try_from(i + 1).unwrap(), C::GENERATOR_TABLE * keys[i], challenges[i], sigs[i])
|
2022-05-25 00:57:00 -04:00
|
|
|
).collect::<Vec<_>>();
|
|
|
|
|
schnorr::batch_verify(rng, &triplets).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 triplets = triplets.clone();
|
|
|
|
|
triplets[1].3.s += C::F::one();
|
|
|
|
|
triplets[2].3.s -= C::F::one();
|
|
|
|
|
if let Err(blame) = schnorr::batch_verify(rng, &triplets) {
|
|
|
|
|
assert_eq!(blame, 2);
|
|
|
|
|
} else {
|
|
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure a completely invalid signature fails when included
|
2022-05-27 02:01:01 -04:00
|
|
|
for i in 0 .. 5 {
|
|
|
|
|
let mut triplets = triplets.clone();
|
|
|
|
|
triplets[i].3.s = C::F::random(&mut *rng);
|
|
|
|
|
if let Err(blame) = schnorr::batch_verify(rng, &triplets) {
|
|
|
|
|
assert_eq!(blame, u16::try_from(i + 1).unwrap());
|
|
|
|
|
} else {
|
|
|
|
|
assert!(false);
|
|
|
|
|
}
|
2022-05-25 00:57:00 -04:00
|
|
|
}
|
|
|
|
|
}
|
2022-06-03 19:08:25 -04:00
|
|
|
|
|
|
|
|
fn sign_core<R: RngCore + CryptoRng, C: Curve>(
|
|
|
|
|
rng: &mut R,
|
|
|
|
|
group_key: C::G,
|
2022-06-05 07:33:15 -04:00
|
|
|
keys: &HashMap<u16, Arc<MultisigKeys<C>>>
|
2022-06-03 19:08:25 -04:00
|
|
|
) {
|
|
|
|
|
const MESSAGE: &'static [u8] = b"Hello, World!";
|
|
|
|
|
|
|
|
|
|
let machines = algorithm_machines(rng, Schnorr::<C, TestHram<C>>::new(), keys);
|
|
|
|
|
let sig = sign_test(&mut *rng, machines, MESSAGE);
|
|
|
|
|
assert!(schnorr::verify(group_key, TestHram::<C>::hram(&sig.R, &group_key, MESSAGE), &sig));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct TestHram<C: Curve> {
|
|
|
|
|
_curve: PhantomData<C>
|
|
|
|
|
}
|
|
|
|
|
impl<C: Curve> Hram<C> for TestHram<C> {
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F {
|
|
|
|
|
C::hash_to_F(b"challenge", &[&C::G_to_bytes(R), &C::G_to_bytes(A), m].concat())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sign<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
|
|
|
|
let keys = key_gen::<_, C>(&mut *rng);
|
|
|
|
|
sign_core(rng, keys[&1].group_key(), &keys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sign_with_offset<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
|
|
|
|
let mut keys = key_gen::<_, C>(&mut *rng);
|
|
|
|
|
let group_key = keys[&1].group_key();
|
|
|
|
|
|
|
|
|
|
let offset = C::hash_to_F(b"FROST Test sign_with_offset", b"offset");
|
|
|
|
|
for i in 1 ..= u16::try_from(keys.len()).unwrap() {
|
2022-06-05 07:33:15 -04:00
|
|
|
keys.insert(i, Arc::new(keys[&i].offset(offset)));
|
2022-06-03 19:08:25 -04:00
|
|
|
}
|
2022-06-03 23:22:08 -04:00
|
|
|
let offset_key = group_key + (C::GENERATOR_TABLE * offset);
|
2022-06-03 19:08:25 -04:00
|
|
|
|
|
|
|
|
sign_core(rng, offset_key, &keys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn test_schnorr<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
|
|
|
|
// Test Schnorr signatures work as expected
|
|
|
|
|
// This is a bit unnecessary, as they should for any valid curve, yet this establishes sanity
|
|
|
|
|
core_sign::<_, C>(rng);
|
|
|
|
|
core_verify::<_, C>(rng);
|
|
|
|
|
core_batch_verify::<_, C>(rng);
|
|
|
|
|
|
|
|
|
|
// Test Schnorr signatures under FROST
|
|
|
|
|
sign::<_, C>(rng);
|
|
|
|
|
sign_with_offset::<_, C>(rng);
|
|
|
|
|
}
|