Add initial eVRF DKG test

This commit is contained in:
Luke Parker
2024-07-26 21:00:05 -04:00
parent ef68885600
commit 4bd0d71406
6 changed files with 111 additions and 13 deletions

1
Cargo.lock generated
View File

@@ -2141,6 +2141,7 @@ dependencies = [
"generic-array 1.1.0",
"multiexp",
"pasta_curves",
"rand",
"rand_chacha",
"rand_core",
"schnorr-signatures",

View File

@@ -48,6 +48,7 @@ generalized-bulletproofs-ec-gadgets = { path = "../evrf/ec-gadgets", optional =
[dev-dependencies]
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
rand = { version = "0.8", default-features = false, features = ["std"] }
ciphersuite = { path = "../ciphersuite", default-features = false, features = ["ristretto"] }
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", features = ["tests"] }
ec-divisors = { path = "../evrf/divisors", features = ["pasta"] }

View File

@@ -90,7 +90,8 @@ use ec_divisors::DivisorCurve;
use crate::{Participant, DkgError, ThresholdParams, ThresholdCore};
pub(crate) mod proof;
pub use proof::*;
use proof::*;
pub use proof::EvrfCurve;
/// Participation in the DKG.
///
@@ -191,7 +192,7 @@ fn share_verification_statements<C: Ciphersuite>(
}
/// Struct to perform/verify the DKG with.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct EvrfDkg<C: EvrfCurve> {
t: u16,
n: u16,
@@ -204,6 +205,11 @@ where
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
{
/// Sample generators for this ciphersuite.
pub fn generators(max_threshold: u16, max_participants: u16) -> Generators<C> {
Evrf::<C>::generators(max_threshold, max_participants)
}
/// Participate in performing the DKG for the specified parameters.
///
/// The context MUST be unique across invocations. Reuse of context will lead to sharing
@@ -257,6 +263,10 @@ where
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
participations: &HashMap<Participant, Participation<C>>,
) -> Result<Self, Vec<Participant>> {
if generators.g() != C::generator() {
todo!("TODO");
}
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { todo!("TODO") };
if (t == 0) || (t > n) {
todo!("TODO");
@@ -371,7 +381,7 @@ where
}
pub fn keys(
self,
&self,
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
) -> Option<ThresholdCore<C>> {
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();

View File

@@ -33,6 +33,16 @@ pub trait EvrfCurve: Ciphersuite {
type EmbeddedCurveParameters: DiscreteLogParameters;
}
fn sample_point<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
let mut repr = <C::G as GroupEncoding>::Repr::default();
loop {
rng.fill_bytes(repr.as_mut());
if let Ok(point) = C::read_G(&mut repr.as_ref()) {
return point;
}
}
}
/// The result of proving for an eVRF.
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
/// The coefficients for use in the DKG.
@@ -44,6 +54,7 @@ pub(crate) struct EvrfProveResult<C: Ciphersuite> {
}
/// The result of verifying an eVRF.
#[derive(Clone)]
pub(crate) struct EvrfVerifyResult<C: EvrfCurve> {
/// The commitments to the coefficients for use in the DKG.
pub(crate) coefficients: Vec<C::G>,
@@ -66,6 +77,22 @@ where
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
{
// TODO: Wrap these Generators so we can enforce g == C::generator() with type safety
pub(crate) fn generators(max_threshold: u16, max_participants: u16) -> Generators<C> {
let g = C::generator();
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(g.to_bytes()).into());
let h = sample_point::<C>(&mut rng);
let (_, generators) =
Evrf::<C>::muls_and_generators_to_use(max_threshold.into(), max_participants.into());
let mut g_bold = vec![];
let mut h_bold = vec![];
for _ in 0 .. generators {
g_bold.push(sample_point::<C>(&mut rng));
h_bold.push(sample_point::<C>(&mut rng));
}
Generators::new(g, h, g_bold, h_bold).unwrap()
}
// Sample uniform points (via rejection-sampling) on the embedded elliptic curve
fn transcript_to_points(
seed: [u8; 32],
@@ -76,12 +103,8 @@ where
let mut rng = ChaCha20Rng::from_seed(seed);
let mut res = Vec::with_capacity(quantity);
while res.len() < quantity {
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
rng.fill_bytes(repr.as_mut());
if let Ok(point) = C::EmbeddedCurve::read_G(&mut repr.as_ref()) {
res.push(point);
}
for _ in 0 .. quantity {
res.push(sample_point::<C::EmbeddedCurve>(&mut rng));
}
res
}
@@ -154,7 +177,7 @@ where
const MULS_PER_DH: usize = 7;
// 1 DH to prove the discrete logarithm corresponds to the eVRF public key
// 2 DHs per generated coefficient
// 2 DHs per generated ECDG
// 2 DHs per generated ECDH
let expected_muls = MULS_PER_DH * (1 + (2 * coefficients) + (2 * 2 * ecdhs));
let generators_to_use = {
let mut padded_pow_of_2 = 1;

View File

@@ -1 +1,64 @@
use std::collections::HashMap;
use zeroize::Zeroizing;
use rand_core::OsRng;
use rand::seq::SliceRandom;
use ciphersuite::{group::ff::Field, Ciphersuite};
use crate::{
Participant, ThresholdKeys,
evrf::*,
tests::{THRESHOLD, PARTICIPANTS},
};
mod proof;
use proof::{Pallas, Vesta};
#[test]
fn evrf_dkg() {
let generators = EvrfDkg::<Pallas>::generators(THRESHOLD, PARTICIPANTS);
let mut priv_keys = vec![];
let mut pub_keys = vec![];
for i in 0 .. PARTICIPANTS {
let priv_key = <Vesta as Ciphersuite>::F::random(&mut OsRng);
pub_keys.push(<Vesta as Ciphersuite>::generator() * priv_key);
priv_keys.push((Participant::new(1 + i).unwrap(), Zeroizing::new(priv_key)));
}
let mut participations = HashMap::new();
// Shuffle the private keys so we iterate over a random subset of them
priv_keys.shuffle(&mut OsRng);
for (i, priv_key) in priv_keys.iter().take(usize::from(THRESHOLD)) {
participations.insert(
*i,
EvrfDkg::<Pallas>::participate(
&mut OsRng,
&generators,
[0; 32],
THRESHOLD,
&pub_keys,
priv_key,
)
.unwrap(),
);
}
let dkg = EvrfDkg::<Pallas>::verify(
&mut OsRng,
&generators,
[0; 32],
THRESHOLD,
&pub_keys,
&participations,
)
.unwrap();
for (i, priv_key) in priv_keys {
let keys = ThresholdKeys::from(dkg.keys(&priv_key).unwrap());
assert_eq!(keys.params().i(), i);
assert_eq!(keys.params().t(), THRESHOLD);
assert_eq!(keys.params().n(), PARTICIPANTS);
}
}

View File

@@ -21,7 +21,7 @@ use generalized_bulletproofs_ec_gadgets::DiscreteLogParameters;
use crate::evrf::proof::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
struct Pallas;
pub(crate) struct Pallas;
impl Ciphersuite for Pallas {
type F = Fq;
type G = Ep;
@@ -38,7 +38,7 @@ impl Ciphersuite for Pallas {
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
struct Vesta;
pub(crate) struct Vesta;
impl Ciphersuite for Vesta {
type F = Fp;
type G = Eq;
@@ -54,7 +54,7 @@ impl Ciphersuite for Vesta {
}
}
struct VestaParams;
pub struct VestaParams;
impl DiscreteLogParameters for VestaParams {
type ScalarBits = U<{ <<Vesta as Ciphersuite>::F as PrimeField>::NUM_BITS as usize }>;
type XCoefficients = Quot<Sum<Self::ScalarBits, U1>, U2>;