2022-07-13 23:29:48 -04:00
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
|
|
2022-06-30 05:42:29 -04:00
|
|
|
use rand_core::{RngCore, CryptoRng};
|
|
|
|
|
|
|
|
|
|
use transcript::Transcript;
|
|
|
|
|
|
|
|
|
|
use ff::{Field, PrimeField};
|
|
|
|
|
use group::prime::PrimeGroup;
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "serialize")]
|
|
|
|
|
use std::io::{self, ErrorKind, Error, Read, Write};
|
|
|
|
|
|
2022-07-07 09:52:10 -04:00
|
|
|
#[cfg(feature = "experimental")]
|
2022-06-30 05:42:29 -04:00
|
|
|
pub mod cross_group;
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests;
|
|
|
|
|
|
2022-06-30 11:23:13 -04:00
|
|
|
pub(crate) fn challenge<T: Transcript, F: PrimeField>(transcript: &mut T) -> F {
|
2022-06-30 05:42:29 -04:00
|
|
|
// From here, there are three ways to get a scalar under the ff/group API
|
|
|
|
|
// 1: Scalar::random(ChaCha12Rng::from_seed(self.transcript.rng_seed(b"challenge")))
|
|
|
|
|
// 2: Grabbing a UInt library to perform reduction by the modulus, then determining endianess
|
|
|
|
|
// and loading it in
|
|
|
|
|
// 3: Iterating over each byte and manually doubling/adding. This is simplest
|
2022-07-09 00:38:19 -04:00
|
|
|
|
|
|
|
|
// Get a wide amount of bytes to safely reduce without bias
|
|
|
|
|
let target = ((usize::try_from(F::NUM_BITS).unwrap() + 7) / 8) * 2;
|
|
|
|
|
let mut challenge_bytes = transcript.challenge(b"challenge").as_ref().to_vec();
|
|
|
|
|
while challenge_bytes.len() < target {
|
|
|
|
|
// Secure given transcripts updating on challenge
|
|
|
|
|
challenge_bytes.extend(transcript.challenge(b"challenge_extension").as_ref());
|
|
|
|
|
}
|
|
|
|
|
challenge_bytes.truncate(target);
|
2022-06-30 05:42:29 -04:00
|
|
|
|
|
|
|
|
let mut challenge = F::zero();
|
2022-07-09 00:38:19 -04:00
|
|
|
for b in challenge_bytes {
|
2022-06-30 05:42:29 -04:00
|
|
|
for _ in 0 .. 8 {
|
|
|
|
|
challenge = challenge.double();
|
|
|
|
|
}
|
2022-07-09 00:38:19 -04:00
|
|
|
challenge += F::from(u64::from(b));
|
2022-06-30 05:42:29 -04:00
|
|
|
}
|
|
|
|
|
challenge
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "serialize")]
|
|
|
|
|
fn read_scalar<R: Read, F: PrimeField>(r: &mut R) -> io::Result<F> {
|
|
|
|
|
let mut repr = F::Repr::default();
|
|
|
|
|
r.read_exact(repr.as_mut())?;
|
|
|
|
|
let scalar = F::from_repr(repr);
|
|
|
|
|
if scalar.is_none().into() {
|
|
|
|
|
Err(Error::new(ErrorKind::Other, "invalid scalar"))?;
|
|
|
|
|
}
|
|
|
|
|
Ok(scalar.unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-13 23:29:48 -04:00
|
|
|
#[derive(Debug)]
|
2022-06-30 05:42:29 -04:00
|
|
|
pub enum DLEqError {
|
2022-07-15 01:26:07 -04:00
|
|
|
InvalidProof,
|
2022-06-30 05:42:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
|
pub struct DLEqProof<G: PrimeGroup> {
|
|
|
|
|
c: G::Scalar,
|
2022-07-15 01:26:07 -04:00
|
|
|
s: G::Scalar,
|
2022-06-30 05:42:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
impl<G: PrimeGroup> DLEqProof<G> {
|
2022-07-13 23:29:48 -04:00
|
|
|
fn transcript<T: Transcript>(transcript: &mut T, generator: G, nonce: G, point: G) {
|
|
|
|
|
transcript.append_message(b"generator", generator.to_bytes().as_ref());
|
|
|
|
|
transcript.append_message(b"nonce", nonce.to_bytes().as_ref());
|
|
|
|
|
transcript.append_message(b"point", point.to_bytes().as_ref());
|
2022-06-30 05:42:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn prove<R: RngCore + CryptoRng, T: Transcript>(
|
|
|
|
|
rng: &mut R,
|
|
|
|
|
transcript: &mut T,
|
2022-07-13 23:29:48 -04:00
|
|
|
generators: &[G],
|
2022-07-15 01:26:07 -04:00
|
|
|
scalar: G::Scalar,
|
2022-06-30 05:42:29 -04:00
|
|
|
) -> DLEqProof<G> {
|
|
|
|
|
let r = G::Scalar::random(rng);
|
2022-07-13 23:29:48 -04:00
|
|
|
|
|
|
|
|
transcript.domain_separate(b"dleq");
|
|
|
|
|
for generator in generators {
|
|
|
|
|
Self::transcript(transcript, *generator, *generator * r, *generator * scalar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let c = challenge(transcript);
|
2022-06-30 05:42:29 -04:00
|
|
|
let s = r + (c * scalar);
|
|
|
|
|
|
|
|
|
|
DLEqProof { c, s }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn verify<T: Transcript>(
|
|
|
|
|
&self,
|
|
|
|
|
transcript: &mut T,
|
2022-07-13 23:29:48 -04:00
|
|
|
generators: &[G],
|
2022-07-15 01:26:07 -04:00
|
|
|
points: &[G],
|
2022-06-30 05:42:29 -04:00
|
|
|
) -> Result<(), DLEqError> {
|
2022-07-13 23:29:48 -04:00
|
|
|
if generators.len() != points.len() {
|
|
|
|
|
Err(DLEqError::InvalidProof)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transcript.domain_separate(b"dleq");
|
|
|
|
|
for (generator, point) in generators.iter().zip(points) {
|
|
|
|
|
Self::transcript(transcript, *generator, (*generator * self.s) - (*point * self.c), *point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.c != challenge(transcript) {
|
2022-06-30 05:42:29 -04:00
|
|
|
Err(DLEqError::InvalidProof)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "serialize")]
|
|
|
|
|
pub fn serialize<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
|
|
|
|
w.write_all(self.c.to_repr().as_ref())?;
|
|
|
|
|
w.write_all(self.s.to_repr().as_ref())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "serialize")]
|
|
|
|
|
pub fn deserialize<R: Read>(r: &mut R) -> io::Result<DLEqProof<G>> {
|
|
|
|
|
Ok(DLEqProof { c: read_scalar(r)?, s: read_scalar(r)? })
|
|
|
|
|
}
|
|
|
|
|
}
|