#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![doc = include_str!("../README.md")]
use core::ops::Deref;
use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, Zeroizing};
use transcript::Transcript;
use ff::{Field, PrimeField};
use group::prime::PrimeGroup;
#[cfg(feature = "serialize")]
use std::io::{self, Error, Read, Write};
#[cfg(feature = "experimental")]
pub mod cross_group;
#[cfg(test)]
mod tests;
pub(crate) fn challenge<T: Transcript, F: PrimeField>(transcript: &mut T) -> F {
let mut challenge = F::ZERO;
let target_bytes = ((usize::try_from(F::NUM_BITS).unwrap() + 7) / 8) * 2;
let mut challenge_bytes = transcript.challenge(b"challenge");
let challenge_bytes_len = challenge_bytes.as_ref().len();
let needed_challenges = (target_bytes + (challenge_bytes_len - 1)) / challenge_bytes_len;
let mut handled_bytes = 0;
'outer: for _ in 0 ..= needed_challenges {
let mut b = 0;
while b < challenge_bytes_len {
let chunk_bytes = (target_bytes - handled_bytes).min(8).min(challenge_bytes_len - b);
let mut chunk = 0;
for _ in 0 .. chunk_bytes {
chunk <<= 8;
chunk |= u64::from(challenge_bytes.as_ref()[b]);
b += 1;
}
challenge += F::from(chunk);
handled_bytes += chunk_bytes;
if handled_bytes == target_bytes {
break 'outer;
}
let next_chunk_bytes = (target_bytes - handled_bytes).min(8).min(challenge_bytes_len);
for _ in 0 .. (next_chunk_bytes * 8) {
challenge = challenge.double();
}
}
challenge_bytes = transcript.challenge(b"challenge_extension");
}
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::other("invalid scalar"))?;
}
Ok(scalar.unwrap())
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DLEqError {
InvalidProof,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct DLEqProof<G: PrimeGroup<Scalar: Zeroize>> {
c: G::Scalar,
s: G::Scalar,
}
#[allow(non_snake_case)]
impl<G: PrimeGroup<Scalar: Zeroize>> DLEqProof<G> {
fn transcript<T: Transcript>(transcript: &mut T, generator: G, nonce: G, point: G) {
transcript.append_message(b"generator", generator.to_bytes());
transcript.append_message(b"nonce", nonce.to_bytes());
transcript.append_message(b"point", point.to_bytes());
}
pub fn prove<R: RngCore + CryptoRng, T: Transcript>(
rng: &mut R,
transcript: &mut T,
generators: &[G],
scalar: &Zeroizing<G::Scalar>,
) -> DLEqProof<G> {
let r = Zeroizing::new(G::Scalar::random(rng));
transcript.domain_separate(b"dleq");
for generator in generators {
Self::transcript(transcript, *generator, *generator * r.deref(), *generator * scalar.deref());
}
let c = challenge(transcript);
let s = (c * scalar.deref()) + r.deref();
DLEqProof { c, s }
}
fn verify_statement<T: Transcript>(
transcript: &mut T,
generator: G,
point: G,
c: G::Scalar,
s: G::Scalar,
) {
Self::transcript(transcript, generator, (generator * s) - (point * c), point);
}
pub fn verify<T: Transcript>(
&self,
transcript: &mut T,
generators: &[G],
points: &[G],
) -> Result<(), DLEqError> {
if generators.len() != points.len() {
Err(DLEqError::InvalidProof)?;
}
transcript.domain_separate(b"dleq");
for (generator, point) in generators.iter().zip(points) {
Self::verify_statement(transcript, *generator, *point, self.c, self.s);
}
if self.c != challenge(transcript) {
Err(DLEqError::InvalidProof)?;
}
Ok(())
}
#[cfg(feature = "serialize")]
pub fn write<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 read<R: Read>(r: &mut R) -> io::Result<DLEqProof<G>> {
Ok(DLEqProof { c: read_scalar(r)?, s: read_scalar(r)? })
}
#[cfg(feature = "serialize")]
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
res
}
}
#[cfg(feature = "std")]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct MultiDLEqProof<G: PrimeGroup<Scalar: Zeroize>> {
c: G::Scalar,
s: Vec<G::Scalar>,
}
#[cfg(feature = "std")]
#[allow(non_snake_case)]
impl<G: PrimeGroup<Scalar: Zeroize>> MultiDLEqProof<G> {
pub fn prove<R: RngCore + CryptoRng, T: Transcript>(
rng: &mut R,
transcript: &mut T,
generators: &[Vec<G>],
scalars: &[Zeroizing<G::Scalar>],
) -> MultiDLEqProof<G> {
assert_eq!(
generators.len(),
scalars.len(),
"amount of series of generators doesn't match the amount of scalars"
);
transcript.domain_separate(b"multi_dleq");
let mut nonces = vec![];
for (i, (scalar, generators)) in scalars.iter().zip(generators).enumerate() {
transcript.append_message(b"discrete_logarithm", i.to_le_bytes());
let nonce = Zeroizing::new(G::Scalar::random(&mut *rng));
for generator in generators {
DLEqProof::transcript(
transcript,
*generator,
*generator * nonce.deref(),
*generator * scalar.deref(),
);
}
nonces.push(nonce);
}
let c = challenge(transcript);
let mut s = vec![];
for (scalar, nonce) in scalars.iter().zip(nonces) {
s.push((c * scalar.deref()) + nonce.deref());
}
MultiDLEqProof { c, s }
}
pub fn verify<T: Transcript>(
&self,
transcript: &mut T,
generators: &[Vec<G>],
points: &[Vec<G>],
) -> Result<(), DLEqError> {
if points.len() != generators.len() {
Err(DLEqError::InvalidProof)?;
}
if self.s.len() != generators.len() {
Err(DLEqError::InvalidProof)?;
}
transcript.domain_separate(b"multi_dleq");
for (i, (generators, points)) in generators.iter().zip(points).enumerate() {
if points.len() != generators.len() {
Err(DLEqError::InvalidProof)?;
}
transcript.append_message(b"discrete_logarithm", i.to_le_bytes());
for (generator, point) in generators.iter().zip(points) {
DLEqProof::verify_statement(transcript, *generator, *point, self.c, self.s[i]);
}
}
if self.c != challenge(transcript) {
Err(DLEqError::InvalidProof)?;
}
Ok(())
}
#[cfg(feature = "serialize")]
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_all(self.c.to_repr().as_ref())?;
for s in &self.s {
w.write_all(s.to_repr().as_ref())?;
}
Ok(())
}
#[cfg(feature = "serialize")]
pub fn read<R: Read>(r: &mut R, discrete_logs: usize) -> io::Result<MultiDLEqProof<G>> {
let c = read_scalar(r)?;
let mut s = vec![];
for _ in 0 .. discrete_logs {
s.push(read_scalar(r)?);
}
Ok(MultiDLEqProof { c, s })
}
#[cfg(feature = "serialize")]
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
res
}
}