make batch testing generic; remove duplicated Ristretto255 implementation
This commit is contained in:
parent
828279d847
commit
be43c4a082
|
@ -6,6 +6,7 @@ use rand_core::{CryptoRng, RngCore};
|
|||
|
||||
use crate::Ciphersuite;
|
||||
|
||||
pub mod batch;
|
||||
pub mod proptests;
|
||||
pub mod vectors;
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
// }
|
||||
// }
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue