Update to the new eVRF proof

This commit is contained in:
Luke Parker
2024-07-25 02:55:10 -04:00
parent eca82f3f7b
commit 00dc3087bd
8 changed files with 485 additions and 303 deletions

26
Cargo.lock generated
View File

@@ -2128,18 +2128,24 @@ dependencies = [
name = "dkg" name = "dkg"
version = "0.5.1" version = "0.5.1"
dependencies = [ dependencies = [
"blake2",
"borsh", "borsh",
"chacha20", "chacha20",
"ciphersuite", "ciphersuite",
"dleq", "dleq",
"ec-divisors", "ec-divisors",
"evrf",
"flexible-transcript", "flexible-transcript",
"generalized-bulletproofs", "generalized-bulletproofs",
"generalized-bulletproofs-circuit-abstraction",
"generalized-bulletproofs-ec-gadgets",
"generic-array 1.1.0",
"multiexp", "multiexp",
"pasta_curves",
"rand_chacha",
"rand_core", "rand_core",
"schnorr-signatures", "schnorr-signatures",
"std-shims", "std-shims",
"subtle",
"thiserror", "thiserror",
"zeroize", "zeroize",
] ]
@@ -2447,24 +2453,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "evrf"
version = "0.1.0"
dependencies = [
"blake2",
"ciphersuite",
"ec-divisors",
"generalized-bulletproofs",
"generalized-bulletproofs-circuit-abstraction",
"generalized-bulletproofs-ec-gadgets",
"generic-array 1.1.0",
"pasta_curves",
"rand_chacha",
"rand_core",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "exit-future" name = "exit-future"
version = "0.2.0" version = "0.2.0"

View File

@@ -39,7 +39,6 @@ members = [
"crypto/evrf/circuit-abstraction", "crypto/evrf/circuit-abstraction",
"crypto/evrf/divisors", "crypto/evrf/divisors",
"crypto/evrf/ec-gadgets", "crypto/evrf/ec-gadgets",
"crypto/evrf",
"crypto/dkg", "crypto/dkg",
"crypto/frost", "crypto/frost",

View File

@@ -43,14 +43,14 @@ blake2 = { version = "0.10", default-features = false, features = ["std"], optio
rand_chacha = { version = "0.3", default-features = false, features = ["std"], optional = true } rand_chacha = { version = "0.3", default-features = false, features = ["std"], optional = true }
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", default-features = false, optional = true } generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", default-features = false, optional = true }
ec-divisors = { path = "../evrf/divisors", default-features = false, optional = true } ec-divisors = { path = "../evrf/divisors", default-features = false, optional = true }
generalized-bulletproofs-circuit-abstraction = { path = "./circuit-abstraction", optional = true } generalized-bulletproofs-circuit-abstraction = { path = "../evrf/circuit-abstraction", optional = true }
generalized-bulletproofs-ec-gadgets = { path = "./ec-gadgets", optional = true } generalized-bulletproofs-ec-gadgets = { path = "../evrf/ec-gadgets", optional = true }
[dev-dependencies] [dev-dependencies]
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
ciphersuite = { path = "../ciphersuite", default-features = false, features = ["ristretto"] } ciphersuite = { path = "../ciphersuite", default-features = false, features = ["ristretto"] }
generalized-bulletproofs = { path = "./generalized-bulletproofs", features = ["tests"] } generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", features = ["tests"] }
ec-divisors = { path = "./divisors", features = ["pasta"] } ec-divisors = { path = "../evrf/divisors", features = ["pasta"] }
pasta_curves = "0.5" pasta_curves = "0.5"
[features] [features]

View File

@@ -10,11 +10,17 @@
`a * P_i, b * P_i`. The eVRF proceeds to commit to `A_i.x + B_i.x` in a Pedersen Commitment. `a * P_i, b * P_i`. The eVRF proceeds to commit to `A_i.x + B_i.x` in a Pedersen Commitment.
Our eVRF uses Our eVRF uses
[Generalized Bulletproofs](https://repo.getmonero.org/monero-project/ccs-proposals/uploads/a9baa50c38c6312efc0fea5c6a188bb9/gbp.pdf). [Generalized Bulletproofs](
https://repo.getmonero.org/monero-project/ccs-proposals
/uploads/a9baa50c38c6312efc0fea5c6a188bb9/gbp.pdf
).
This allows us much larger witnesses without growing the reference string, and enables us to This allows us much larger witnesses without growing the reference string, and enables us to
efficiently sample challenges off in-circuit variables (via placing the variables in a vector efficiently sample challenges off in-circuit variables (via placing the variables in a vector
commitment, then challenging from a transcript of the commitments). We proceed to use commitment, then challenging from a transcript of the commitments). We proceed to use
[elliptic curve divisors](https://repo.getmonero.org/-/project/54/uploads/eb1bf5b4d4855a3480c38abf895bd8e8/Veridise_Divisor_Proofs.pdf) [elliptic curve divisors](
https://repo.getmonero.org/-/project/54/
uploads/eb1bf5b4d4855a3480c38abf895bd8e8/Veridise_Divisor_Proofs.pdf
)
(which require the ability to sample a challenge off in-circuit variables) to prove discrete (which require the ability to sample a challenge off in-circuit variables) to prove discrete
logarithms efficiently. logarithms efficiently.
@@ -34,8 +40,8 @@
We have the sender sample two scalars per recipient, denoted `x_i, y_i` (where `i` is the We have the sender sample two scalars per recipient, denoted `x_i, y_i` (where `i` is the
recipient index). They perform the eVRF to prove a Pedersen Commitment commits to recipient index). They perform the eVRF to prove a Pedersen Commitment commits to
`z_i = (x_i * P_i).x + (y_i * P_i).x`. They then publish the encrypted share `s_i + z_i` and `z_i = (x_i * P_i).x + (y_i * P_i).x` and `x_i, y_i` are the discrete logarithms of `X_i, Y_i`
`X_i = x_i * G, Y_i = y_i * G`. over `G`. They then publish the encrypted share `s_i + z_i` and `X_i, Y_i`.
The recipient is able to decrypt the share via calculating The recipient is able to decrypt the share via calculating
`s_i - ((p_i * X_i).x + (p_i * Y_i).x)`. `s_i - ((p_i * X_i).x + (p_i * Y_i).x)`.
@@ -59,6 +65,9 @@
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},
@@ -443,3 +452,4 @@ where
}) })
} }
} }
*/

