make batch testing generic; remove duplicated Ristretto255 implementation

This commit is contained in:
Conrado Gouvea 2022-09-30 17:11:57 -03:00 committed by Deirdre Connolly
parent 828279d847
commit be43c4a082
8 changed files with 46 additions and 335 deletions

View File

@ -6,6 +6,7 @@ use rand_core::{CryptoRng, RngCore};
use crate::Ciphersuite;
pub mod batch;
pub mod proptests;
pub mod vectors;

View File

@ -1,18 +1,12 @@
use rand::thread_rng;
//! Ciphersuite-generic batch test functions.
use crate::*;
use frost_core::*;
mod common;
use common::ciphersuite::Ristretto255Sha512 as R;
#[test]
fn batch_verify() {
let mut rng = thread_rng();
let mut batch = batch::Verifier::<R>::new();
for _ in 0..32 {
/// Test batch verification with a Ciphersuite.
pub fn batch_verify<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
let mut batch = batch::Verifier::<C>::new();
for _ in 0..1 {
let sk = SigningKey::new(&mut rng);
let vk = VerifyingKey::<R>::from(&sk);
let vk = VerifyingKey::<C>::from(&sk);
let msg = b"BatchVerifyTest";
let sig = sk.sign(&mut rng, &msg[..]);
assert!(vk.verify(msg, &sig).is_ok());
@ -21,17 +15,16 @@ fn batch_verify() {
assert!(batch.verify(rng).is_ok());
}
#[test]
fn bad_batch_verify() {
let mut rng = thread_rng();
/// Test failure case of batch verification with a Ciphersuite.
pub fn bad_batch_verify<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
let bad_index = 4; // must be even
let mut batch = batch::Verifier::<R>::new();
let mut batch = batch::Verifier::<C>::new();
let mut items = Vec::new();
for i in 0..32 {
let item: batch::Item<R> = match i % 2 {
let item: batch::Item<C> = match i % 2 {
0 => {
let sk = SigningKey::new(&mut rng);
let vk = VerifyingKey::<R>::from(&sk);
let vk = VerifyingKey::<C>::from(&sk);
let msg = b"BatchVerifyTest";
let sig = if i != bad_index {
sk.sign(&mut rng, &msg[..])
@ -42,7 +35,7 @@ fn bad_batch_verify() {
}
1 => {
let sk = SigningKey::new(&mut rng);
let vk = VerifyingKey::<R>::from(&sk);
let vk = VerifyingKey::<C>::from(&sk);
let msg = b"BatchVerifyTest";
let sig = sk.sign(&mut rng, &msg[..]);
(vk, sig, msg).into()

View File

@ -1,192 +0,0 @@
#![allow(non_snake_case)]
use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
traits::Identity,
};
use rand_core::{CryptoRng, RngCore};
use sha2::{digest::Update, Digest, Sha512};
use frost_core::{Ciphersuite, Error, Field, Group};
#[derive(Clone, Copy)]
pub struct RistrettoScalarField;
impl Field for RistrettoScalarField {
type Scalar = Scalar;
type Serialization = [u8; 32];
fn zero() -> Self::Scalar {
Scalar::zero()
}
fn one() -> Self::Scalar {
Scalar::one()
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, Error> {
// [`curve25519_dalek::scalar::Scalar`]'s Eq/PartialEq does a constant-time comparison using
// `ConstantTimeEq`
if *scalar == <Self as Field>::zero() {
Err(Error::InvalidZeroScalar)
} else {
Ok(scalar.invert())
}
}
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Scalar::random(rng)
}
fn random_nonzero<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
loop {
let scalar = Scalar::random(rng);
// This impl of `Eq` calls to `ConstantTimeEq` under the hood
if scalar != Scalar::zero() {
return scalar;
}
}
}
fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
scalar.to_bytes()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, Error> {
match Scalar::from_canonical_bytes(*buf) {
Some(s) => Ok(s),
None => Err(Error::MalformedScalar),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct RistrettoGroup;
impl Group for RistrettoGroup {
type Field = RistrettoScalarField;
type Element = RistrettoPoint;
type Serialization = [u8; 32];
fn cofactor() -> <Self::Field as Field>::Scalar {
Scalar::one()
}
fn identity() -> Self::Element {
RistrettoPoint::identity()
}
fn generator() -> Self::Element {
RISTRETTO_BASEPOINT_POINT
}
fn serialize(element: &Self::Element) -> Self::Serialization {
element.compress().to_bytes()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
match CompressedRistretto::from_slice(buf.as_ref()).decompress() {
Some(point) => Ok(point),
None => Err(Error::MalformedElement),
}
}
}
/// Context string 'FROST-RISTRETTO255-SHA512-v5' from the ciphersuite in the [spec]
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.2-1
const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v10";
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Ristretto255Sha512;
impl Ciphersuite for Ristretto255Sha512 {
type Group = RistrettoGroup;
type HashOutput = [u8; 64];
type SignatureSerialization = [u8; 64];
/// H1 for FROST(ristretto255, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.2-2.2.2.1
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("rho")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
}
/// H2 for FROST(ristretto255, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.2-2.2.2.2
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("chal")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
}
/// H3 for FROST(ristretto255, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.2-2.2.2.3
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("nonce")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
}
/// H4 for FROST(ristretto255, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.2-2.2.2.4
fn H4(m: &[u8]) -> Self::HashOutput {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("msg")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
output
}
/// H5 for FROST(ristretto255, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.2-2.2.2.5
fn H5(m: &[u8]) -> Self::HashOutput {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("com")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
output
}
}
#[test]
fn use_parameterized_types() {
let h3_image = Ristretto255Sha512::H3(b"test_message");
println!("h3_image: {:?}", h3_image);
}

View File

@ -1,12 +0,0 @@
//! Shared code for `frost-ristretto255` integration tests.
//!
//! # Warning
//!
//! Test functions in this file and its submodules will not be run.
//! This file is only for test library code.
//!
//! This module uses the legacy directory structure,
//! to avoid compiling an empty "common" test binary:
//! <https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-tests>
pub mod ciphersuite;

View File

@ -1,68 +0,0 @@
{
"config": {
"MAX_PARTICIPANTS": "3",
"NUM_PARTICIPANTS": "2",
"MIN_PARTICIPANTS": "2",
"name": "FROST(ristretto255, SHA-512)",
"group": "ristretto255",
"hash": "SHA-512"
},
"inputs": {
"group_secret_key": "1b25a55e463cfd15cf14a5d3acc3d15053f08da49c8afcf3ab265f2ebc4f970b",
"group_public_key": "e2a62f39eede11269e3bd5a7d97554f5ca384f9f6d3dd9c3c0d05083c7254f57",
"message": "74657374",
"share_polynomial_coefficients": [
"410f8b744b19325891d73736923525a4f596c805d060dfb9c98009d34e3fec02"
],
"participants": {
"1": {
"participant_share": "5c3430d391552f6e60ecdc093ff9f6f4488756aa6cebdbad75a768010b8f830e"
},
"2": {
"participant_share": "b06fc5eac20b4f6e1b271d9df2343d843e1e1fb03c4cbb673f2872d459ce6f01"
},
"3": {
"participant_share": "f17e505f0e2581c6acfe54d3846a622834b5e7b50cad9a2109a97ba7a80d5c04"
}
}
},
"round_one_outputs": {
"participant_list": "1,3",
"participants": {
"1": {
"hiding_nonce_randomness": "0a016efd0abf8e556fd67288950bb7fc0843be63e306c7264bc9d24d1d65e0ee",
"binding_nonce_randomness": "35b6bab19e3e931e36c612ccc6b3c9d3a3479d2704aac3324b79c7bb6665acfb",
"hiding_nonce": "de3e8f526dcb51a1b9b48cc284aeca27c385aa3ba1a92a0c8440d51e1a1d2f00",
"binding_nonce": "fa8dca5ec7a05d5a7b782be847ba3dde1509de1dbcf0569fc980cff795db5404",
"hiding_nonce_commitment": "3677297a5df660bf63bb8fcae79b7f98cf4f2e99f61bc762de9795cacd1cba62",
"binding_nonce_commitment": "142aece8aa8b16766664d8aaa5a5e709404bb8443309ef1ea9ad9254794a1f09",
"binding_factor_input": "c70ac0b3effa113b8f4d8a6b1393ef7f0910862d143fde83e410db94f3818295ff49ed5aed0e57b2712f2ce0f9166f1ffdce282786c7ee8c2db2df295c61dc5fd0f93a769d09d44352c4e709c2e239fc34a1b89db44cb2410602285ffd70f3fa0a62dd70cfdb369ac0a7efc587f6f671a88412b2570280da24bd36f8ffda6d280100000000000000000000000000000000000000000000000000000000000000",
"binding_factor": "dbaa0ae3c5663816cdc646281be46b0b09eca6a1ecf7781f29475be27d30fd08"
},
"3": {
"hiding_nonce_randomness": "ac4e65529397de3a868a902e9040e38b26547c18b7267fa1d1bbfe4ed14d6b5f",
"binding_nonce_randomness": "74213c820b7266c4990a0758f4c520685375cb98822499406654bdb1a426582e",
"hiding_nonce": "e07061a9ab6735de9a75b0c64f086c5b999894611d0cdc03f85c4e87c8aae602",
"binding_nonce": "38b17578e8e6ad4077071ce6b0bf9cb85ac35fee7868dcb6d9bfa97f0e153e0e",
"hiding_nonce_commitment": "f8d758ad9373754c1d2bca9c38478e4eb857aa032836ade6eb0726f5e1d08037",
"binding_nonce_commitment": "529823e80220849c195072a26acca88f65639d4181927bb7fcd96e43d9a34649",
"binding_factor_input": "c70ac0b3effa113b8f4d8a6b1393ef7f0910862d143fde83e410db94f3818295ff49ed5aed0e57b2712f2ce0f9166f1ffdce282786c7ee8c2db2df295c61dc5fd0f93a769d09d44352c4e709c2e239fc34a1b89db44cb2410602285ffd70f3fa0a62dd70cfdb369ac0a7efc587f6f671a88412b2570280da24bd36f8ffda6d280300000000000000000000000000000000000000000000000000000000000000",
"binding_factor": "aa076fec41410f6c0667e47443fcd1ed828854d84b19d1d08624d084720c7d05"
}
}
},
"round_two_outputs": {
"participant_list": "1,3",
"participants": {
"1": {
"sig_share": "a5f046916a6a111672111e47f9825586e1188da8a0f3b7c61f2b6b432c636e07"
},
"3": {
"sig_share": "4c175c7e43bd197980c2021774036eb288f54179f079fbf21b7d2f9f52846401"
}
}
},
"final_output": {
"sig": "94b11def3f919503c3544452ad2a59f198f64cc323bd758bb1c65b42032a7473f107a30fae272b8ff2d3205e6d86c3386a0ecf21916db3b93ba89ae27ee7d208"
}
}

View File

@ -1,43 +0,0 @@
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
mod common;
use common::ciphersuite::*;
lazy_static! {
pub static ref RISTRETTO255_SHA512: Value =
serde_json::from_str(include_str!("common/vectors.json").trim())
.expect("Test vector is valid JSON");
}
/// This is testing that Shamir's secret sharing to compute and arbitrary
/// value is working.
#[test]
fn check_share_generation_ristretto255_sha512() {
let rng = thread_rng();
frost_core::tests::check_share_generation::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_sign_with_test_vectors() {
frost_core::tests::vectors::check_sign_with_test_vectors::<Ristretto255Sha512>(
&RISTRETTO255_SHA512,
)
}
// This allows checking that to_scalar() works for all possible inputs;
// but requires making to_scalar() public.
// #[test]
// fn test_identifier_to_scalar() {
// type R = Ristretto255Sha512;
// let one = <<<R as Ciphersuite>::Group as Group>::Field as Field>::one();
// let mut sum = <<<R as Ciphersuite>::Group as Group>::Field as Field>::one();
// for i in 1..0xFFFFu16 {
// let identifier: Identifier<R> = i.try_into().unwrap();
// assert_eq!(sum, identifier.to_scalar());
// sum = sum + one;
// }
// }

View File

@ -7,3 +7,21 @@ fn check_sign_with_dealer() {
frost_core::tests::check_sign_with_dealer::<P256Sha256, _>(rng);
}
// TODO: re-enable after batch is changed to work with big-endian scalars
// #[test]
#[allow(unused)]
fn check_batch_verify() {
let rng = thread_rng();
frost_core::tests::batch::batch_verify::<P256Sha256, _>(rng);
}
// TODO: re-enable after batch is changed to work with big-endian scalars
// #[test]
#[allow(unused)]
fn check_bad_batch_verify() {
let rng = thread_rng();
frost_core::tests::batch::bad_batch_verify::<P256Sha256, _>(rng);
}

View File

@ -7,3 +7,17 @@ fn check_sign_with_dealer() {
frost_core::tests::check_sign_with_dealer::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_batch_verify() {
let rng = thread_rng();
frost_core::tests::batch::batch_verify::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_bad_batch_verify() {
let rng = thread_rng();
frost_core::tests::batch::bad_batch_verify::<Ristretto255Sha512, _>(rng);
}