#![cfg_attr(not(feature = "std"), no_std)]
use crate::error::SchnorrError;
use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM};
use ark_ff::PrimeField;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{cfg_iter, fmt::Debug, io::Write, ops::Add, vec::Vec};
use digest::Digest;
use zeroize::{Zeroize, ZeroizeOnDrop};
use dock_crypto_utils::hashing_utils::field_elem_from_try_and_incr;
use dock_crypto_utils::serde_utils::*;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use dock_crypto_utils::expect_equality;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
pub mod discrete_log;
pub mod error;
pub mod inequality;
pub trait SchnorrChallengeContributor {
fn challenge_contribution<W: Write>(&self, writer: W) -> Result<(), SchnorrError>;
}
#[serde_as]
#[derive(
Clone,
Debug,
PartialEq,
Eq,
Zeroize,
ZeroizeOnDrop,
CanonicalSerialize,
CanonicalDeserialize,
Serialize,
Deserialize,
)]
pub struct SchnorrCommitment<G: AffineRepr> {
#[serde_as(as = "Vec<ArkObjectBytes>")]
pub blindings: Vec<G::ScalarField>,
#[zeroize(skip)]
#[serde_as(as = "ArkObjectBytes")]
pub t: G,
}
impl<G> SchnorrCommitment<G>
where
G: AffineRepr,
{
pub fn new(bases: &[G], blindings: Vec<G::ScalarField>) -> Self {
let t = G::Group::msm_unchecked(bases, &blindings).into_affine();
Self { blindings, t }
}
pub fn response(
&self,
witnesses: &[G::ScalarField],
challenge: &G::ScalarField,
) -> Result<SchnorrResponse<G>, SchnorrError> {
expect_equality!(
self.blindings.len(),
witnesses.len(),
SchnorrError::ExpectedSameSizeSequences
);
let responses = cfg_iter!(self.blindings)
.zip(cfg_iter!(witnesses))
.map(|(b, w)| *b + (*w * *challenge))
.collect::<Vec<_>>();
Ok(SchnorrResponse(responses))
}
}
impl<G> SchnorrChallengeContributor for SchnorrCommitment<G>
where
G: AffineRepr,
{
fn challenge_contribution<W: Write>(&self, writer: W) -> Result<(), SchnorrError> {
self.t.serialize_compressed(writer).map_err(|e| e.into())
}
}
#[serde_as]
#[derive(
Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
)]
pub struct SchnorrResponse<G: AffineRepr>(
#[serde_as(as = "Vec<ArkObjectBytes>")] pub Vec<G::ScalarField>,
);
impl<G> SchnorrResponse<G>
where
G: AffineRepr,
{
pub fn is_valid(
&self,
bases: &[G],
y: &G,
t: &G,
challenge: &G::ScalarField,
) -> Result<(), SchnorrError> {
expect_equality!(
self.0.len(),
bases.len(),
SchnorrError::ExpectedSameSizeSequences
);
if (G::Group::msm_unchecked(bases, &self.0).add(y.mul_bigint((-*challenge).into_bigint())))
.into_affine()
== *t
{
Ok(())
} else {
Err(SchnorrError::InvalidResponse)
}
}
pub fn get_response(&self, idx: usize) -> Result<&G::ScalarField, SchnorrError> {
if idx >= self.0.len() {
Err(SchnorrError::IndexOutOfBounds(idx, self.0.len()))
} else {
Ok(&self.0[idx])
}
}
pub fn len(&self) -> usize {
self.0.len()
}
}
pub fn compute_random_oracle_challenge<F: PrimeField, D: Digest>(challenge_bytes: &[u8]) -> F {
field_elem_from_try_and_incr::<F, D>(challenge_bytes)
}
#[cfg(test)]
mod tests {
use super::*;
use ark_bls12_381::Bls12_381;
use ark_ec::{pairing::Pairing, VariableBaseMSM};
use ark_std::{
rand::{rngs::StdRng, SeedableRng},
UniformRand,
};
type Fr = <Bls12_381 as Pairing>::ScalarField;
#[macro_export]
macro_rules! test_serialization {
($obj_type:ty, $obj: ident) => {
let mut serz = vec![];
ark_serialize::CanonicalSerialize::serialize_compressed(&$obj, &mut serz).unwrap();
let deserz: $obj_type =
ark_serialize::CanonicalDeserialize::deserialize_compressed(&serz[..]).unwrap();
assert_eq!(deserz, $obj);
let mut serz = vec![];
$obj.serialize_compressed(&mut serz).unwrap();
let deserz: $obj_type =
CanonicalDeserialize::deserialize_compressed(&serz[..]).unwrap();
assert_eq!(deserz, $obj);
let obj_ser = serde_json::to_string(&$obj).unwrap();
let obj_deser = serde_json::from_str::<$obj_type>(&obj_ser).unwrap();
assert_eq!($obj, obj_deser);
let ser = rmp_serde::to_vec_named(&$obj).unwrap();
let deser = rmp_serde::from_slice::<$obj_type>(&ser).unwrap();
assert_eq!($obj, deser);
};
}
macro_rules! test_schnorr_in_group {
( $group_element_proj:ident, $group_element_affine:ident ) => {
let mut rng = StdRng::seed_from_u64(0u64);
let count = 10;
let bases = (0..count)
.into_iter()
.map(|_| <Bls12_381 as Pairing>::$group_element_proj::rand(&mut rng).into_affine())
.collect::<Vec<_>>();
let witnesses = (0..count)
.into_iter()
.map(|_| Fr::rand(&mut rng))
.collect::<Vec<_>>();
let y =
<<Bls12_381 as Pairing>::$group_element_proj>::msm_unchecked(&bases, &witnesses)
.into_affine();
let blindings = (0..count)
.into_iter()
.map(|_| Fr::rand(&mut rng))
.collect::<Vec<_>>();
let comm = SchnorrCommitment::new(&bases, blindings);
test_serialization!(
SchnorrCommitment<<Bls12_381 as Pairing>::$group_element_affine>,
comm
);
let challenge = Fr::rand(&mut rng);
let resp = comm.response(&witnesses, &challenge).unwrap();
resp.is_valid(&bases, &y, &comm.t, &challenge).unwrap();
drop(comm);
test_serialization!(
SchnorrResponse<<Bls12_381 as Pairing>::$group_element_affine>,
resp
);
};
}
#[test]
fn schnorr_vector() {
test_schnorr_in_group!(G1, G1Affine);
test_schnorr_in_group!(G2, G2Affine);
}
}