use super::common::*;
use crate::{
common::*,
curve_arithmetic::*,
pedersen_commitment::{Commitment, CommitmentKey, Randomness, Value},
ps_sig::{BlindedSignature, BlindingRandomness, PublicKey as PsSigPublicKey},
random_oracle::RandomOracle,
};
use itertools::izip;
use rand::*;
#[derive(Clone, Debug, Serialize)]
pub struct Response<P: Pairing, C: Curve<Scalar = P::ScalarField>> {
response_rho: P::ScalarField,
#[size_length = 4]
response_commit: Vec<(P::ScalarField, C::Scalar)>,
}
pub struct ComEqSig<P: Pairing, C: Curve<Scalar = P::ScalarField>> {
pub blinded_sig: BlindedSignature<P>,
pub commitments: Vec<Commitment<C>>,
pub ps_pub_key: PsSigPublicKey<P>,
pub comm_key: CommitmentKey<C>,
}
pub type ValuesAndRands<C> = (Value<C>, Randomness<C>);
pub struct ComEqSigSecret<P: Pairing, C: Curve<Scalar = P::ScalarField>> {
pub blind_rand: BlindingRandomness<P>,
pub values_and_rands: Vec<ValuesAndRands<C>>,
}
pub struct ComEqSigState<P: Pairing, C: Curve<Scalar = P::ScalarField>> {
pub rho_prime: P::ScalarField,
pub mus_and_rs: Vec<(Value<C>, Randomness<C>)>,
}
#[allow(non_snake_case)]
impl<P: Pairing, C: Curve<Scalar = P::ScalarField>> SigmaProtocol for ComEqSig<P, C> {
type CommitMessage = (P::TargetField, Vec<Commitment<C>>);
type ProtocolChallenge = C::Scalar;
type ProverState = ComEqSigState<P, C>;
type Response = Response<P, C>;
type SecretData = ComEqSigSecret<P, C>;
#[inline]
fn public(&self, ro: &mut RandomOracle) {
ro.append_message(b"blinded_sig", &self.blinded_sig);
ro.extend_from(b"commitments", self.commitments.iter());
ro.append_message(b"ps_pub_key", &self.ps_pub_key);
ro.append_message(b"comm_key", &self.comm_key)
}
#[inline]
fn get_challenge(
&self,
challenge: &crate::random_oracle::Challenge,
) -> Self::ProtocolChallenge {
C::scalar_from_bytes(challenge)
}
#[inline]
fn compute_commit_message<R: Rng>(
&self,
csprng: &mut R,
) -> Option<(Self::CommitMessage, Self::ProverState)> {
let g_tilda = self.ps_pub_key.g_tilda;
let a_hat = self.blinded_sig.sig.0;
let _b_hat = self.blinded_sig.sig.1;
let _cX_tilda = self.ps_pub_key.x_tilda;
let cY_tilda = |i| self.ps_pub_key.y_tildas[i];
let cmm_key = self.comm_key;
let n = self.commitments.len();
if n > self.ps_pub_key.len() {
return None;
}
let mut mus_cRs = Vec::with_capacity(n);
let rho_prime = <P::G2 as Curve>::generate_non_zero_scalar(csprng);
let mut point = g_tilda.mul_by_scalar(&rho_prime);
let mut commitments = Vec::with_capacity(n);
for i in 0..n {
let mu_i = Value::generate_non_zero(csprng);
let cU_i = cY_tilda(i).mul_by_scalar(&mu_i);
let (c_i, cR_i) = cmm_key.commit(&mu_i, csprng);
commitments.push(c_i);
mus_cRs.push((mu_i, cR_i));
point = point.plus_point(&cU_i);
}
let paired = P::pair(&a_hat, &point);
Some((
(paired, commitments),
ComEqSigState {
rho_prime,
mus_and_rs: mus_cRs,
},
))
}
#[inline]
fn compute_response(
&self,
secret: Self::SecretData,
state: Self::ProverState,
challenge: &Self::ProtocolChallenge,
) -> Option<Self::Response> {
if secret.values_and_rands.len() != state.mus_and_rs.len() {
return None;
}
let n = secret.values_and_rands.len();
let r_prime = secret.blind_rand.1;
let mut res_r_prime = *challenge;
res_r_prime.mul_assign(&r_prime);
res_r_prime.negate();
res_r_prime.add_assign(&state.rho_prime);
let mut res_messages_randoms = Vec::with_capacity(n);
for ((ref m, ref r), (ref mu, ref rho)) in izip!(secret.values_and_rands, state.mus_and_rs)
{
let mut res_m = *challenge;
res_m.mul_assign(m);
res_m.negate();
res_m.add_assign(mu);
let mut res_r = *challenge;
res_r.mul_assign(r);
res_r.negate();
res_r.add_assign(rho);
res_messages_randoms.push((res_m, res_r));
}
Some(Response {
response_rho: res_r_prime,
response_commit: res_messages_randoms,
})
}
#[inline]
fn extract_commit_message(
&self,
challenge: &Self::ProtocolChallenge,
response: &Self::Response,
) -> Option<Self::CommitMessage> {
let g_tilda = self.ps_pub_key.g_tilda;
let a_hat = self.blinded_sig.sig.0;
let b_hat = self.blinded_sig.sig.1;
let cX_tilda = self.ps_pub_key.x_tilda;
let cY_tildas = &self.ps_pub_key.y_tildas;
let cmm_key = self.comm_key;
let commitments = &self.commitments;
let n = commitments.len();
if response.response_commit.len() != n {
return None;
}
if n > cY_tildas.len() {
return None;
}
let mut gs = Vec::with_capacity(n + 2);
let mut es = Vec::with_capacity(n + 2);
gs.push(g_tilda);
es.push(response.response_rho);
let mut cmms = Vec::with_capacity(n);
for (cC_i, cY_tilda, (res_m, res_r)) in izip!(
commitments.iter(),
cY_tildas,
response.response_commit.iter()
) {
let bases = [cC_i.0, cmm_key.g, cmm_key.h];
let powers = [*challenge, *res_m, *res_r];
let cP = multiexp(&bases, &powers);
cmms.push(Commitment(cP));
gs.push(*cY_tilda);
es.push(*res_m);
}
gs.push(cX_tilda);
{
let mut x = *challenge;
x.negate();
es.push(x);
}
let point = multiexp(&gs, &es);
let paired = P::pairing_product(&b_hat, &g_tilda.mul_by_scalar(challenge), &a_hat, &point);
paired.map(|paired| (paired, cmms))
}
#[cfg(test)]
fn with_valid_data<R: Rng>(
data_size: usize,
csprng: &mut R,
f: impl FnOnce(Self, Self::SecretData, &mut R),
) {
use crate::ps_sig::{SecretKey as PsSigSecretKey, SigRetrievalRandomness, UnknownMessage};
let ps_sk: PsSigSecretKey<P> = PsSigSecretKey::generate(data_size, csprng);
let ps_pk: PsSigPublicKey<P> = PsSigPublicKey::from(&ps_sk);
let cmm_key = CommitmentKey::generate(csprng);
let mut secrets = Vec::with_capacity(data_size);
let mask = SigRetrievalRandomness::generate_non_zero(csprng);
let mut comm_to_signer: P::G1 = ps_pk.g.mul_by_scalar(&mask);
let mut commitments = Vec::with_capacity(data_size);
for cY_j in ps_pk.ys.iter().take(csprng.gen_range(0..data_size)) {
let v_j = Value::generate(csprng);
let (c_j, r_j) = cmm_key.commit(&v_j, csprng);
comm_to_signer = comm_to_signer.plus_point(&cY_j.mul_by_scalar(&v_j));
secrets.push((v_j, r_j));
commitments.push(c_j);
}
let unknown_message = UnknownMessage(comm_to_signer);
let sig = ps_sk
.sign_unknown_message(&unknown_message, csprng)
.retrieve(&mask);
let (blinded_sig, blind_rand) = sig.blind(csprng);
let ces = ComEqSig {
commitments,
ps_pub_key: ps_pk,
comm_key: cmm_key,
blinded_sig,
};
let secret = ComEqSigSecret {
blind_rand,
values_and_rands: secrets,
};
f(ces, secret, csprng)
}
}
#[cfg(test)]
mod tests {
use ark_bls12_381::G1Projective;
use super::*;
use crate::{
curve_arithmetic::arkworks_instances::ArkGroup,
ps_sig::{SecretKey as PsSigSecretKey, Signature},
};
type G1 = ArkGroup<G1Projective>;
type Bls12 = ark_ec::models::bls12::Bls12<ark_bls12_381::Config>;
#[test]
#[allow(non_snake_case)]
pub fn test_com_eq_sig_correctness() {
let mut csprng = thread_rng();
for i in 1..20 {
ComEqSig::<Bls12, G1>::with_valid_data(i, &mut csprng, |ces, secret, csprng| {
let challenge_prefix = generate_challenge_prefix(csprng);
let mut ro = RandomOracle::domain(challenge_prefix);
let proof =
prove(&mut ro.split(), &ces, secret, csprng).expect("Proving should succeed.");
assert!(verify(&mut ro, &ces, &proof));
})
}
}
#[test]
#[allow(non_snake_case)]
pub fn test_com_eq_sig_soundness() {
let mut csprng = thread_rng();
for i in 1..20 {
ComEqSig::<Bls12, G1>::with_valid_data(i, &mut csprng, |ces, secret, csprng| {
let challenge_prefix = generate_challenge_prefix(csprng);
let ro = RandomOracle::domain(challenge_prefix);
let proof =
prove(&mut ro.split(), &ces, secret, csprng).expect("Proving should succeed.");
assert!(verify(&mut ro.split(), &ces, &proof));
let mut wrong_ro = RandomOracle::domain(generate_challenge_prefix(csprng));
if verify(&mut wrong_ro, &ces, &proof) {
assert_eq!(wrong_ro, ro);
}
let mut wrong_ces = ces;
{
let tmp = wrong_ces.blinded_sig;
wrong_ces.blinded_sig = BlindedSignature {
sig: Signature(G1::generate(csprng), G1::generate(csprng)),
};
assert!(!verify(&mut ro.split(), &wrong_ces, &proof));
wrong_ces.blinded_sig = tmp;
}
{
if !wrong_ces.commitments.is_empty() {
let idx = csprng.gen_range(0..wrong_ces.commitments.len());
let tmp = wrong_ces.commitments[idx];
wrong_ces.commitments[idx] = wrong_ces
.comm_key
.commit(&Value::<G1>::generate(csprng), csprng)
.0;
assert!(!verify(&mut ro.split(), &wrong_ces, &proof));
wrong_ces.commitments[idx] = tmp;
}
}
{
let tmp = wrong_ces.comm_key;
wrong_ces.comm_key = CommitmentKey::generate(csprng);
assert!(!verify(&mut ro.split(), &wrong_ces, &proof));
wrong_ces.comm_key = tmp;
}
{
let tmp = wrong_ces.ps_pub_key;
wrong_ces.ps_pub_key =
PsSigPublicKey::from(&PsSigSecretKey::generate(i, csprng));
assert!(!verify(&mut ro.split(), &wrong_ces, &proof));
wrong_ces.ps_pub_key = tmp;
}
})
}
}
}