From 65efbf46c77c43241b70307791c49929aa09f089 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 28 Jul 2024 23:41:31 -0400 Subject: [PATCH] Support participating multiple times in the eVRF DKG --- crypto/dkg/src/evrf/mod.rs | 114 ++++++++++++++++++++--------- crypto/dkg/src/evrf/proof.rs | 31 +------- crypto/dkg/src/tests/evrf/mod.rs | 3 + crypto/dkg/src/tests/evrf/proof.rs | 4 +- 4 files changed, 88 insertions(+), 64 deletions(-) diff --git a/crypto/dkg/src/evrf/mod.rs b/crypto/dkg/src/evrf/mod.rs index 37ca7955..055a1913 100644 --- a/crypto/dkg/src/evrf/mod.rs +++ b/crypto/dkg/src/evrf/mod.rs @@ -75,10 +75,11 @@ use rand_core::{RngCore, CryptoRng}; use zeroize::{Zeroize, Zeroizing}; +use blake2::{Digest, Blake2s256}; use ciphersuite::{ group::{ ff::{Field, PrimeField}, - Group, + Group, GroupEncoding, }, Ciphersuite, }; @@ -242,6 +243,21 @@ where <::EmbeddedCurve as Ciphersuite>::G: DivisorCurve::F>, { + // Form the initial transcript for the proofs. + fn initial_transcript( + invocation: [u8; 32], + evrf_public_keys: &[::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. /// /// The context MUST be unique across invocations. Reuse of context will lead to sharing @@ -255,8 +271,7 @@ where t: u16, evrf_public_keys: &[::G], evrf_private_key: &Zeroizing<::F>, - ) -> Result, EvrfError> { - let evrf_public_key = ::generator() * evrf_private_key.deref(); + ) -> Result>, EvrfError> { let Ok(n) = u16::try_from(evrf_public_keys.len()) else { Err(EvrfError::TooManyParticipants)? }; if (t == 0) || (t > n) { Err(EvrfError::InvalidThreshold)?; @@ -264,40 +279,63 @@ where if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) { Err(EvrfError::PublicKeyWasIdentity)?; }; - if !evrf_public_keys.iter().any(|key| *key == evrf_public_key) { - Err(EvrfError::NotAParticipant)?; - }; - let EvrfProveResult { coefficients, encryption_masks, proof } = match Evrf::prove( - rng, - &generators.0, - evrf_private_key, - context, - usize::from(t), - evrf_public_keys, - ) { - Ok(res) => res, - Err(AcError::NotEnoughGenerators) => Err(EvrfError::NotEnoughGenerators)?, - Err( - AcError::DifferingLrLengths | - AcError::InconsistentAmountOfConstraints | - AcError::ConstrainedNonExistentTerm | - AcError::ConstrainedNonExistentCommitment | - AcError::InconsistentWitness | - AcError::Ip(_) | - AcError::IncompleteProof, - ) => { - panic!("failed to prove for the eVRF proof") + let evrf_public_key = ::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 mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n)); - for (l, encryption_mask) in (1 ..= n).map(Participant).zip(encryption_masks) { - let share = polynomial::(&coefficients, l); - encrypted_secret_shares.insert(l, *share + *encryption_mask); + 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( + rng, + &generators.0, + per_proof_transcript.finalize().into(), + usize::from(t), + evrf_public_keys, + evrf_private_key, + ) { + Ok(res) => res, + Err(AcError::NotEnoughGenerators) => Err(EvrfError::NotEnoughGenerators)?, + Err( + AcError::DifferingLrLengths | + AcError::InconsistentAmountOfConstraints | + AcError::ConstrainedNonExistentTerm | + AcError::ConstrainedNonExistentCommitment | + AcError::InconsistentWitness | + AcError::Ip(_) | + AcError::IncompleteProof, + ) => { + panic!("failed to prove for the eVRF proof") + } + }; + + let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n)); + for (l, encryption_mask) in (1 ..= n).map(Participant).zip(encryption_masks) { + let share = polynomial::(&coefficients, l); + encrypted_secret_shares.insert(l, *share + *encryption_mask); + } + + res.push(Participation { proof, encrypted_secret_shares }); } - Ok(Participation { proof, encrypted_secret_shares }) + if res.is_empty() { + Err(EvrfError::NotAParticipant)?; + } + + Ok(res) } /// Check if a batch of `Participation`s are valid. @@ -333,18 +371,24 @@ where let mut valid = HashMap::with_capacity(participations.len()); let mut faulty = HashSet::new(); + let transcript = Self::initial_transcript(context, evrf_public_keys, t); + let mut evrf_verifier = generators.0.batch_verifier(); 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 let mut verifier_clone = evrf_verifier.clone(); let Ok(data) = Evrf::::verify( rng, &generators.0, &mut verifier_clone, - evrf_public_keys[usize::from(u16::from(*i)) - 1], - context, + per_proof_transcript.finalize().into(), usize::from(t), evrf_public_keys, + evrf_public_keys[usize::from(u16::from(*i)) - 1], &participation.proof, ) else { faulty.insert(*i); @@ -368,10 +412,10 @@ where rng, &generators.0, &mut evrf_verifier, - evrf_public_keys[usize::from(u16::from(*i)) - 1], context, usize::from(t), evrf_public_keys, + evrf_public_keys[usize::from(u16::from(*i)) - 1], &participation.proof, ) .expect("evrf failed basic checks yet prover wasn't prior marked faulty"); diff --git a/crypto/dkg/src/evrf/proof.rs b/crypto/dkg/src/evrf/proof.rs index 16eae424..1cb45f36 100644 --- a/crypto/dkg/src/evrf/proof.rs +++ b/crypto/dkg/src/evrf/proof.rs @@ -458,41 +458,20 @@ where decomposition } - fn transcript( - invocation: [u8; 32], - evrf_public_key: ::G, - ecdh_public_keys: &[::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. pub(crate) fn prove( rng: &mut (impl RngCore + CryptoRng), generators: &Generators, - evrf_private_key: &Zeroizing<<::EmbeddedCurve as Ciphersuite>::F>, - invocation: [u8; 32], + transcript: [u8; 32], coefficients: usize, ecdh_public_keys: &[<::EmbeddedCurve as Ciphersuite>::G], + evrf_private_key: &Zeroizing<<::EmbeddedCurve as Ciphersuite>::F>, ) -> Result, AcError> { let curve_spec = CurveSpec { a: <::EmbeddedCurve as Ciphersuite>::G::a(), b: <::EmbeddedCurve as Ciphersuite>::G::b(), }; - // Combine the invocation and the public key into a transcript - let transcript = Self::transcript( - invocation, - <::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] let mut vector_commitment_tape = vec![]; @@ -766,10 +745,10 @@ where rng: &mut (impl RngCore + CryptoRng), generators: &Generators, verifier: &mut BatchVerifier, - evrf_public_key: <::EmbeddedCurve as Ciphersuite>::G, - invocation: [u8; 32], + transcript: [u8; 32], coefficients: usize, ecdh_public_keys: &[<::EmbeddedCurve as Ciphersuite>::G], + evrf_public_key: <::EmbeddedCurve as Ciphersuite>::G, proof: &[u8], ) -> Result, ()> { let curve_spec = CurveSpec { @@ -777,8 +756,6 @@ where b: <::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 (x, y) = diff --git a/crypto/dkg/src/tests/evrf/mod.rs b/crypto/dkg/src/tests/evrf/mod.rs index e6fd2230..fd35df41 100644 --- a/crypto/dkg/src/tests/evrf/mod.rs +++ b/crypto/dkg/src/tests/evrf/mod.rs @@ -42,6 +42,9 @@ fn evrf_dkg() { &pub_keys, priv_key, ) + .unwrap() + .into_iter() + .next() .unwrap(), ); } diff --git a/crypto/dkg/src/tests/evrf/proof.rs b/crypto/dkg/src/tests/evrf/proof.rs index e8b586cf..8296ee8a 100644 --- a/crypto/dkg/src/tests/evrf/proof.rs +++ b/crypto/dkg/src/tests/evrf/proof.rs @@ -77,10 +77,10 @@ fn evrf_proof_pasta_test() { let res = Evrf::::prove( &mut OsRng, &generators, - &vesta_private_key, [0; 32], 1, &ecdh_public_keys, + &vesta_private_key, ) .unwrap(); println!("Proving time: {:?}", time.elapsed()); @@ -91,10 +91,10 @@ fn evrf_proof_pasta_test() { &mut OsRng, &generators, &mut verifier, - Vesta::generator() * *vesta_private_key, [0; 32], 1, &ecdh_public_keys, + Vesta::generator() * *vesta_private_key, &res.proof, ) .unwrap());