mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 06:29:25 +00:00
Finish routing the eVRF functionality
Still needs errors and serialization, along with a few other TODOs.
This commit is contained in:
@@ -65,13 +65,10 @@
|
|||||||
robust to threshold `t`.
|
robust to threshold `t`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) mod proof;
|
|
||||||
|
|
||||||
/*
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
collections::HashMap,
|
collections::{HashSet, HashMap},
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
@@ -79,35 +76,33 @@ use rand_core::{RngCore, CryptoRng};
|
|||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::ff::{Field, PrimeField},
|
group::{
|
||||||
|
ff::{Field, PrimeField},
|
||||||
|
Group,
|
||||||
|
},
|
||||||
Ciphersuite,
|
Ciphersuite,
|
||||||
};
|
};
|
||||||
use multiexp::multiexp_vartime;
|
use multiexp::multiexp_vartime;
|
||||||
|
|
||||||
use generalized_bulletproofs::{Generators, BatchVerifier, arithmetic_circuit_proof::*};
|
use generalized_bulletproofs::{Generators, arithmetic_circuit_proof::*};
|
||||||
use ec_divisors::DivisorCurve;
|
use ec_divisors::DivisorCurve;
|
||||||
use evrf::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{Participant, DkgError, ThresholdParams, ThresholdCore};
|
||||||
Participant, DkgError, ThresholdParams, ThresholdCore,
|
|
||||||
encryption::{ReadWrite, EncryptedMessage, Encryption, EncryptionKeyProof},
|
|
||||||
pedpop::SecretShare,
|
|
||||||
};
|
|
||||||
|
|
||||||
type EvrfError<C> = DkgError<EncryptionKeyProof<C>>;
|
pub(crate) mod proof;
|
||||||
|
pub use proof::*;
|
||||||
|
|
||||||
/// The commitments message, intended to be broadcast to all other parties.
|
/// Participation in the DKG.
|
||||||
///
|
///
|
||||||
/// Every participant should only provide one set of commitments to all parties. If any
|
/// `Participation` is meant to be broadcast to all other participants over an authenticated,
|
||||||
/// participant sends multiple sets of commitments, they are faulty and should be presumed
|
/// reliable broadcast channel.
|
||||||
/// malicious. As this library does not handle networking, it is unable to detect if any
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
/// participant is so faulty. That responsibility lies with the caller.
|
pub struct Participation<C: Ciphersuite> {
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
|
||||||
pub struct Commitments {
|
|
||||||
proof: Vec<u8>,
|
proof: Vec<u8>,
|
||||||
|
encrypted_secret_shares: HashMap<Participant, C::F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReadWrite for Commitments {
|
impl<C: Ciphersuite> Participation<C> {
|
||||||
fn read<R: Read>(reader: &mut R, _params: ThresholdParams) -> io::Result<Self> {
|
fn read<R: Read>(reader: &mut R, _params: ThresholdParams) -> io::Result<Self> {
|
||||||
// TODO: Replace `len` with some calculcation deterministic to the params
|
// TODO: Replace `len` with some calculcation deterministic to the params
|
||||||
let mut len = [0; 4];
|
let mut len = [0; 4];
|
||||||
@@ -126,12 +121,13 @@ impl ReadWrite for Commitments {
|
|||||||
reader.read_exact(&mut proof[old_proof_len ..])?;
|
reader.read_exact(&mut proof[old_proof_len ..])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Commitments { proof })
|
Ok(Self { proof, encrypted_secret_shares: todo!("TODO") })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
writer.write_all(&u32::try_from(self.proof.len()).unwrap().to_le_bytes())?;
|
writer.write_all(&u32::try_from(self.proof.len()).unwrap().to_le_bytes())?;
|
||||||
writer.write_all(&self.proof)?;
|
writer.write_all(&self.proof)?;
|
||||||
|
// TODO: secret shares
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,303 +149,288 @@ fn polynomial<F: PrimeField + Zeroize>(
|
|||||||
share
|
share
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn share_verification_statements<C: Ciphersuite>(
|
||||||
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
|
commitments: &[C::G],
|
||||||
|
n: u16,
|
||||||
|
encryption_commitments: &[C::G],
|
||||||
|
encrypted_secret_shares: &HashMap<Participant, C::F>,
|
||||||
|
) -> (C::F, Vec<(C::F, C::G)>) {
|
||||||
|
debug_assert_eq!(usize::from(n), encryption_commitments.len());
|
||||||
|
debug_assert_eq!(usize::from(n), encrypted_secret_shares.len());
|
||||||
|
|
||||||
|
let mut g_scalar = C::F::ZERO;
|
||||||
|
let mut pairs = Vec::with_capacity(commitments.len() + encryption_commitments.len());
|
||||||
|
for commitment in commitments {
|
||||||
|
pairs.push((C::F::ZERO, *commitment));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut weight;
|
||||||
|
for (i, enc_share) in encrypted_secret_shares {
|
||||||
|
let enc_commitment = encryption_commitments[usize::from(u16::from(*i)) - 1];
|
||||||
|
|
||||||
|
weight = C::F::random(&mut *rng);
|
||||||
|
|
||||||
|
// s_i F
|
||||||
|
g_scalar += weight * enc_share;
|
||||||
|
// - Z_i
|
||||||
|
let weight = -weight;
|
||||||
|
pairs.push((weight, enc_commitment));
|
||||||
|
// - V_i
|
||||||
|
{
|
||||||
|
let i = C::F::from(u64::from(u16::from(*i)));
|
||||||
|
// The first `commitments.len()` pairs are for the commitments
|
||||||
|
(0 .. commitments.len()).fold(weight, |exp, j| {
|
||||||
|
pairs[j].0 += exp;
|
||||||
|
exp * i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(g_scalar, pairs)
|
||||||
|
}
|
||||||
|
|
||||||
/// Struct to perform/verify the DKG with.
|
/// Struct to perform/verify the DKG with.
|
||||||
#[derive(Debug, Zeroize)]
|
#[derive(Debug)]
|
||||||
pub struct EvrfDkg;
|
pub struct EvrfDkg<C: EvrfCurve> {
|
||||||
|
t: u16,
|
||||||
enum AccumulationStrategy<C: EvrfCurve> {
|
n: u16,
|
||||||
#[rustfmt::skip]
|
|
||||||
WaitingForThreshold {
|
|
||||||
pending_verification: HashMap<Participant, (Commitments, Zeroizing<C::F>)>,
|
|
||||||
},
|
|
||||||
Incremental {
|
|
||||||
accumulated: HashMap<Participant, (Vec<C::G>, Zeroizing<C::F>)>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EvrfAccumulatorCore<'a, C: EvrfCurve> {
|
|
||||||
generators: &'a Generators<C>,
|
|
||||||
evrf_public_keys: Vec<<C::EmbeddedCurve as Ciphersuite>::G>,
|
evrf_public_keys: Vec<<C::EmbeddedCurve as Ciphersuite>::G>,
|
||||||
context: [u8; 32],
|
participations: HashMap<Participant, (HashMap<Participant, C::F>, EvrfVerifyResult<C>)>,
|
||||||
params: ThresholdParams,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EvrfAccumulator<'a, C: EvrfCurve> {
|
impl<C: EvrfCurve> EvrfDkg<C>
|
||||||
core: EvrfAccumulatorCore<'a, C>,
|
where
|
||||||
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
||||||
encryption: Encryption<C::EmbeddedCurve>,
|
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
||||||
|
{
|
||||||
our_commitments: Vec<C::G>,
|
|
||||||
accumulation: AccumulationStrategy<C>,
|
|
||||||
resulting_share: Zeroizing<C::F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EvrfShare<C: EvrfCurve> {
|
|
||||||
commitments: Commitments,
|
|
||||||
shares: HashMap<Participant, EncryptedMessage<C::EmbeddedCurve, SecretShare<C::F>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EvrfDkg {
|
|
||||||
/// 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
|
||||||
/// prior-shared secrets.
|
/// prior-shared secrets.
|
||||||
// TODO: Have this return an accumulator
|
pub fn participate(
|
||||||
pub fn share<'a, C: EvrfCurve>(
|
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
generators: &'a Generators<C>,
|
generators: &Generators<C>,
|
||||||
evrf_public_keys: Vec<<C::EmbeddedCurve as Ciphersuite>::G>,
|
|
||||||
context: [u8; 32],
|
context: [u8; 32],
|
||||||
params: ThresholdParams,
|
t: u16,
|
||||||
evrf_private_key: Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||||
) -> Result<(EvrfAccumulator<'a, C>, EvrfShare<C>), AcError>
|
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
where
|
) -> Result<Participation<C>, AcError> {
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
if generators.g() != C::generator() {
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
todo!("TODO");
|
||||||
{
|
|
||||||
// TODO: Confirm `n` == the amount of evrf_public_keys
|
|
||||||
// TODO: Confirm evrf_public_keys[i] == evrf_private_key * G
|
|
||||||
// TODO: Hash context to include the list of public keys
|
|
||||||
|
|
||||||
let EvrfProveResult { scalars, proof } =
|
|
||||||
Evrf::prove(rng, generators, evrf_private_key.clone(), context, usize::from(params.t()))?;
|
|
||||||
|
|
||||||
/*
|
|
||||||
We reuse the eVRF key for receiving encrypted messages.
|
|
||||||
|
|
||||||
For encrypting to other parties, we use a randomly generated ephemeral key, so there's no
|
|
||||||
risk there.
|
|
||||||
|
|
||||||
When decrypting, we calculcate the ECDH of our private key with the ephemeral public key. If
|
|
||||||
the decryption fails, we publish the ECDH with a proof. If the ephemeral public key is one
|
|
||||||
of the eVRF points, this would leak a secret. Since ephemeral public keys must be associated
|
|
||||||
with PoKs for their discrete logarithms, and the eVRF points have unknown discrete
|
|
||||||
logarithms, this is still secure.
|
|
||||||
*/
|
|
||||||
let mut encryption = Encryption::new(context, params.i(), evrf_private_key);
|
|
||||||
for (i, evrf_public_key) in evrf_public_keys.iter().enumerate() {
|
|
||||||
encryption
|
|
||||||
.register(Participant::new(u16::try_from(i + 1).unwrap()).unwrap(), *evrf_public_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut resulting_share = None;
|
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
||||||
let mut shares = HashMap::new();
|
let Ok(n) = u16::try_from(evrf_public_keys.len()) else {
|
||||||
for l in (1 ..= params.n()).map(Participant) {
|
todo!("TODO");
|
||||||
let share = polynomial::<C::F>(&scalars, l);
|
};
|
||||||
|
if (t == 0) || (t > n) {
|
||||||
|
todo!("TODO");
|
||||||
|
}
|
||||||
|
if !evrf_public_keys.iter().any(|key| *key == evrf_public_key) {
|
||||||
|
todo!("TODO");
|
||||||
|
};
|
||||||
|
|
||||||
// Don't insert our own share as we don't need to send out our own share
|
let EvrfProveResult { coefficients, encryption_masks, proof } =
|
||||||
if l == params.i() {
|
Evrf::prove(rng, generators, evrf_private_key, context, usize::from(t), evrf_public_keys)?;
|
||||||
resulting_share = Some(share);
|
|
||||||
|
let mut encrypted_secret_shares = HashMap::new();
|
||||||
|
for (l, encryption_mask) in (1 ..= n).map(Participant).zip(encryption_masks) {
|
||||||
|
let share = polynomial::<C::F>(&coefficients, l);
|
||||||
|
encrypted_secret_shares.insert(l, *share + *encryption_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Participation { proof, encrypted_secret_shares })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a batch of `Participation`s are valid.
|
||||||
|
///
|
||||||
|
/// if any `Participation` is invalid, it will be returned in the `Err` of the result. If all
|
||||||
|
/// `Participation`s are valid and there's at least `t`, an instance of this struct (usable to
|
||||||
|
/// obtain a threshold share of generated key) is returned. If all are valid and there's not at
|
||||||
|
/// least `t`, an error of an empty list is returned after validation.
|
||||||
|
pub fn verify(
|
||||||
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
|
generators: &Generators<C>,
|
||||||
|
context: [u8; 32],
|
||||||
|
t: u16,
|
||||||
|
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||||
|
participations: &HashMap<Participant, Participation<C>>,
|
||||||
|
) -> Result<Self, Vec<Participant>> {
|
||||||
|
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { todo!("TODO") };
|
||||||
|
if (t == 0) || (t > n) {
|
||||||
|
todo!("TODO");
|
||||||
|
}
|
||||||
|
for i in participations.keys() {
|
||||||
|
if u16::from(*i) > n {
|
||||||
|
todo!("TODO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = HashMap::new();
|
||||||
|
let mut faulty = HashSet::new();
|
||||||
|
|
||||||
|
let mut evrf_verifier = generators.batch_verifier();
|
||||||
|
for (i, participation) in participations {
|
||||||
|
// 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::<C>::verify(
|
||||||
|
rng,
|
||||||
|
generators,
|
||||||
|
&mut verifier_clone,
|
||||||
|
evrf_public_keys[usize::from(u16::from(*i)) - 1],
|
||||||
|
context,
|
||||||
|
usize::from(t),
|
||||||
|
evrf_public_keys,
|
||||||
|
&participation.proof,
|
||||||
|
) else {
|
||||||
|
faulty.insert(*i);
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
|
evrf_verifier = verifier_clone;
|
||||||
|
|
||||||
let share_bytes = Zeroizing::new(SecretShare::<C::F>(share.to_repr()));
|
res.insert(*i, (participation.encrypted_secret_shares.clone(), data));
|
||||||
shares.insert(l, encryption.encrypt(rng, l, share_bytes));
|
|
||||||
}
|
}
|
||||||
|
debug_assert_eq!(res.len() + faulty.len(), participations.len());
|
||||||
|
|
||||||
let accumulator = EvrfAccumulator {
|
// Perform the batch verification of the eVRFs
|
||||||
core: EvrfAccumulatorCore { generators, evrf_public_keys, context, params },
|
if !generators.verify(evrf_verifier) {
|
||||||
|
// If the batch failed, verify them each individually
|
||||||
|
for (i, participation) in participations {
|
||||||
|
if faulty.contains(i) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut evrf_verifier = generators.batch_verifier();
|
||||||
|
Evrf::<C>::verify(
|
||||||
|
rng,
|
||||||
|
generators,
|
||||||
|
&mut evrf_verifier,
|
||||||
|
evrf_public_keys[usize::from(u16::from(*i)) - 1],
|
||||||
|
context,
|
||||||
|
usize::from(t),
|
||||||
|
evrf_public_keys,
|
||||||
|
&participation.proof,
|
||||||
|
)
|
||||||
|
.expect("evrf failed basic checks yet prover wasn't prior marked faulty");
|
||||||
|
if !generators.verify(evrf_verifier) {
|
||||||
|
res.remove(i);
|
||||||
|
faulty.insert(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_assert_eq!(res.len() + faulty.len(), participations.len());
|
||||||
|
|
||||||
encryption,
|
// Perform the batch verification of the shares
|
||||||
|
|
||||||
our_commitments: scalars.iter().map(|scalar| C::generator() * **scalar).collect(),
|
|
||||||
accumulation: AccumulationStrategy::WaitingForThreshold {
|
|
||||||
pending_verification: HashMap::new(),
|
|
||||||
},
|
|
||||||
resulting_share: resulting_share.unwrap(),
|
|
||||||
};
|
|
||||||
Ok((accumulator, EvrfShare { commitments: Commitments { proof }, shares }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exponential<C: Ciphersuite>(i: Participant, values: &[C::G]) -> C::G {
|
|
||||||
let i = C::F::from(u16::from(i).into());
|
|
||||||
let mut res = Vec::with_capacity(values.len());
|
|
||||||
(0 .. values.len()).fold(C::F::ONE, |exp, l| {
|
|
||||||
res.push((exp, values[l]));
|
|
||||||
exp * i
|
|
||||||
});
|
|
||||||
multiexp_vartime(&res)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Blame;
|
|
||||||
|
|
||||||
impl<'a, C: EvrfCurve> EvrfAccumulatorCore<'a, C>
|
|
||||||
where
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
|
||||||
{
|
|
||||||
fn verify_evrf(
|
|
||||||
&mut self,
|
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
|
||||||
verifier: &mut BatchVerifier<C>,
|
|
||||||
from: Participant,
|
|
||||||
commitments: &Commitments,
|
|
||||||
) -> Result<Vec<C::G>, ()> {
|
|
||||||
// TODO: Verify from is in-range and distinct from params.i()
|
|
||||||
let from_public_key = self.evrf_public_keys[usize::from(u16::from(from) - 1)];
|
|
||||||
Evrf::verify(
|
|
||||||
rng,
|
|
||||||
self.generators,
|
|
||||||
verifier,
|
|
||||||
from_public_key,
|
|
||||||
self.context,
|
|
||||||
usize::from(self.params.t()),
|
|
||||||
&commitments.proof,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, C: EvrfCurve> EvrfAccumulator<'a, C>
|
|
||||||
where
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
|
||||||
{
|
|
||||||
/// Verify a secret sharing.
|
|
||||||
pub fn accumulate(
|
|
||||||
&mut self,
|
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
|
||||||
from: Participant,
|
|
||||||
commitments: Commitments,
|
|
||||||
share: EncryptedMessage<C::EmbeddedCurve, SecretShare<C::F>>,
|
|
||||||
) -> Vec<Blame> {
|
|
||||||
// TODO: Confirm `n` == the amount of evrf_public_keys
|
|
||||||
// TODO: Confirm evrf_public_keys[i] == evrf_private_key * G
|
|
||||||
// TODO: Hash context to include the list of public keys
|
|
||||||
// TODO: Check not prior accumulated
|
|
||||||
|
|
||||||
// This uses an ephemeral BatchVerifier as if we verify an invalid proof, it'll corrupt the
|
|
||||||
// BatchVerifier. If we tried to form a BatchVerifier, it'd need reconstruction on such error,
|
|
||||||
// increasing complexity and opening potential DoS vectors
|
|
||||||
let mut ephemeral_verifier = self.core.generators.batch_verifier();
|
|
||||||
let Ok(actual_commitments) =
|
|
||||||
self.core.verify_evrf(rng, &mut ephemeral_verifier, from, &commitments)
|
|
||||||
else {
|
|
||||||
return vec![Blame];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decrypt the share
|
|
||||||
let mut batch = multiexp::BatchVerifier::new(1);
|
|
||||||
let (mut share_bytes, blame) = self.encryption.decrypt(rng, &mut batch, (), from, share);
|
|
||||||
let Some(share) = Option::<C::F>::from(C::F::from_repr(share_bytes.0)) else {
|
|
||||||
return vec![Blame];
|
|
||||||
};
|
|
||||||
let share = Zeroizing::new(share);
|
|
||||||
share_bytes.zeroize();
|
|
||||||
|
|
||||||
if exponential::<C>(self.core.params.i(), &actual_commitments) !=
|
|
||||||
(self.core.generators.g() * *share)
|
|
||||||
{
|
{
|
||||||
return vec![Blame];
|
let mut share_verification_statements_actual = HashMap::with_capacity(res.len());
|
||||||
}
|
if !{
|
||||||
|
let mut g_scalar = C::F::ZERO;
|
||||||
|
let mut pairs = Vec::with_capacity(res.len() * (usize::from(t) + evrf_public_keys.len()));
|
||||||
|
for (i, (encrypted_secret_shares, data)) in &res {
|
||||||
|
let (this_g_scalar, mut these_pairs) = share_verification_statements::<C>(
|
||||||
|
&mut *rng,
|
||||||
|
&data.coefficients,
|
||||||
|
evrf_public_keys
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("n prior checked to be <= u16::MAX couldn't be converted to a u16"),
|
||||||
|
&data.encryption_commitments,
|
||||||
|
encrypted_secret_shares,
|
||||||
|
);
|
||||||
|
g_scalar += this_g_scalar;
|
||||||
|
pairs.extend(&these_pairs);
|
||||||
|
|
||||||
match &mut self.accumulation {
|
these_pairs.push((this_g_scalar, generators.g()));
|
||||||
AccumulationStrategy::WaitingForThreshold { ref mut pending_verification } => {
|
share_verification_statements_actual.insert(*i, these_pairs);
|
||||||
pending_verification.insert(from, (commitments, share));
|
}
|
||||||
|
pairs.push((g_scalar, generators.g()));
|
||||||
// If we now have the necessary threshold to consider this DKG as having succeeded, verify
|
bool::from(multiexp_vartime(&pairs).is_identity())
|
||||||
// the proofs with a batch verification
|
} {
|
||||||
if pending_verification.len() == usize::from(self.core.params.t()) {
|
// If the batch failed, verify them each individually
|
||||||
let mut batch_verifier = self.core.generators.batch_verifier();
|
for (i, pairs) in share_verification_statements_actual {
|
||||||
let mut all_pending_verification = HashMap::new();
|
if !bool::from(multiexp_vartime(&pairs).is_identity()) {
|
||||||
for (participant, (commitments, share)) in &mut *pending_verification {
|
res.remove(&i);
|
||||||
let actual_commitments = self
|
faulty.insert(i);
|
||||||
.core
|
|
||||||
.verify_evrf(rng, &mut batch_verifier, *participant, commitments)
|
|
||||||
.expect("prior verified evrf proof now errors upon verification");
|
|
||||||
all_pending_verification.insert(*participant, (actual_commitments, share.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.core.generators.verify(batch_verifier) {
|
|
||||||
// If the verification succeeded, marked the proofs pending verification as accumulated
|
|
||||||
self.accumulation =
|
|
||||||
AccumulationStrategy::Incremental { accumulated: all_pending_verification };
|
|
||||||
} else {
|
|
||||||
// Find the faulty proof(s)
|
|
||||||
let mut accumulated = HashMap::new();
|
|
||||||
let mut blames = vec![];
|
|
||||||
for (participant, (commitments, share)) in &mut *pending_verification {
|
|
||||||
let mut verifier = self.core.generators.batch_verifier();
|
|
||||||
let actual_commitments = self
|
|
||||||
.core
|
|
||||||
.verify_evrf(rng, &mut verifier, *participant, commitments)
|
|
||||||
.expect("prior verified evrf proof now errors upon verification");
|
|
||||||
if self.core.generators.verify(verifier) {
|
|
||||||
accumulated.insert(*participant, (actual_commitments, share.clone()));
|
|
||||||
} else {
|
|
||||||
blames.push(Blame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.accumulation = AccumulationStrategy::Incremental { accumulated };
|
|
||||||
|
|
||||||
// Now that we've marked all proofs as accumulated/faulty, return the blame
|
|
||||||
return blames;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AccumulationStrategy::Incremental { ref mut accumulated } => {
|
}
|
||||||
if self.core.generators.verify(ephemeral_verifier) {
|
debug_assert_eq!(res.len() + faulty.len(), participations.len());
|
||||||
accumulated.insert(from, (actual_commitments, share));
|
|
||||||
} else {
|
let mut faulty = faulty.into_iter().collect::<Vec<_>>();
|
||||||
return vec![Blame];
|
if !faulty.is_empty() {
|
||||||
}
|
faulty.sort_unstable();
|
||||||
|
Err(faulty)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.len() < usize::from(t) {
|
||||||
|
Err(vec![])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EvrfDkg { t, n, evrf_public_keys: evrf_public_keys.to_vec(), participations: res })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keys(
|
||||||
|
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();
|
||||||
|
let Some(i) = self.evrf_public_keys.iter().position(|key| *key == evrf_public_key) else {
|
||||||
|
None?
|
||||||
|
};
|
||||||
|
let i = u16::try_from(i).expect("n <= u16::MAX yet i > u16::MAX?");
|
||||||
|
let i = Participant(1 + i);
|
||||||
|
|
||||||
|
let mut secret_share = Zeroizing::new(C::F::ZERO);
|
||||||
|
for (shares, evrf_data) in self.participations.values() {
|
||||||
|
let mut ecdh = Zeroizing::new(C::F::ZERO);
|
||||||
|
for point in evrf_data.ecdh_keys[usize::from(u16::from(i)) - 1] {
|
||||||
|
// TODO: Explicitly ban 0-ECDH commitments, 0-eVRF public keys, and gen non-zero keys
|
||||||
|
let (mut x, mut y) =
|
||||||
|
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(point * evrf_private_key.deref()).unwrap();
|
||||||
|
*ecdh += x;
|
||||||
|
x.zeroize();
|
||||||
|
y.zeroize();
|
||||||
}
|
}
|
||||||
|
*secret_share += shares[&i] - ecdh.deref();
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![]
|
// Stripe commitments per t and sum them in advance. Calculating verification shares relies on
|
||||||
}
|
// these sums so preprocessing them is a massive speedup
|
||||||
|
let mut stripes = Vec::with_capacity(usize::from(self.t));
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
for t in 0 .. usize::from(self.t) {
|
||||||
pub fn process_blame(&mut self, blame: Blame) {
|
stripes.push(
|
||||||
todo!("TODO");
|
self.participations.values().map(|(_, evrf_data)| evrf_data.coefficients[t]).sum::<C::G>(),
|
||||||
}
|
);
|
||||||
|
|
||||||
pub fn introspect_group_key(&self) -> Result<C::G, ()> {
|
|
||||||
let AccumulationStrategy::Incremental { accumulated } = &self.accumulation else { Err(())? };
|
|
||||||
if (1 + accumulated.len()) < usize::from(self.core.params.t()) {
|
|
||||||
Err(())?
|
|
||||||
}
|
|
||||||
Ok(
|
|
||||||
accumulated.values().map(|(commitments, _)| commitments[0]).sum::<C::G>() +
|
|
||||||
self.our_commitments[0],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finish accumulation.
|
|
||||||
pub fn complete(mut self) -> Result<ThresholdCore<C>, ()> {
|
|
||||||
let AccumulationStrategy::Incremental { accumulated } = self.accumulation else { Err(())? };
|
|
||||||
|
|
||||||
if (1 + accumulated.len()) < usize::from(self.core.params.t()) {
|
|
||||||
Err(())?
|
|
||||||
}
|
|
||||||
|
|
||||||
let commitments = accumulated
|
|
||||||
.values()
|
|
||||||
.map(|(commitments, _)| commitments)
|
|
||||||
.chain(core::iter::once(&self.our_commitments));
|
|
||||||
// Stripe commitments per t and sum them in advance
|
|
||||||
// Calculating verification shares relies on these sums so preprocessing them is a massive
|
|
||||||
// speedup
|
|
||||||
let mut stripes = Vec::with_capacity(usize::from(self.core.params.t()));
|
|
||||||
for t in 0 .. usize::from(self.core.params.t()) {
|
|
||||||
stripes.push(commitments.clone().map(|commitments| commitments[t]).sum());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate each user's verification share
|
// Calculate each user's verification share
|
||||||
let mut verification_shares = HashMap::new();
|
let mut verification_shares = HashMap::new();
|
||||||
for i in (1 ..= self.core.params.n()).map(Participant) {
|
for j in (1 ..= self.n).map(Participant) {
|
||||||
verification_shares.insert(i, exponential::<C>(i, &stripes));
|
verification_shares.insert(
|
||||||
|
j,
|
||||||
|
if j == i {
|
||||||
|
C::generator() * secret_share.deref()
|
||||||
|
} else {
|
||||||
|
fn exponential<C: Ciphersuite>(i: Participant, values: &[C::G]) -> Vec<(C::F, C::G)> {
|
||||||
|
let i = C::F::from(u16::from(i).into());
|
||||||
|
let mut res = Vec::with_capacity(values.len());
|
||||||
|
(0 .. values.len()).fold(C::F::ONE, |exp, l| {
|
||||||
|
res.push((exp, values[l]));
|
||||||
|
exp * i
|
||||||
|
});
|
||||||
|
res
|
||||||
|
}
|
||||||
|
multiexp_vartime(&exponential::<C>(j, &stripes))
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, share) in accumulated.values() {
|
Some(ThresholdCore {
|
||||||
*self.resulting_share += **share;
|
params: ThresholdParams::new(self.t, self.n, i).unwrap(),
|
||||||
}
|
secret_share,
|
||||||
Ok(ThresholdCore {
|
|
||||||
params: self.core.params,
|
|
||||||
secret_share: self.resulting_share,
|
|
||||||
group_key: stripes[0],
|
group_key: stripes[0],
|
||||||
verification_shares,
|
verification_shares,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use core::{marker::PhantomData, fmt};
|
use core::{marker::PhantomData, ops::Deref, fmt};
|
||||||
|
|
||||||
use subtle::*;
|
use subtle::*;
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
@@ -37,21 +37,23 @@ pub trait EvrfCurve: Ciphersuite {
|
|||||||
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
||||||
/// The coefficients for use in the DKG.
|
/// The coefficients for use in the DKG.
|
||||||
pub(crate) coefficients: Vec<Zeroizing<C::F>>,
|
pub(crate) coefficients: Vec<Zeroizing<C::F>>,
|
||||||
/// The ECDHs to encrypt secret shares with.
|
/// The masks to encrypt secret shares with.
|
||||||
pub(crate) ecdhs: Vec<Zeroizing<C::F>>,
|
pub(crate) encryption_masks: Vec<Zeroizing<C::F>>,
|
||||||
/// The proof itself.
|
/// The proof itself.
|
||||||
pub(crate) proof: Vec<u8>,
|
pub(crate) proof: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of verifying an eVRF.
|
/// The result of verifying an eVRF.
|
||||||
pub(crate) struct EvrfVerifyResult<C: Ciphersuite> {
|
pub(crate) struct EvrfVerifyResult<C: EvrfCurve> {
|
||||||
/// The commitments to the coefficients for use in the DKG.
|
/// The commitments to the coefficients for use in the DKG.
|
||||||
pub(crate) coefficients: Vec<C::G>,
|
pub(crate) coefficients: Vec<C::G>,
|
||||||
/// The commitments to the ECDHs used to encrypt secret shares with.
|
/// The ephemeral public keys to perform ECDHs with
|
||||||
pub(crate) ecdhs: Vec<C::G>,
|
pub(crate) ecdh_keys: Vec<[<C::EmbeddedCurve as Ciphersuite>::G; 2]>,
|
||||||
|
/// The commitments to the masks used to encrypt secret shares with.
|
||||||
|
pub(crate) encryption_commitments: Vec<C::G>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Ciphersuite> fmt::Debug for EvrfVerifyResult<C> {
|
impl<C: EvrfCurve> fmt::Debug for EvrfVerifyResult<C> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt.debug_struct("EvrfVerifyResult").finish_non_exhaustive()
|
fmt.debug_struct("EvrfVerifyResult").finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
@@ -352,7 +354,7 @@ where
|
|||||||
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>,
|
evrf_private_key: &Zeroizing<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
invocation: [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],
|
||||||
@@ -365,15 +367,14 @@ where
|
|||||||
// Combine the invocation and the public key into a transcript
|
// Combine the invocation and the public key into a transcript
|
||||||
let transcript = Self::transcript(
|
let transcript = Self::transcript(
|
||||||
invocation,
|
invocation,
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * *evrf_private_key,
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref(),
|
||||||
ecdh_public_keys,
|
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![];
|
||||||
|
|
||||||
let mut generator_tables =
|
let mut generator_tables = Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
|
||||||
Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
|
|
||||||
|
|
||||||
// A function to calculate a divisor and push it onto the tape
|
// A function to calculate a divisor and push it onto the tape
|
||||||
// This defines a vec, divisor_points, outside of the fn to reuse its allocation
|
// This defines a vec, divisor_points, outside of the fn to reuse its allocation
|
||||||
@@ -400,7 +401,10 @@ where
|
|||||||
}
|
}
|
||||||
generator = generator.double();
|
generator = generator.double();
|
||||||
}
|
}
|
||||||
debug_assert_eq!(dlog.iter().sum::<u64>(), u64::from(<C::EmbeddedCurve as Ciphersuite>::F::NUM_BITS));
|
debug_assert_eq!(
|
||||||
|
dlog.iter().sum::<u64>(),
|
||||||
|
u64::from(<C::EmbeddedCurve as Ciphersuite>::F::NUM_BITS)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
divisor_points.push(-dh);
|
divisor_points.push(-dh);
|
||||||
let mut divisor = new_divisor(&divisor_points).unwrap().normalize_x_coefficient();
|
let mut divisor = new_divisor(&divisor_points).unwrap().normalize_x_coefficient();
|
||||||
@@ -443,7 +447,7 @@ where
|
|||||||
let evrf_public_key;
|
let evrf_public_key;
|
||||||
let mut actual_coefficients = Vec::with_capacity(coefficients);
|
let mut actual_coefficients = Vec::with_capacity(coefficients);
|
||||||
{
|
{
|
||||||
let mut dlog = Self::scalar_to_bits(&evrf_private_key);
|
let mut dlog = Self::scalar_to_bits(evrf_private_key);
|
||||||
let points = Self::transcript_to_points(transcript, coefficients);
|
let points = Self::transcript_to_points(transcript, coefficients);
|
||||||
|
|
||||||
// Start by pushing the discrete logarithm onto the tape
|
// Start by pushing the discrete logarithm onto the tape
|
||||||
@@ -457,14 +461,20 @@ where
|
|||||||
&dlog,
|
&dlog,
|
||||||
true,
|
true,
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator(),
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator(),
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * *evrf_private_key,
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Push a divisor for each point we use in the eVRF
|
// Push a divisor for each point we use in the eVRF
|
||||||
for pair in points.chunks(2) {
|
for pair in points.chunks(2) {
|
||||||
let mut res = Zeroizing::new(C::F::ZERO);
|
let mut res = Zeroizing::new(C::F::ZERO);
|
||||||
for point in pair {
|
for point in pair {
|
||||||
let (dh_x, _) = divisor(&mut vector_commitment_tape, &dlog, true, *point, *point * *evrf_private_key);
|
let (dh_x, _) = divisor(
|
||||||
|
&mut vector_commitment_tape,
|
||||||
|
&dlog,
|
||||||
|
true,
|
||||||
|
*point,
|
||||||
|
*point * evrf_private_key.deref(),
|
||||||
|
);
|
||||||
*res += dh_x;
|
*res += dh_x;
|
||||||
}
|
}
|
||||||
actual_coefficients.push(res);
|
actual_coefficients.push(res);
|
||||||
@@ -474,8 +484,8 @@ where
|
|||||||
dlog.zeroize();
|
dlog.zeroize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now do the ECDHs
|
// Now do the ECDHs for the encryption
|
||||||
let mut ecdhs = Vec::with_capacity(ecdh_public_keys.len());
|
let mut encryption_masks = Vec::with_capacity(ecdh_public_keys.len());
|
||||||
let mut ecdh_commitments = Vec::with_capacity(2 * ecdh_public_keys.len());
|
let mut ecdh_commitments = Vec::with_capacity(2 * ecdh_public_keys.len());
|
||||||
let mut ecdh_commitments_xy = Vec::with_capacity(ecdh_public_keys.len());
|
let mut ecdh_commitments_xy = Vec::with_capacity(ecdh_public_keys.len());
|
||||||
for ecdh_public_key in ecdh_public_keys {
|
for ecdh_public_key in ecdh_public_keys {
|
||||||
@@ -504,15 +514,21 @@ where
|
|||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key,
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key,
|
||||||
);
|
);
|
||||||
// Push a divisor for the key we're performing the ECDH with
|
// Push a divisor for the key we're performing the ECDH with
|
||||||
let (dh_x, _) = divisor(&mut vector_commitment_tape, &dlog, j == 0, *ecdh_public_key, *ecdh_public_key * ecdh_private_key);
|
let (dh_x, _) = divisor(
|
||||||
|
&mut vector_commitment_tape,
|
||||||
|
&dlog,
|
||||||
|
j == 0,
|
||||||
|
*ecdh_public_key,
|
||||||
|
*ecdh_public_key * ecdh_private_key,
|
||||||
|
);
|
||||||
*res += dh_x;
|
*res += dh_x;
|
||||||
|
|
||||||
ecdh_private_key.zeroize();
|
ecdh_private_key.zeroize();
|
||||||
dlog.zeroize();
|
dlog.zeroize();
|
||||||
}
|
}
|
||||||
ecdhs.push(res);
|
encryption_masks.push(res);
|
||||||
}
|
}
|
||||||
debug_assert_eq!(ecdhs.len(), ecdh_public_keys.len());
|
debug_assert_eq!(encryption_masks.len(), ecdh_public_keys.len());
|
||||||
|
|
||||||
// Now that we have the vector commitment tape, chunk it
|
// Now that we have the vector commitment tape, chunk it
|
||||||
let (_, generators_to_use) =
|
let (_, generators_to_use) =
|
||||||
@@ -537,8 +553,8 @@ where
|
|||||||
for coefficient in &actual_coefficients {
|
for coefficient in &actual_coefficients {
|
||||||
commitments.push(PedersenCommitment { value: **coefficient, mask: C::F::random(&mut *rng) });
|
commitments.push(PedersenCommitment { value: **coefficient, mask: C::F::random(&mut *rng) });
|
||||||
}
|
}
|
||||||
for ecdh in &ecdhs {
|
for enc_mask in &encryption_masks {
|
||||||
commitments.push(PedersenCommitment { value: **ecdh, mask: C::F::random(&mut *rng) });
|
commitments.push(PedersenCommitment { value: **enc_mask, mask: C::F::random(&mut *rng) });
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut transcript = ProverTranscript::new(transcript);
|
let mut transcript = ProverTranscript::new(transcript);
|
||||||
@@ -607,7 +623,11 @@ where
|
|||||||
r.zeroize();
|
r.zeroize();
|
||||||
x.zeroize();
|
x.zeroize();
|
||||||
|
|
||||||
Ok(EvrfProveResult { coefficients: actual_coefficients, ecdhs, proof: transcript.complete() })
|
Ok(EvrfProveResult {
|
||||||
|
coefficients: actual_coefficients,
|
||||||
|
encryption_masks,
|
||||||
|
proof: transcript.complete(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Dedicated error
|
// TODO: Dedicated error
|
||||||
@@ -629,8 +649,7 @@ where
|
|||||||
|
|
||||||
let transcript = Self::transcript(invocation, evrf_public_key, ecdh_public_keys);
|
let transcript = Self::transcript(invocation, evrf_public_key, ecdh_public_keys);
|
||||||
|
|
||||||
let mut generator_tables =
|
let mut generator_tables = Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
|
||||||
Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
|
|
||||||
{
|
{
|
||||||
let (x, y) =
|
let (x, y) =
|
||||||
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(<C::EmbeddedCurve as Ciphersuite>::generator())
|
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(<C::EmbeddedCurve as Ciphersuite>::generator())
|
||||||
@@ -660,34 +679,34 @@ where
|
|||||||
let dlog_proof_len = divisor_len + 2;
|
let dlog_proof_len = divisor_len + 2;
|
||||||
|
|
||||||
let coeffs_vc_variables = dlog_len + ((1 + (2 * coefficients)) * dlog_proof_len);
|
let coeffs_vc_variables = dlog_len + ((1 + (2 * coefficients)) * dlog_proof_len);
|
||||||
let ecdhs_vc_variables = ((2 * ecdh_public_keys.len()) * dlog_len) + ((2 * 2 * ecdh_public_keys.len()) * dlog_proof_len);
|
let ecdhs_vc_variables = ((2 * ecdh_public_keys.len()) * dlog_len) +
|
||||||
|
((2 * 2 * ecdh_public_keys.len()) * dlog_proof_len);
|
||||||
let vcs = (coeffs_vc_variables + ecdhs_vc_variables).div_ceil(2 * generators_to_use);
|
let vcs = (coeffs_vc_variables + ecdhs_vc_variables).div_ceil(2 * generators_to_use);
|
||||||
|
|
||||||
let all_commitments =
|
let all_commitments =
|
||||||
transcript.read_commitments(vcs, coefficients + ecdh_public_keys.len()).map_err(|_| ())?;
|
transcript.read_commitments(vcs, coefficients + ecdh_public_keys.len()).map_err(|_| ())?;
|
||||||
let commitments = all_commitments.V().to_vec();
|
let commitments = all_commitments.V().to_vec();
|
||||||
|
|
||||||
let mut ecdh_commitments_xy = Vec::with_capacity(ecdh_public_keys.len());
|
let mut ecdh_keys = Vec::with_capacity(ecdh_public_keys.len());
|
||||||
|
let mut ecdh_keys_xy = Vec::with_capacity(ecdh_public_keys.len());
|
||||||
for _ in 0 .. ecdh_public_keys.len() {
|
for _ in 0 .. ecdh_public_keys.len() {
|
||||||
ecdh_commitments_xy.push([
|
let ecdh_keys_i = [
|
||||||
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(
|
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
||||||
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
||||||
)
|
];
|
||||||
.ok_or(())?,
|
ecdh_keys.push(ecdh_keys_i);
|
||||||
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(
|
ecdh_keys_xy.push([
|
||||||
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_keys_i[0]).ok_or(())?,
|
||||||
)
|
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_keys_i[1]).ok_or(())?,
|
||||||
.ok_or(())?,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut circuit = Circuit::verify();
|
let mut circuit = Circuit::verify();
|
||||||
Self::circuit(
|
Self::circuit(
|
||||||
&curve_spec,
|
&curve_spec,
|
||||||
// TODO: Use a better error here
|
|
||||||
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(evrf_public_key).ok_or(())?,
|
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(evrf_public_key).ok_or(())?,
|
||||||
coefficients,
|
coefficients,
|
||||||
&ecdh_commitments_xy,
|
&ecdh_keys_xy,
|
||||||
&generator_tables,
|
&generator_tables,
|
||||||
&mut circuit,
|
&mut circuit,
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
@@ -735,8 +754,8 @@ where
|
|||||||
Err(())?
|
Err(())?
|
||||||
};
|
};
|
||||||
|
|
||||||
let ecdhs = openings[coefficients ..].to_vec();
|
let encryption_commitments = openings[coefficients ..].to_vec();
|
||||||
let coefficients = openings[.. coefficients].to_vec();
|
let coefficients = openings[.. coefficients].to_vec();
|
||||||
Ok(EvrfVerifyResult { coefficients, ecdhs })
|
Ok(EvrfVerifyResult { coefficients, ecdh_keys, encryption_commitments })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ fn evrf_proof_pasta_test() {
|
|||||||
let res = Evrf::<Pallas>::prove(
|
let res = Evrf::<Pallas>::prove(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&generators,
|
&generators,
|
||||||
vesta_private_key.clone(),
|
&vesta_private_key,
|
||||||
[0; 32],
|
[0; 32],
|
||||||
1,
|
1,
|
||||||
&ecdh_public_keys,
|
&ecdh_public_keys,
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ pub struct Generators<C: Ciphersuite> {
|
|||||||
|
|
||||||
/// A batch verifier of proofs.
|
/// A batch verifier of proofs.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct BatchVerifier<C: Ciphersuite> {
|
pub struct BatchVerifier<C: Ciphersuite> {
|
||||||
g: C::F,
|
g: C::F,
|
||||||
h: C::F,
|
h: C::F,
|
||||||
|
|||||||
Reference in New Issue
Block a user