Support participating multiple times in the eVRF DKG

This commit is contained in:
Luke Parker
2024-07-28 23:41:31 -04:00
parent c5cc0dc883
commit 65efbf46c7
4 changed files with 88 additions and 64 deletions

View File

@@ -75,10 +75,11 @@ use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
use blake2::{Digest, Blake2s256};
use ciphersuite::{ use ciphersuite::{
group::{ group::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
Group, Group, GroupEncoding,
}, },
Ciphersuite, Ciphersuite,
}; };
@@ -242,6 +243,21 @@ where
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
DivisorCurve<FieldElement = <C as Ciphersuite>::F>, DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
{ {
// Form the initial transcript for the proofs.
fn initial_transcript(
invocation: [u8; 32],
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
t: u16,
) -> [u8; 32] {
let mut transcript = Blake2s256::new();
transcript.update(invocation);
for key in evrf_public_keys {
transcript.update(key.to_bytes().as_ref());
}
transcript.update(t.to_le_bytes());
transcript.finalize().into()
}
/// Participate in performing the DKG for the specified parameters. /// Participate in performing the DKG for the specified parameters.
/// ///
/// The context MUST be unique across invocations. Reuse of context will lead to sharing /// The context MUST be unique across invocations. Reuse of context will lead to sharing
@@ -255,8 +271,7 @@ where
t: u16, t: u16,
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G], evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>, evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
) -> Result<Participation<C>, EvrfError> { ) -> Result<Vec<Participation<C>>, EvrfError> {
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { Err(EvrfError::TooManyParticipants)? }; let Ok(n) = u16::try_from(evrf_public_keys.len()) else { Err(EvrfError::TooManyParticipants)? };
if (t == 0) || (t > n) { if (t == 0) || (t > n) {
Err(EvrfError::InvalidThreshold)?; Err(EvrfError::InvalidThreshold)?;
@@ -264,17 +279,33 @@ where
if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) { if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) {
Err(EvrfError::PublicKeyWasIdentity)?; Err(EvrfError::PublicKeyWasIdentity)?;
}; };
if !evrf_public_keys.iter().any(|key| *key == evrf_public_key) {
Err(EvrfError::NotAParticipant)?;
};
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
let mut res = vec![];
for (i, this_evrf_public_key) in evrf_public_keys.iter().enumerate() {
let i = u16::try_from(i + 1).expect("n <= u16::MAX yet not i?");
if *this_evrf_public_key != evrf_public_key {
continue;
}
let transcript = Self::initial_transcript(context, evrf_public_keys, t);
// Further bind to the participant index so each index gets unique generators
// This allows reusing eVRF public keys as the prover
let mut per_proof_transcript = Blake2s256::new();
per_proof_transcript.update(transcript);
per_proof_transcript.update(i.to_le_bytes());
// The above transcript is expected to be binding to all arguments here
// The generators are constant to this ciphersuite's generator, and the parameters are
// transcripted
let EvrfProveResult { coefficients, encryption_masks, proof } = match Evrf::prove( let EvrfProveResult { coefficients, encryption_masks, proof } = match Evrf::prove(
rng, rng,
&generators.0, &generators.0,
evrf_private_key, per_proof_transcript.finalize().into(),
context,
usize::from(t), usize::from(t),
evrf_public_keys, evrf_public_keys,
evrf_private_key,
) { ) {
Ok(res) => res, Ok(res) => res,
Err(AcError::NotEnoughGenerators) => Err(EvrfError::NotEnoughGenerators)?, Err(AcError::NotEnoughGenerators) => Err(EvrfError::NotEnoughGenerators)?,
@@ -297,7 +328,14 @@ where
encrypted_secret_shares.insert(l, *share + *encryption_mask); encrypted_secret_shares.insert(l, *share + *encryption_mask);
} }
Ok(Participation { proof, encrypted_secret_shares }) res.push(Participation { proof, encrypted_secret_shares });
}
if res.is_empty() {
Err(EvrfError::NotAParticipant)?;
}
Ok(res)
} }
/// Check if a batch of `Participation`s are valid. /// Check if a batch of `Participation`s are valid.
@@ -333,18 +371,24 @@ where
let mut valid = HashMap::with_capacity(participations.len()); let mut valid = HashMap::with_capacity(participations.len());
let mut faulty = HashSet::new(); let mut faulty = HashSet::new();
let transcript = Self::initial_transcript(context, evrf_public_keys, t);
let mut evrf_verifier = generators.0.batch_verifier(); let mut evrf_verifier = generators.0.batch_verifier();
for (i, participation) in participations { for (i, participation) in participations {
let mut per_proof_transcript = Blake2s256::new();
per_proof_transcript.update(transcript);
per_proof_transcript.update(u16::from(*i).to_le_bytes());
// Clone the verifier so if this proof is faulty, it doesn't corrupt the verifier // Clone the verifier so if this proof is faulty, it doesn't corrupt the verifier
let mut verifier_clone = evrf_verifier.clone(); let mut verifier_clone = evrf_verifier.clone();
let Ok(data) = Evrf::<C>::verify( let Ok(data) = Evrf::<C>::verify(
rng, rng,
&generators.0, &generators.0,
&mut verifier_clone, &mut verifier_clone,
evrf_public_keys[usize::from(u16::from(*i)) - 1], per_proof_transcript.finalize().into(),
context,
usize::from(t), usize::from(t),
evrf_public_keys, evrf_public_keys,
evrf_public_keys[usize::from(u16::from(*i)) - 1],
&participation.proof, &participation.proof,
) else { ) else {
faulty.insert(*i); faulty.insert(*i);
@@ -368,10 +412,10 @@ where
rng, rng,
&generators.0, &generators.0,
&mut evrf_verifier, &mut evrf_verifier,
evrf_public_keys[usize::from(u16::from(*i)) - 1],
context, context,
usize::from(t), usize::from(t),
evrf_public_keys, evrf_public_keys,
evrf_public_keys[usize::from(u16::from(*i)) - 1],
&participation.proof, &participation.proof,
) )
.expect("evrf failed basic checks yet prover wasn't prior marked faulty"); .expect("evrf failed basic checks yet prover wasn't prior marked faulty");

View File

@@ -458,41 +458,20 @@ where
decomposition decomposition
} }
fn transcript(
invocation: [u8; 32],
evrf_public_key: <C::EmbeddedCurve as Ciphersuite>::G,
ecdh_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
) -> [u8; 32] {
let mut transcript = Blake2s256::new();
transcript.update(invocation);
transcript.update(evrf_public_key.to_bytes().as_ref());
for ecdh in ecdh_public_keys {
transcript.update(ecdh.to_bytes().as_ref());
}
transcript.finalize().into()
}
/// Prove a point on an elliptic curve had its discrete logarithm generated via an eVRF. /// Prove a point on an elliptic curve had its discrete logarithm generated via an eVRF.
pub(crate) fn prove( pub(crate) fn prove(
rng: &mut (impl RngCore + CryptoRng), rng: &mut (impl RngCore + CryptoRng),
generators: &Generators<C>, generators: &Generators<C>,
evrf_private_key: &Zeroizing<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>, transcript: [u8; 32],
invocation: [u8; 32],
coefficients: usize, coefficients: usize,
ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G], ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G],
evrf_private_key: &Zeroizing<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
) -> Result<EvrfProveResult<C>, AcError> { ) -> Result<EvrfProveResult<C>, AcError> {
let curve_spec = CurveSpec { let curve_spec = CurveSpec {
a: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::a(), a: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::a(),
b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(), b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(),
}; };
// Combine the invocation and the public key into a transcript
let transcript = Self::transcript(
invocation,
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref(),
ecdh_public_keys,
);
// A tape of the discrete logarithm, then [zero, x**i, y x**i, y, x_coord, y_coord] // A tape of the discrete logarithm, then [zero, x**i, y x**i, y, x_coord, y_coord]
let mut vector_commitment_tape = vec![]; let mut vector_commitment_tape = vec![];
@@ -766,10 +745,10 @@ where
rng: &mut (impl RngCore + CryptoRng), rng: &mut (impl RngCore + CryptoRng),
generators: &Generators<C>, generators: &Generators<C>,
verifier: &mut BatchVerifier<C>, verifier: &mut BatchVerifier<C>,
evrf_public_key: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G, transcript: [u8; 32],
invocation: [u8; 32],
coefficients: usize, coefficients: usize,
ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G], ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G],
evrf_public_key: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G,
proof: &[u8], proof: &[u8],
) -> Result<EvrfVerifyResult<C>, ()> { ) -> Result<EvrfVerifyResult<C>, ()> {
let curve_spec = CurveSpec { let curve_spec = CurveSpec {
@@ -777,8 +756,6 @@ where
b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(), b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(),
}; };
let transcript = Self::transcript(invocation, evrf_public_key, ecdh_public_keys);
let mut generator_tables = Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len()); let mut generator_tables = Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
{ {
let (x, y) = let (x, y) =

View File

@@ -42,6 +42,9 @@ fn evrf_dkg() {
&pub_keys, &pub_keys,
priv_key, priv_key,
) )
.unwrap()
.into_iter()
.next()
.unwrap(), .unwrap(),
); );
} }

View File

@@ -77,10 +77,10 @@ fn evrf_proof_pasta_test() {
let res = Evrf::<Pallas>::prove( let res = Evrf::<Pallas>::prove(
&mut OsRng, &mut OsRng,
&generators, &generators,
&vesta_private_key,
[0; 32], [0; 32],
1, 1,
&ecdh_public_keys, &ecdh_public_keys,
&vesta_private_key,
) )
.unwrap(); .unwrap();
println!("Proving time: {:?}", time.elapsed()); println!("Proving time: {:?}", time.elapsed());
@@ -91,10 +91,10 @@ fn evrf_proof_pasta_test() {
&mut OsRng, &mut OsRng,
&generators, &generators,
&mut verifier, &mut verifier,
Vesta::generator() * *vesta_private_key,
[0; 32], [0; 32],
1, 1,
&ecdh_public_keys, &ecdh_public_keys,
Vesta::generator() * *vesta_private_key,
&res.proof, &res.proof,
) )
.unwrap()); .unwrap());