diff --git a/coins/monero/src/tests/frost.rs b/coins/monero/src/tests/frost.rs index 59f2b707..710328f8 100644 --- a/coins/monero/src/tests/frost.rs +++ b/coins/monero/src/tests/frost.rs @@ -3,15 +3,24 @@ use rand::rngs::OsRng; use sha2::Sha512; use dalek_ff_group as dfg; -use frost::{Curve, algorithm::Hram, tests::{curve::test_curve, vectors::{Vectors, vectors}}}; +use frost::{ + Curve, + algorithm::Hram, + tests::{curve::test_curve, schnorr::test_schnorr, vectors::{Vectors, vectors}} +}; use crate::frost::{Ed25519, Ed25519Internal}; #[test] -fn frost_ed25519() { +fn frost_ed25519_curve() { test_curve::<_, Ed25519>(&mut OsRng); } +#[test] +fn frost_ed25519_schnorr() { + test_schnorr::<_, Ed25519>(&mut OsRng); +} + // Not spec-compliant, as this shouldn't use wide reduction // Is vectors compliant, which is why the below tests pass // See https://github.com/cfrg/draft-irtf-cfrg-frost/issues/204 diff --git a/crypto/frost/src/tests/curve.rs b/crypto/frost/src/tests/curve.rs index ab83a0d9..33f4f516 100644 --- a/crypto/frost/src/tests/curve.rs +++ b/crypto/frost/src/tests/curve.rs @@ -1,9 +1,6 @@ use rand_core::{RngCore, CryptoRng}; -use crate::{ - Curve, MultisigKeys, - tests::{schnorr::{sign, verify, batch_verify}, key_gen} -}; +use crate::{Curve, MultisigKeys, tests::key_gen}; // Test generation of FROST keys fn key_generation(rng: &mut R) { @@ -21,13 +18,6 @@ fn keys_serialization(rng: &mut R) { pub fn test_curve(rng: &mut R) { // TODO: Test the Curve functions themselves - // Test Schnorr signatures work as expected - // This is a bit unnecessary, as they should for any valid curve, yet this provides tests with - // meaning, which the above tests won't have - sign::<_, C>(rng); - verify::<_, C>(rng); - batch_verify::<_, C>(rng); - // Test FROST key generation and serialization of MultisigKeys works as expected key_generation::<_, C>(rng); keys_serialization::<_, C>(rng); diff --git a/crypto/frost/src/tests/literal/mod.rs b/crypto/frost/src/tests/literal/mod.rs index adb87b1a..eea846ee 100644 --- a/crypto/frost/src/tests/literal/mod.rs +++ b/crypto/frost/src/tests/literal/mod.rs @@ -1,2 +1 @@ mod p256; -mod schnorr; diff --git a/crypto/frost/src/tests/literal/p256.rs b/crypto/frost/src/tests/literal/p256.rs index c2668329..d98d4824 100644 --- a/crypto/frost/src/tests/literal/p256.rs +++ b/crypto/frost/src/tests/literal/p256.rs @@ -12,7 +12,7 @@ use p256::{elliptic_curve::bigint::{Encoding, U384}, Scalar, ProjectivePoint}; use crate::{ CurveError, Curve, algorithm::Hram, - tests::{curve::test_curve, vectors::{Vectors, vectors}} + tests::{curve::test_curve, schnorr::test_schnorr, vectors::{Vectors, vectors}} }; const CONTEXT_STRING: &[u8] = b"FROST-P256-SHA256-v5"; @@ -179,8 +179,13 @@ fn p256_curve() { test_curve::<_, P256>(&mut OsRng); } +#[test] +fn p256_schnorr() { + test_schnorr::<_, P256>(&mut OsRng); +} + #[derive(Clone)] -pub struct IetfP256Hram {} +pub struct IetfP256Hram; impl Hram for IetfP256Hram { #[allow(non_snake_case)] fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar { diff --git a/crypto/frost/src/tests/literal/schnorr.rs b/crypto/frost/src/tests/literal/schnorr.rs deleted file mode 100644 index 0d81bf8a..00000000 --- a/crypto/frost/src/tests/literal/schnorr.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::rc::Rc; - -use rand::rngs::OsRng; - -use crate::{ - Curve, schnorr, algorithm::{Hram, Schnorr}, - tests::{key_gen, algorithm_machines, sign as sign_test, literal::p256::{P256, IetfP256Hram}} -}; - -const MESSAGE: &[u8] = b"Hello World"; - -#[test] -fn sign() { - sign_test( - &mut OsRng, - algorithm_machines( - &mut OsRng, - Schnorr::::new(), - &key_gen::<_, P256>(&mut OsRng) - ), - MESSAGE - ); -} - -#[test] -fn sign_with_offset() { - let mut keys = key_gen::<_, P256>(&mut OsRng); - let group_key = keys[&1].group_key(); - - let offset = P256::hash_to_F(b"offset", &[]); - for i in 1 ..= u16::try_from(keys.len()).unwrap() { - keys.insert(i, Rc::new(keys[&i].offset(offset))); - } - let offset_key = group_key + (P256::generator_table() * offset); - - let sig = sign_test( - &mut OsRng, - algorithm_machines(&mut OsRng, Schnorr::::new(), &keys), - MESSAGE - ); - assert!(schnorr::verify(offset_key, IetfP256Hram::hram(&sig.R, &offset_key, MESSAGE), &sig)); -} diff --git a/crypto/frost/src/tests/mod.rs b/crypto/frost/src/tests/mod.rs index cc9c8aad..5a1b58f1 100644 --- a/crypto/frost/src/tests/mod.rs +++ b/crypto/frost/src/tests/mod.rs @@ -13,11 +13,9 @@ use crate::{ sign::{StateMachine, AlgorithmMachine} }; -// Internal tests -mod schnorr; - // Test suites for public usage pub mod curve; +pub mod schnorr; pub mod vectors; // Literal test definitions to run during `cargo test` @@ -56,7 +54,7 @@ pub fn key_gen( i, key_gen::StateMachine::::new( params[&i], - "FROST test key_gen".to_string() + "FROST Test key_gen".to_string() ) ); commitments.insert( diff --git a/crypto/frost/src/tests/schnorr.rs b/crypto/frost/src/tests/schnorr.rs index 5f64c303..4b39fcce 100644 --- a/crypto/frost/src/tests/schnorr.rs +++ b/crypto/frost/src/tests/schnorr.rs @@ -1,10 +1,15 @@ +use std::{marker::PhantomData, rc::Rc, collections::HashMap}; + use rand_core::{RngCore, CryptoRng}; use ff::Field; -use crate::{Curve, schnorr, algorithm::SchnorrSignature}; +use crate::{ + Curve, MultisigKeys, schnorr::{self, SchnorrSignature}, algorithm::{Hram, Schnorr}, + tests::{key_gen, algorithm_machines, sign as sign_test} +}; -pub(crate) fn sign(rng: &mut R) { +pub(crate) fn core_sign(rng: &mut R) { 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 @@ -20,7 +25,7 @@ pub(crate) fn sign(rng: &mut R) { // 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 verify(rng: &mut R) { +pub(crate) fn core_verify(rng: &mut R) { assert!( !schnorr::verify::( C::generator_table() * C::F::random(&mut *rng), @@ -30,7 +35,7 @@ pub(crate) fn verify(rng: &mut R) { ); } -pub(crate) fn batch_verify(rng: &mut R) { +pub(crate) fn core_batch_verify(rng: &mut R) { // Create 5 signatures let mut keys = vec![]; let mut challenges = vec![]; @@ -71,3 +76,56 @@ pub(crate) fn batch_verify(rng: &mut R) { } } } + +fn sign_core( + rng: &mut R, + group_key: C::G, + keys: &HashMap>> +) { + const MESSAGE: &'static [u8] = b"Hello, World!"; + + let machines = algorithm_machines(rng, Schnorr::>::new(), keys); + let sig = sign_test(&mut *rng, machines, MESSAGE); + assert!(schnorr::verify(group_key, TestHram::::hram(&sig.R, &group_key, MESSAGE), &sig)); +} + +#[derive(Clone)] +pub struct TestHram { + _curve: PhantomData +} +impl Hram for TestHram { + #[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(rng: &mut R) { + let keys = key_gen::<_, C>(&mut *rng); + sign_core(rng, keys[&1].group_key(), &keys); +} + +fn sign_with_offset(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() { + keys.insert(i, Rc::new(keys[&i].offset(offset))); + } + let offset_key = group_key + (C::generator_table() * offset); + + sign_core(rng, offset_key, &keys); +} + +pub fn test_schnorr(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); +}