use crate::{
error::BBSPlusError,
prelude::PreparedPublicKeyG2,
setup::{PreparedSignatureParams23G1, SignatureParams23G1},
signature_23::Signature23G1,
};
use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, VariableBaseMSM};
use ark_ff::{Field, 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 PoKOfSignature23G1Protocol<E: Pairing> {
#[zeroize(skip)]
#[serde_as(as = "ArkObjectBytes")]
pub A_bar: E::G1Affine,
#[zeroize(skip)]
#[serde_as(as = "ArkObjectBytes")]
pub B_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 PoKOfSignature23G1Proof<E: Pairing> {
#[serde_as(as = "ArkObjectBytes")]
pub A_bar: E::G1Affine,
#[serde_as(as = "ArkObjectBytes")]
pub B_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> PoKOfSignature23G1Protocol<E> {
pub fn init<'a, MBI, R: RngCore>(
rng: &mut R,
signature: &Signature23G1<E>,
params: &SignatureParams23G1<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 r1 = E::ScalarField::rand(rng);
let mut r2 = E::ScalarField::rand(rng);
while r2.is_zero() {
r2 = E::ScalarField::rand(rng);
}
let r3 = r2.inverse().unwrap();
let b = params.b(messages.iter().enumerate())?;
let d = b * r2;
let A_bar = signature.A * (r1 * r2);
let A_bar_affine = A_bar.into_affine();
let B_bar = d * r1 - (A_bar * signature.e);
let d_affine = d.into_affine();
let sc_comm_1 = PokPedersenCommitmentProtocol::init(
-signature.e,
E::ScalarField::rand(rng),
&A_bar_affine,
r1,
E::ScalarField::rand(rng),
&d_affine,
);
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.chain([(d_affine, rand(rng), -r3)].into_iter()));
let sc_comm_2 = SchnorrCommitment::new(&bases_2, randomness_2);
Ok(Self {
A_bar: A_bar_affine,
B_bar: B_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: &SignatureParams23G1<E>,
writer: W,
) -> Result<(), BBSPlusError> {
Self::compute_challenge_contribution(
&self.A_bar,
&self.B_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<PoKOfSignature23G1Proof<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(PoKOfSignature23G1Proof {
A_bar: self.A_bar,
B_bar: self.B_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<PoKOfSignature23G1Proof<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(PoKOfSignature23G1Proof {
A_bar: self.A_bar,
B_bar: self.B_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_bar: &E::G1Affine,
B_bar: &E::G1Affine,
d: &E::G1Affine,
T1: &E::G1Affine,
T2: &E::G1Affine,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
params: &SignatureParams23G1<E>,
mut writer: W,
) -> Result<(), BBSPlusError> {
A_bar.serialize_compressed(&mut writer)?;
B_bar.serialize_compressed(&mut writer)?;
d.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> PoKOfSignature23G1Proof<E> {
pub fn verify(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParams23G1<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<PreparedSignatureParams23G1<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<PreparedSignatureParams23G1<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<PreparedSignatureParams23G1<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 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 challenge_contribution<W: Write>(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
params: &SignatureParams23G1<E>,
writer: W,
) -> Result<(), BBSPlusError> {
PoKOfSignature23G1Protocol::compute_challenge_contribution(
&self.A_bar,
&self.B_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 _verify(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParams23G1<E>>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
let params = params.into();
let g1 = params.g1;
let g2 = params.g2;
let h = params.h;
self.verify_except_pairings(revealed_msgs, challenge, g1, h, missing_responses)?;
if !E::multi_pairing(
[
E::G1Prepared::from(self.A_bar),
E::G1Prepared::from(-(self.B_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<PreparedSignatureParams23G1<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 h = params.h;
self.verify_except_pairings(revealed_msgs, challenge, g1, h, missing_responses)?;
pairing_checker.add_sources(&self.A_bar, pk.into().0, &self.B_bar, g2);
Ok(())
}
pub fn verify_schnorr_proofs(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
g1: E::G1Affine,
h: Vec<E::G1Affine>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
if !self
.sc_resp_1
.verify(&self.B_bar, &self.A_bar, &self.d, challenge)
{
return Err(BBSPlusError::FirstSchnorrVerificationFailed);
}
let mut bases_2 = Vec::with_capacity(1 + 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);
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)
}
}
fn verify_except_pairings(
&self,
revealed_msgs: &BTreeMap<usize, E::ScalarField>,
challenge: &E::ScalarField,
g1: E::G1Affine,
h: Vec<E::G1Affine>,
missing_responses: Option<BTreeMap<usize, E::ScalarField>>,
) -> Result<(), BBSPlusError> {
if self.A_bar.is_zero() {
return Err(BBSPlusError::ZeroSignature);
}
self.verify_schnorr_proofs(revealed_msgs, challenge, g1, h, missing_responses)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
gen_test_PoK_multiple_sigs_with_randomized_pairing_check,
gen_test_PoK_multiple_sigs_with_same_msg, gen_test_pok_signature_revealed_message,
gen_test_pok_signature_schnorr_response, proof_23::tests::sig_setup, setup::KeypairG2,
test_serialization,
};
use ark_bls12_381::{Bls12_381, Fr};
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};
#[test]
fn pok_signature_revealed_message() {
gen_test_pok_signature_revealed_message!(
PoKOfSignature23G1Protocol,
PoKOfSignature23G1Proof
)
}
#[test]
fn test_PoK_multiple_sigs_with_same_msg() {
gen_test_PoK_multiple_sigs_with_same_msg!(
SignatureParams23G1,
Signature23G1,
generate_using_rng_and_bbs23_params,
PoKOfSignature23G1Protocol
)
}
#[test]
fn pok_signature_schnorr_response() {
gen_test_pok_signature_schnorr_response!(sig_setup, PoKOfSignature23G1Protocol, sc_resp_2);
}
#[test]
fn test_PoK_multiple_sigs_with_randomized_pairing_check() {
gen_test_PoK_multiple_sigs_with_randomized_pairing_check!(
SignatureParams23G1,
PreparedSignatureParams23G1,
Signature23G1,
generate_using_rng_and_bbs23_params,
PoKOfSignature23G1Protocol
)
}
}