mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-14 15:09:23 +00:00
Support participating multiple times in the eVRF DKG
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
@@ -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) =
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ fn evrf_dkg() {
|
|||||||
&pub_keys,
|
&pub_keys,
|
||||||
priv_key,
|
priv_key,
|
||||||
)
|
)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
Reference in New Issue
Block a user