View File

@@ -1,3 +1,5 @@
use core::{marker::PhantomData, fmt};
use subtle::*; use subtle::*;
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
@@ -25,17 +27,7 @@ use generalized_bulletproofs_circuit_abstraction::*;
use ec_divisors::{DivisorCurve, new_divisor}; use ec_divisors::{DivisorCurve, new_divisor};
use generalized_bulletproofs_ec_gadgets::*; use generalized_bulletproofs_ec_gadgets::*;
#[cfg(test)] /// A pair of curves to perform the eVRF with.
mod tests;
/*
The following circuit has two roles.
1) Generating every coefficient used in the DKG, per the eVRF paper, using the fixed eVRF key.
*/
/// A curve to perform the eVRF with.
pub trait EvrfCurve: Ciphersuite { pub trait EvrfCurve: Ciphersuite {
type EmbeddedCurve: Ciphersuite; type EmbeddedCurve: Ciphersuite;
type EmbeddedCurveParameters: DiscreteLogParameters; type EmbeddedCurveParameters: DiscreteLogParameters;
@@ -43,92 +35,125 @@ pub trait EvrfCurve: Ciphersuite {
/// The result of proving for an eVRF. /// The result of proving for an eVRF.
pub(crate) struct EvrfProveResult<C: Ciphersuite> { pub(crate) struct EvrfProveResult<C: Ciphersuite> {
pub(crate) encrypted_scalars: Vec<C::F>, /// The coefficients for use in the DKG.
pub(crate) coefficients: Vec<Zeroizing<C::F>>,
/// The ECDHs to encrypt secret shares with.
pub(crate) ecdhs: Vec<Zeroizing<C::F>>,
/// The proof itself.
pub(crate) proof: Vec<u8>, pub(crate) proof: Vec<u8>,
} }
/// The result of verifying an eVRF.
pub(crate) struct EvrfVerifyResult<C: Ciphersuite> {
/// The commitments to the coefficients for use in the DKG.
pub(crate) coefficients: Vec<C::G>,
/// The commitments to the ECDHs used to encrypt secret shares with.
pub(crate) ecdhs: Vec<C::G>,
}
impl<C: Ciphersuite> fmt::Debug for EvrfVerifyResult<C> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("EvrfVerifyResult").finish_non_exhaustive()
}
}
/// A struct to prove/verify eVRFs with. /// A struct to prove/verify eVRFs with.
pub(crate) struct Evrf; pub(crate) struct Evrf<C: EvrfCurve>(PhantomData<C>);
impl Evrf { impl<C: EvrfCurve> Evrf<C>
fn transcript_to_points<C: Ciphersuite>(seed: [u8; 32], quantity: usize) -> Vec<C::G> { where
// We need to do two Diffie-Hellman's per point in order to achieve an unbiased result <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
let quantity = 2 * quantity; DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
{
// Sample uniform points (via rejection-sampling) on the embedded elliptic curve
fn transcript_to_points(
seed: [u8; 32],
coefficients: usize,
) -> Vec<<C::EmbeddedCurve as Ciphersuite>::G> {
// We need to do two Diffie-Hellman's per coefficient in order to achieve an unbiased result
let quantity = 2 * coefficients;
let mut rng = ChaCha20Rng::from_seed(seed); let mut rng = ChaCha20Rng::from_seed(seed);
let mut res = Vec::with_capacity(quantity); let mut res = Vec::with_capacity(quantity);
while res.len() < quantity { while res.len() < quantity {
let mut repr = <C::G as GroupEncoding>::Repr::default(); let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
rng.fill_bytes(repr.as_mut()); rng.fill_bytes(repr.as_mut());
if let Ok(point) = C::read_G(&mut repr.as_ref()) { if let Ok(point) = C::EmbeddedCurve::read_G(&mut repr.as_ref()) {
res.push(point); res.push(point);
} }
} }
res res
} }
fn point_with_dlogs<Parameters: DiscreteLogParameters>( /// Read a Variable from a theoretical vector commitment tape
fn read_one_from_tape(generators_to_use: usize, start: &mut usize) -> Variable {
// Each commitment has twice as many variables as generators in use
let commitment = *start / (2 * generators_to_use);
// The index will be less than the amount of generators in use, as half are left and half are
// right
let index = *start % generators_to_use;
let res = if (*start / generators_to_use) % 2 == 0 {
Variable::CG { commitment, index }
} else {
Variable::CH { commitment, index }
};
*start += 1;
res
}
/// Read a set of variables from a theoretical vector commitment tape
fn read_from_tape<N: ArrayLength>(
generators_to_use: usize,
start: &mut usize,
) -> GenericArray<Variable, N> {
let mut buf = Vec::with_capacity(N::USIZE);
for _ in 0 .. N::USIZE {
buf.push(Self::read_one_from_tape(generators_to_use, start));
}
GenericArray::from_slice(&buf).clone()
}
/// Read `PointWithDlog`s, which share a discrete logarithm, from the theoretical vector
/// commitment tape.
fn point_with_dlogs(
start: &mut usize,
quantity: usize, quantity: usize,
generators_to_use: usize, generators_to_use: usize,
) -> Vec<PointWithDlog<Parameters>> { ) -> Vec<PointWithDlog<C::EmbeddedCurveParameters>> {
let quantity = 2 * quantity; // We define a serialized tape of the discrete logarithm, then for each divisor/point, we push:
fn read_one_from_tape(generators_to_use: usize, start: &mut usize) -> Variable {
let commitment = *start / (2 * generators_to_use);
let index = *start % generators_to_use;
let res = if (*start / generators_to_use) % 2 == 0 {
Variable::CG { commitment, index }
} else {
Variable::CH { commitment, index }
};
*start += 1;
res
}
fn read_from_tape<N: ArrayLength>(
generators_to_use: usize,
start: &mut usize,
) -> GenericArray<Variable, N> {
let mut buf = Vec::with_capacity(N::USIZE);
for _ in 0 .. N::USIZE {
buf.push(read_one_from_tape(generators_to_use, start));
}
GenericArray::from_slice(&buf).clone()
}
// We define a serialized tape of the discrete logarithm, then for each divisor/point:
// zero, x**i, y x**i, y, x_coord, y_coord // zero, x**i, y x**i, y, x_coord, y_coord
// We then chunk that into vector commitments // We then chunk that into vector commitments
// Here, we take the assumed layout and generate the expected `Variable`s for this layout // Here, we take the assumed layout and generate the expected `Variable`s for this layout
let mut start = 0;
let dlog = read_from_tape(generators_to_use, &mut start); let dlog = Self::read_from_tape(generators_to_use, start);
let mut res = Vec::with_capacity(quantity + 1); let mut res = Vec::with_capacity(quantity);
let mut read_point_with_dlog = || { let mut read_point_with_dlog = || {
let zero = read_one_from_tape(generators_to_use, &mut start); let zero = Self::read_one_from_tape(generators_to_use, start);
let x_from_power_of_2 = read_from_tape(generators_to_use, &mut start); let x_from_power_of_2 = Self::read_from_tape(generators_to_use, start);
let yx = read_from_tape(generators_to_use, &mut start); let yx = Self::read_from_tape(generators_to_use, start);
let y = read_one_from_tape(generators_to_use, &mut start); let y = Self::read_one_from_tape(generators_to_use, start);
let divisor = Divisor { zero, x_from_power_of_2, yx, y }; let divisor = Divisor { zero, x_from_power_of_2, yx, y };
let point = ( let point = (
read_one_from_tape(generators_to_use, &mut start), Self::read_one_from_tape(generators_to_use, start),
read_one_from_tape(generators_to_use, &mut start), Self::read_one_from_tape(generators_to_use, start),
); );
res.push(PointWithDlog { dlog: dlog.clone(), divisor, point }); res.push(PointWithDlog { dlog: dlog.clone(), divisor, point });
}; };
for _ in 0 .. quantity { for _ in 0 .. quantity {
// One for each DH proven
read_point_with_dlog(); read_point_with_dlog();
} }
// And one more for the proof this is the discrete log of the public key
read_point_with_dlog();
res res
} }
fn muls_and_generators_to_use(quantity: usize) -> (usize, usize) { fn muls_and_generators_to_use(coefficients: usize, ecdhs: usize) -> (usize, usize) {
let expected_muls = 7 * (1 + (2 * quantity)); 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
let expected_muls = MULS_PER_DH * (1 + (2 * coefficients) + (2 * 2 * ecdhs));
let generators_to_use = { let generators_to_use = {
let mut padded_pow_of_2 = 1; let mut padded_pow_of_2 = 1;
while padded_pow_of_2 < expected_muls { while padded_pow_of_2 < expected_muls {
@@ -141,26 +166,84 @@ impl Evrf {
(expected_muls, generators_to_use) (expected_muls, generators_to_use)
} }
fn circuit<C: EvrfCurve>( fn circuit(
curve_spec: &CurveSpec<C::F>, curve_spec: &CurveSpec<C::F>,
evrf_public_key: (C::F, C::F), evrf_public_key: (C::F, C::F),
quantity: usize, coefficients: usize,
ecdh_commitments: &[[(C::F, C::F); 2]],
generator_tables: &[GeneratorTable<C::F, C::EmbeddedCurveParameters>], generator_tables: &[GeneratorTable<C::F, C::EmbeddedCurveParameters>],
circuit: &mut Circuit<C>, circuit: &mut Circuit<C>,
transcript: &mut impl Transcript, transcript: &mut impl Transcript,
) { ) {
let (expected_muls, generators_to_use) = Self::muls_and_generators_to_use(quantity); let (expected_muls, generators_to_use) =
Self::muls_and_generators_to_use(coefficients, ecdh_commitments.len());
let (challenge, challenged_generators) = let (challenge, challenged_generators) =
circuit.discrete_log_challenge(transcript, curve_spec, generator_tables); circuit.discrete_log_challenge(transcript, curve_spec, generator_tables);
debug_assert_eq!(challenged_generators.len(), 1 + (2 * coefficients) + ecdh_commitments.len());
let mut point_with_dlogs = // The generators tables/challenged generators are expected to have the following layouts
Self::point_with_dlogs::<C::EmbeddedCurveParameters>(quantity, generators_to_use).into_iter(); // G, coefficients * [A, B], ecdhs * [P]
#[allow(non_snake_case)]
let challenged_G = &challenged_generators[0];
// Verify the DLog claims for the sampled points // Execute the circuit for the coefficients
for (i, pair) in challenged_generators.chunks(2).take(quantity).enumerate() { let mut tape_pos = 0;
{
let mut point_with_dlogs =
Self::point_with_dlogs(&mut tape_pos, 1 + (2 * coefficients), generators_to_use)
.into_iter();
// Verify the discrete logarithm is in the fact the discrete logarithm of the eVRF public key
let point = circuit.discrete_log(
curve_spec,
point_with_dlogs.next().unwrap(),
&challenge,
challenged_G,
);
circuit.equality(LinComb::from(point.x()), &LinComb::empty().constant(evrf_public_key.0));
circuit.equality(LinComb::from(point.y()), &LinComb::empty().constant(evrf_public_key.1));
// Verify the DLog claims against the sampled points
for (i, pair) in challenged_generators[1 ..].chunks(2).take(coefficients).enumerate() {
let mut lincomb = LinComb::empty();
debug_assert_eq!(pair.len(), 2);
for challenged_generator in pair {
let point = circuit.discrete_log(
curve_spec,
point_with_dlogs.next().unwrap(),
&challenge,
challenged_generator,
);
// For each point in this pair, add its x coordinate to a lincomb
lincomb = lincomb.term(C::F::ONE, point.x());
}
// Constrain the sum of the two x coordinates to be equal to the value in the Pedersen
// commitment
circuit.equality(lincomb, &LinComb::from(Variable::V(i)));
}
debug_assert!(point_with_dlogs.next().is_none());
}
// Now execute the circuit for the ECDHs
let mut challenged_generators = challenged_generators.iter().skip(1 + (2 * coefficients));
for (i, ecdh) in ecdh_commitments.iter().enumerate() {
let challenged_generator = challenged_generators.next().unwrap();
let mut lincomb = LinComb::empty(); let mut lincomb = LinComb::empty();
debug_assert_eq!(pair.len(), 2); for ecdh in ecdh {
for challenged_generator in pair { let mut point_with_dlogs =
Self::point_with_dlogs(&mut tape_pos, 2, generators_to_use).into_iter();
// One proof of the ECDH secret * G for the commitment published
let point = circuit.discrete_log(
curve_spec,
point_with_dlogs.next().unwrap(),
&challenge,
challenged_G,
);
circuit.equality(LinComb::from(point.x()), &LinComb::empty().constant(ecdh.0));
circuit.equality(LinComb::from(point.y()), &LinComb::empty().constant(ecdh.1));
// One proof of the ECDH secret * P for the ECDH
let point = circuit.discrete_log( let point = circuit.discrete_log(
curve_spec, curve_spec,
point_with_dlogs.next().unwrap(), point_with_dlogs.next().unwrap(),
@@ -170,77 +253,40 @@ impl Evrf {
// For each point in this pair, add its x coordinate to a lincomb // For each point in this pair, add its x coordinate to a lincomb
lincomb = lincomb.term(C::F::ONE, point.x()); lincomb = lincomb.term(C::F::ONE, point.x());
} }
// Constrain the sum of the two x coordinates to be equal to the value in the Pedersen // Constrain the sum of the two x coordinates to be equal to the value in the Pedersen
// commitment // commitment
circuit.equality(lincomb, &LinComb::from(Variable::V(i))); circuit.equality(lincomb, &LinComb::from(Variable::V(coefficients + i)));
} }
let point = circuit.discrete_log(
curve_spec,
point_with_dlogs.next().unwrap(),
&challenge,
challenged_generators.last().unwrap(),
);
circuit.equality(LinComb::from(point.x()), &LinComb::empty().constant(evrf_public_key.0));
circuit.equality(LinComb::from(point.y()), &LinComb::empty().constant(evrf_public_key.1));
debug_assert_eq!(expected_muls, circuit.muls()); debug_assert_eq!(expected_muls, circuit.muls());
debug_assert!(point_with_dlogs.next().is_none()); debug_assert!(challenged_generators.next().is_none());
} }
/// Prove a point on an elliptic curve had its discrete logarithm generated via an eVRF. /// Convert a scalar to a sequence of coefficients for the polynomial 2**i, where the sum of the
pub(crate) fn prove<C: EvrfCurve>( /// coefficients is F::NUM_BITS.
rng: &mut (impl RngCore + CryptoRng), ///
generators: &Generators<C>, /// We'll presumably use this scalar in a discrete log proof. That requires calculating a divisor
evrf_private_key: Zeroizing<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>, /// which is variable time to the sum of the coefficients in the polynomial. This causes all
invocation: [u8; 32], /// scalars to have a constant sum of their coefficients (instead one variable to the bits set).
quantity: usize, ///
) -> Result<EvrfProveResult<C>, AcError> /// We achieve this by finding the highest non-0 coefficient, decrementing it, and increasing the
where /// immediately less significant coefficient by 2. This increases the sum of the coefficients by
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G: /// 1 (-1+2=1).
DivisorCurve<FieldElement = <C as Ciphersuite>::F>, // TODO: Support scalars which have a value < F::NUM_BITS
{ #[allow(clippy::cast_possible_truncation)]
let curve_spec = CurveSpec { fn scalar_to_bits(scalar: &<C::EmbeddedCurve as Ciphersuite>::F) -> Vec<u64> {
a: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::a(), let num_bits = <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::NUM_BITS;
b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(),
};
// Combine the invocation and the public key into a transcript
let transcript = Blake2s256::digest(
[
invocation.as_slice(),
(<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * *evrf_private_key)
.to_bytes()
.as_ref(),
]
.concat(),
)
.into();
let points = Self::transcript_to_points::<C::EmbeddedCurve>(transcript, quantity);
let num_bits: u32 = <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::NUM_BITS;
// Obtain the bits of the private key // Obtain the bits of the private key
let mut sum_of_coefficients: u32 = 0; let mut sum_of_coefficients: u64 = 0;
let mut dlog = vec![<C as Ciphersuite>::F::ZERO; num_bits as usize]; let mut dlog = vec![0; num_bits as usize];
for (i, bit) in evrf_private_key.to_le_bits().into_iter().take(num_bits as usize).enumerate() { for (i, bit) in scalar.to_le_bits().into_iter().take(num_bits as usize).enumerate() {
let bit = Choice::from(u8::from(bit)); let bit = u64::from(u8::from(bit));
dlog[i] = dlog[i] = bit;
<_>::conditional_select(&<C as Ciphersuite>::F::ZERO, &<C as Ciphersuite>::F::ONE, bit); sum_of_coefficients += bit;
sum_of_coefficients += u32::conditional_select(&0, &1, bit);
} }
/*
Now that we have the discrete logarithm as the coefficients 0/1 for a polynomial of 2**i, we
want to malleate it such that the sum of its coefficients is NUM_BITS. The divisor
calculcation is a non-trivial amount of work and would be extremely vulnerable to timing
attacks without such efforts.
We find the highest non-0 coefficient, decrement it, and increase the prior coefficient by 2.
This increase the sum of the coefficients by 1.
*/
let two = <C as Ciphersuite>::F::ONE.double();
for _ in 0 .. num_bits { for _ in 0 .. num_bits {
// Find the highest coefficient currently non-zero // Find the highest coefficient currently non-zero
let mut h = 1u32; let mut h = 1u32;
@@ -250,140 +296,233 @@ impl Evrf {
// TODO: Squash the following two loops by iterating from the top bit to the bottom bit // TODO: Squash the following two loops by iterating from the top bit to the bottom bit
let mut prior_scalar = dlog[(h as usize) - 1]; let mut prior_coefficient = dlog[(h as usize) - 1];
for (i, scalar) in dlog.iter().enumerate().skip(h as usize) { for (i, coefficient) in dlog.iter().enumerate().skip(h as usize) {
let is_zero = <C as Ciphersuite>::F::ZERO.ct_eq(scalar); let is_zero = 0.ct_eq(coefficient);
// Set `h_*` if this value is non-0 // Set `h_*` if this value is non-0
h = u32::conditional_select(&h, &(i as u32), !is_zero); h = u32::conditional_select(&h, &(i as u32), !is_zero);
h_value = <C as Ciphersuite>::F::conditional_select(&h_value, scalar, !is_zero); h_value = <_>::conditional_select(&h_value, coefficient, !is_zero);
h_prior_value = h_prior_value = <_>::conditional_select(&h_prior_value, &prior_coefficient, !is_zero);
<C as Ciphersuite>::F::conditional_select(&h_prior_value, &prior_scalar, !is_zero);
// Update prior_scalar // Update prior_coefficient
prior_scalar = *scalar; prior_coefficient = *coefficient;
} }
// We should not have selected a value equivalent to 0 // We should not have selected a value equivalent to 0
// TODO: Ban evrf keys < NUM_BITS and accordingly unable to be so coerced // TODO: Ban evrf keys < NUM_BITS and accordingly unable to be so coerced
// TODO: Preprocess this decomposition of the eVRF key? assert!(!bool::from(h_value.ct_eq(&0)));
assert!(!bool::from(h_value.ct_eq(&<C as Ciphersuite>::F::ZERO)));
// Update h_value, h_prior_value as necessary // Update h_value, h_prior_value as necessary
h_value -= <C as Ciphersuite>::F::ONE; h_value -= 1;
h_prior_value += two; h_prior_value += 2;
// Now, set these values if we should // Now, set these values if we should
let should_set = !sum_of_coefficients.ct_eq(&num_bits); let should_set = !sum_of_coefficients.ct_eq(&u64::from(num_bits));
sum_of_coefficients += u32::conditional_select(&0, &1, should_set); sum_of_coefficients += u64::conditional_select(&0, &1, should_set);
for (i, scalar) in dlog.iter_mut().enumerate() { for (i, coefficient) in dlog.iter_mut().enumerate() {
let this_is_prior = (i as u32).ct_eq(&(h - 1)); let this_is_prior = (i as u32).ct_eq(&(h - 1));
let this_is_high = (i as u32).ct_eq(&h); let this_is_high = (i as u32).ct_eq(&h);
*scalar = <_>::conditional_select(scalar, &h_prior_value, should_set & this_is_prior); *coefficient =
*scalar = <_>::conditional_select(scalar, &h_value, should_set & this_is_high); <_>::conditional_select(coefficient, &h_prior_value, should_set & this_is_prior);
*coefficient = <_>::conditional_select(coefficient, &h_value, should_set & this_is_high);
} }
} }
debug_assert!(bool::from( debug_assert!(bool::from(dlog.iter().sum::<u64>().ct_eq(&u64::from(num_bits))));
dlog
.iter() dlog
.sum::<<C as Ciphersuite>::F>() }
.ct_eq(&<C as Ciphersuite>::F::from(u64::from(num_bits)))
)); 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.
pub(crate) fn prove(
rng: &mut (impl RngCore + CryptoRng),
generators: &Generators<C>,
evrf_private_key: Zeroizing<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
invocation: [u8; 32],
coefficients: usize,
ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G],
) -> Result<EvrfProveResult<C>, AcError> {
let curve_spec = CurveSpec {
a: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::a(),
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,
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![];
// Start by pushing the discrete logarithm onto the tape let mut generator_tables =
for coefficient in &dlog { Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
vector_commitment_tape.push(*coefficient);
}
let mut generator_tables = Vec::with_capacity(1 + (2 * quantity));
// 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
let mut divisor_points = Vec::with_capacity((num_bits as usize) + 1); let mut divisor_points =
let mut divisor = |mut generator: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G| { Vec::with_capacity((<C::EmbeddedCurve as Ciphersuite>::F::NUM_BITS as usize) + 1);
{ let mut divisor =
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(generator).unwrap(); |vector_commitment_tape: &mut Vec<_>,
generator_tables.push(GeneratorTable::new(&curve_spec, x, y)); dlog: &[u64],
} push_generator: bool,
generator: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G,
let dh = generator * *evrf_private_key; dh: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G| {
{ if push_generator {
for coefficient in &dlog { let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(generator).unwrap();
let mut coefficient = *coefficient; generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
while coefficient != <C as Ciphersuite>::F::ZERO {
coefficient -= <C as Ciphersuite>::F::ONE;
divisor_points.push(generator);
}
generator = generator.double();
} }
}
divisor_points.push(-dh);
let mut divisor = new_divisor(&divisor_points).unwrap().normalize_x_coefficient();
divisor_points.zeroize();
vector_commitment_tape.push(divisor.zero_coefficient); {
let mut generator = generator;
for coefficient in dlog {
let mut coefficient = *coefficient;
while coefficient != 0 {
coefficient -= 1;
divisor_points.push(generator);
}
generator = generator.double();
}
debug_assert_eq!(dlog.iter().sum::<u64>(), u64::from(<C::EmbeddedCurve as Ciphersuite>::F::NUM_BITS));
}
divisor_points.push(-dh);
let mut divisor = new_divisor(&divisor_points).unwrap().normalize_x_coefficient();
divisor_points.zeroize();
for coefficient in divisor.x_coefficients.iter().skip(1) { vector_commitment_tape.push(divisor.zero_coefficient);
vector_commitment_tape.push(*coefficient);
} for coefficient in divisor.x_coefficients.iter().skip(1) {
for _ in divisor.x_coefficients.len() .. vector_commitment_tape.push(*coefficient);
<C::EmbeddedCurveParameters as DiscreteLogParameters>::XCoefficientsMinusOne::USIZE }
{ for _ in divisor.x_coefficients.len() ..
vector_commitment_tape.push(<C as Ciphersuite>::F::ZERO); <C::EmbeddedCurveParameters as DiscreteLogParameters>::XCoefficientsMinusOne::USIZE
{
vector_commitment_tape.push(<C as Ciphersuite>::F::ZERO);
}
for coefficient in divisor.yx_coefficients.first().unwrap_or(&vec![]) {
vector_commitment_tape.push(*coefficient);
}
for _ in divisor.yx_coefficients.first().unwrap_or(&vec![]).len() ..
<C::EmbeddedCurveParameters as DiscreteLogParameters>::YxCoefficients::USIZE
{
vector_commitment_tape.push(<C as Ciphersuite>::F::ZERO);
}
vector_commitment_tape
.push(divisor.y_coefficients.first().copied().unwrap_or(<C as Ciphersuite>::F::ZERO));
divisor.zeroize();
drop(divisor);
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(dh).unwrap();
vector_commitment_tape.push(x);
vector_commitment_tape.push(y);
(x, y)
};
// Start with the coefficients
let evrf_public_key;
let mut actual_coefficients = Vec::with_capacity(coefficients);
{
let mut dlog = Self::scalar_to_bits(&evrf_private_key);
let points = Self::transcript_to_points(transcript, coefficients);
// Start by pushing the discrete logarithm onto the tape
for coefficient in &dlog {
vector_commitment_tape.push(<_>::from(*coefficient));
} }
for coefficient in divisor.yx_coefficients.first().unwrap_or(&vec![]) { // Push a divisor for proving that we're using the correct scalar
vector_commitment_tape.push(*coefficient); evrf_public_key = divisor(
} &mut vector_commitment_tape,
for _ in divisor.yx_coefficients.first().unwrap_or(&vec![]).len() .. &dlog,
<C::EmbeddedCurveParameters as DiscreteLogParameters>::YxCoefficients::USIZE true,
{ <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator(),
vector_commitment_tape.push(<C as Ciphersuite>::F::ZERO); <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * *evrf_private_key,
);
// Push a divisor for each point we use in the eVRF
for pair in points.chunks(2) {
let mut res = Zeroizing::new(C::F::ZERO);
for point in pair {
let (dh_x, _) = divisor(&mut vector_commitment_tape, &dlog, true, *point, *point * *evrf_private_key);
*res += dh_x;
}
actual_coefficients.push(res);
} }
debug_assert_eq!(actual_coefficients.len(), coefficients);
vector_commitment_tape dlog.zeroize();
.push(divisor.y_coefficients.first().cloned().unwrap_or(<C as Ciphersuite>::F::ZERO));
divisor.zeroize();
drop(divisor);
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(dh).unwrap();
vector_commitment_tape.push(x);
vector_commitment_tape.push(y);
(x, y)
};
// Push a divisor for each point we use in the eVRF
let mut scalars = Vec::with_capacity(quantity);
for pair in points.chunks(2) {
let mut res = Zeroizing::new(C::F::ZERO);
for point in pair {
let (dh_x, _) = divisor(*point);
*res += dh_x;
}
scalars.push(res);
} }
debug_assert_eq!(scalars.len(), quantity);
// Also push a divisor for proving that we're using the correct scalar // Now do the ECDHs
let evrf_public_key = divisor(<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator()); let mut ecdhs = Vec::with_capacity(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());
for ecdh_public_key in ecdh_public_keys {
ecdh_commitments_xy.push([(C::F::ZERO, C::F::ZERO); 2]);
dlog.zeroize(); let mut res = Zeroizing::new(C::F::ZERO);
drop(dlog); for j in 0 .. 2 {
let mut ecdh_private_key = <C::EmbeddedCurve as Ciphersuite>::F::random(&mut *rng);
let mut dlog = Self::scalar_to_bits(&ecdh_private_key);
let ecdh_commitment = <C::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key;
ecdh_commitments.push(ecdh_commitment);
ecdh_commitments_xy.last_mut().unwrap()[j] =
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitment).unwrap();
// Start by pushing the discrete logarithm onto the tape
for coefficient in &dlog {
vector_commitment_tape.push(<_>::from(*coefficient));
}
// Push a divisor for proving that we're using the correct scalar for the commitment
divisor(
&mut vector_commitment_tape,
&dlog,
false,
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator(),
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key,
);
// 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);
*res += dh_x;
ecdh_private_key.zeroize();
dlog.zeroize();
}
ecdhs.push(res);
}
debug_assert_eq!(ecdhs.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) = Self::muls_and_generators_to_use(quantity); let (_, generators_to_use) =
Self::muls_and_generators_to_use(coefficients, ecdh_public_keys.len());
let mut vector_commitments = let mut vector_commitments =
Vec::with_capacity(vector_commitment_tape.len().div_ceil(generators_to_use)); Vec::with_capacity(vector_commitment_tape.len().div_ceil(2 * generators_to_use));
for chunk in vector_commitment_tape.chunks(generators_to_use * 2) { for chunk in vector_commitment_tape.chunks(2 * generators_to_use) {
let g_values = chunk[.. generators_to_use].to_vec().into(); let g_values = chunk[.. generators_to_use.min(chunk.len())].to_vec().into();
let h_values = chunk[generators_to_use ..].to_vec().into(); let h_values = chunk[generators_to_use.min(chunk.len()) ..].to_vec().into();
vector_commitments.push(PedersenVectorCommitment { vector_commitments.push(PedersenVectorCommitment {
g_values, g_values,
h_values, h_values,
@@ -394,9 +533,12 @@ impl Evrf {
vector_commitment_tape.zeroize(); vector_commitment_tape.zeroize();
drop(vector_commitment_tape); drop(vector_commitment_tape);
let mut commitments = Vec::with_capacity(quantity); let mut commitments = Vec::with_capacity(coefficients + ecdh_public_keys.len());
for scalar in &scalars { for coefficient in &actual_coefficients {
commitments.push(PedersenCommitment { value: **scalar, mask: C::F::random(&mut *rng) }); commitments.push(PedersenCommitment { value: **coefficient, mask: C::F::random(&mut *rng) });
}
for ecdh in &ecdhs {
commitments.push(PedersenCommitment { value: **ecdh, mask: C::F::random(&mut *rng) });
} }
let mut transcript = ProverTranscript::new(transcript); let mut transcript = ProverTranscript::new(transcript);
@@ -414,12 +556,16 @@ impl Evrf {
.map(|commitment| commitment.commit(generators.g(), generators.h())) .map(|commitment| commitment.commit(generators.g(), generators.h()))
.collect(), .collect(),
); );
for ecdh_commitment in ecdh_commitments {
transcript.push_point(ecdh_commitment);
}
let mut circuit = Circuit::prove(vector_commitments, commitments.clone()); let mut circuit = Circuit::prove(vector_commitments, commitments.clone());
Self::circuit::<C>( Self::circuit(
&curve_spec, &curve_spec,
evrf_public_key, evrf_public_key,
quantity, coefficients,
&ecdh_commitments_xy,
&generator_tables, &generator_tables,
&mut circuit, &mut circuit,
&mut transcript, &mut transcript,
@@ -437,14 +583,14 @@ impl Evrf {
statement.prove(&mut *rng, &mut transcript, witness).unwrap(); statement.prove(&mut *rng, &mut transcript, witness).unwrap();
// Push the reveal onto the transcript // Push the reveal onto the transcript
for scalar in &scalars { for commitment in &commitments {
transcript.push_point(generators.g() * **scalar); transcript.push_point(generators.g() * commitment.value);
} }
// Define a weight to aggregate the commitments with // Define a weight to aggregate the commitments with
let mut agg_weights = Vec::with_capacity(quantity); let mut agg_weights = Vec::with_capacity(commitments.len());
agg_weights.push(C::F::ONE); agg_weights.push(C::F::ONE);
while agg_weights.len() < quantity { while agg_weights.len() < commitments.len() {
agg_weights.push(transcript.challenge::<C::F>()); agg_weights.push(transcript.challenge::<C::F>());
} }
let mut x = commitments let mut x = commitments
@@ -461,69 +607,87 @@ impl Evrf {
r.zeroize(); r.zeroize();
x.zeroize(); x.zeroize();
Ok(EvrfProveResult { scalars, proof: transcript.complete() }) Ok(EvrfProveResult { coefficients: actual_coefficients, ecdhs, proof: transcript.complete() })
} }
// TODO: Dedicated error // TODO: Dedicated error
/// Verify an eVRF proof, returning the commitments output. /// Verify an eVRF proof, returning the commitments output.
pub(crate) fn verify<C: EvrfCurve>( pub(crate) fn verify(
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, evrf_public_key: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G,
invocation: [u8; 32], invocation: [u8; 32],
quantity: usize, coefficients: usize,
ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G],
proof: &[u8], proof: &[u8],
) -> Result<Vec<C::G>, ()> ) -> Result<EvrfVerifyResult<C>, ()> {
where
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
{
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(),
}; };
let transcript = let transcript = Self::transcript(invocation, evrf_public_key, ecdh_public_keys);
Blake2s256::digest([invocation.as_slice(), evrf_public_key.to_bytes().as_ref()].concat())
.into();
let points = Self::transcript_to_points::<C::EmbeddedCurve>(transcript, quantity); let mut generator_tables =
let mut generator_tables = Vec::with_capacity(1 + (2 * quantity)); Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
for generator in points {
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(generator).unwrap();
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
}
{ {
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())
.unwrap(); .unwrap();
generator_tables.push(GeneratorTable::new(&curve_spec, x, y)); generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
} }
let points = Self::transcript_to_points(transcript, coefficients);
for generator in points {
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(generator).unwrap();
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
}
for generator in ecdh_public_keys {
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(*generator).unwrap();
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
}
let (_, generators_to_use) = Self::muls_and_generators_to_use(quantity); let (_, generators_to_use) =
Self::muls_and_generators_to_use(coefficients, ecdh_public_keys.len());
let mut transcript = VerifierTranscript::new(transcript, proof); let mut transcript = VerifierTranscript::new(transcript, proof);
let dlog_len = <C::EmbeddedCurveParameters as DiscreteLogParameters>::ScalarBits::USIZE;
let divisor_len = 1 + let divisor_len = 1 +
<C::EmbeddedCurveParameters as DiscreteLogParameters>::XCoefficientsMinusOne::USIZE + <C::EmbeddedCurveParameters as DiscreteLogParameters>::XCoefficientsMinusOne::USIZE +
<C::EmbeddedCurveParameters as DiscreteLogParameters>::YxCoefficients::USIZE + <C::EmbeddedCurveParameters as DiscreteLogParameters>::YxCoefficients::USIZE +
1; 1;
let dlog_proof_len = divisor_len + 2; let dlog_proof_len = divisor_len + 2;
let vcs = (<C::EmbeddedCurveParameters as DiscreteLogParameters>::ScalarBits::USIZE +
((1 + (2 * quantity)) * dlog_proof_len))
.div_ceil(2 * generators_to_use);
let all_commitments = transcript.read_commitments(vcs, quantity).map_err(|_| ())?; 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 vcs = (coeffs_vc_variables + ecdhs_vc_variables).div_ceil(2 * generators_to_use);
let all_commitments =
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());
for _ in 0 .. ecdh_public_keys.len() {
ecdh_commitments_xy.push([
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
)
.ok_or(())?,
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
)
.ok_or(())?,
]);
}
let mut circuit = Circuit::verify(); let mut circuit = Circuit::verify();
Self::circuit::<C>( Self::circuit(
&curve_spec, &curve_spec,
// TODO: Use a better error here // 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(())?,
quantity, coefficients,
&ecdh_commitments_xy,
&generator_tables, &generator_tables,
&mut circuit, &mut circuit,
&mut transcript, &mut transcript,
@@ -537,20 +701,21 @@ impl Evrf {
statement.verify(rng, verifier, &mut transcript).map_err(|_| ())?; statement.verify(rng, verifier, &mut transcript).map_err(|_| ())?;
// Read the unblinded public keys // Read the openings for the commitments
let mut res = Vec::with_capacity(quantity); let mut openings = Vec::with_capacity(commitments.len());
for _ in 0 .. quantity { for _ in 0 .. commitments.len() {
res.push(transcript.read_point::<C>().map_err(|_| ())?); openings.push(transcript.read_point::<C>().map_err(|_| ())?);
} }
let mut agg_weights = Vec::with_capacity(quantity); // Verify the openings of the commitments
let mut agg_weights = Vec::with_capacity(commitments.len());
agg_weights.push(C::F::ONE); agg_weights.push(C::F::ONE);
while agg_weights.len() < quantity { while agg_weights.len() < commitments.len() {
agg_weights.push(transcript.challenge::<C::F>()); agg_weights.push(transcript.challenge::<C::F>());
} }
let sum_points = let sum_points =
res.iter().zip(&agg_weights).map(|(point, weight)| *point * *weight).sum::<C::G>(); openings.iter().zip(&agg_weights).map(|(point, weight)| *point * *weight).sum::<C::G>();
let sum_commitments = let sum_commitments =
commitments.into_iter().zip(agg_weights).map(|(point, weight)| point * weight).sum::<C::G>(); commitments.into_iter().zip(agg_weights).map(|(point, weight)| point * weight).sum::<C::G>();
#[allow(non_snake_case)] #[allow(non_snake_case)]
@@ -570,6 +735,8 @@ impl Evrf {
Err(())? Err(())?
}; };
Ok(res) let ecdhs = openings[coefficients ..].to_vec();
let coefficients = openings[.. coefficients].to_vec();
Ok(EvrfVerifyResult { coefficients, ecdhs })
} }
} }

View File

@@ -0,0 +1 @@
mod proof;

View File

@@ -7,14 +7,18 @@ use generic_array::typenum::{Sum, Diff, Quot, U, U1, U2};
use blake2::{Digest, Blake2b512}; use blake2::{Digest, Blake2b512};
use ciphersuite::{ use ciphersuite::{
group::ff::{FromUniformBytes, PrimeField}, group::{
ff::{FromUniformBytes, Field, PrimeField},
Group,
},
Ciphersuite, Ciphersuite,
}; };
use pasta_curves::{Ep, Eq, Fp, Fq}; use pasta_curves::{Ep, Eq, Fp, Fq};
use generalized_bulletproofs::tests::generators; use generalized_bulletproofs::tests::generators;
use generalized_bulletproofs_ec_gadgets::DiscreteLogParameters;
use crate::*; use crate::evrf::proof::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
struct Pallas; struct Pallas;
@@ -64,26 +68,36 @@ impl EvrfCurve for Pallas {
} }
#[test] #[test]
fn pasta_test() { fn evrf_proof_pasta_test() {
let generators = generators(1024); let generators = generators(1024);
let vesta_private_key = Zeroizing::new(<Vesta as Ciphersuite>::F::random(&mut OsRng)); let vesta_private_key = Zeroizing::new(<Vesta as Ciphersuite>::F::random(&mut OsRng));
let ecdh_public_keys =
[<Vesta as Ciphersuite>::G::random(&mut OsRng), <Vesta as Ciphersuite>::G::random(&mut OsRng)];
let time = Instant::now(); let time = Instant::now();
let res = let res = Evrf::<Pallas>::prove(
Evrf::prove::<Pallas>(&mut OsRng, &generators, vesta_private_key.clone(), [0; 32], 1).unwrap(); &mut OsRng,
println!("Proving time: {:?}", Instant::now() - time); &generators,
vesta_private_key.clone(),
[0; 32],
1,
&ecdh_public_keys,
)
.unwrap();
println!("Proving time: {:?}", time.elapsed());
let time = Instant::now(); let time = Instant::now();
let mut verifier = generators.batch_verifier(); let mut verifier = generators.batch_verifier();
dbg!(Evrf::verify::<Pallas>( dbg!(Evrf::<Pallas>::verify(
&mut OsRng, &mut OsRng,
&generators, &generators,
&mut verifier, &mut verifier,
Vesta::generator() * *vesta_private_key, Vesta::generator() * *vesta_private_key,
[0; 32], [0; 32],
1, 1,
&ecdh_public_keys,
&res.proof, &res.proof,
) )
.unwrap()); .unwrap());
assert!(generators.verify(verifier)); assert!(generators.verify(verifier));
println!("Verifying time: {:?}", Instant::now() - time); println!("Verifying time: {:?}", time.elapsed());
} }

View File

@@ -19,6 +19,9 @@ use pedpop::pedpop_gen;
mod promote; mod promote;
use promote::test_generator_promotion; use promote::test_generator_promotion;
#[cfg(all(test, feature = "evrf"))]
mod evrf;
/// Constant amount of participants to use when testing. /// Constant amount of participants to use when testing.
pub const PARTICIPANTS: u16 = 5; pub const PARTICIPANTS: u16 = 5;
/// Constant threshold of participants to use when testing. /// Constant threshold of participants to use when testing.