use crate::{
error::BBSPlusError,
prelude::PreparedPublicKeyG2,
setup::{PreparedSignatureParamsG1, SignatureParamsG1},
signature::SignatureG1,
};
use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, Group, VariableBaseMSM};
use ark_ff::{Field, PrimeField, Zero};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{
collections::{BTreeMap, BTreeSet},
fmt::Debug,
io::Write,
rand::RngCore,
vec::Vec,
UniformRand,
};
use core::mem;
use dock_crypto_utils::{
misc::rand,
randomized_pairing_check::RandomizedPairingChecker,
serde_utils::*,
signature::{
msg_index_map_to_schnorr_response_map, msg_index_to_schnorr_response_index,
schnorr_responses_to_msg_index_map, split_messages_and_blindings, MessageOrBlinding,
MultiMessageSignatureParams,
},
};
use itertools::multiunzip;
use schnorr_pok::{
discrete_log::{PokPedersenCommitment, PokPedersenCommitmentProtocol},
error::SchnorrError,
partial::PartialSchnorrResponse,
SchnorrCommitment, SchnorrResponse,
};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[serde_as]
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Zeroize,
ZeroizeOnDrop,
CanonicalSerialize,
CanonicalDeserialize,
Serialize,
Deserialize,
)]
pub struct PoKOfSignatureG1Protocol<E: Pairing> {
#[zeroize(skip)]
#[serde_as(as = "ArkObjectBytes")]
pub A_prime: E::G1Affine,
#[zeroize(skip)]
#[serde_as(as = "ArkObjectBytes")]
pub A_bar: E::G1Affine,
#[zeroize(skip)]
#[serde_as(as = "ArkObjectBytes")]
pub d: E::G1Affine,
pub sc_comm_1: PokPedersenCommitmentProtocol<E::G1Affine>,
pub sc_comm_2: SchnorrCommitment<E::G1Affine>,
#[serde_as(as = "Vec<ArkObjectBytes>")]
sc_wits_2: Vec<E::ScalarField>,
}
#[serde_as]
#[derive(
Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
)]
pub struct PoKOfSignatureG1Proof<E: Pairing> {
#[serde_as(as = "ArkObjectBytes")]
pub A_prime: E::G1Affine,
#[serde_as(as = "ArkObjectBytes")]
pub A_bar: E::G1Affine,
#[serde_as(as = "ArkObjectBytes")]
pub d: E::G1Affine,
pub sc_resp_1: PokPedersenCommitment<E::G1Affine>,
#[serde_as(as = "ArkObjectBytes")]
pub T2: E::G1Affine,
pub sc_resp_2: Option<SchnorrResponse<E::G1Affine>>,
pub sc_partial_resp_2: Option<PartialSchnorrResponse<E::G1Affine>>,
}
impl<E: Pairing> PoKOfSignatureG1Protocol<E> {
pub fn init<'a, MBI, R: RngCore>(
rng: &mut R,
signature: &SignatureG1<E>,
params: &SignatureParamsG1<E>,
messages_and_blindings: MBI,
) -> Result<Self, BBSPlusError>
where
MBI: IntoIterator<Item = MessageOrBlinding<'a, E::ScalarField>>,
{
let (messages, indexed_blindings) =
match split_messages_and_blindings(rng, messages_and_blindings, params) {
Ok(t) => t,
Err(l) => {
return Err(BBSPlusError::MessageCountIncompatibleWithSigParams(
l,
params.supported_message_count(),
))
}
};
let mut r1 = E::ScalarField::rand(rng);
while r1.is_zero() {
r1 = E::ScalarField::rand(rng);
}
let r2 = E::ScalarField::rand(rng);
let r3 = r1.inverse().unwrap();
let b = params.b(messages.iter().enumerate(), &signature.s)?;
let A_prime = signature.A.mul_bigint(r1.into_bigint());
let b_r1 = b * r1;
let A_bar = b_r1 - (A_prime.mul_bigint(signature.e.into_bigint()));
let d = b_r1 - params.h_0.mul_bigint(r2.into_bigint());
let d_affine = d.into_affine();
let s_prime = signature.s - (r2 * r3);
let A_prime_affine = A_prime.into_affine();
let sc_comm_1 = PokPedersenCommitmentProtocol::init(
-signature.e,
E::ScalarField::rand(rng),
&A_prime_affine,
r2,
E::ScalarField::rand(rng),
¶ms.h_0,
);
let h_blinding_message = indexed_blindings
.into_iter()
.map(|(idx, blinding)| (params.h[idx], blinding, messages[idx]));
let (bases_2, randomness_2, wits_2): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(
h_blinding_message
.into_iter()
.chain([(d_affine, rand(rng), -r3), (params.h_0, rand(rng), s_prime)]),
);
let sc_comm_2 = SchnorrCommitment::new(&bases_2, randomness_2);
Ok(Self {
A_prime: A_prime_affine,
A_bar: A_bar.into_affine(),
d: d_affine,
sc_comm_1,
sc_comm_2,
sc_wits_2: wits_2,
})
}
pub fn challenge_contribution<W: Write>(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
params: &SignatureParamsG1<E>,
writer: W,
) -> Result<(), BBSPlusError> {
Self::compute_challenge_contribution(
&self.A_prime,
&self.A_bar,
&self.d,
&self.sc_comm_1.t,
&self.sc_comm_2.t,
revealed_msgs,
params,
writer,
)
}
pub fn gen_proof(
mut self,
challenge: &E::ScalarField,
) -> Result<PoKOfSignatureG1Proof<E>, BBSPlusError> {
let sc_resp_1 = mem::take(&mut self.sc_comm_1).gen_proof(challenge);
let sc_resp_2 = self.sc_comm_2.response(&self.sc_wits_2, challenge)?;
Ok(PoKOfSignatureG1Proof {
A_prime: self.A_prime,
A_bar: self.A_bar,
d: self.d,
sc_resp_1,
T2: self.sc_comm_2.t,
sc_resp_2: Some(sc_resp_2),
sc_partial_resp_2: None,
})
}
pub fn gen_partial_proof(
mut self,
challenge: &E::ScalarField,
revealed_msg_ids: &BTreeSet<usize>,
skip_responses_for: &BTreeSet<usize>,
) -> Result<PoKOfSignatureG1Proof<E>, BBSPlusError> {
if !skip_responses_for.is_disjoint(revealed_msg_ids) {
return Err(BBSPlusError::CommonIndicesFoundInRevealedAndSkip);
}
let sc_resp_1 = mem::take(&mut self.sc_comm_1).gen_proof(challenge);
let wits = schnorr_responses_to_msg_index_map(
mem::take(&mut self.sc_wits_2),
revealed_msg_ids,
skip_responses_for,
);
let sc_resp_2 = self.sc_comm_2.partial_response(wits, challenge)?;
Ok(PoKOfSignatureG1Proof {
A_prime: self.A_prime,
A_bar: self.A_bar,
d: self.d,
sc_resp_1,
T2: self.sc_comm_2.t,
sc_resp_2: None,
sc_partial_resp_2: Some(sc_resp_2),
})
}
pub fn compute_challenge_contribution<W: Write>(
A_prime: &E::G1Affine,
A_bar: &E::G1Affine,
d: &E::G1Affine,
T1: &E::G1Affine,
T2: &E::G1Affine,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
params: &SignatureParamsG1<E>,
mut writer: W,
) -> Result<(), BBSPlusError> {
A_prime.serialize_compressed(&mut writer)?;
A_bar.serialize_compressed(&mut writer)?;
d.serialize_compressed(&mut writer)?;
params.h_0.serialize_compressed(&mut writer)?;
params.g1.serialize_compressed(&mut writer)?;
T1.serialize_compressed(&mut writer)?;
T2.serialize_compressed(&mut writer)?;
for i in 0..params.h.len() {
params.h[i].serialize_compressed(&mut writer)?;
if let Some(m) = revealed_msgs.get(&i) {
m.serialize_compressed(&mut writer)?;
}
}
Ok(())
}
}
impl<E: Pairing> PoKOfSignatureG1Proof<E> {
pub fn verify(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
) -> Result<(), BBSPlusError> {
self._verify(revealed_msgs, challenge, pk, params, None)
}
pub fn verify_with_randomized_pairing_checker(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
pairing_checker: &mut RandomizedPairingChecker<E>,
) -> Result<(), BBSPlusError> {
self._verify_with_randomized_pairing_checker(
revealed_msgs,
challenge,
pk,
params,
pairing_checker,
None,
)
}
pub fn verify_partial(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
missing_responses: BTreeMap<usize, E::ScalarField>,
) -> Result<(), BBSPlusError> {
self._verify(
revealed_msgs,
challenge,
pk,
params,
Some(missing_responses),
)
}
pub fn verify_partial_with_randomized_pairing_checker(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
pairing_checker: &mut RandomizedPairingChecker<E>,
missing_responses: BTreeMap<usize, E::ScalarField>,
) -> Result<(), BBSPlusError> {
self._verify_with_randomized_pairing_checker(
revealed_msgs,
challenge,
pk,
params,
pairing_checker,
Some(missing_responses),
)
}
pub fn challenge_contribution<W: Write>(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
params: &SignatureParamsG1<E>,
writer: W,
) -> Result<(), BBSPlusError> {
PoKOfSignatureG1Protocol::compute_challenge_contribution(
&self.A_prime,
&self.A_bar,
&self.d,
&self.sc_resp_1.t,
&self.T2,
revealed_msgs,
params,
writer,
)
}
pub fn get_resp_for_message(
&self,
msg_idx: usize,
revealed_msg_ids: &BTreeSet<usize>,
) -> Result<&E::ScalarField, BBSPlusError> {
let adjusted_idx = msg_index_to_schnorr_response_index(msg_idx, revealed_msg_ids)
.ok_or_else(|| BBSPlusError::InvalidMsgIdxForResponse(msg_idx))?;
if let Some(resp) = self.sc_resp_2.as_ref() {
Ok(resp.get_response(adjusted_idx)?)
} else if let Some(resp) = self.sc_partial_resp_2.as_ref() {
Ok(resp.get_response(adjusted_idx)?)
} else {
Err(BBSPlusError::NeedEitherPartialOrCompleteSchnorrResponse)
}
}
pub fn get_responses(
&self,
msg_ids: &BTreeSet<usize>,
revealed_msg_ids: &BTreeSet<usize>,
) -> Result<BTreeMap<usize, E::ScalarField>, BBSPlusError> {
let mut resps = BTreeMap::new();
for msg_idx in msg_ids {
resps.insert(
*msg_idx,
*self.get_resp_for_message(*msg_idx, revealed_msg_ids)?,
);
}
Ok(resps)
}
pub fn _verify(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
let params = params.into();
let g1 = params.g1;
let g2 = params.g2;
let h0 = params.h_0;
let h = params.h;
self.verify_except_pairings(revealed_msgs, challenge, g1, h0, h, missing_responses)?;
if !E::multi_pairing(
[
E::G1Prepared::from(self.A_prime),
E::G1Prepared::from(-(self.A_bar.into_group())),
],
[pk.into().0, g2],
)
.is_zero()
{
return Err(BBSPlusError::PairingCheckFailed);
}
Ok(())
}
pub fn _verify_with_randomized_pairing_checker(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
pairing_checker: &mut RandomizedPairingChecker<E>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
let params = params.into();
let g1 = params.g1;
let g2 = params.g2;
let h0 = params.h_0;
let h = params.h;
self.verify_except_pairings(revealed_msgs, challenge, g1, h0, h, missing_responses)?;
pairing_checker.add_sources(&self.A_prime, pk.into().0, &self.A_bar, g2);
Ok(())
}
fn verify_except_pairings(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
g1: E::G1Affine,
h_0: E::G1Affine,
h: Vec<E::G1Affine>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
if self.A_prime.is_zero() {
return Err(BBSPlusError::ZeroSignature);
}
self.verify_schnorr_proofs(revealed_msgs, challenge, g1, h_0, h, missing_responses)
}
pub fn verify_schnorr_proofs(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
g1: E::G1Affine,
h_0: E::G1Affine,
h: Vec<E::G1Affine>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
let A_bar_minus_d = (self.A_bar.into_group() - self.d.into_group()).into_affine();
if !self
.sc_resp_1
.verify(&A_bar_minus_d, &self.A_prime, &h_0, challenge)
{
return Err(BBSPlusError::FirstSchnorrVerificationFailed);
}
let mut bases_2 = Vec::with_capacity(2 + h.len() - revealed_msgs.len());
let mut bases_revealed = Vec::with_capacity(revealed_msgs.len());
let mut exponents = Vec::with_capacity(revealed_msgs.len());
for i in 0..h.len() {
if revealed_msgs.contains_key(&i) {
let message = revealed_msgs.get(&i).unwrap();
bases_revealed.push(h[i]);
exponents.push(*message);
} else {
bases_2.push(h[i]);
}
}
bases_2.push(self.d);
bases_2.push(h_0);
let pr = -E::G1::msm_unchecked(&bases_revealed, &exponents) - g1;
let pr = pr.into_affine();
if let Some(resp) = &self.sc_resp_2 {
if missing_responses.is_some() {
return Err(BBSPlusError::MissingResponsesProvidedForFullSchnorrProofVerification);
}
return match resp.is_valid(&bases_2, &pr, &self.T2, challenge) {
Ok(()) => Ok(()),
Err(SchnorrError::InvalidResponse) => {
Err(BBSPlusError::SecondSchnorrVerificationFailed)
}
Err(other) => Err(BBSPlusError::SchnorrError(other)),
};
} else if let Some(resp) = &self.sc_partial_resp_2 {
if missing_responses.is_none() {
return Err(BBSPlusError::MissingResponsesNeededForPartialSchnorrProofVerification);
}
let adjusted_missing = msg_index_map_to_schnorr_response_map(
missing_responses.unwrap(),
revealed_msgs.keys(),
);
return match resp.is_valid(&bases_2, &pr, &self.T2, challenge, adjusted_missing) {
Ok(()) => Ok(()),
Err(SchnorrError::InvalidResponse) => {
Err(BBSPlusError::SecondSchnorrVerificationFailed)
}
Err(other) => Err(BBSPlusError::SchnorrError(other)),
};
} else {
Err(BBSPlusError::NeedEitherPartialOrCompleteSchnorrResponse)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{setup::KeypairG2, test_serialization};
use ark_bls12_381::Bls12_381;
use ark_serialize::CanonicalDeserialize;
use ark_std::{
rand::{rngs::StdRng, SeedableRng},
UniformRand,
};
use blake2::Blake2b512;
use schnorr_pok::compute_random_oracle_challenge;
use std::time::{Duration, Instant};
type Fr = <Bls12_381 as Pairing>::ScalarField;
fn sig_setup<R: RngCore>(
rng: &mut R,
message_count: u32,
) -> (
Vec<Fr>,
SignatureParamsG1<Bls12_381>,
KeypairG2<Bls12_381>,
SignatureG1<Bls12_381>,
) {
let messages: Vec<Fr> = (0..message_count).map(|_| Fr::rand(rng)).collect();
let params = SignatureParamsG1::<Bls12_381>::generate_using_rng(rng, message_count);
let keypair = KeypairG2::<Bls12_381>::generate_using_rng(rng, ¶ms);
let sig =
SignatureG1::<Bls12_381>::new(rng, &messages, &keypair.secret_key, ¶ms).unwrap();
(messages, params, keypair, sig)
}
#[macro_export]
macro_rules! gen_test_pok_signature_revealed_message {
($protocol: ident, $proof: ident) => {{
let mut rng = StdRng::seed_from_u64(0u64);
let message_count = 20;
let (messages, params, keypair, sig) = sig_setup(&mut rng, message_count);
sig.verify(&messages, keypair.public_key.clone(), params.clone())
.unwrap();
let mut revealed_indices = BTreeSet::new();
revealed_indices.insert(0);
revealed_indices.insert(2);
let mut revealed_msgs = BTreeMap::new();
for i in revealed_indices.iter() {
revealed_msgs.insert(*i, messages[*i]);
}
let mut proof_create_duration = Duration::default();
let start = Instant::now();
let pok = $protocol::init(
&mut rng,
&sig,
¶ms,
messages.iter().enumerate().map(|(idx, msg)| {
if revealed_indices.contains(&idx) {
MessageOrBlinding::RevealMessage(msg)
} else {
MessageOrBlinding::BlindMessageRandomly(msg)
}
}),
)
.unwrap();
proof_create_duration += start.elapsed();
test_serialization!($protocol<Bls12_381>, pok);
let start = Instant::now();
let mut chal_bytes_prover = vec![];
keypair
.public_key
.serialize_compressed(&mut chal_bytes_prover)
.unwrap();
pok.challenge_contribution(&revealed_msgs, ¶ms, &mut chal_bytes_prover)
.unwrap();
let challenge_prover =
compute_random_oracle_challenge::<Fr, Blake2b512>(&chal_bytes_prover);
let proof = pok.gen_proof(&challenge_prover).unwrap();
proof_create_duration += start.elapsed();
let public_key = &keypair.public_key;
assert!(params.is_valid());
assert!(public_key.is_valid());
let start = Instant::now();
let mut chal_bytes_verifier = vec![];
keypair
.public_key
.serialize_compressed(&mut chal_bytes_verifier)
.unwrap();
proof
.challenge_contribution(&revealed_msgs, ¶ms, &mut chal_bytes_verifier)
.unwrap();
let challenge_verifier =
compute_random_oracle_challenge::<Fr, Blake2b512>(&chal_bytes_verifier);
assert_eq!(chal_bytes_prover, chal_bytes_verifier);
proof
.verify(
&revealed_msgs,
&challenge_verifier,
public_key.clone(),
params.clone(),
)
.unwrap();
let proof_verif_duration = start.elapsed();
test_serialization!($proof<Bls12_381>, proof);
println!(
"Time to create proof with message size {} and revealing {} messages is {:?}",
message_count,
revealed_indices.len(),
proof_create_duration
);
println!(
"Time to verify proof with message size {} and revealing {} messages is {:?}",
message_count,
revealed_indices.len(),
proof_verif_duration
);
}};
}
#[macro_export]
macro_rules! gen_test_PoK_multiple_sigs_with_same_msg {
($params: ident, $sig: ident, $fn_name: ident, $protocol: ident) => {{
let mut rng = StdRng::seed_from_u64(0u64);
let message_1_count = 10;
let message_2_count = 9;
let message_3_count = 8;
let params_1 =
$params::<Bls12_381>::new::<Blake2b512>("test".as_bytes(), message_1_count);
let params_2 =
$params::<Bls12_381>::new::<Blake2b512>("test-1".as_bytes(), message_2_count);
let params_3 =
$params::<Bls12_381>::new::<Blake2b512>("test-2".as_bytes(), message_3_count);
let keypair_1 = KeypairG2::<Bls12_381>::$fn_name(&mut rng, ¶ms_1);
let keypair_2 = KeypairG2::<Bls12_381>::$fn_name(&mut rng, ¶ms_2);
let keypair_3 = KeypairG2::<Bls12_381>::$fn_name(&mut rng, ¶ms_3);
let same_msg_idx = BTreeSet::from([0, 3, 4, 7]);
let mut messages_1: Vec<Fr> = (0..message_1_count - same_msg_idx.len() as u32)
.map(|_| Fr::rand(&mut rng))
.collect();
let mut messages_2: Vec<Fr> = (0..message_2_count - same_msg_idx.len() as u32)
.map(|_| Fr::rand(&mut rng))
.collect();
let mut messages_3: Vec<Fr> = (0..message_3_count - same_msg_idx.len() as u32)
.map(|_| Fr::rand(&mut rng))
.collect();
let same_msgs = same_msg_idx.clone().into_iter().map(|i| (i, Fr::rand(&mut rng))).collect::<BTreeMap<usize, Fr>>();
for (i, m) in &same_msgs {
messages_1.insert(*i, m.clone());
messages_2.insert(*i, m.clone());
messages_3.insert(*i, m.clone());
}
let sig_1 =
$sig::<Bls12_381>::new(&mut rng, &messages_1, &keypair_1.secret_key, ¶ms_1)
.unwrap();
sig_1
.verify(&messages_1, keypair_1.public_key.clone(), params_1.clone())
.unwrap();
let sig_2 =
$sig::<Bls12_381>::new(&mut rng, &messages_2, &keypair_2.secret_key, ¶ms_2)
.unwrap();
sig_2
.verify(&messages_2, keypair_2.public_key.clone(), params_2.clone())
.unwrap();
let sig_3 =
$sig::<Bls12_381>::new(&mut rng, &messages_3, &keypair_3.secret_key, ¶ms_3)
.unwrap();
sig_3
.verify(&messages_3, keypair_3.public_key.clone(), params_3.clone())
.unwrap();
let revealed_indices = BTreeSet::from([2, 5, 6]);
let mut revealed_msgs_1 = BTreeMap::new();
let mut revealed_msgs_2 = BTreeMap::new();
let mut revealed_msgs_3 = BTreeMap::new();
for i in revealed_indices.iter() {
revealed_msgs_1.insert(*i, messages_1[*i]);
revealed_msgs_2.insert(*i, messages_2[*i]);
revealed_msgs_3.insert(*i, messages_3[*i]);
}
let same_blindings = same_msg_idx.clone().into_iter().map(|i| (i, Fr::rand(&mut rng))).collect::<BTreeMap<usize, Fr>>();
let mut blindings_1 = BTreeMap::new();
let mut blindings_2 = BTreeMap::new();
let mut blindings_3 = BTreeMap::new();
for (i, b) in &same_blindings {
blindings_1.insert(*i, *b);
blindings_2.insert(*i, *b);
blindings_3.insert(*i, *b);
}
blindings_1.insert(1, Fr::rand(&mut rng));
blindings_3.insert(2, Fr::rand(&mut rng));
let pok_1 = $protocol::init(
&mut rng,
&sig_1,
¶ms_1,
messages_1.iter().enumerate().map(|(idx, message)| {
if revealed_indices.contains(&idx) {
MessageOrBlinding::RevealMessage(message)
} else if let Some(blinding) = blindings_1.remove(&idx) {
MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding }
} else {
MessageOrBlinding::BlindMessageRandomly(message)
}
}),
)
.unwrap();
let pok_2 = $protocol::init(
&mut rng,
&sig_2,
¶ms_2,
messages_2.iter().enumerate().map(|(idx, message)| {
if revealed_indices.contains(&idx) {
MessageOrBlinding::RevealMessage(message)
} else if let Some(blinding) = blindings_2.remove(&idx) {
MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding }
} else {
MessageOrBlinding::BlindMessageRandomly(message)
}
}),
)
.unwrap();
let pok_3 = $protocol::init(
&mut rng,
&sig_3,
¶ms_3,
messages_3.iter().enumerate().map(|(idx, message)| {
if revealed_indices.contains(&idx) {
MessageOrBlinding::RevealMessage(message)
} else if let Some(blinding) = blindings_3.remove(&idx) {
MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding }
} else {
MessageOrBlinding::BlindMessageRandomly(message)
}
}),
)
.unwrap();
let mut chal_bytes_prover = vec![];
keypair_1.public_key.serialize_compressed(&mut chal_bytes_prover).unwrap();
keypair_2.public_key.serialize_compressed(&mut chal_bytes_prover).unwrap();
keypair_3.public_key.serialize_compressed(&mut chal_bytes_prover).unwrap();
pok_1
.challenge_contribution(&revealed_msgs_1, ¶ms_1, &mut chal_bytes_prover)
.unwrap();
pok_2
.challenge_contribution(&revealed_msgs_2, ¶ms_2, &mut chal_bytes_prover)
.unwrap();
pok_3
.challenge_contribution(&revealed_msgs_3, ¶ms_3, &mut chal_bytes_prover)
.unwrap();
let challenge_prover =
compute_random_oracle_challenge::<Fr, Blake2b512>(&chal_bytes_prover);
let proof_1 = pok_1.gen_proof(&challenge_prover).unwrap();
let proof_2 = pok_2.gen_proof(&challenge_prover).unwrap();
let proof_3 = pok_3.gen_partial_proof(&challenge_prover, &revealed_indices, &same_msg_idx).unwrap();
let mut chal_bytes_verifier = vec![];
keypair_1.public_key.serialize_compressed(&mut chal_bytes_verifier).unwrap();
keypair_2.public_key.serialize_compressed(&mut chal_bytes_verifier).unwrap();
keypair_3.public_key.serialize_compressed(&mut chal_bytes_verifier).unwrap();
proof_1
.challenge_contribution(&revealed_msgs_1, ¶ms_1, &mut chal_bytes_verifier)
.unwrap();
proof_2
.challenge_contribution(&revealed_msgs_2, ¶ms_2, &mut chal_bytes_verifier)
.unwrap();
proof_3
.challenge_contribution(&revealed_msgs_3, ¶ms_3, &mut chal_bytes_verifier)
.unwrap();
let challenge_verifier =
compute_random_oracle_challenge::<Fr, Blake2b512>(&chal_bytes_verifier);
for i in &same_msg_idx {
assert_eq!(
proof_1
.get_resp_for_message(*i, &revealed_indices)
.unwrap(),
proof_2
.get_resp_for_message(*i, &revealed_indices)
.unwrap()
);
assert!(proof_3.get_resp_for_message(*i, &revealed_indices).is_err())
}
let missing_resps = proof_1.get_responses(&same_msg_idx, &revealed_indices).unwrap();
proof_1
.verify(
&revealed_msgs_1,
&challenge_verifier,
keypair_1.public_key.clone(),
params_1,
)
.unwrap();
proof_2
.verify(
&revealed_msgs_2,
&challenge_verifier,
keypair_2.public_key.clone(),
params_2,
)
.unwrap();
proof_3
.verify_partial(
&revealed_msgs_3,
&challenge_verifier,
keypair_3.public_key.clone(),
params_3,
missing_resps
)
.unwrap();
assert!(proof_3.get_responses(&same_msg_idx, &revealed_indices).is_err());
let mut partial_resp_ids = BTreeSet::new();
for i in 0..message_3_count as usize {
if (!same_msg_idx.contains(&i)) && !revealed_indices.contains(&i) {
partial_resp_ids.insert(i);
}
}
let partial_resps = proof_3.get_responses(&partial_resp_ids, &revealed_indices).unwrap();
for i in partial_resp_ids {
assert_eq!(proof_3.get_resp_for_message(i,&revealed_indices).unwrap(), partial_resps.get(&i).unwrap());
}
}}
}
#[macro_export]
macro_rules! gen_test_PoK_multiple_sigs_with_randomized_pairing_check {
($params: ident, $prepared_params: ident, $sig: ident, $fn_name: ident, $protocol: ident) => {{
let mut rng = StdRng::seed_from_u64(0u64);
let message_count = 5;
let params =
$params::<Bls12_381>::new::<Blake2b512>("test".as_bytes(), message_count);
let keypair = KeypairG2::<Bls12_381>::$fn_name(&mut rng, ¶ms);
let prepared_pk = PreparedPublicKeyG2::from(keypair.public_key.clone());
let prepared_params = $prepared_params::from(params.clone());
test_serialization!(PreparedPublicKeyG2<Bls12_381>, prepared_pk);
test_serialization!($prepared_params<Bls12_381>, prepared_params);
let sig_count = 10;
let mut msgs = vec![];
let mut sigs = vec![];
let mut chal_bytes_prover = vec![];
let mut poks = vec![];
let mut proofs = vec![];
keypair.public_key.serialize_compressed(&mut chal_bytes_prover).unwrap();
for i in 0..sig_count {
msgs.push(
(0..message_count)
.map(|_| Fr::rand(&mut rng))
.collect::<Vec<Fr>>(),
);
sigs.push(
$sig::<Bls12_381>::new(&mut rng, &msgs[i], &keypair.secret_key, ¶ms)
.unwrap(),
);
let pok = $protocol::init(
&mut rng,
&sigs[i],
¶ms,
msgs[i].iter().map(MessageOrBlinding::BlindMessageRandomly),
)
.unwrap();
pok.challenge_contribution(&BTreeMap::new(), ¶ms, &mut chal_bytes_prover)
.unwrap();
poks.push(pok);
}
let challenge_prover =
compute_random_oracle_challenge::<Fr, Blake2b512>(&chal_bytes_prover);
for pok in poks {
proofs.push(pok.gen_proof(&challenge_prover).unwrap());
}
let mut chal_bytes_verifier = vec![];
keypair.public_key.serialize_compressed(&mut chal_bytes_verifier).unwrap();
for proof in &proofs {
proof
.challenge_contribution(&BTreeMap::new(), ¶ms, &mut chal_bytes_verifier)
.unwrap();
}
let challenge_verifier =
compute_random_oracle_challenge::<Fr, Blake2b512>(&chal_bytes_verifier);
let start = Instant::now();
for proof in proofs.clone() {
proof
.verify(
&BTreeMap::new(),
&challenge_verifier,
keypair.public_key.clone(),
params.clone(),
)
.unwrap();
}
println!("Time to verify {} sigs: {:?}", sig_count, start.elapsed());
let start = Instant::now();
for proof in proofs.clone() {
proof
.verify(
&BTreeMap::new(),
&challenge_verifier,
prepared_pk.clone(),
prepared_params.clone(),
)
.unwrap();
}
println!(
"Time to verify {} sigs using prepared public key and params: {:?}",
sig_count,
start.elapsed()
);
let mut pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true);
let start = Instant::now();
for proof in proofs.clone() {
proof
.verify_with_randomized_pairing_checker(
&BTreeMap::new(),
&challenge_verifier,
keypair.public_key.clone(),
params.clone(),
&mut pairing_checker,
)
.unwrap();
}
assert!(pairing_checker.verify());
println!(
"Time to verify {} sigs using randomized pairing checker: {:?}",
sig_count,
start.elapsed()
);
let mut pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true);
let start = Instant::now();
for proof in proofs.clone() {
proof
.verify_with_randomized_pairing_checker(
&BTreeMap::new(),
&challenge_verifier,
prepared_pk.clone(),
prepared_params.clone(),
&mut pairing_checker,
)
.unwrap();
}
assert!(pairing_checker.verify());
println!(
"Time to verify {} sigs using prepared public key and params and randomized pairing checker: {:?}",
sig_count,
start.elapsed()
);
}}
}
#[macro_export]
macro_rules! gen_test_pok_signature_schnorr_response {
($setup_fn_name: ident, $protocol: ident, $resp_name: ident) => {{
let mut rng = StdRng::seed_from_u64(0u64);
let message_count = 6;
let (messages, params, _keypair, sig) = $setup_fn_name(&mut rng, message_count);
let challenge = Fr::rand(&mut rng);
let revealed_indices_1 = BTreeSet::new();
let pok_1 = $protocol::init(
&mut rng,
&sig,
¶ms,
messages.iter().enumerate().map(|(idx, msg)| {
if revealed_indices_1.contains(&idx) {
MessageOrBlinding::RevealMessage(msg)
} else {
MessageOrBlinding::BlindMessageRandomly(msg)
}
}),
)
.unwrap();
let proof_1 = pok_1.gen_proof(&challenge).unwrap();
let resps = proof_1.$resp_name.as_ref().unwrap();
for i in 0..message_count as usize {
assert_eq!(
*proof_1
.get_resp_for_message(i, &revealed_indices_1)
.unwrap(),
resps.0[i]
);
}
let mut revealed_indices_2 = BTreeSet::new();
revealed_indices_2.insert(0);
revealed_indices_2.insert(2);
revealed_indices_2.insert(5);
let pok_2 = $protocol::init(
&mut rng,
&sig,
¶ms,
messages.iter().enumerate().map(|(idx, msg)| {
if revealed_indices_2.contains(&idx) {
MessageOrBlinding::RevealMessage(msg)
} else {
MessageOrBlinding::BlindMessageRandomly(msg)
}
}),
)
.unwrap();
let proof_2 = pok_2.gen_proof(&challenge).unwrap();
assert!(proof_2
.get_resp_for_message(0, &revealed_indices_2)
.is_err());
assert!(proof_2
.get_resp_for_message(2, &revealed_indices_2)
.is_err());
assert!(proof_2
.get_resp_for_message(5, &revealed_indices_2)
.is_err());
let resps = proof_2.$resp_name.as_ref().unwrap();
assert_eq!(
*proof_2
.get_resp_for_message(1, &revealed_indices_2)
.unwrap(),
resps.0[0]
);
assert_eq!(
*proof_2
.get_resp_for_message(3, &revealed_indices_2)
.unwrap(),
resps.0[1]
);
assert_eq!(
*proof_2
.get_resp_for_message(4, &revealed_indices_2)
.unwrap(),
resps.0[2]
);
let mut revealed_indices_3 = BTreeSet::new();
revealed_indices_3.insert(0);
revealed_indices_3.insert(3);
let pok_3 = $protocol::init(
&mut rng,
&sig,
¶ms,
messages.iter().enumerate().map(|(idx, msg)| {
if revealed_indices_3.contains(&idx) {
MessageOrBlinding::RevealMessage(msg)
} else {
MessageOrBlinding::BlindMessageRandomly(msg)
}
}),
)
.unwrap();
let proof_3 = pok_3.gen_proof(&challenge).unwrap();
assert!(proof_3
.get_resp_for_message(0, &revealed_indices_3)
.is_err());
assert!(proof_3
.get_resp_for_message(3, &revealed_indices_3)
.is_err());
let resps = proof_3.$resp_name.as_ref().unwrap();
assert_eq!(
*proof_3
.get_resp_for_message(1, &revealed_indices_3)
.unwrap(),
resps.0[0]
);
assert_eq!(
*proof_3
.get_resp_for_message(2, &revealed_indices_3)
.unwrap(),
resps.0[1]
);
assert_eq!(
*proof_3
.get_resp_for_message(4, &revealed_indices_3)
.unwrap(),
resps.0[2]
);
assert_eq!(
*proof_3
.get_resp_for_message(5, &revealed_indices_3)
.unwrap(),
resps.0[3]
);
for i in 0..message_count as usize {
let mut revealed_indices = BTreeSet::new();
revealed_indices.insert(i);
let pok = $protocol::init(
&mut rng,
&sig,
¶ms,
messages.iter().enumerate().map(|(idx, msg)| {
if revealed_indices.contains(&idx) {
MessageOrBlinding::RevealMessage(msg)
} else {
MessageOrBlinding::BlindMessageRandomly(msg)
}
}),
)
.unwrap();
let proof = pok.gen_proof(&challenge).unwrap();
let resps = proof.$resp_name.as_ref().unwrap();
for j in 0..message_count as usize {
if i == j {
assert!(proof.get_resp_for_message(j, &revealed_indices).is_err());
} else if i < j {
assert_eq!(
*proof.get_resp_for_message(j, &revealed_indices).unwrap(),
resps.0[j - 1]
);
} else {
assert_eq!(
*proof.get_resp_for_message(j, &revealed_indices).unwrap(),
resps.0[j]
);
}
}
}
}}
}
#[test]
fn pok_signature_revealed_message() {
gen_test_pok_signature_revealed_message!(PoKOfSignatureG1Protocol, PoKOfSignatureG1Proof)
}
#[test]
fn test_PoK_multiple_sigs_with_same_msg() {
gen_test_PoK_multiple_sigs_with_same_msg!(
SignatureParamsG1,
SignatureG1,
generate_using_rng,
PoKOfSignatureG1Protocol
)
}
#[test]
fn pok_signature_schnorr_response() {
gen_test_pok_signature_schnorr_response!(sig_setup, PoKOfSignatureG1Protocol, sc_resp_2);
}
#[test]
fn test_PoK_multiple_sigs_with_randomized_pairing_check() {
gen_test_PoK_multiple_sigs_with_randomized_pairing_check!(
SignatureParamsG1,
PreparedSignatureParamsG1,
SignatureG1,
generate_using_rng,
PoKOfSignatureG1Protocol
)
}
}