From e4f727fda9b101f7d1f99751c32152e218f835ce Mon Sep 17 00:00:00 2001 From: lovesh Date: Wed, 24 Jan 2024 01:58:47 +0530 Subject: [PATCH] Keyed-Verification Anonymous Credentials - BDDT16 Signed-off-by: lovesh --- bbs_plus/src/lib.rs | 2 +- bbs_plus/src/proof.rs | 175 ++--- bbs_plus/src/proof_23.rs | 40 +- bbs_plus/src/proof_23_cdl.rs | 84 +- bbs_plus/src/setup.rs | 14 +- bbs_plus/src/signature.rs | 14 +- bbs_plus/src/signature_23.rs | 14 +- bbs_plus/src/threshold/threshold_bbs.rs | 5 +- bbs_plus/src/threshold/threshold_bbs_plus.rs | 8 +- kvac/Cargo.toml | 7 +- kvac/src/bddt_2016/delegated_proof.rs | 237 ++++++ kvac/src/bddt_2016/mac.rs | 302 ++++++++ kvac/src/bddt_2016/mod.rs | 62 +- kvac/src/bddt_2016/proof.rs | 721 ++++++++++++++++++ kvac/src/bddt_2016/proof_cdh.rs | 710 +++++++++++++++++ kvac/src/bddt_2016/setup.rs | 170 +++++ kvac/src/error.rs | 49 ++ kvac/src/lib.rs | 2 + proof_system/src/derived_params.rs | 8 +- proof_system/src/proof_spec.rs | 5 +- proof_system/src/setup_params.rs | 5 +- proof_system/src/statement/inequality.rs | 9 +- proof_system/src/sub_protocols/bbs_23.rs | 17 +- proof_system/src/sub_protocols/bbs_plus.rs | 13 +- proof_system/src/sub_protocols/inequality.rs | 13 +- proof_system/src/verifier.rs | 2 +- .../tests/bbs_plus_and_accumulator.rs | 4 +- schnorr_pok/src/discrete_log.rs | 259 ++++++- schnorr_pok/src/inequality.rs | 56 +- schnorr_pok/src/lib.rs | 49 -- secret_sharing_and_dkg/src/gennaro_dkg.rs | 15 +- secret_sharing_and_dkg/src/pedersen_dvss.rs | 19 +- secret_sharing_and_dkg/src/pedersen_vss.rs | 58 +- short_group_sig/src/bb_sig_pok.rs | 173 ++--- short_group_sig/src/bb_sig_pok_cdh.rs | 64 +- short_group_sig/src/weak_bb_sig_pok.rs | 94 +-- short_group_sig/src/weak_bb_sig_pok_cdh.rs | 65 +- utils/src/commitment.rs | 43 ++ utils/src/lib.rs | 2 + utils/src/signature.rs | 56 ++ .../src/kb_positive_accumulator/proofs.rs | 10 +- .../src/kb_positive_accumulator/proofs_cdh.rs | 10 +- vb_accumulator/src/proofs_cdh.rs | 6 +- .../src/proofs_keyed_verification.rs | 49 +- 44 files changed, 2997 insertions(+), 723 deletions(-) create mode 100644 kvac/src/bddt_2016/delegated_proof.rs create mode 100644 kvac/src/bddt_2016/mac.rs create mode 100644 kvac/src/bddt_2016/proof.rs create mode 100644 kvac/src/bddt_2016/proof_cdh.rs create mode 100644 kvac/src/bddt_2016/setup.rs create mode 100644 kvac/src/error.rs create mode 100644 utils/src/commitment.rs create mode 100644 utils/src/signature.rs diff --git a/bbs_plus/src/lib.rs b/bbs_plus/src/lib.rs index 6ca155bf..a4a706e8 100644 --- a/bbs_plus/src/lib.rs +++ b/bbs_plus/src/lib.rs @@ -65,7 +65,7 @@ pub mod threshold; pub mod prelude { pub use crate::{ error::BBSPlusError, - proof::{MessageOrBlinding, PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}, + proof::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}, proof_23_cdl::{PoKOfSignature23G1Proof, PoKOfSignature23G1Protocol}, setup::*, signature::{SignatureG1, SignatureG2}, diff --git a/bbs_plus/src/proof.rs b/bbs_plus/src/proof.rs index 9da45184..180dd937 100644 --- a/bbs_plus/src/proof.rs +++ b/bbs_plus/src/proof.rs @@ -58,7 +58,7 @@ use crate::{ error::BBSPlusError, prelude::PreparedPublicKeyG2, - setup::{MultiMessageSignatureParams, PreparedSignatureParamsG1, SignatureParamsG1}, + setup::{PreparedSignatureParamsG1, SignatureParamsG1}, signature::SignatureG1, }; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, Group, VariableBaseMSM}; @@ -69,16 +69,21 @@ use ark_std::{ fmt::Debug, io::Write, rand::RngCore, - vec, vec::Vec, - One, UniformRand, + UniformRand, }; use dock_crypto_utils::{ - extend_some::ExtendSome, misc::rand, randomized_pairing_check::RandomizedPairingChecker, + misc::rand, + randomized_pairing_check::RandomizedPairingChecker, serde_utils::*, + signature::{split_messages_and_blindings, MessageOrBlinding, MultiMessageSignatureParams}, }; use itertools::multiunzip; -use schnorr_pok::{error::SchnorrError, SchnorrCommitment, SchnorrResponse}; +use schnorr_pok::{ + discrete_log::{PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol}, + error::SchnorrError, + SchnorrCommitment, SchnorrResponse, +}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -116,9 +121,7 @@ pub struct PoKOfSignatureG1Protocol { #[serde_as(as = "ArkObjectBytes")] pub d: E::G1Affine, /// For proving relation `A_bar - d = A_prime * -e + h_0 * r2` - pub sc_comm_1: SchnorrCommitment, - #[serde_as(as = "(ArkObjectBytes, ArkObjectBytes)")] - sc_wits_1: (E::ScalarField, E::ScalarField), + pub sc_comm_1: PokTwoDiscreteLogsProtocol, /// For proving relation `g1 + \sum_{i in D}(h_i*m_i)` = `d*r3 + {h_0}*{-s'} + sum_{j notin D}(h_j*m_j)` pub sc_comm_2: SchnorrCommitment, #[serde_as(as = "Vec")] @@ -139,33 +142,13 @@ pub struct PoKOfSignatureG1Proof { #[serde_as(as = "ArkObjectBytes")] pub d: E::G1Affine, /// Proof of relation `A_bar - d = A_prime * -e + h_0 * r2` - #[serde_as(as = "ArkObjectBytes")] - pub T1: E::G1Affine, - pub sc_resp_1: SchnorrResponse, + pub sc_resp_1: PokTwoDiscreteLogs, /// Proof of relation `g1 + h1*m1 + h2*m2 +.... + h_i*m_i` = `d*r3 + {h_0}*{-s'} + h1*{-m1} + h2*{-m2} + .... + h_j*{-m_j}` for all disclosed messages `m_i` and for all undisclosed messages `m_j` #[serde_as(as = "ArkObjectBytes")] pub T2: E::G1Affine, pub sc_resp_2: SchnorrResponse, } -/// Each message can be either randomly blinded, unblinded, or blinded using supplied blinding. -/// By default, a message is blinded with random blinding. -pub enum MessageOrBlinding<'a, F: PrimeField> { - /// Message will be blinded using random blinding. - BlindMessageRandomly(&'a F), - /// Message will be revealed, and thus won't be included in PoK. - RevealMessage(&'a F), - /// Message will be blinded using the supplied blinding. - BlindMessageWithConcreteBlinding { message: &'a F, blinding: F }, -} - -impl<'a, F: PrimeField> MessageOrBlinding<'a, F> { - /// Blinds given `message` using supplied `blinding`. - pub fn blind_message_with(message: &'a F, blinding: F) -> Self { - Self::BlindMessageWithConcreteBlinding { message, blinding } - } -} - impl PoKOfSignatureG1Protocol { /// Initiate the protocol, i.e. pre-challenge phase. This will generate the randomized signature and execute /// the commit-to-randomness step (Step 1) of both Schnorr protocols. @@ -179,30 +162,23 @@ impl PoKOfSignatureG1Protocol { where MBI: IntoIterator>, { - let (messages, ExtendSome::>(indexed_blindings)): (Vec<_>, _) = - messages_and_blindings - .into_iter() - .enumerate() - .map(|(idx, msg_or_blinding)| match msg_or_blinding { - MessageOrBlinding::BlindMessageRandomly(message) => { - (message, (idx, rand(rng)).into()) - } - MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } => { - (message, (idx, blinding).into()) - } - MessageOrBlinding::RevealMessage(message) => (message, None), - }) - .unzip(); - if messages.len() != params.supported_message_count() { - Err(BBSPlusError::MessageCountIncompatibleWithSigParams( - messages.len(), - params.supported_message_count(), - ))? - } + 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 r1 = E::ScalarField::rand(rng); + while r1.is_zero() { + r1 = E::ScalarField::rand(rng); + } let r2 = E::ScalarField::rand(rng); - let r3 = r1.inverse().ok_or(BBSPlusError::CannotInvert0)?; + let r3 = r1.inverse().unwrap(); // b = (e+x) * A = g1 + h_0*s + sum(h_i*m_i) for all i in I let b = params.b(messages.iter().enumerate(), &signature.s)?; @@ -227,15 +203,16 @@ impl PoKOfSignatureG1Protocol { // of `(e, r2)`, and the second of `(r3, s', {m_j}_{j \notin D})`. The secret knowledge items are // referred to as witnesses, and the public items as instances. let A_prime_affine = A_prime.into_affine(); - let bases_1 = [A_prime_affine, params.h_0]; - let randomness_1 = vec![E::ScalarField::rand(rng), E::ScalarField::rand(rng)]; - let wits_1 = (-signature.e, r2); // Commit to randomness with `h_0` and `A'`, i.e. `bases_1[0]*randomness_1[0] + bases_1[1]*randomness_1[1]` - let sc_comm_1 = SchnorrCommitment::new(&bases_1, randomness_1); - - // Pull the bases out of the array as array is no longer needed - let [A_prime_affine, h_0] = bases_1; + let sc_comm_1 = PokTwoDiscreteLogsProtocol::init( + -signature.e, + E::ScalarField::rand(rng), + &A_prime_affine, + r2, + E::ScalarField::rand(rng), + ¶ms.h_0, + ); // For proving relation `g1 + \sum_{i \in D}(h_i*m_i)` = `d*r3 + {h_0}*{-s_prime} + \sum_{j \notin D}(h_j*{-m_j})` // for all disclosed messages `m_i` and for all undisclosed messages `m_j`, usually the number of disclosed @@ -252,7 +229,7 @@ impl PoKOfSignatureG1Protocol { .map(|(idx, blinding)| (params.h[idx], blinding, messages[idx])); let (bases_2, randomness_2, wits_2): (Vec<_>, Vec<_>, Vec<_>) = multiunzip( - [(d_affine, rand(rng), -r3), (h_0, rand(rng), s_prime)] + [(d_affine, rand(rng), -r3), (params.h_0, rand(rng), s_prime)] .into_iter() .chain(h_blinding_message), ); @@ -265,7 +242,6 @@ impl PoKOfSignatureG1Protocol { A_bar: A_bar.into_affine(), d: bases_2[0], sc_comm_1, - sc_wits_1: wits_1, sc_comm_2, sc_wits_2: wits_2, }) @@ -296,20 +272,17 @@ impl PoKOfSignatureG1Protocol { challenge: &E::ScalarField, ) -> Result, BBSPlusError> { // Schnorr response for relation `A_bar - d == A'*{-e} + h_0*r2` - let resp_1 = self - .sc_comm_1 - .response(&[self.sc_wits_1.0, self.sc_wits_1.1], challenge)?; + let sc_resp_1 = self.sc_comm_1.clone().gen_proof(challenge); // Schnorr response for relation `g1 + \sum_{i in D}(h_i*m_i)` = `d*r3 + {h_0}*{-s'} + \sum_{j not in D}(h_j*{-m_j})` - let resp_2 = self.sc_comm_2.response(&self.sc_wits_2, 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, - T1: self.sc_comm_1.t, - sc_resp_1: resp_1, + sc_resp_1, T2: self.sc_comm_2.t, - sc_resp_2: resp_2, + sc_resp_2, }) } @@ -325,38 +298,20 @@ impl PoKOfSignatureG1Protocol { params: &SignatureParamsG1, mut writer: W, ) -> Result<(), BBSPlusError> { - A_bar.serialize_compressed(&mut writer)?; - - // For 1st Schnorr A_prime.serialize_compressed(&mut writer)?; + A_bar.serialize_compressed(&mut writer)?; + d.serialize_compressed(&mut writer)?; params.h_0.serialize_compressed(&mut writer)?; - // A_bar - d - let mut A_bar_minus_d = A_bar.into_group(); - A_bar_minus_d -= d.into_group(); - let A_bar_minus_d = A_bar_minus_d.into_affine(); - A_bar_minus_d.serialize_compressed(&mut writer)?; - T1.serialize_compressed(&mut writer)?; - - // For 2nd Schnorr - // `bases_revealed` and `exponents` below are used to create g1 + \sum_{i in D}(h_i*m_i) - // Note: exponents are really scalars here - let mut bases_revealed = Vec::with_capacity(1 + revealed_msgs.len()); - let mut exponents = Vec::with_capacity(1 + revealed_msgs.len()); - params.g1.serialize_compressed(&mut writer)?; - bases_revealed.push(params.g1); - let r = E::ScalarField::one(); - r.serialize_compressed(&mut writer)?; - exponents.push(r); - for (i, msg) in revealed_msgs { - assert!(*i < params.h.len()); - params.h[*i].serialize_compressed(&mut writer)?; - bases_revealed.push(params.h[*i]); - msg.serialize_compressed(&mut writer)?; - exponents.push(*msg); + 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)?; + } } - E::G1::msm_unchecked(&bases_revealed, &exponents).serialize_compressed(&mut writer)?; - T2.serialize_compressed(&mut writer).map_err(|e| e.into()) + Ok(()) } } @@ -424,7 +379,7 @@ where &self.A_prime, &self.A_bar, &self.d, - &self.T1, + &self.sc_resp_1.t, &self.T2, revealed_msgs, params, @@ -451,8 +406,7 @@ where } } // 2 added to the index, since 0th and 1st index are reserved for `s'` and `r2` - let r = self.sc_resp_2.get_response(2 + adjusted_idx)?; - Ok(r) + Ok(self.sc_resp_2.get_response(2 + adjusted_idx)?) } pub fn verify_schnorr_proofs( @@ -464,20 +418,13 @@ where h: Vec, ) -> Result<(), BBSPlusError> { // Verify the 1st Schnorr proof - let bases_1 = [self.A_prime, h_0]; // A_bar - d - let mut A_bar_minus_d = self.A_bar.into_group(); - A_bar_minus_d -= self.d.into_group(); - let A_bar_minus_d = A_bar_minus_d.into_affine(); - match self + let A_bar_minus_d = (self.A_bar.into_group() - self.d.into_group()).into_affine(); + if !self .sc_resp_1 - .is_valid(&bases_1, &A_bar_minus_d, &self.T1, challenge) + .verify(&A_bar_minus_d, &self.A_prime, &h_0, challenge) { - Ok(()) => (), - Err(SchnorrError::InvalidResponse) => { - return Err(BBSPlusError::FirstSchnorrVerificationFailed) - } - Err(other) => return Err(BBSPlusError::SchnorrError(other)), + return Err(BBSPlusError::FirstSchnorrVerificationFailed); } // Verify the 2nd Schnorr proof @@ -485,10 +432,8 @@ where bases_2.push(self.d); bases_2.push(h_0); - let mut bases_revealed = Vec::with_capacity(1 + revealed_msgs.len()); - let mut exponents = Vec::with_capacity(1 + revealed_msgs.len()); - bases_revealed.push(g1); - exponents.push(E::ScalarField::one()); + 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(); @@ -499,7 +444,7 @@ where } } // pr = -g1 + \sum_{i in D}(h_i*{-m_i}) = -(g1 + \sum_{i in D}(h_i*{m_i})) - let pr = -E::G1::msm_unchecked(&bases_revealed, &exponents); + let pr = -E::G1::msm_unchecked(&bases_revealed, &exponents) - g1; let pr = pr.into_affine(); match self.sc_resp_2.is_valid(&bases_2, &pr, &self.T2, challenge) { Ok(()) => (), @@ -700,7 +645,7 @@ mod tests { .verify(&messages_2, keypair_2.public_key.clone(), params_2.clone()) .unwrap(); - // Add the same blinding for the message which has to be proven equal across messages + // Add the same blinding for the message which has to be proven equal across signatures let same_blinding = Fr::rand(&mut rng); let mut blindings_1 = BTreeMap::new(); diff --git a/bbs_plus/src/proof_23.rs b/bbs_plus/src/proof_23.rs index 7276845e..3562eac6 100644 --- a/bbs_plus/src/proof_23.rs +++ b/bbs_plus/src/proof_23.rs @@ -20,12 +20,11 @@ use crate::{ error::BBSPlusError, - proof::MessageOrBlinding, - setup::{MultiMessageSignatureParams, PreparedPublicKeyG2, SignatureParams23G1}, + setup::{PreparedPublicKeyG2, SignatureParams23G1}, signature_23::Signature23G1, }; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, Group, VariableBaseMSM}; -use ark_ff::{One, PrimeField, Zero}; +use ark_ff::{PrimeField, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ collections::{BTreeMap, BTreeSet}, @@ -37,8 +36,11 @@ use itertools::{multiunzip, MultiUnzip}; use crate::setup::PreparedSignatureParams23G1; use dock_crypto_utils::{ - extend_some::ExtendSome, misc::rand, randomized_pairing_check::RandomizedPairingChecker, + extend_some::ExtendSome, + misc::rand, + randomized_pairing_check::RandomizedPairingChecker, serde_utils::ArkObjectBytes, + signature::{MessageOrBlinding, MultiMessageSignatureParams}, }; use schnorr_pok::{error::SchnorrError, SchnorrCommitment, SchnorrResponse}; use serde::{Deserialize, Serialize}; @@ -238,31 +240,15 @@ impl PoKOfSignature23G1Protocol { ) -> Result<(), BBSPlusError> { B_bar.serialize_compressed(&mut writer)?; A_bar.serialize_compressed(&mut writer)?; - - // `bases_revealed` and `exponents` below are used to create g1 + \sum_{i in J}(h_i*m_i) - let mut bases_revealed = Vec::with_capacity(1 + revealed_msgs.len()); - let mut exponents = Vec::with_capacity(1 + revealed_msgs.len()); - bases_revealed.push(params.g1); - exponents.push(E::ScalarField::one()); - - for (i, h_i) in params.h.iter().enumerate() { - if let Some(msg) = revealed_msgs.get(&i) { - bases_revealed.push(*h_i); - exponents.push(*msg); - } else { - // Each h_i will be used in proving knowledge of an unrevealed message - h_i.serialize_compressed(&mut writer)?; + params.g1.serialize_compressed(&mut writer)?; + T.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)?; } } - - for (i, msg) in revealed_msgs { - assert!(*i < params.h.len()); - bases_revealed.push(params.h[*i]); - exponents.push(*msg); - } - E::G1::msm_unchecked(&bases_revealed, &exponents).serialize_compressed(&mut writer)?; - - T.serialize_compressed(&mut writer).map_err(|e| e.into()) + Ok(()) } } diff --git a/bbs_plus/src/proof_23_cdl.rs b/bbs_plus/src/proof_23_cdl.rs index 0c05d371..1b24eaa2 100644 --- a/bbs_plus/src/proof_23_cdl.rs +++ b/bbs_plus/src/proof_23_cdl.rs @@ -18,8 +18,7 @@ use crate::{ error::BBSPlusError, prelude::PreparedPublicKeyG2, - proof::MessageOrBlinding, - setup::{MultiMessageSignatureParams, PreparedSignatureParams23G1, SignatureParams23G1}, + setup::{PreparedSignatureParams23G1, SignatureParams23G1}, signature_23::Signature23G1, }; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, VariableBaseMSM}; @@ -35,8 +34,10 @@ use ark_std::{ One, UniformRand, }; use dock_crypto_utils::{ - extend_some::ExtendSome, misc::rand, randomized_pairing_check::RandomizedPairingChecker, + misc::rand, + randomized_pairing_check::RandomizedPairingChecker, serde_utils::*, + signature::{split_messages_and_blindings, MessageOrBlinding, MultiMessageSignatureParams}, }; use itertools::multiunzip; use schnorr_pok::{error::SchnorrError, SchnorrCommitment, SchnorrResponse}; @@ -114,31 +115,24 @@ impl PoKOfSignature23G1Protocol { where MBI: IntoIterator>, { - let (messages, ExtendSome::>(indexed_blindings)): (Vec<_>, _) = - messages_and_blindings - .into_iter() - .enumerate() - .map(|(idx, msg_or_blinding)| match msg_or_blinding { - MessageOrBlinding::BlindMessageRandomly(message) => { - (message, (idx, rand(rng)).into()) - } - MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } => { - (message, (idx, blinding).into()) - } - MessageOrBlinding::RevealMessage(message) => (message, None), - }) - .unzip(); - if messages.len() != params.supported_message_count() { - Err(BBSPlusError::MessageCountIncompatibleWithSigParams( - messages.len(), - params.supported_message_count(), - ))? - } + 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 r2 = E::ScalarField::rand(rng); + let mut r2 = E::ScalarField::rand(rng); + while r2.is_zero() { + r2 = E::ScalarField::rand(rng); + } // r3 = 1/r2 - let r3 = r2.inverse().ok_or(BBSPlusError::CannotInvert0)?; + let r3 = r2.inverse().unwrap(); // b = (e+x) * A = g1 + sum(h_i*m_i) for all i in I let b = params.b(messages.iter().enumerate())?; @@ -253,37 +247,20 @@ impl PoKOfSignature23G1Protocol { params: &SignatureParams23G1, mut writer: W, ) -> Result<(), BBSPlusError> { - B_bar.serialize_compressed(&mut writer)?; - - // For 1st Schnorr A_bar.serialize_compressed(&mut writer)?; - // B_bar - d - let mut B_bar_minus_d = B_bar.into_group(); - B_bar_minus_d -= d.into_group(); - let B_bar_minus_d = B_bar_minus_d.into_affine(); - B_bar_minus_d.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 2nd Schnorr - // `bases_revealed` and `exponents` below are used to create g1 + \sum_{i in D}(h_i*m_i) - // Note: exponents are really scalars here - let mut bases_revealed = Vec::with_capacity(1 + revealed_msgs.len()); - let mut exponents = Vec::with_capacity(1 + revealed_msgs.len()); - - params.g1.serialize_compressed(&mut writer)?; - bases_revealed.push(params.g1); - let r = E::ScalarField::one(); - r.serialize_compressed(&mut writer)?; - exponents.push(r); - for (i, msg) in revealed_msgs { - assert!(*i < params.h.len()); - params.h[*i].serialize_compressed(&mut writer)?; - bases_revealed.push(params.h[*i]); - msg.serialize_compressed(&mut writer)?; - exponents.push(*msg); + 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)?; + } } - E::G1::msm_unchecked(&bases_revealed, &exponents).serialize_compressed(&mut writer)?; - T2.serialize_compressed(&mut writer).map_err(|e| e.into()) + Ok(()) } } @@ -376,8 +353,7 @@ where } } // 1 added to the index, since 0th index is reserved for `r2` - let r = self.sc_resp_2.get_response(1 + adjusted_idx)?; - Ok(r) + Ok(self.sc_resp_2.get_response(1 + adjusted_idx)?) } pub fn verify_schnorr_proofs( diff --git a/bbs_plus/src/setup.rs b/bbs_plus/src/setup.rs index ece66491..8d49ab58 100644 --- a/bbs_plus/src/setup.rs +++ b/bbs_plus/src/setup.rs @@ -69,6 +69,7 @@ use dock_crypto_utils::{ join, misc::{n_projective_group_elements, seq_pairs_satisfy}, serde_utils::*, + signature::MultiMessageSignatureParams, try_iter::CheckLeft, }; use itertools::process_results; @@ -78,13 +79,6 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -// TODO: Move to utils to that coconut can use it -/// Trait implemented by a signature scheme params that can sign multiple messages -pub trait MultiMessageSignatureParams { - /// Number of messages supported in the multi-message - fn supported_message_count(&self) -> usize; -} - /// Secret key used by the signer to sign messages #[serde_as] #[derive( @@ -119,6 +113,12 @@ macro_rules! impl_multi_msg_sig_params { self.h.len() } } + + impl MultiMessageSignatureParams for &$name { + fn supported_message_count(&self) -> usize { + self.h.len() + } + } }; } diff --git a/bbs_plus/src/signature.rs b/bbs_plus/src/signature.rs index f216bc40..43aa1721 100644 --- a/bbs_plus/src/signature.rs +++ b/bbs_plus/src/signature.rs @@ -92,12 +92,9 @@ use ark_std::{ use crate::{ prelude::PreparedSignatureParamsG1, - setup::{ - MultiMessageSignatureParams, PreparedPublicKeyG2, PublicKeyG1, SecretKey, - SignatureParamsG1, SignatureParamsG2, - }, + setup::{PreparedPublicKeyG2, PublicKeyG1, SecretKey, SignatureParamsG1, SignatureParamsG2}, }; -use dock_crypto_utils::serde_utils::*; +use dock_crypto_utils::{serde_utils::*, signature::MultiMessageSignatureParams}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -196,9 +193,12 @@ macro_rules! impl_signature_alg { // i.e. partial_sig = g_1 + {h_0}*s + sum(h_i * m_i) for all i in uncommitted_messages let b = params.b(uncommitted_messages, &s)?; - let e = E::ScalarField::rand(rng); + let mut e = E::ScalarField::rand(rng); + while (e + sk.0).is_zero() { + e = E::ScalarField::rand(rng) + } // 1/(e+x) - let e_plus_x_inv = (e + sk.0).inverse().ok_or(BBSPlusError::CannotInvert0)?; + let e_plus_x_inv = (e + sk.0).inverse().unwrap(); // {commitment + b} * {1/(e+x)} let commitment_plus_b = b + commitment; diff --git a/bbs_plus/src/signature_23.rs b/bbs_plus/src/signature_23.rs index 6f7bf9d0..3e0cc7f5 100644 --- a/bbs_plus/src/signature_23.rs +++ b/bbs_plus/src/signature_23.rs @@ -2,10 +2,7 @@ use crate::{ error::BBSPlusError, - setup::{ - MultiMessageSignatureParams, PreparedPublicKeyG2, PreparedSignatureParams23G1, SecretKey, - SignatureParams23G1, - }, + setup::{PreparedPublicKeyG2, PreparedSignatureParams23G1, SecretKey, SignatureParams23G1}, }; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, Group}; use ark_ff::{fields::Field, PrimeField}; @@ -13,7 +10,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ collections::BTreeMap, fmt::Debug, ops::Mul, rand::RngCore, vec::Vec, UniformRand, Zero, }; -use dock_crypto_utils::serde_utils::*; +use dock_crypto_utils::{serde_utils::*, signature::MultiMessageSignatureParams}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -93,9 +90,12 @@ impl Signature23G1 { // i.e. partial_sig = g_1 + sum(h_i * m_i) for all i in uncommitted_messages let b = params.b(uncommitted_messages)?; - let e = E::ScalarField::rand(rng); + let mut e = E::ScalarField::rand(rng); + while (e + sk.0).is_zero() { + e = E::ScalarField::rand(rng) + } // 1/(e+x) - let e_plus_x_inv = (e + sk.0).inverse().ok_or(BBSPlusError::CannotInvert0)?; + let e_plus_x_inv = (e + sk.0).inverse().unwrap(); // {commitment + b} * {1/(e+x)} let commitment_plus_b = b + commitment; diff --git a/bbs_plus/src/threshold/threshold_bbs.rs b/bbs_plus/src/threshold/threshold_bbs.rs index 6b9ff2df..c330fd05 100644 --- a/bbs_plus/src/threshold/threshold_bbs.rs +++ b/bbs_plus/src/threshold/threshold_bbs.rs @@ -12,11 +12,10 @@ use ark_std::{ use digest::DynDigest; use crate::{ - error::BBSPlusError, - setup::{MultiMessageSignatureParams, SignatureParams23G1}, - signature_23::Signature23G1, + error::BBSPlusError, setup::SignatureParams23G1, signature_23::Signature23G1, threshold::randomness_generation_phase::Phase1, }; +use dock_crypto_utils::signature::MultiMessageSignatureParams; use oblivious_transfer_protocols::ParticipantId; /// The length of vectors `r`, `e`, `masked_signing_key_shares`, `masked_rs` should diff --git a/bbs_plus/src/threshold/threshold_bbs_plus.rs b/bbs_plus/src/threshold/threshold_bbs_plus.rs index 74ce3cef..e12388c7 100644 --- a/bbs_plus/src/threshold/threshold_bbs_plus.rs +++ b/bbs_plus/src/threshold/threshold_bbs_plus.rs @@ -11,14 +11,12 @@ use ark_std::{ use digest::DynDigest; use oblivious_transfer_protocols::ParticipantId; +use super::{multiplication_phase::Phase2Output, utils::compute_R_and_u}; use crate::{ - error::BBSPlusError, - setup::{MultiMessageSignatureParams, SignatureParamsG1}, - signature::SignatureG1, + error::BBSPlusError, setup::SignatureParamsG1, signature::SignatureG1, threshold::randomness_generation_phase::Phase1, }; - -use super::{multiplication_phase::Phase2Output, utils::compute_R_and_u}; +use dock_crypto_utils::signature::MultiMessageSignatureParams; /// The length of vectors `r`, `e`, `s`, `masked_signing_key_shares`, `masked_rs` should /// be `batch_size` each item of the vector corresponds to 1 signature diff --git a/kvac/Cargo.toml b/kvac/Cargo.toml index f2bdd26d..f0cdab64 100644 --- a/kvac/Cargo.toml +++ b/kvac/Cargo.toml @@ -17,6 +17,9 @@ zeroize.workspace = true dock_crypto_utils = { version = "0.16.0", default-features = false, path = "../utils" } schnorr_pok = { version = "0.16.0", default-features = false, path = "../schnorr_pok" } rayon = {workspace = true, optional = true} +serde.workspace = true +serde_with.workspace = true +itertools.workspace = true [dev-dependencies] blake2.workspace = true @@ -27,5 +30,5 @@ ark-secp256k1 = { version = "^0.4.0", default-features = false } [features] default = [ "parallel"] -std = [ "ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std"] -parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-std/parallel", "rayon"] \ No newline at end of file +std = [ "ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std", "dock_crypto_utils/std", "schnorr_pok/std"] +parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-std/parallel", "rayon", "dock_crypto_utils/parallel", "schnorr_pok/parallel"] \ No newline at end of file diff --git a/kvac/src/bddt_2016/delegated_proof.rs b/kvac/src/bddt_2016/delegated_proof.rs new file mode 100644 index 00000000..37af0d28 --- /dev/null +++ b/kvac/src/bddt_2016/delegated_proof.rs @@ -0,0 +1,237 @@ +use crate::{bddt_2016::setup::SecretKey, error::KVACError}; +use ark_ec::{pairing::Pairing, AffineRepr}; +use ark_ff::{Field, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use digest::Digest; +use dock_crypto_utils::{ + affine_group_element_from_byte_slices, commitment::PedersenCommitmentKey, + serde_utils::ArkObjectBytes, +}; +use schnorr_pok::{ + compute_random_oracle_challenge, + discrete_log::{ + PokDiscreteLog, PokDiscreteLogProtocol, PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol, + }, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +/// The part of the proof requiring secret key to verify. +/// The purpose is to split the signer's/verifier's task into 2 parts where `Proof::verify_schnorr_proofs` +/// can be done by an "untrusted helper" who does not know secret key `y` but `DelegatedProof::verify` requires knowing +/// secret key. This lets us build for use-cases where the signer, acting as the credential issuer, would not want the credential to be used without +/// its permission, like when he wants to be paid by the verifier who acts as the "untrusted helper" which verifies the Schnorr proofs +/// and learns the revealed messages (credential attributes) but these are not learnt by the signer thus maintaining the user's privacy. +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct DelegatedProof { + /// The randomized MAC + #[serde_as(as = "ArkObjectBytes")] + pub B_0: G, + /// `C = B_0 * y` where `y` is the secret key + #[serde_as(as = "ArkObjectBytes")] + pub C: G, +} + +/// A public key to verify a `DelegatedProof`. The secret key can be used to create any number of delegated public +/// keys and the delegated. Its a tuple of the form `(P, Q=P*i/y)` where `P` and `Q` are elements in group G2 and `y` +/// is the secret key. +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct DelegatedPublicKey( + #[serde_as(as = "ArkObjectBytes")] pub E::G2Affine, + #[serde_as(as = "ArkObjectBytes")] pub E::G2Affine, +); + +#[serde_as] +#[derive( + Clone, Debug, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct PreparedDelegatedPublicKey( + #[serde_as(as = "ArkObjectBytes")] pub E::G2Prepared, + #[serde_as(as = "ArkObjectBytes")] pub E::G2Prepared, +); + +/// A Pedersen commitment to the secret key, `Comm = G * y + H * r` +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct SecretKeyCommitment(pub G); + +/// A proof that the `DelegatedProof` can be verified correctly. It proves secret key `y` is same in the +/// `DelegatedProof` and the `SecretKeyCommitment`, i.e. `C = B_0 * y, Comm = G * y + H * r` +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct ProofOfValidityOfDelegatedProof { + /// Proof of knowledge of opening of `SecretKeyCommitment` + pub sc_comm: PokTwoDiscreteLogs, + /// Proof of knowledge of secret key in `DelegatedProof` + pub sc_proof: PokDiscreteLog, +} + +impl DelegatedPublicKey { + pub fn new(label: &[u8], sk: &SecretKey) -> Self { + let P = affine_group_element_from_byte_slices!(label, b" : P"); + let Q = P * sk.0.inverse().unwrap(); + Self(P, Q.into()) + } +} + +impl From> for PreparedDelegatedPublicKey { + fn from(pk: DelegatedPublicKey) -> Self { + Self(E::G2Prepared::from(pk.0), E::G2Prepared::from(pk.1)) + } +} + +impl DelegatedProof { + /// Verify the proof using secret key + pub fn verify(&self, secret_key: &SecretKey) -> Result<(), KVACError> { + if self.C != (self.B_0 * secret_key.0).into() { + return Err(KVACError::InvalidRandomizedMAC); + } + Ok(()) + } + + pub fn verify_with_delegated_public_key( + &self, + pk: impl Into>, + ) -> Result<(), KVACError> + where + ::G1Prepared: From, + { + let pk = pk.into(); + // check e(B_0, pk.0) = e(C, pk.1) + if !E::multi_pairing( + [ + E::G1Prepared::from(self.B_0), + E::G1Prepared::from(self.C.into_group().neg().into()), + ], + [pk.0, pk.1], + ) + .is_zero() + { + return Err(KVACError::InvalidRandomizedMAC); + } + Ok(()) + } + + pub fn create_proof_of_validity( + &self, + rng: &mut R, + secret_key: SecretKey, + comm_randomness: G::ScalarField, + comm: &SecretKeyCommitment, + comm_key: &PedersenCommitmentKey, + ) -> ProofOfValidityOfDelegatedProof { + let sk_blinding = G::ScalarField::rand(rng); + let sc_comm = PokTwoDiscreteLogsProtocol::init( + secret_key.0, + sk_blinding, + &comm_key.g, + comm_randomness, + G::ScalarField::rand(rng), + &comm_key.h, + ); + let sc_proof = PokDiscreteLogProtocol::init(secret_key.0, sk_blinding, &self.B_0); + let mut challenge_bytes = vec![]; + sc_comm + .challenge_contribution(&comm_key.g, &comm_key.h, &comm.0, &mut challenge_bytes) + .unwrap(); + sc_proof + .challenge_contribution(&self.B_0, &self.C, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + let sc_comm = sc_comm.gen_proof(&challenge); + let sc_proof = sc_proof.gen_proof(&challenge); + ProofOfValidityOfDelegatedProof { sc_comm, sc_proof } + } +} + +impl SecretKeyCommitment { + pub fn new( + secret_key: &SecretKey, + randomness: &G::ScalarField, + comm_key: &PedersenCommitmentKey, + ) -> Self { + Self(comm_key.commit(&secret_key.0, randomness)) + } +} + +impl ProofOfValidityOfDelegatedProof { + pub fn verify( + &self, + proof: &DelegatedProof, + comm: &SecretKeyCommitment, + comm_key: &PedersenCommitmentKey, + ) -> Result<(), KVACError> { + if self.sc_proof.response != self.sc_comm.response1 { + return Err(KVACError::InvalidDelegatedProof); + } + let mut challenge_bytes = vec![]; + self.sc_comm + .challenge_contribution(&comm_key.g, &comm_key.h, &comm.0, &mut challenge_bytes) + .unwrap(); + self.sc_proof + .challenge_contribution(&proof.B_0, &proof.C, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + if !self + .sc_comm + .verify(&comm.0, &comm_key.g, &comm_key.h, &challenge) + { + return Err(KVACError::InvalidDelegatedProof); + } + if !self.sc_proof.verify(&proof.C, &proof.B_0, &challenge) { + return Err(KVACError::InvalidDelegatedProof); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_ec::CurveGroup; + use ark_std::{ + rand::{prelude::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + + #[test] + fn verification_using_delegated_public_key() { + let mut rng = StdRng::seed_from_u64(0u64); + let sk = SecretKey::new(&mut rng); + let pk = DelegatedPublicKey::::new::(b"test", &sk); + let B_0 = G1Affine::rand(&mut rng); + let C = (B_0 * sk.0).into_affine(); + + let dp = DelegatedProof { B_0, C }; + dp.verify(&sk).unwrap(); + + dp.verify_with_delegated_public_key(pk).unwrap(); + + let comm_key = PedersenCommitmentKey::new::(b"test"); + let sk_comm_randomness = Fr::rand(&mut rng); + let sk_comm = SecretKeyCommitment::new(&sk, &sk_comm_randomness, &comm_key); + let validity_proof = dp.create_proof_of_validity::<_, Blake2b512>( + &mut rng, + sk, + sk_comm_randomness, + &sk_comm, + &comm_key, + ); + validity_proof + .verify::(&dp, &sk_comm, &comm_key) + .unwrap(); + } +} diff --git a/kvac/src/bddt_2016/mac.rs b/kvac/src/bddt_2016/mac.rs new file mode 100644 index 00000000..29dbb899 --- /dev/null +++ b/kvac/src/bddt_2016/mac.rs @@ -0,0 +1,302 @@ +//! MAC_BB - A MAC based on Boneh-Boyen Signatures. Follows section 3.2 of the paper + +use crate::{ + bddt_2016::setup::{MACParams, PublicKey, SecretKey}, + error::KVACError, +}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{Field, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{collections::BTreeMap, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use digest::Digest; +use dock_crypto_utils::{serde_utils::ArkObjectBytes, signature::MultiMessageSignatureParams}; +use schnorr_pok::{ + compute_random_oracle_challenge, + discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol}, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// MAC of list of messages +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, + Zeroize, + ZeroizeOnDrop, +)] +pub struct MAC { + #[serde_as(as = "ArkObjectBytes")] + pub A: G, + /// Called `r` in the paper + #[serde_as(as = "ArkObjectBytes")] + pub e: G::ScalarField, + #[serde_as(as = "ArkObjectBytes")] + pub s: G::ScalarField, +} + +/// A proof corresponding to a MAC that it is correctly created, i.e. can be verified successfully by someone possessing +/// the secret key. Verifying the proof does not require the secret key. +/// Consists of 2 protocols for discrete log relations, and both have the same discrete log +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct ProofOfValidityOfMAC { + /// For proving `B = A * sk` where `sk` is the secret key and `B = h + g * s + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n` + pub sc_B: PokDiscreteLog, + /// For proving knowledge of secret key, i.e. `pk = g_0 * sk` + pub sc_pk: PokDiscreteLog, +} + +impl MAC { + pub fn new( + rng: &mut R, + messages: &[G::ScalarField], + secret_key: &SecretKey, + params: &MACParams, + ) -> Result { + if messages.is_empty() { + return Err(KVACError::NoMessageGiven); + } + if messages.len() != params.supported_message_count() { + return Err(KVACError::MessageCountIncompatibleWithMACParams( + messages.len(), + params.supported_message_count(), + )); + } + let s = G::ScalarField::rand(rng); + let mut e = G::ScalarField::rand(rng); + while (e + secret_key.0).is_zero() { + e = G::ScalarField::rand(rng) + } + // 1/(e+x) + let e_plus_x_inv = (e + secret_key.0).inverse().unwrap(); + let A = params.b(messages.iter().enumerate(), &s)? * e_plus_x_inv; + Ok(Self { + A: A.into_affine(), + e, + s, + }) + } + + /// Issuer creates a MAC on some blinded attributes. + /// This is for "Blind Issuance" mentioned in section 4.2 of the paper with a modification, the issuer does not + /// contribute any randomness that goes towards `s` + pub fn new_with_committed_messages( + rng: &mut R, + commitment: &G, + uncommitted_messages: BTreeMap, + sk: &SecretKey, + params: &MACParams, + ) -> Result { + if uncommitted_messages.is_empty() { + return Err(KVACError::NoMessageGiven); + } + // `>` as commitment will have 0 or more messages. In practice, commitment should have + // at least 1 message + if uncommitted_messages.len() > params.supported_message_count() { + return Err(KVACError::MessageCountIncompatibleWithMACParams( + uncommitted_messages.len(), + params.supported_message_count(), + )); + } + + let s = G::ScalarField::rand(rng); + // `b` is the part of signature on uncommitted messages, + // i.e. partial_sig = h + sum(g_vec__i * m_i) for all i in uncommitted_messages + let b = params.b(uncommitted_messages, &s)?; + + let mut e = G::ScalarField::rand(rng); + while (e + sk.0).is_zero() { + e = G::ScalarField::rand(rng) + } + // 1/(e+x) + let e_plus_x_inv = (e + sk.0).inverse().unwrap(); + + // {commitment + b} * {1/(e+x)} + let commitment_plus_b = b + commitment; + let A = commitment_plus_b * e_plus_x_inv; + Ok(MAC { + A: A.into_affine(), + e, + s, + }) + } + + pub fn verify( + &self, + messages: &[G::ScalarField], + sk: &SecretKey, + params: &MACParams, + ) -> Result<(), KVACError> { + if messages.is_empty() { + return Err(KVACError::NoMessageGiven); + } + if messages.len() != params.supported_message_count() { + return Err(KVACError::MessageCountIncompatibleWithMACParams( + messages.len(), + params.supported_message_count(), + )); + } + let b = params.b(messages.iter().enumerate(), &self.s)?; + let e_plus_x_inv = (self.e + sk.0).inverse().ok_or(KVACError::CannotInvert0)?; + if (b * e_plus_x_inv).into_affine() != self.A { + return Err(KVACError::InvalidMAC); + } + Ok(()) + } + + /// Used to unblind a blinded MAC from signer + pub fn unblind(self, blinding: &G::ScalarField) -> Self { + MAC { + A: self.A, + s: self.s + blinding, + e: self.e, + } + .into() + } +} + +impl ProofOfValidityOfMAC { + pub fn new( + rng: &mut R, + mac: &MAC, + secret_key: &SecretKey, + public_key: &PublicKey, + params: &MACParams, + ) -> Self { + let witness = secret_key.0; + let blinding = G::ScalarField::rand(rng); + let B = (mac.A * witness).into_affine(); + let mut challenge_bytes = vec![]; + // As witness has to be proven same in both protocols. + let p1 = PokDiscreteLogProtocol::init(witness, blinding, &mac.A); + let p2 = PokDiscreteLogProtocol::init(witness, blinding, ¶ms.g_0); + p1.challenge_contribution(&mac.A, &B, &mut challenge_bytes) + .unwrap(); + p2.challenge_contribution(¶ms.g_0, &public_key.0, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + Self { + sc_B: p1.gen_proof(&challenge), + sc_pk: p2.gen_proof(&challenge), + } + } + + pub fn verify( + &self, + mac: &MAC, + messages: &[G::ScalarField], + public_key: &PublicKey, + params: &MACParams, + ) -> Result<(), KVACError> { + if self.sc_B.response != self.sc_pk.response { + return Err(KVACError::InvalidMACProof); + } + // B = h + g * s + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n + let B = + (params.b(messages.iter().enumerate(), &mac.s)? + mac.A * mac.e.neg()).into_affine(); + + let mut challenge_bytes = vec![]; + self.sc_B + .challenge_contribution(&mac.A, &B, &mut challenge_bytes) + .unwrap(); + self.sc_pk + .challenge_contribution(¶ms.g_0, &public_key.0, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + if !self.sc_B.verify(&B, &mac.A, &challenge) { + return Err(KVACError::InvalidMACProof); + } + if !self.sc_pk.verify(&public_key.0, ¶ms.g_0, &challenge) { + return Err(KVACError::InvalidMACProof); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Fr, G1Affine}; + use ark_std::rand::{prelude::StdRng, SeedableRng}; + use blake2::Blake2b512; + use std::collections::BTreeSet; + + #[test] + fn mac_verification() { + let mut rng = StdRng::seed_from_u64(0u64); + let message_count = 10; + let messages = (0..message_count) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params = MACParams::::new::(b"test", message_count); + let sk = SecretKey::new(&mut rng); + let pk = PublicKey::new(&sk, ¶ms.g_0); + let mac = MAC::new(&mut rng, &messages, &sk, ¶ms).unwrap(); + mac.verify(&messages, &sk, ¶ms).unwrap(); + + let proof = ProofOfValidityOfMAC::new::<_, Blake2b512>(&mut rng, &mac, &sk, &pk, ¶ms); + proof + .verify::(&mac, &messages, &pk, ¶ms) + .unwrap(); + } + + #[test] + fn blind_issuance() { + let mut rng = StdRng::seed_from_u64(0u64); + let message_count = 10; + let messages = (0..message_count) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params = MACParams::::new::(b"test", message_count); + let sk = SecretKey::new(&mut rng); + + // 4 messages are not known to signer but are given in a commitment + let blinding = Fr::rand(&mut rng); + // Commit messages with indices 0, 1, 4, 9 + let mut committed_indices = BTreeSet::new(); + committed_indices.insert(0); + committed_indices.insert(1); + committed_indices.insert(4); + committed_indices.insert(9); + + let committed_messages = committed_indices + .iter() + .map(|i| (*i, &messages[*i])) + .collect::>(); + let commitment = params + .commit_to_messages(committed_messages, &blinding) + .unwrap(); + + let mut uncommitted_messages = BTreeMap::new(); + for (i, msg) in messages.iter().enumerate() { + if committed_indices.contains(&i) { + continue; + } + uncommitted_messages.insert(i, msg); + } + + let blinded_mac = MAC::new_with_committed_messages( + &mut rng, + &commitment, + uncommitted_messages, + &sk, + ¶ms, + ) + .unwrap(); + + assert!(blinded_mac.verify(&messages, &sk, ¶ms).is_err()); + + let mac = blinded_mac.unblind(&blinding); + mac.verify(&messages, &sk, ¶ms).unwrap(); + } +} diff --git a/kvac/src/bddt_2016/mod.rs b/kvac/src/bddt_2016/mod.rs index 0b00546e..a0e2107e 100644 --- a/kvac/src/bddt_2016/mod.rs +++ b/kvac/src/bddt_2016/mod.rs @@ -1,57 +1,7 @@ //! Implements KVAC from [Improved Algebraic MACs and Practical Keyed-Verification Anonymous Credentials](https://link.springer.com/chapter/10.1007/978-3-319-69453-5_20) -//! -//! MAC_BB - Follows section 3.2 of the paper -//! - All parties have access to `MACParams`, i.e. random G1 element `f, h, g, g_0, g_1, g_2, ..., g_n`. `MACParams` are like `SignatureParams` in BBS+. -//! - Signer creates secret key as a random field element `y` and public key `Y = g_0 * y` in group G1. -//! - MAC `generate` and `verify` work as mentioned in the paper. -//! - `MAC::generate` should accept `messages`, `sk` and `MACParams` -//! - `MAC::generate_with_committed_messages` should accept an additional commitment to messages. These are similar to `SignatureG1::new` and `SignatureG1::new_with_committed_messages` -//! -//! Signature - Follows Fig.2 (1) with some changes mentioned below. -//! - The signing API is almost same as BBS+ but unlike BBS+, signature will always be in group G1 -//! - Like BBS+, signer might not know all attribute or might only some and receive a commitment to the the hidden attributes. -//! - Deviating from the paper, the user only generates his own `s` during blind signing, same as in BBS+. Also the signer -//! does not send the proof `pi_2` with the signature and user stores only `A`, `s` and `r` but not `C_m` as that can be -//! computed because he knows all `m`. -//! - The user does not send proof `pi_1` as this is created using `proof_system` crate, just like with BBS+. Its assumed that -//! signer has verified that proof before calling sign. -//! - User unblinds the signature like in BBS+ when received a blind signature -//! - Uses MAC_BB from above -//! -//! Proof of knowledge of signature - Follows Fig.2 (2) -//! - Proof generation follow similar API as `PoKOfSignatureG1Protocol` - `init`, `challenge_contribution` and `gen_proof`. -//! - In `init` -//! - Given signature `A`, `s` and `r` and accept blindings for hidden messages like `PoKOfSignatureG1Protocol::init` -//! - Pick random field elements `l` and `t` -//! - Compute `C_m = \sum_i(g_i * m_i) + g * s + h` for all messages `m_i` regardless of them being revealed or not. -//! - Compute `B_0 = A * l` and `C = C_m * l + B_0 * -r` -//! - 3 Schnorr's PoK will be created for relations -//! 1. `E = C * 1/l + f * t` -//! 2. `C = E * l + f * -l*t` -//! 3. `E - h = g * s + B_0 * -r/l + f * t + \sum_j(g_j * m_j)` for messages `m_j` not being revealed -//! - 3 `SchnorrCommitment` and witness sets will be created as below, 1 for each of the above relation. Blindings `r_*` are randomly picked -//! 1. Create `SchnorrCommitment` with bases `[C, f]` and blindings `[r_1, r_2]`. Set its witness as `[1/l, t]` -//! 2. Create `SchnorrCommitment` with bases `[E, f]` and blindings `[r_3, r_4]`. Set its witness as `[l, -l*t]` -//! 3. Create `SchnorrCommitment` with bases `[g, B_0, f, ]`. `` correspond to `g_*` for messages that are not revealed. -//! Set blindings as `[r_5, r_6, r_2, ]` where `` correspond to blindings for hidden messages. These are randomly generated -//! if not provided to `init`. Set its witness as `[s, -r/l, t, ]` where `` are the messages not revealed -//! - In `challenge_contribution`, serialize the following for challenge -//! - `E`, `C`, `f`, `h`, `g`, `B_0` and all `g_j` corresponding to all `m_j` not revealed. -//! - `\sum_i(g_i * m_i)` for all revealed messages `m_i` -//! - In `gen_proof` -//! - Generate responses for all 3 `SchnorrCommitment` above -//! - The proof struct will contains above 3 responses, `t` from all 3 Schnorr commitments and `B_0`, `C` and `E` -//! - In `Proof::verify` -//! 1. Check if `C == B_0 * y` -//! 2. Verify 1st Schnorr response by passing bases mentioned above, `E` and challenge -//! 3. Verify 2nd Schnorr response by passing bases mentioned above, `C` and challenge -//! 4. For 3rd Schnorr response, create bases as above and for argument `y` of `SchnorrResponse::is_valid`, pass `E - h - \sum_i{g_i * m_i}` for all `m_i` that are revealed -//! - Add a function called `Proof::verify_schnorr_proofs` which is same as `Proof::verify` except it does not do check 1. -//! - Add a function called `Proof::to_delegated_proof` to output `DelegatedProof` which contains only `C` and `B_0` -//! - In `DelegatedProof::verify`, do check 1 from `Proof::verify` -//! - The purpose of above is to split the signer's/verifier's task into 2 parts where `Proof::verify_schnorr_proofs` -//! can be done by an "untrusted helper" who does not know secret key `y` but `DelegatedProof::verify` requires knowing -//! secret key. This lets us build for use-cases where the signer, acting as the credential issuer, would not want the credential to be used without -//! its permission, like when he wants to be paid by the verifier who acts as the "untrusted helper" which verifies the Schnorr proofs -//! and learns the revealed messages (credential attributes) but these are not learnt by the signer thus maintaining the user's privacy. -//! - Add a function `Proof::get_resp_for_message` to get Schnorr responses for the hidden messages + +pub mod delegated_proof; +pub mod mac; +pub mod proof; +pub mod proof_cdh; +pub mod setup; diff --git a/kvac/src/bddt_2016/proof.rs b/kvac/src/bddt_2016/proof.rs new file mode 100644 index 00000000..992dc3ee --- /dev/null +++ b/kvac/src/bddt_2016/proof.rs @@ -0,0 +1,721 @@ +//! Protocol to prove knowledge of the MAC. This is the "Show" protocol described in Fig.2 (2) in the paper + +use crate::{ + bddt_2016::{ + delegated_proof::DelegatedProof, + mac::MAC, + setup::{MACParams, SecretKey}, + }, + error::KVACError, +}; +use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; +use ark_ff::Field; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ + collections::{BTreeMap, BTreeSet}, + io::Write, + rand::RngCore, + vec::Vec, + UniformRand, +}; +use dock_crypto_utils::{ + misc::rand, + serde_utils::ArkObjectBytes, + signature::{split_messages_and_blindings, MessageOrBlinding, MultiMessageSignatureParams}, +}; +use itertools::multiunzip; +use schnorr_pok::{ + discrete_log::{PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol}, + SchnorrCommitment, SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Protocol to prove knowledge of a MAC. +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + Zeroize, + ZeroizeOnDrop, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, +)] +pub struct PoKOfMACProtocol { + /// Randomized MAC `B_0 = A * l` + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub B_0: G, + /// `C = b * l - B_0 * e`, here `b = A * (e + y)` + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub C: G, + /// `E = C * 1/l + f * t` + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub E: G, + /// Protocol to prove knowledge of `1/l, t` in `E` + pub sc_E: PokTwoDiscreteLogsProtocol, + /// Protocol to prove knowledge of `l, r` in `C` + pub sc_C: PokTwoDiscreteLogsProtocol, + /// For proving relation `E - h - \sum_{i in D}(g_vec_i*m_i)` = `sum_{j notin D}(g_vec_j*m_j) + B_0*{-r/l} + f*t` + pub sc_comm_msgs: SchnorrCommitment, + #[serde_as(as = "Vec")] + sc_wits_msgs: Vec, +} + +/// Proof of knowledge of a MAC. +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct PoKOfMAC { + #[serde_as(as = "ArkObjectBytes")] + pub B_0: G, + #[serde_as(as = "ArkObjectBytes")] + pub E: G, + #[serde_as(as = "ArkObjectBytes")] + pub C: G, + pub sc_E: PokTwoDiscreteLogs, + pub sc_C: PokTwoDiscreteLogs, + #[serde_as(as = "ArkObjectBytes")] + pub t_msgs: G, + pub sc_resp_msgs: SchnorrResponse, +} + +impl PoKOfMACProtocol { + pub fn init<'a, MBI, R: RngCore>( + rng: &mut R, + mac: &MAC, + params: &MACParams, + messages_and_blindings: MBI, + f: impl Into, + ) -> Result + where + MBI: IntoIterator>, + { + let (messages, indexed_blindings) = + match split_messages_and_blindings(rng, messages_and_blindings, params) { + Ok(t) => t, + Err(l) => { + return Err(KVACError::MessageCountIncompatibleWithMACParams( + l, + params.supported_message_count(), + )) + } + }; + + let f = f.into(); + let minus_e = -mac.e; + + let l = G::ScalarField::rand(rng); + let t = G::ScalarField::rand(rng); + let alpha = l.inverse().unwrap(); + let lambda = minus_e * alpha; + let gamma = -l * t; + + let B_0 = mac.A * l; + let B_0_affine = B_0.into_affine(); + let C = params.b(messages.iter().enumerate(), &mac.s)? * l + B_0 * minus_e; + let C_affine = C.into_affine(); + let E = C * alpha + f * t; + let E_affine = E.into_affine(); + let t_blinding = G::ScalarField::rand(rng); + let sc_E = PokTwoDiscreteLogsProtocol::init(alpha, rand(rng), &C_affine, t, t_blinding, &f); + let sc_C = PokTwoDiscreteLogsProtocol::init(l, rand(rng), &E_affine, gamma, rand(rng), &f); + + // Iterator of tuples of form `(g_vec_i, blinding_i, message_i)` + let msg_comm_iter = indexed_blindings + .into_iter() + .map(|(idx, blinding)| (params.g_vec[idx], blinding, messages[idx])); + let (bases, randomness, sc_wits_msgs): (Vec<_>, Vec<_>, Vec<_>) = multiunzip( + msg_comm_iter.chain( + [ + (params.g, rand(rng), mac.s), + (B_0_affine, rand(rng), lambda), + (f, t_blinding, t), + ] + .into_iter(), + ), + ); + let sc_comm_msgs = SchnorrCommitment::new(&bases, randomness); + Ok(Self { + B_0: B_0_affine, + C: C_affine, + E: E_affine, + sc_E, + sc_C, + sc_comm_msgs, + sc_wits_msgs, + }) + } + + pub fn challenge_contribution( + &self, + revealed_msgs: &BTreeMap, + params: &MACParams, + f: &G, + writer: W, + ) -> Result<(), KVACError> { + Self::compute_challenge_contribution( + &self.B_0, + &self.C, + &self.E, + &self.sc_C.t, + &self.sc_E.t, + revealed_msgs, + params, + f, + writer, + ) + } + + pub fn gen_proof(self, challenge: &G::ScalarField) -> Result, KVACError> { + let sc_E = self.sc_E.clone().gen_proof(challenge); + let sc_C = self.sc_C.clone().gen_proof(challenge); + let sc_resp_msgs = self.sc_comm_msgs.response(&self.sc_wits_msgs, challenge)?; + Ok(PoKOfMAC { + B_0: self.B_0, + E: self.E, + C: self.C, + sc_E, + sc_C, + t_msgs: self.sc_comm_msgs.t, + sc_resp_msgs, + }) + } + + pub fn compute_challenge_contribution( + B_0: &G, + C: &G, + E: &G, + C_t: &G, + E_t: &G, + revealed_msgs: &BTreeMap, + params: &MACParams, + f: &G, + mut writer: W, + ) -> Result<(), KVACError> { + B_0.serialize_compressed(&mut writer)?; + E.serialize_compressed(&mut writer)?; + C.serialize_compressed(&mut writer)?; + f.serialize_compressed(&mut writer)?; + params.h.serialize_compressed(&mut writer)?; + params.g.serialize_compressed(&mut writer)?; + C_t.serialize_compressed(&mut writer)?; + E_t.serialize_compressed(&mut writer)?; + for i in 0..params.g_vec.len() { + params.g_vec[i].serialize_compressed(&mut writer)?; + if let Some(m) = revealed_msgs.get(&i) { + m.serialize_compressed(&mut writer)?; + } + } + Ok(()) + } +} + +impl PoKOfMAC { + /// Verify the proof of knowledge of MAC. Requires the knowledge of secret key. It can be seen as composed of 2 parts, + /// one requiring knowledge of secret key and the other not requiring it. The latter can thus be verified by anyone. + /// The former doesnt contain any revealed messages and contains no user specific data. + pub fn verify( + &self, + revealed_msgs: &BTreeMap, + challenge: &G::ScalarField, + secret_key: &SecretKey, + params: &MACParams, + f: impl Into, + ) -> Result<(), KVACError> { + if self.C != (self.B_0 * secret_key.0).into() { + return Err(KVACError::InvalidRandomizedMAC); + } + self.verify_schnorr_proofs(revealed_msgs, challenge, params, f)?; + Ok(()) + } + + /// Create a new sub-proof that can be verified by someone with the secret key + pub fn to_delegated_proof(&self) -> DelegatedProof { + DelegatedProof { + B_0: self.B_0, + C: self.C, + } + } + + /// This verifies `pi_3` from the paper. + pub fn verify_schnorr_proofs( + &self, + revealed_msgs: &BTreeMap, + challenge: &G::ScalarField, + params: &MACParams, + f: impl Into, + ) -> Result<(), KVACError> { + let f = f.into(); + if !self.sc_E.verify(&self.E, &self.C, &f, challenge) { + return Err(KVACError::InvalidSchnorrProof); + } + if !self.sc_C.verify(&self.C, &self.E, &f, challenge) { + return Err(KVACError::InvalidSchnorrProof); + } + if self.sc_E.response2 + != *self + .sc_resp_msgs + .get_response(self.sc_resp_msgs.len() - 1)? + { + return Err(KVACError::InvalidSchnorrProof); + } + let mut bases = Vec::with_capacity(3 + params.g_vec.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..params.g_vec.len() { + if revealed_msgs.contains_key(&i) { + let message = revealed_msgs.get(&i).unwrap(); + bases_revealed.push(params.g_vec[i]); + exponents.push(*message); + } else { + bases.push(params.g_vec[i]); + } + } + + let y = self.E.into_group() + - params.h.into_group() + - G::Group::msm_unchecked(&bases_revealed, &exponents); + bases.push(params.g); + bases.push(self.B_0); + bases.push(f); + self.sc_resp_msgs + .is_valid(&bases, &y.into_affine(), &self.t_msgs, challenge)?; + Ok(()) + } + + pub fn get_resp_for_message( + &self, + msg_idx: usize, + revealed_msg_ids: &BTreeSet, + ) -> Result<&G::ScalarField, KVACError> { + // Revealed messages are not part of Schnorr protocol + if revealed_msg_ids.contains(&msg_idx) { + return Err(KVACError::InvalidMsgIdxForResponse(msg_idx)); + } + let mut adjusted_idx = msg_idx; + for i in revealed_msg_ids { + if *i < msg_idx { + adjusted_idx -= 1; + } + } + Ok(self.sc_resp_msgs.get_response(adjusted_idx)?) + } + + pub fn challenge_contribution( + &self, + revealed_msgs: &BTreeMap, + params: &MACParams, + f: &G, + writer: W, + ) -> Result<(), KVACError> { + PoKOfMACProtocol::compute_challenge_contribution( + &self.B_0, + &self.C, + &self.E, + &self.sc_C.t, + &self.sc_E.t, + revealed_msgs, + params, + f, + writer, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Fr, G1Affine}; + use ark_std::rand::{prelude::StdRng, SeedableRng}; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + use std::{ + collections::BTreeSet, + time::{Duration, Instant}, + }; + + #[test] + fn proof_of_knowledge_of_MAC() { + let mut rng = StdRng::seed_from_u64(0u64); + let message_count = 10; + let messages = (0..message_count) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params = MACParams::::new::(b"test", message_count); + let sk = SecretKey::new(&mut rng); + let f = G1Affine::rand(&mut rng); + + let mac = MAC::new(&mut rng, &messages, &sk, ¶ms).unwrap(); + mac.verify(&messages, &sk, ¶ms).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 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶ms, + messages.iter().enumerate().map(|(idx, msg)| { + if revealed_indices.contains(&idx) { + MessageOrBlinding::RevealMessage(msg) + } else { + MessageOrBlinding::BlindMessageRandomly(msg) + } + }), + f.clone(), + ) + .unwrap(); + let mut chal_bytes_prover = vec![]; + pok.challenge_contribution(&revealed_msgs, ¶ms, &f, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let proof = pok.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let mut proof_verif_duration = Duration::default(); + let start = Instant::now(); + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(&revealed_msgs, ¶ms, &f, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + proof + .verify(&revealed_msgs, &challenge_verifier, &sk, ¶ms, f) + .unwrap(); + proof_verif_duration += start.elapsed(); + + 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 + ); + + let delegated_proof = proof.to_delegated_proof(); + delegated_proof.verify(&sk).unwrap(); + proof + .verify_schnorr_proofs(&revealed_msgs, &challenge_verifier, ¶ms, f) + .unwrap(); + } + + #[test] + fn test_PoK_multiple_MACs_with_same_msg() { + // Knowledge of 2 MACs and their corresponding messages is being proven. + + let mut rng = StdRng::seed_from_u64(0u64); + + let message_1_count = 10; + let message_2_count = 7; + let mut messages_1 = (0..message_1_count - 1) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let mut messages_2 = (0..message_2_count - 1) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params_1 = MACParams::::new::(b"test1", message_1_count); + let params_2 = MACParams::::new::(b"test2", message_2_count); + + let sk_1 = SecretKey::new(&mut rng); + let sk_2 = SecretKey::new(&mut rng); + let f = G1Affine::rand(&mut rng); + + let same_msg_idx = 4; + let same_msg = Fr::rand(&mut rng); + messages_1.insert(same_msg_idx, same_msg); + messages_2.insert(same_msg_idx, same_msg); + + // A particular message is same + assert_eq!(messages_1[same_msg_idx], messages_2[same_msg_idx]); + assert_ne!(messages_1, messages_2); + + let mac_1 = MAC::new(&mut rng, &messages_1, &sk_1, ¶ms_1).unwrap(); + mac_1.verify(&messages_1, &sk_1, ¶ms_1).unwrap(); + let mac_2 = MAC::new(&mut rng, &messages_2, &sk_2, ¶ms_2).unwrap(); + mac_2.verify(&messages_2, &sk_2, ¶ms_2).unwrap(); + + // Add the same blinding for the message which has to be proven equal across MACs + let same_blinding = Fr::rand(&mut rng); + + let mut blindings_1 = BTreeMap::new(); + blindings_1.insert(same_msg_idx, same_blinding); + + let mut blindings_2 = BTreeMap::new(); + blindings_2.insert(same_msg_idx, same_blinding); + + // Add some more blindings randomly, + blindings_1.insert(0, Fr::rand(&mut rng)); + blindings_1.insert(1, Fr::rand(&mut rng)); + blindings_2.insert(2, Fr::rand(&mut rng)); + + // Blinding for the same message is kept same + assert_eq!( + blindings_1.get(&same_msg_idx), + blindings_2.get(&same_msg_idx) + ); + assert_ne!(blindings_1, blindings_2); + + let pok_1 = PoKOfMACProtocol::init( + &mut rng, + &mac_1, + ¶ms_1, + messages_1.iter().enumerate().map(|(idx, message)| { + if let Some(blinding) = blindings_1.remove(&idx) { + MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } + } else { + MessageOrBlinding::BlindMessageRandomly(message) + } + }), + f.clone(), + ) + .unwrap(); + let pok_2 = PoKOfMACProtocol::init( + &mut rng, + &mac_2, + ¶ms_2, + messages_2.iter().enumerate().map(|(idx, message)| { + if let Some(blinding) = blindings_2.remove(&idx) { + MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } + } else { + MessageOrBlinding::BlindMessageRandomly(message) + } + }), + f.clone(), + ) + .unwrap(); + + let challenge = Fr::rand(&mut rng); + + let proof_1 = pok_1.gen_proof(&challenge).unwrap(); + let proof_2 = pok_2.gen_proof(&challenge).unwrap(); + + // Response for the same message should be same (this check is made by the verifier) + assert_eq!( + proof_1 + .get_resp_for_message(same_msg_idx, &BTreeSet::new()) + .unwrap(), + proof_2 + .get_resp_for_message(same_msg_idx, &BTreeSet::new()) + .unwrap() + ); + + proof_1 + .verify(&BTreeMap::new(), &challenge, &sk_1, ¶ms_1, f.clone()) + .unwrap(); + proof_2 + .verify(&BTreeMap::new(), &challenge, &sk_2, ¶ms_2, f.clone()) + .unwrap(); + } + + #[test] + fn pok_MAC_schnorr_response() { + let mut rng = StdRng::seed_from_u64(0u64); + + let message_count = 6; + let messages = (0..message_count) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params = MACParams::::new::(b"test", message_count); + let sk = SecretKey::new(&mut rng); + let f = G1Affine::rand(&mut rng); + + let mac = MAC::new(&mut rng, &messages, &sk, ¶ms).unwrap(); + mac.verify(&messages, &sk, ¶ms).unwrap(); + + let challenge = Fr::rand(&mut rng); + + // Test response when no hidden message + let revealed_indices_1 = BTreeSet::new(); + let pok_1 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶ms, + messages.iter().enumerate().map(|(idx, msg)| { + if revealed_indices_1.contains(&idx) { + MessageOrBlinding::RevealMessage(msg) + } else { + MessageOrBlinding::BlindMessageRandomly(msg) + } + }), + f.clone(), + ) + .unwrap(); + let proof_1 = pok_1.gen_proof(&challenge).unwrap(); + for i in 0..message_count as usize { + assert_eq!( + *proof_1 + .get_resp_for_message(i, &revealed_indices_1) + .unwrap(), + proof_1.sc_resp_msgs.0[i] + ); + } + + // Test response when some messages are revealed + 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 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶ms, + messages.iter().enumerate().map(|(idx, msg)| { + if revealed_indices_2.contains(&idx) { + MessageOrBlinding::RevealMessage(msg) + } else { + MessageOrBlinding::BlindMessageRandomly(msg) + } + }), + f.clone(), + ) + .unwrap(); + let proof_2 = pok_2.gen_proof(&challenge).unwrap(); + + // Getting response for messages that are revealed throws error as they are not included in + // the proof of knowledge + 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()); + + assert_eq!( + *proof_2 + .get_resp_for_message(1, &revealed_indices_2) + .unwrap(), + proof_2.sc_resp_msgs.0[0] + ); + assert_eq!( + *proof_2 + .get_resp_for_message(3, &revealed_indices_2) + .unwrap(), + proof_2.sc_resp_msgs.0[1] + ); + assert_eq!( + *proof_2 + .get_resp_for_message(4, &revealed_indices_2) + .unwrap(), + proof_2.sc_resp_msgs.0[2] + ); + + let mut revealed_indices_3 = BTreeSet::new(); + revealed_indices_3.insert(0); + revealed_indices_3.insert(3); + let pok_3 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶ms, + messages.iter().enumerate().map(|(idx, msg)| { + if revealed_indices_3.contains(&idx) { + MessageOrBlinding::RevealMessage(msg) + } else { + MessageOrBlinding::BlindMessageRandomly(msg) + } + }), + f.clone(), + ) + .unwrap(); + let proof_3 = pok_3.gen_proof(&challenge).unwrap(); + + // Getting response for messages that are revealed throws error as they are not included in + // the proof of knowledge + 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()); + + assert_eq!( + *proof_3 + .get_resp_for_message(1, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[0] + ); + assert_eq!( + *proof_3 + .get_resp_for_message(2, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[1] + ); + assert_eq!( + *proof_3 + .get_resp_for_message(4, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[2] + ); + assert_eq!( + *proof_3 + .get_resp_for_message(5, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[3] + ); + + // Reveal one message only + for i in 0..message_count as usize { + let mut revealed_indices = BTreeSet::new(); + revealed_indices.insert(i); + let pok = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶ms, + messages.iter().enumerate().map(|(idx, msg)| { + if revealed_indices.contains(&idx) { + MessageOrBlinding::RevealMessage(msg) + } else { + MessageOrBlinding::BlindMessageRandomly(msg) + } + }), + f.clone(), + ) + .unwrap(); + let proof = pok.gen_proof(&challenge).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(), + proof.sc_resp_msgs.0[j - 1] + ); + } else { + assert_eq!( + *proof.get_resp_for_message(j, &revealed_indices).unwrap(), + proof.sc_resp_msgs.0[j] + ); + } + } + } + } +} diff --git a/kvac/src/bddt_2016/proof_cdh.rs b/kvac/src/bddt_2016/proof_cdh.rs new file mode 100644 index 00000000..95081512 --- /dev/null +++ b/kvac/src/bddt_2016/proof_cdh.rs @@ -0,0 +1,710 @@ +//! Protocol to prove knowledge of the MAC. This is adapted from the protocol to prove knowledge of BBS+ signatures as defined +//! in section 4.5 of the paper [Anonymous Attestation Using the Strong Diffie Hellman Assumption Revisited](https://eprint.iacr.org/2016/663). +//! The difference is that for BBS+, pairings are used to verify the randomized signature but here the correctness of the randomized +//! signature can be verified by using the secret key. The protocol is described below. +//! Signer's secret key is `y` and the MAC is `(A, e, s)` +//! 1. Prover chooses random `r1, r2` from `Z_p` and `r3 = 1/r1`, +//! 2. Prover randomizes the MAC as `B_0 = A * r1`, `C = B_0 * -e + b * r1`, `s' = s - r2 * r3` where `b = A*(e+y)`. Note that `C = B_0 * y`. +//! 3. Prover creates `d = b * r1 - g * r2` +//! 4. Prover sends `B_0, C, d` to the verifier and proves the knowledge of `-e, r2` in `C - d = B_0 * -e + g * r2` and `s', r3` +//! and disclosed messages `m_i` for all `i` not in `D`, the set of indices of disclosed messages +//! in `h + \sum_{i in D}(g_vec_i*m_i)` = `d*r3 + g*{-s'} + sum_{j notin D}(g_vec_j*m_j)`. +//! 5. Verifier uses the secret key `y` to check `C = B_0 * y` and the proofs of knowledge. + +use crate::{ + bddt_2016::{ + delegated_proof::DelegatedProof, + mac::MAC, + setup::{MACParams, SecretKey}, + }, + error::KVACError, +}; +use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; +use ark_ff::{Field, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ + collections::{BTreeMap, BTreeSet}, + io::Write, + rand::RngCore, + vec::Vec, + UniformRand, +}; +use dock_crypto_utils::{ + misc::rand, + serde_utils::ArkObjectBytes, + signature::{split_messages_and_blindings, MessageOrBlinding, MultiMessageSignatureParams}, +}; +use itertools::multiunzip; +use schnorr_pok::{ + discrete_log::{PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol}, + SchnorrCommitment, SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Protocol to prove knowledge of a MAC. +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + Zeroize, + ZeroizeOnDrop, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, +)] +pub struct PoKOfMACProtocol { + /// Randomized MAC `B_0 = A * r1` + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub B_0: G, + /// `C = b * r1 - B_0 * e` + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub C: G, + /// `d = b * r1 - g * r2` + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub d: G, + /// For proving relation `C - d = B_0 * -e + g * r2` + pub sc_C: PokTwoDiscreteLogsProtocol, + /// For proving relation `h + \sum_{i in D}(g_vec_i*m_i)` = `d*r3 + g*{-s'} + sum_{j notin D}(g_vec_j*m_j)` + pub sc_comm_msgs: SchnorrCommitment, + #[serde_as(as = "Vec")] + sc_wits_msgs: Vec, +} + +/// Proof of knowledge of a MAC. +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct PoKOfMAC { + #[serde_as(as = "ArkObjectBytes")] + pub B_0: G, + #[serde_as(as = "ArkObjectBytes")] + pub C: G, + #[serde_as(as = "ArkObjectBytes")] + pub d: G, + pub sc_C: PokTwoDiscreteLogs, + #[serde_as(as = "ArkObjectBytes")] + pub t_msgs: G, + pub sc_resp_msgs: SchnorrResponse, +} + +impl PoKOfMACProtocol { + pub fn init<'a, MBI, R: RngCore>( + rng: &mut R, + mac: &MAC, + params: &MACParams, + messages_and_blindings: MBI, + ) -> Result + where + MBI: IntoIterator>, + { + let (messages, indexed_blindings) = + match split_messages_and_blindings(rng, messages_and_blindings, params) { + Ok(t) => t, + Err(l) => { + return Err(KVACError::MessageCountIncompatibleWithMACParams( + l, + params.supported_message_count(), + )) + } + }; + + let mut r1 = G::ScalarField::rand(rng); + while r1.is_zero() { + r1 = G::ScalarField::rand(rng); + } + let r2 = G::ScalarField::rand(rng); + let r3 = r1.inverse().unwrap(); + + let minus_e = -mac.e; + // s' = s - r2*r3 + let s_prime = mac.s - (r2 * r3); + + let B_0 = mac.A * r1; + let B_0_affine = B_0.into_affine(); + // b = (e+x) * A = h + g*s + sum(g_vec_i*m_i) for all i in I + let b = params.b(messages.iter().enumerate(), &mac.s)?; + let b_r1 = b * r1; + + let C = b_r1 + B_0 * minus_e; + let d = b_r1 - params.g * r2; + let d_affine = d.into(); + + let sc_C = PokTwoDiscreteLogsProtocol::init( + minus_e, + G::ScalarField::rand(rng), + &B_0_affine, + r2, + G::ScalarField::rand(rng), + ¶ms.g, + ); + + // Iterator of tuples of form `(g_vec_i, blinding_i, message_i)` + let msg_comm_iter = indexed_blindings + .into_iter() + .map(|(idx, blinding)| (params.g_vec[idx], blinding, messages[idx])); + let (bases, randomness, sc_wits_msgs): (Vec<_>, Vec<_>, Vec<_>) = multiunzip( + msg_comm_iter.chain( + [ + (d_affine, G::ScalarField::rand(rng), -r3), + (params.g, rand(rng), s_prime), + ] + .into_iter(), + ), + ); + let sc_comm_msgs = SchnorrCommitment::new(&bases, randomness); + Ok(Self { + B_0: B_0_affine, + C: C.into(), + d: d_affine, + sc_C, + sc_comm_msgs, + sc_wits_msgs, + }) + } + + pub fn challenge_contribution( + &self, + revealed_msgs: &BTreeMap, + params: &MACParams, + writer: W, + ) -> Result<(), KVACError> { + Self::compute_challenge_contribution( + &self.B_0, + &self.C, + &self.d, + &self.sc_C.t, + &self.sc_comm_msgs.t, + revealed_msgs, + params, + writer, + ) + } + + pub fn gen_proof(self, challenge: &G::ScalarField) -> Result, KVACError> { + let sc_C = self.sc_C.clone().gen_proof(challenge); + let sc_resp_msgs = self.sc_comm_msgs.response(&self.sc_wits_msgs, challenge)?; + Ok(PoKOfMAC { + B_0: self.B_0, + C: self.C, + d: self.d, + sc_C, + t_msgs: self.sc_comm_msgs.t, + sc_resp_msgs, + }) + } + + pub fn compute_challenge_contribution( + B_0: &G, + C: &G, + d: &G, + t_C: &G, + t_msgs: &G, + revealed_msgs: &BTreeMap, + params: &MACParams, + mut writer: W, + ) -> Result<(), KVACError> { + B_0.serialize_compressed(&mut writer)?; + C.serialize_compressed(&mut writer)?; + d.serialize_compressed(&mut writer)?; + params.h.serialize_compressed(&mut writer)?; + params.g.serialize_compressed(&mut writer)?; + t_C.serialize_compressed(&mut writer)?; + t_msgs.serialize_compressed(&mut writer)?; + for i in 0..params.g_vec.len() { + params.g_vec[i].serialize_compressed(&mut writer)?; + if let Some(m) = revealed_msgs.get(&i) { + m.serialize_compressed(&mut writer)?; + } + } + Ok(()) + } +} + +impl PoKOfMAC { + /// Verify the proof of knowledge of MAC. Requires the knowledge of secret key. It can be seen as composed of 2 parts, + /// one requiring knowledge of secret key and the other not requiring it. The latter can thus be verified by anyone. + /// The former doesnt contain any revealed messages and contains no user specific data. + pub fn verify( + &self, + revealed_msgs: &BTreeMap, + challenge: &G::ScalarField, + secret_key: &SecretKey, + params: &MACParams, + ) -> Result<(), KVACError> { + if self.C != (self.B_0 * secret_key.0).into() { + return Err(KVACError::InvalidRandomizedMAC); + } + self.verify_schnorr_proofs(revealed_msgs, challenge, params)?; + Ok(()) + } + + /// Create a new sub-proof that can be verified by someone with the secret key + pub fn to_delegated_proof(&self) -> DelegatedProof { + DelegatedProof { + B_0: self.B_0, + C: self.C, + } + } + + pub fn verify_schnorr_proofs( + &self, + revealed_msgs: &BTreeMap, + challenge: &G::ScalarField, + params: &MACParams, + ) -> Result<(), KVACError> { + if !self.sc_C.verify( + &(self.C.into_group() - self.d).into(), + &self.B_0, + ¶ms.g, + challenge, + ) { + return Err(KVACError::InvalidSchnorrProof); + } + let mut bases = Vec::with_capacity(2 + params.g_vec.len() - revealed_msgs.len()); + let mut bases_revealed = Vec::with_capacity(1 + revealed_msgs.len()); + let mut exponents = Vec::with_capacity(1 + revealed_msgs.len()); + for i in 0..params.g_vec.len() { + if revealed_msgs.contains_key(&i) { + let message = revealed_msgs.get(&i).unwrap(); + bases_revealed.push(params.g_vec[i]); + exponents.push(*message); + } else { + bases.push(params.g_vec[i]); + } + } + bases.push(self.d); + bases.push(params.g); + let y = -G::Group::msm_unchecked(&bases_revealed, &exponents) - params.h; + self.sc_resp_msgs + .is_valid(&bases, &y.into(), &self.t_msgs, challenge)?; + Ok(()) + } + + /// Get the response from post-challenge phase of the Schnorr protocol for the given message index + /// `msg_idx`. Used when comparing message equality + pub fn get_resp_for_message( + &self, + msg_idx: usize, + revealed_msg_ids: &BTreeSet, + ) -> Result<&G::ScalarField, KVACError> { + // Revealed messages are not part of Schnorr protocol + if revealed_msg_ids.contains(&msg_idx) { + return Err(KVACError::InvalidMsgIdxForResponse(msg_idx)); + } + // Adjust message index as the revealed messages are not part of the Schnorr protocol + let mut adjusted_idx = msg_idx; + for i in revealed_msg_ids { + if *i < msg_idx { + adjusted_idx -= 1; + } + } + // 2 added to the index, since 0th and 1st index are reserved for `s'` and `r2` + Ok(self.sc_resp_msgs.get_response(adjusted_idx)?) + } + + pub fn challenge_contribution( + &self, + revealed_msgs: &BTreeMap, + params: &MACParams, + writer: W, + ) -> Result<(), KVACError> { + PoKOfMACProtocol::compute_challenge_contribution( + &self.B_0, + &self.C, + &self.d, + &self.sc_C.t, + &self.t_msgs, + revealed_msgs, + params, + writer, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Fr, G1Affine}; + use ark_std::rand::{prelude::StdRng, SeedableRng}; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + use std::{ + collections::BTreeSet, + time::{Duration, Instant}, + }; + + #[test] + fn proof_of_knowledge_of_MAC() { + let mut rng = StdRng::seed_from_u64(0u64); + let message_count = 10; + let messages = (0..message_count) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params = MACParams::::new::(b"test", message_count); + let sk = SecretKey::new(&mut rng); + + let mac = MAC::new(&mut rng, &messages, &sk, ¶ms).unwrap(); + mac.verify(&messages, &sk, ¶ms).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 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶ms, + messages.iter().enumerate().map(|(idx, msg)| { + if revealed_indices.contains(&idx) { + MessageOrBlinding::RevealMessage(msg) + } else { + MessageOrBlinding::BlindMessageRandomly(msg) + } + }), + ) + .unwrap(); + let mut chal_bytes_prover = vec![]; + pok.challenge_contribution(&revealed_msgs, ¶ms, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let proof = pok.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let mut proof_verif_duration = Duration::default(); + let start = Instant::now(); + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(&revealed_msgs, ¶ms, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + proof + .verify(&revealed_msgs, &challenge_verifier, &sk, ¶ms) + .unwrap(); + proof_verif_duration += start.elapsed(); + + 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 + ); + + let delegated_proof = proof.to_delegated_proof(); + delegated_proof.verify(&sk).unwrap(); + proof + .verify_schnorr_proofs(&revealed_msgs, &challenge_verifier, ¶ms) + .unwrap(); + } + + #[test] + fn test_PoK_multiple_MACs_with_same_msg() { + // Knowledge of 2 MACs and their corresponding messages is being proven. + + let mut rng = StdRng::seed_from_u64(0u64); + + let message_1_count = 10; + let message_2_count = 7; + let mut messages_1 = (0..message_1_count - 1) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let mut messages_2 = (0..message_2_count - 1) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params_1 = MACParams::::new::(b"test1", message_1_count); + let params_2 = MACParams::::new::(b"test2", message_2_count); + + let sk_1 = SecretKey::new(&mut rng); + let sk_2 = SecretKey::new(&mut rng); + + let same_msg_idx = 4; + let same_msg = Fr::rand(&mut rng); + messages_1.insert(same_msg_idx, same_msg); + messages_2.insert(same_msg_idx, same_msg); + + // A particular message is same + assert_eq!(messages_1[same_msg_idx], messages_2[same_msg_idx]); + assert_ne!(messages_1, messages_2); + + let mac_1 = MAC::new(&mut rng, &messages_1, &sk_1, ¶ms_1).unwrap(); + mac_1.verify(&messages_1, &sk_1, ¶ms_1).unwrap(); + let mac_2 = MAC::new(&mut rng, &messages_2, &sk_2, ¶ms_2).unwrap(); + mac_2.verify(&messages_2, &sk_2, ¶ms_2).unwrap(); + + // Add the same blinding for the message which has to be proven equal across MACs + let same_blinding = Fr::rand(&mut rng); + + let mut blindings_1 = BTreeMap::new(); + blindings_1.insert(same_msg_idx, same_blinding); + + let mut blindings_2 = BTreeMap::new(); + blindings_2.insert(same_msg_idx, same_blinding); + + // Add some more blindings randomly, + blindings_1.insert(0, Fr::rand(&mut rng)); + blindings_1.insert(1, Fr::rand(&mut rng)); + blindings_2.insert(2, Fr::rand(&mut rng)); + + // Blinding for the same message is kept same + assert_eq!( + blindings_1.get(&same_msg_idx), + blindings_2.get(&same_msg_idx) + ); + assert_ne!(blindings_1, blindings_2); + + let pok_1 = PoKOfMACProtocol::init( + &mut rng, + &mac_1, + ¶ms_1, + messages_1.iter().enumerate().map(|(idx, message)| { + if let Some(blinding) = blindings_1.remove(&idx) { + MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } + } else { + MessageOrBlinding::BlindMessageRandomly(message) + } + }), + ) + .unwrap(); + let pok_2 = PoKOfMACProtocol::init( + &mut rng, + &mac_2, + ¶ms_2, + messages_2.iter().enumerate().map(|(idx, message)| { + if let Some(blinding) = blindings_2.remove(&idx) { + MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } + } else { + MessageOrBlinding::BlindMessageRandomly(message) + } + }), + ) + .unwrap(); + + let challenge = Fr::rand(&mut rng); + + let proof_1 = pok_1.gen_proof(&challenge).unwrap(); + let proof_2 = pok_2.gen_proof(&challenge).unwrap(); + + // Response for the same message should be same (this check is made by the verifier) + assert_eq!( + proof_1 + .get_resp_for_message(same_msg_idx, &BTreeSet::new()) + .unwrap(), + proof_2 + .get_resp_for_message(same_msg_idx, &BTreeSet::new()) + .unwrap() + ); + + proof_1 + .verify(&BTreeMap::new(), &challenge, &sk_1, ¶ms_1) + .unwrap(); + proof_2 + .verify(&BTreeMap::new(), &challenge, &sk_2, ¶ms_2) + .unwrap(); + } + + #[test] + fn pok_MAC_schnorr_response() { + let mut rng = StdRng::seed_from_u64(0u64); + + let message_count = 6; + let messages = (0..message_count) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let params = MACParams::::new::(b"test", message_count); + let sk = SecretKey::new(&mut rng); + + let mac = MAC::new(&mut rng, &messages, &sk, ¶ms).unwrap(); + mac.verify(&messages, &sk, ¶ms).unwrap(); + + let challenge = Fr::rand(&mut rng); + + // Test response when no hidden message + let revealed_indices_1 = BTreeSet::new(); + let pok_1 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶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(); + for i in 0..message_count as usize { + assert_eq!( + *proof_1 + .get_resp_for_message(i, &revealed_indices_1) + .unwrap(), + proof_1.sc_resp_msgs.0[i] + ); + } + + // Test response when some messages are revealed + 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 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶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(); + + // Getting response for messages that are revealed throws error as they are not included in + // the proof of knowledge + 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()); + + assert_eq!( + *proof_2 + .get_resp_for_message(1, &revealed_indices_2) + .unwrap(), + proof_2.sc_resp_msgs.0[0] + ); + assert_eq!( + *proof_2 + .get_resp_for_message(3, &revealed_indices_2) + .unwrap(), + proof_2.sc_resp_msgs.0[1] + ); + assert_eq!( + *proof_2 + .get_resp_for_message(4, &revealed_indices_2) + .unwrap(), + proof_2.sc_resp_msgs.0[2] + ); + + let mut revealed_indices_3 = BTreeSet::new(); + revealed_indices_3.insert(0); + revealed_indices_3.insert(3); + let pok_3 = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶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(); + + // Getting response for messages that are revealed throws error as they are not included in + // the proof of knowledge + 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()); + + assert_eq!( + *proof_3 + .get_resp_for_message(1, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[0] + ); + assert_eq!( + *proof_3 + .get_resp_for_message(2, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[1] + ); + assert_eq!( + *proof_3 + .get_resp_for_message(4, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[2] + ); + assert_eq!( + *proof_3 + .get_resp_for_message(5, &revealed_indices_3) + .unwrap(), + proof_3.sc_resp_msgs.0[3] + ); + + // Reveal one message only + for i in 0..message_count as usize { + let mut revealed_indices = BTreeSet::new(); + revealed_indices.insert(i); + let pok = PoKOfMACProtocol::init( + &mut rng, + &mac, + ¶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(); + 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(), + proof.sc_resp_msgs.0[j - 1] + ); + } else { + assert_eq!( + *proof.get_resp_for_message(j, &revealed_indices).unwrap(), + proof.sc_resp_msgs.0[j] + ); + } + } + } + } +} diff --git a/kvac/src/bddt_2016/setup.rs b/kvac/src/bddt_2016/setup.rs new file mode 100644 index 00000000..2156a1ce --- /dev/null +++ b/kvac/src/bddt_2016/setup.rs @@ -0,0 +1,170 @@ +use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{rand::RngCore, vec::Vec}; +use core::iter::once; +use digest::Digest; +use dock_crypto_utils::{ + affine_group_element_from_byte_slices, concat_slices, join, + misc::{n_projective_group_elements, seq_pairs_satisfy}, + serde_utils::ArkObjectBytes, +}; +use itertools::process_results; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::error::KVACError; +use dock_crypto_utils::{iter::pair_valid_items_with_slice, try_iter::CheckLeft}; + +use dock_crypto_utils::signature::MultiMessageSignatureParams; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +/// Public parameters used by the MAC creator and verifier +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, + Zeroize, + ZeroizeOnDrop, +)] +pub struct MACParams { + #[serde_as(as = "ArkObjectBytes")] + pub g_0: G, + #[serde_as(as = "ArkObjectBytes")] + pub g: G, + #[serde_as(as = "ArkObjectBytes")] + pub h: G, + #[serde_as(as = "Vec")] + pub g_vec: Vec, +} + +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, + Zeroize, + ZeroizeOnDrop, +)] +pub struct SecretKey(#[serde_as(as = "ArkObjectBytes")] pub F); + +/// An optional key that can be used to verify that the MAC is correctly constructed without verifying it or when the MAC +/// is used as a signature +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, + Zeroize, + ZeroizeOnDrop, +)] +pub struct PublicKey(#[serde_as(as = "ArkObjectBytes")] pub G); + +impl MACParams { + pub fn new(label: &[u8], message_count: u32) -> Self { + assert_ne!(message_count, 0); + // Group element by hashing `label`||`g_0`, `label`||`g`, `label`||`h` , and `label`||`g_i` for i in 1 to message_count. + let (g_0, g, h, g_vec) = join!( + affine_group_element_from_byte_slices!(label, b" : g_0"), + affine_group_element_from_byte_slices!(label, b" : g"), + affine_group_element_from_byte_slices!(label, b" : h"), + { + let h: Vec<_> = n_projective_group_elements::( + 1..message_count + 1, + &concat_slices!(label, b" : g_"), + ) + .collect(); + G::Group::normalize_batch(&h) + } + ); + + Self { g_0, g, h, g_vec } + } + + /// Commit to given messages using the parameters and the given blinding as a Pedersen commitment. + /// `indexed_messages_sorted_by_index` must produce items sorted by unique indices, otherwise, + /// an error will be returned. + /// Eg. if given messages `m_i`, `m_j`, and `m_k` in the iterator, the commitment converts messages to + /// scalars and multiplies them by the parameter curve points: + /// `params.g * blinding + params.g_vec_i * m_i + params.g_vec_j * m_j + params.g_vec_k * m_k` + /// Computes using multi-scalar multiplication + pub fn commit_to_messages<'a, MI>( + &self, + indexed_messages_sorted_by_index: MI, + blinding: &'a G::ScalarField, + ) -> Result + where + MI: IntoIterator, + { + let (bases, scalars): (Vec<_>, Vec<_>) = process_results( + pair_valid_items_with_slice::<_, _, _, KVACError, _>( + indexed_messages_sorted_by_index, + CheckLeft(seq_pairs_satisfy(|a, b| a < b)), + &self.g_vec, + ), + |iter| iter.chain(once((&self.g, blinding))).unzip(), + )?; + + Ok(G::Group::msm_unchecked(&bases, &scalars).into_affine()) + } + + /// Compute `b = A*{e+x}` + /// `indexed_messages_sorted_by_index` must produce items sorted by unique indices, otherwise, + /// an error will be returned. + /// Commits to the given messages and adds `self.h` to it, + /// `b = h + sum(g_vec_i * m_i)` for all indices `i` in the map. + pub fn b<'a, MI>( + &self, + indexed_messages_sorted_by_index: MI, + s: &'a G::ScalarField, + ) -> Result + where + MI: IntoIterator, + { + let commitment = self.commit_to_messages(indexed_messages_sorted_by_index, s)?; + Ok(commitment + self.h) + } +} + +impl MultiMessageSignatureParams for MACParams { + fn supported_message_count(&self) -> usize { + self.g_vec.len() + } +} + +impl MultiMessageSignatureParams for &MACParams { + fn supported_message_count(&self) -> usize { + self.g_vec.len() + } +} + +impl SecretKey { + pub fn new(rng: &mut R) -> Self { + Self(F::rand(rng)) + } +} + +impl PublicKey { + pub fn new<'a>(sk: &SecretKey, g_0: impl Into<&'a G>) -> Self { + Self((g_0.into().mul_bigint(sk.0.into_bigint())).into_affine()) + } +} diff --git a/kvac/src/error.rs b/kvac/src/error.rs new file mode 100644 index 00000000..472bbf23 --- /dev/null +++ b/kvac/src/error.rs @@ -0,0 +1,49 @@ +use ark_serialize::SerializationError; +use dock_crypto_utils::{ + serde_utils::ArkSerializationError, + try_iter::{IndexIsOutOfBounds, InvalidPair}, +}; +use schnorr_pok::error::SchnorrError; +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub enum KVACError { + NoMessageGiven, + MessageCountIncompatibleWithMACParams(usize, usize), + MessageIndicesMustBeUniqueAndSorted(InvalidPair), + MessageIndexIsOutOfBounds(IndexIsOutOfBounds), + CannotInvert0, + InvalidMAC, + InvalidMACProof, + #[serde(with = "ArkSerializationError")] + Serialization(SerializationError), + SchnorrError(SchnorrError), + InvalidRandomizedMAC, + InvalidDelegatedProof, + InvalidSchnorrProof, + InvalidMsgIdxForResponse(usize), +} + +impl From> for KVACError { + fn from(err: InvalidPair) -> Self { + Self::MessageIndicesMustBeUniqueAndSorted(err) + } +} + +impl From for KVACError { + fn from(err: IndexIsOutOfBounds) -> Self { + Self::MessageIndexIsOutOfBounds(err) + } +} + +impl From for KVACError { + fn from(e: SchnorrError) -> Self { + Self::SchnorrError(e) + } +} + +impl From for KVACError { + fn from(e: SerializationError) -> Self { + Self::Serialization(e) + } +} diff --git a/kvac/src/lib.rs b/kvac/src/lib.rs index c9205d53..8eee163a 100644 --- a/kvac/src/lib.rs +++ b/kvac/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![allow(non_snake_case)] //! Implements Keyed-Verification Anonymous Credentials (KVAC) schemes from the following papers. //! KVACs are supposed to be verified by the issuer only (or anyone who shares the issuer's key) @@ -13,3 +14,4 @@ pub mod bddt_2016; pub mod cddh_2019; +pub mod error; diff --git a/proof_system/src/derived_params.rs b/proof_system/src/derived_params.rs index 7eae2d51..18d78269 100644 --- a/proof_system/src/derived_params.rs +++ b/proof_system/src/derived_params.rs @@ -17,6 +17,7 @@ use coconut_crypto::setup::{ PreparedPublicKey as PreparedPSPk, PreparedSignatureParams as PreparedPSSigParams, PublicKey as PSPk, SignatureParams as PSSigParams, }; +use dock_crypto_utils::commitment::PedersenCommitmentKey; use legogroth16::{ PreparedVerifyingKey as LegoPreparedVerifyingKey, VerifyingKey as LegoVerifyingKey, }; @@ -29,7 +30,6 @@ use saver::{ PreparedVerifyingKey as SaverPreparedVerifyingKey, VerifyingKey as SaverVerifyingKey, }, }; -use schnorr_pok::inequality::CommitmentKey; use smc_range_proof::prelude::MemberCommitmentKey; use vb_accumulator::{ kb_positive_accumulator::setup::{ @@ -166,10 +166,10 @@ impl<'a, E: Pairing> DerivedParams<'a, MemberCommitmentKey, [E::G1A } } -impl<'a, E: Pairing, G: AffineRepr> DerivedParams<'a, CommitmentKey, [G; 2]> - for DerivedParamsTracker<'a, CommitmentKey, [G; 2], E> +impl<'a, E: Pairing, G: AffineRepr> DerivedParams<'a, PedersenCommitmentKey, [G; 2]> + for DerivedParamsTracker<'a, PedersenCommitmentKey, [G; 2], E> { - fn new_derived(ck: &CommitmentKey) -> [G; 2] { + fn new_derived(ck: &PedersenCommitmentKey) -> [G; 2] { [ck.g, ck.h] } } diff --git a/proof_system/src/proof_spec.rs b/proof_system/src/proof_spec.rs index 7c36de48..bcde42e9 100644 --- a/proof_system/src/proof_spec.rs +++ b/proof_system/src/proof_spec.rs @@ -22,6 +22,7 @@ use coconut_crypto::setup::{ PreparedPublicKey as PreparedPSPk, PreparedSignatureParams as PreparedPSSigParams, PublicKey as PSPk, SignatureParams as PSSigParams, }; +use dock_crypto_utils::commitment::PedersenCommitmentKey; use legogroth16::{ aggregation::srs::{ProverSRS, VerifierSRS}, PreparedVerifyingKey as LegoPreparedVerifyingKey, VerifyingKey as LegoVerifyingKey, @@ -31,7 +32,6 @@ use saver::prelude::{ PreparedEncryptionKey, PreparedVerifyingKey as SaverPreparedVerifyingKey, VerifyingKey as SaverVerifyingKey, }; -use schnorr_pok::inequality::CommitmentKey; use serde::{Deserialize, Serialize}; use smc_range_proof::prelude::MemberCommitmentKey; @@ -258,7 +258,8 @@ where let mut derived_bound_check_bpp_comm = DerivedParamsTracker::<(G, G), [G; 2], E>::new(); let mut derived_bound_check_smc_comm = DerivedParamsTracker::, [E::G1Affine; 2], E>::new(); - let mut derived_ineq_comm = DerivedParamsTracker::, [G; 2], E>::new(); + let mut derived_ineq_comm = + DerivedParamsTracker::, [G; 2], E>::new(); // To avoid creating variable with short lifetime let mut saver_comm_keys = BTreeMap::new(); diff --git a/proof_system/src/setup_params.rs b/proof_system/src/setup_params.rs index c1c651df..bb73232e 100644 --- a/proof_system/src/setup_params.rs +++ b/proof_system/src/setup_params.rs @@ -16,7 +16,7 @@ use bbs_plus::prelude::{ SignatureParamsG1 as BBSSignatureParamsG1, }; use bulletproofs_plus_plus::setup::SetupParams as BppSetupParams; -use dock_crypto_utils::serde_utils::ArkObjectBytes; +use dock_crypto_utils::{commitment::PedersenCommitmentKey, serde_utils::ArkObjectBytes}; use legogroth16::{ circom::R1CS, data_structures::{ProvingKey as LegoSnarkProvingKey, VerifyingKey as LegoSnarkVerifyingKey}, @@ -25,7 +25,6 @@ use saver::prelude::{ ChunkedCommitmentGens, EncryptionGens, EncryptionKey, ProvingKey as SaverSnarkProvingKey, VerifyingKey as SaverSnarkVerifyingKey, }; -use schnorr_pok::inequality::CommitmentKey; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use short_group_sig::common::ProvingKey; @@ -67,7 +66,7 @@ pub enum SetupParams { SmcParamsAndCommKeyAndSk( #[serde_as(as = "ArkObjectBytes")] SmcParamsAndCommitmentKeyAndSecretKey, ), - CommitmentKey(#[serde_as(as = "ArkObjectBytes")] CommitmentKey), + CommitmentKey(#[serde_as(as = "ArkObjectBytes")] PedersenCommitmentKey), BBSigProvingKey(ProvingKey), KBPositiveAccumulatorParams(KBAccumParams), KBPositiveAccumulatorPublicKey(KBAccumPublicKey), diff --git a/proof_system/src/statement/inequality.rs b/proof_system/src/statement/inequality.rs index 59c36686..87f3ca6e 100644 --- a/proof_system/src/statement/inequality.rs +++ b/proof_system/src/statement/inequality.rs @@ -1,12 +1,11 @@ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::vec::Vec; -use dock_crypto_utils::serde_utils::*; +use dock_crypto_utils::{commitment::PedersenCommitmentKey, serde_utils::*}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use crate::{error::ProofSystemError, setup_params::SetupParams, statement::Statement}; -use schnorr_pok::inequality::CommitmentKey; #[serde_as] #[derive( @@ -18,14 +17,14 @@ pub struct PublicInequality { #[serde_as(as = "ArkObjectBytes")] pub inequal_to: G::ScalarField, #[serde_as(as = "Option")] - pub comm_key: Option>, + pub comm_key: Option>, pub comm_key_ref: Option, } impl PublicInequality { pub fn new_statement_from_params( inequal_to: G::ScalarField, - comm_key: CommitmentKey, + comm_key: PedersenCommitmentKey, ) -> Statement { Statement::PublicInequality(Self { inequal_to, @@ -49,7 +48,7 @@ impl PublicInequality { &'a self, setup_params: &'a [SetupParams], st_idx: usize, - ) -> Result<&'a CommitmentKey, ProofSystemError> { + ) -> Result<&'a PedersenCommitmentKey, ProofSystemError> { extract_param!( setup_params, &self.comm_key, diff --git a/proof_system/src/sub_protocols/bbs_23.rs b/proof_system/src/sub_protocols/bbs_23.rs index 8d883229..e3426ba9 100644 --- a/proof_system/src/sub_protocols/bbs_23.rs +++ b/proof_system/src/sub_protocols/bbs_23.rs @@ -1,16 +1,15 @@ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_std::{collections::BTreeMap, io::Write, rand::RngCore}; -use bbs_plus::{ - prelude::{ - BBSPlusError, MultiMessageSignatureParams, PoKOfSignature23G1Proof, - PoKOfSignature23G1Protocol, PreparedPublicKeyG2, PreparedSignatureParams23G1, PublicKeyG2, - SignatureParams23G1, - }, - proof::MessageOrBlinding, +use bbs_plus::prelude::{ + BBSPlusError, PoKOfSignature23G1Proof, PoKOfSignature23G1Protocol, PreparedPublicKeyG2, + PreparedSignatureParams23G1, PublicKeyG2, SignatureParams23G1, }; use dock_crypto_utils::{ - iter::take_while_satisfy, misc::seq_inc_by_n_from, - randomized_pairing_check::RandomizedPairingChecker, try_iter::CheckLeft, + iter::take_while_satisfy, + misc::seq_inc_by_n_from, + randomized_pairing_check::RandomizedPairingChecker, + signature::{MessageOrBlinding, MultiMessageSignatureParams}, + try_iter::CheckLeft, }; use itertools::Itertools; diff --git a/proof_system/src/sub_protocols/bbs_plus.rs b/proof_system/src/sub_protocols/bbs_plus.rs index 68088cff..76ffcee6 100644 --- a/proof_system/src/sub_protocols/bbs_plus.rs +++ b/proof_system/src/sub_protocols/bbs_plus.rs @@ -3,14 +3,17 @@ use ark_std::{collections::BTreeMap, io::Write, rand::RngCore}; use bbs_plus::{ error::BBSPlusError, prelude::{ - MultiMessageSignatureParams, PoKOfSignatureG1Proof, PreparedPublicKeyG2, - PreparedSignatureParamsG1, PublicKeyG2, SignatureParamsG1, + PoKOfSignatureG1Proof, PreparedPublicKeyG2, PreparedSignatureParamsG1, PublicKeyG2, + SignatureParamsG1, }, - proof::{MessageOrBlinding, PoKOfSignatureG1Protocol}, + proof::PoKOfSignatureG1Protocol, }; use dock_crypto_utils::{ - iter::take_while_satisfy, misc::seq_inc_by_n_from, - randomized_pairing_check::RandomizedPairingChecker, try_iter::CheckLeft, + iter::take_while_satisfy, + misc::seq_inc_by_n_from, + randomized_pairing_check::RandomizedPairingChecker, + signature::{MessageOrBlinding, MultiMessageSignatureParams}, + try_iter::CheckLeft, }; use itertools::Itertools; diff --git a/proof_system/src/sub_protocols/inequality.rs b/proof_system/src/sub_protocols/inequality.rs index 6730fbd9..84c70288 100644 --- a/proof_system/src/sub_protocols/inequality.rs +++ b/proof_system/src/sub_protocols/inequality.rs @@ -6,21 +6,26 @@ use crate::{ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_serialize::CanonicalSerialize; use ark_std::{collections::BTreeMap, io::Write, rand::RngCore, vec, UniformRand}; -use schnorr_pok::inequality::{CommitmentKey, DiscreteLogInequalityProtocol}; +use dock_crypto_utils::commitment::PedersenCommitmentKey; +use schnorr_pok::inequality::DiscreteLogInequalityProtocol; #[derive(Clone, Debug, PartialEq)] pub struct InequalityProtocol<'a, G: AffineRepr> { pub id: usize, /// The public value with which the inequalty is being proven pub inequal_to: G::ScalarField, - pub comm_key: &'a CommitmentKey, + pub comm_key: &'a PedersenCommitmentKey, pub comm: Option, pub inequality_protocol: Option>, pub sp: Option>, } impl<'a, G: AffineRepr> InequalityProtocol<'a, G> { - pub fn new(id: usize, inequal_to: G::ScalarField, comm_key: &'a CommitmentKey) -> Self { + pub fn new( + id: usize, + inequal_to: G::ScalarField, + comm_key: &'a PedersenCommitmentKey, + ) -> Self { Self { id, inequal_to, @@ -157,7 +162,7 @@ impl<'a, G: AffineRepr> InequalityProtocol<'a, G> { comm_key_as_slice: &[G], proof: &InequalityProof, inequal_to: &G::ScalarField, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, mut writer: W, ) -> Result<(), ProofSystemError> { proof.proof.challenge_contribution_for_public_inequality( diff --git a/proof_system/src/verifier.rs b/proof_system/src/verifier.rs index 222a4965..3d963153 100644 --- a/proof_system/src/verifier.rs +++ b/proof_system/src/verifier.rs @@ -40,10 +40,10 @@ use crate::{ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{collections::BTreeMap, format, rand::RngCore, vec, vec::Vec}; -use bbs_plus::prelude::MultiMessageSignatureParams; use digest::Digest; use dock_crypto_utils::{ randomized_pairing_check::RandomizedPairingChecker, + signature::MultiMessageSignatureParams, transcript::{MerlinTranscript, Transcript}, }; use saver::encryption::Ciphertext; diff --git a/proof_system/tests/bbs_plus_and_accumulator.rs b/proof_system/tests/bbs_plus_and_accumulator.rs index 862d7ad7..aa01f7ae 100644 --- a/proof_system/tests/bbs_plus_and_accumulator.rs +++ b/proof_system/tests/bbs_plus_and_accumulator.rs @@ -13,6 +13,7 @@ use short_group_sig::common::ProvingKey; use std::time::Instant; use vb_accumulator::prelude::{Accumulator, MembershipProvingKey, NonMembershipProvingKey}; +use dock_crypto_utils::commitment::PedersenCommitmentKey; use proof_system::{ prelude::{EqualWitnesses, MetaStatements, VerifierConfig, Witness, WitnessRef, Witnesses}, proof_spec::ProofSpec, @@ -44,7 +45,6 @@ use proof_system::{ PoKBBSSignatureG1 as PoKSignatureBBSG1Wit, }, }; -use schnorr_pok::inequality::CommitmentKey; use test_utils::{accumulators::*, bbs::*, test_serialization, Fr, ProofG1}; macro_rules! gen_tests { @@ -1644,7 +1644,7 @@ macro_rules! gen_tests { let mut rng = StdRng::seed_from_u64(0u64); - let comm_key = CommitmentKey::::new::(b"test"); + let comm_key = PedersenCommitmentKey::::new::(b"test"); let msg_count = 5; let (msgs, sig_params, sig_keypair, sig) = $setup_fn_name(&mut rng, msg_count as u32); diff --git a/schnorr_pok/src/discrete_log.rs b/schnorr_pok/src/discrete_log.rs index efeb030f..40d6120b 100644 --- a/schnorr_pok/src/discrete_log.rs +++ b/schnorr_pok/src/discrete_log.rs @@ -1,3 +1,16 @@ +//! Schnorr protocol for proving knowledge of discrete logs +//! To prove knowledge of a single discrete log, i.e. given public `y` and `g`, prove knowledge of `x` in `g * x = y`: +//! 1. Prover chooses a random `r` and computes `t = g * r` +//! 2. Hashes `t` towards getting a challenge `c`. +//! 3. Computes response `s = r + c*x` and sends it to the verifier. +//! 4. Verifier checks if `g * s = t + y*c` +//! +//! To prove knowledge of a 2 discrete logs, i.e. given public `y`, `g1` and `g2`, prove knowledge of `x1` and `x2` in `g1 * x1 + g2 * x2 = y`. +//! 1. Prover chooses 2 random `r1` and `r2` and computes `t = g1 * r1 + g2 * r2` +//! 2. Hashes `t` towards getting a challenge `c`. +//! 3. Computes 2 responses `s1 = r1 + c*x1` and `s2 = r2 + c*x2` and sends them to the verifier. +//! 4. Verifier checks if `g1 * s1 + g2 * s2 = t + y*c` + use crate::error::SchnorrError; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::PrimeField; @@ -8,7 +21,7 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use zeroize::{Zeroize, ZeroizeOnDrop}; -/// Protocol for proving knowledge of discrete log +/// Protocol for proving knowledge of discrete log, i.e given public `y` and `g`, prove knowledge of `x` in `g * x = y` #[serde_as] #[derive( Clone, @@ -23,11 +36,14 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; ZeroizeOnDrop, )] pub struct PokDiscreteLogProtocol { + /// Commitment to randomness #[zeroize(skip)] #[serde_as(as = "ArkObjectBytes")] pub t: G, + /// Randomness chosen by the prover #[serde_as(as = "ArkObjectBytes")] blinding: G::ScalarField, + /// Prover's secret `x` #[serde_as(as = "ArkObjectBytes")] witness: G::ScalarField, } @@ -44,10 +60,49 @@ pub struct PokDiscreteLog { pub response: G::ScalarField, } -impl PokDiscreteLogProtocol -where - G: AffineRepr, -{ +/// Protocol for proving knowledge of 2 discrete logs, i.e given public `y`, `g1` and `g2`, prove knowledge of `x1` and `x2` in `g1 * x1 + g2 * x2 = y` +#[serde_as] +#[derive( + Clone, + PartialEq, + Eq, + Debug, + CanonicalSerialize, + CanonicalDeserialize, + Serialize, + Deserialize, + Zeroize, + ZeroizeOnDrop, +)] +pub struct PokTwoDiscreteLogsProtocol { + #[zeroize(skip)] + #[serde_as(as = "ArkObjectBytes")] + pub t: G, + #[serde_as(as = "ArkObjectBytes")] + blinding1: G::ScalarField, + #[serde_as(as = "ArkObjectBytes")] + witness1: G::ScalarField, + #[serde_as(as = "ArkObjectBytes")] + blinding2: G::ScalarField, + #[serde_as(as = "ArkObjectBytes")] + witness2: G::ScalarField, +} + +/// Proof of knowledge of 2 discrete logs +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct PokTwoDiscreteLogs { + #[serde_as(as = "ArkObjectBytes")] + pub t: G, + #[serde_as(as = "ArkObjectBytes")] + pub response1: G::ScalarField, + #[serde_as(as = "ArkObjectBytes")] + pub response2: G::ScalarField, +} + +impl PokDiscreteLogProtocol { pub fn init(witness: G::ScalarField, blinding: G::ScalarField, base: &G) -> Self { let t = base.mul_bigint(blinding.into_bigint()).into_affine(); Self { @@ -86,10 +141,7 @@ where } } -impl PokDiscreteLog -where - G: AffineRepr, -{ +impl PokDiscreteLog { pub fn challenge_contribution( &self, base: &G, @@ -106,3 +158,192 @@ where expected.into_affine() == self.t } } + +impl PokTwoDiscreteLogsProtocol { + pub fn init( + witness1: G::ScalarField, + blinding1: G::ScalarField, + base1: &G, + witness2: G::ScalarField, + blinding2: G::ScalarField, + base2: &G, + ) -> Self { + let t = (base1.mul_bigint(blinding1.into_bigint()) + + base2.mul_bigint(blinding2.into_bigint())) + .into_affine(); + Self { + t, + blinding1, + witness1, + blinding2, + witness2, + } + } + + pub fn challenge_contribution( + &self, + base1: &G, + base2: &G, + y: &G, + writer: W, + ) -> Result<(), SchnorrError> { + Self::compute_challenge_contribution(base1, base2, y, &self.t, writer) + } + + pub fn gen_proof(self, challenge: &G::ScalarField) -> PokTwoDiscreteLogs { + let response1 = self.blinding1 + (self.witness1 * *challenge); + let response2 = self.blinding2 + (self.witness2 * *challenge); + PokTwoDiscreteLogs { + t: self.t, + response1, + response2, + } + } + + pub fn compute_challenge_contribution( + base1: &G, + base2: &G, + y: &G, + t: &G, + mut writer: W, + ) -> Result<(), SchnorrError> { + base1.serialize_compressed(&mut writer)?; + base2.serialize_compressed(&mut writer)?; + y.serialize_compressed(&mut writer)?; + t.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl PokTwoDiscreteLogs { + pub fn challenge_contribution( + &self, + base1: &G, + base2: &G, + y: &G, + writer: W, + ) -> Result<(), SchnorrError> { + PokTwoDiscreteLogsProtocol::compute_challenge_contribution(base1, base2, y, &self.t, writer) + } + + /// `base1*response1 + base2*response2 - y*challenge == t` + pub fn verify(&self, y: &G, base1: &G, base2: &G, challenge: &G::ScalarField) -> bool { + let mut expected = base1.mul_bigint(self.response1.into_bigint()); + expected += base2.mul_bigint(self.response2.into_bigint()); + expected -= y.mul_bigint(challenge.into_bigint()); + expected.into_affine() == self.t + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{compute_random_oracle_challenge, test_serialization}; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_ec::pairing::Pairing; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + + #[test] + fn schnorr_single() { + let mut rng = StdRng::seed_from_u64(0u64); + + macro_rules! check { + ($group_affine:ident, $group_projective:ident) => { + let base = ::$group_projective::rand(&mut rng).into_affine(); + let witness = Fr::rand(&mut rng); + let y = base.mul_bigint(witness.into_bigint()).into_affine(); + let blinding = Fr::rand(&mut rng); + let protocol = + PokDiscreteLogProtocol::<::$group_affine>::init( + witness, blinding, &base, + ); + let mut chal_contrib_prover = vec![]; + protocol + .challenge_contribution(&base, &y, &mut chal_contrib_prover) + .unwrap(); + + test_serialization!( + PokDiscreteLogProtocol<::$group_affine>, + protocol + ); + + let challenge_prover = + compute_random_oracle_challenge::(&chal_contrib_prover); + let proof = protocol.gen_proof(&challenge_prover); + + let mut chal_contrib_verifier = vec![]; + proof + .challenge_contribution(&base, &y, &mut chal_contrib_verifier) + .unwrap(); + + let challenge_verifier = + compute_random_oracle_challenge::(&chal_contrib_verifier); + assert!(proof.verify(&y, &base, &challenge_verifier)); + assert_eq!(chal_contrib_prover, chal_contrib_verifier); + assert_eq!(challenge_prover, challenge_verifier); + + test_serialization!(PokDiscreteLog<::$group_affine>, proof); + }; + } + + check!(G1Affine, G1); + check!(G2Affine, G2); + } + + #[test] + fn schnorr_double() { + let mut rng = StdRng::seed_from_u64(0u64); + + macro_rules! check { + ($group_affine:ident, $group_projective:ident) => { + let base1 = ::$group_projective::rand(&mut rng).into_affine(); + let witness1 = Fr::rand(&mut rng); + let base2 = ::$group_projective::rand(&mut rng).into_affine(); + let witness2 = Fr::rand(&mut rng); + let y = (base1 * witness1 + base2 * witness2).into_affine(); + let blinding1 = Fr::rand(&mut rng); + let blinding2 = Fr::rand(&mut rng); + let protocol = + PokTwoDiscreteLogsProtocol::<::$group_affine>::init( + witness1, blinding1, &base1, witness2, blinding2, &base2, + ); + let mut chal_contrib_prover = vec![]; + protocol + .challenge_contribution(&base1, &base2, &y, &mut chal_contrib_prover) + .unwrap(); + + test_serialization!( + PokTwoDiscreteLogsProtocol<::$group_affine>, + protocol + ); + + let challenge_prover = + compute_random_oracle_challenge::(&chal_contrib_prover); + let proof = protocol.gen_proof(&challenge_prover); + + let mut chal_contrib_verifier = vec![]; + proof + .challenge_contribution(&base1, &base2, &y, &mut chal_contrib_verifier) + .unwrap(); + + let challenge_verifier = + compute_random_oracle_challenge::(&chal_contrib_verifier); + assert!(proof.verify(&y, &base1, &base2, &challenge_verifier)); + assert_eq!(chal_contrib_prover, chal_contrib_verifier); + assert_eq!(challenge_prover, challenge_verifier); + + test_serialization!( + PokTwoDiscreteLogs<::$group_affine>, + proof + ); + }; + } + + check!(G1Affine, G1); + check!(G2Affine, G2); + } +} diff --git a/schnorr_pok/src/inequality.rs b/schnorr_pok/src/inequality.rs index fcf41b57..2862c16c 100644 --- a/schnorr_pok/src/inequality.rs +++ b/schnorr_pok/src/inequality.rs @@ -23,32 +23,11 @@ use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::Zero; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{fmt::Debug, io::Write, rand::RngCore, vec::Vec, UniformRand}; -use digest::Digest; -use dock_crypto_utils::{ - concat_slices, hashing_utils::affine_group_elem_from_try_and_incr, misc::n_rand, -}; +use dock_crypto_utils::misc::n_rand; +use dock_crypto_utils::commitment::PedersenCommitmentKey; use zeroize::{Zeroize, ZeroizeOnDrop}; -/// The commitment key for commitment `C` -#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] -pub struct CommitmentKey { - pub g: G, - pub h: G, -} - -impl CommitmentKey { - pub fn new(label: &[u8]) -> Self { - let g = affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : G"]); - let h = affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : H"]); - Self { g, h } - } - - pub fn commit(&self, member: &G::ScalarField, randomness: &G::ScalarField) -> G { - (self.g * member + self.h * randomness).into() - } -} - /// Protocol to prove inequality of discrete log (committed in a Pedersen commitment) with either a /// public value or another discrete log #[derive( @@ -85,7 +64,7 @@ impl DiscreteLogInequalityProtocol { randomness: G::ScalarField, commitment: &G, inequal_to: &G::ScalarField, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result { if &value == inequal_to { return Err(SchnorrError::ValueMustNotBeEqual); @@ -126,7 +105,7 @@ impl DiscreteLogInequalityProtocol { value2: G::ScalarField, randomness2: G::ScalarField, commitment2: &G, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result { if value1 == value2 { return Err(SchnorrError::ValueMustNotBeEqual); @@ -145,7 +124,7 @@ impl DiscreteLogInequalityProtocol { &self, commitment: &G, inequal_to: &G::ScalarField, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, writer: W, ) -> Result<(), SchnorrError> { Self::compute_challenge_contribution( @@ -164,7 +143,7 @@ impl DiscreteLogInequalityProtocol { &self, commitment1: &G, commitment2: &G, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, writer: W, ) -> Result<(), SchnorrError> { Self::compute_challenge_contribution( @@ -202,7 +181,7 @@ impl DiscreteLogInequalityProtocol { t_c: &G, t_b: &G, t_b_ped: &G, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, mut writer: W, ) -> Result<(), SchnorrError> { comm_key.g.serialize_compressed(&mut writer)?; @@ -220,7 +199,11 @@ impl DiscreteLogInequalityProtocol { (commitment1.into_group() - commitment2.into_group()).into() } - fn base_for_b(commitment: &G, inequal_to: &G::ScalarField, comm_key: &CommitmentKey) -> G { + fn base_for_b( + commitment: &G, + inequal_to: &G::ScalarField, + comm_key: &PedersenCommitmentKey, + ) -> G { (commitment.into_group() - (comm_key.g * inequal_to)).into() } } @@ -231,7 +214,7 @@ impl InequalityProof { commitment: &G, inequal_to: &G::ScalarField, challenge: &G::ScalarField, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result<(), SchnorrError> { if self.b.is_zero() { return Err(SchnorrError::InvalidProofOfEquality); @@ -261,7 +244,7 @@ impl InequalityProof { commitment1: &G, commitment2: &G, challenge: &G::ScalarField, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result<(), SchnorrError> { self.verify_for_inequality_with_public_value( &DiscreteLogInequalityProtocol::transformed_commitments_for_committed_inequality( @@ -278,7 +261,7 @@ impl InequalityProof { &self, commitment: &G, inequal_to: &G::ScalarField, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, writer: W, ) -> Result<(), SchnorrError> { DiscreteLogInequalityProtocol::compute_challenge_contribution( @@ -297,7 +280,7 @@ impl InequalityProof { &self, commitment1: &G, commitment2: &G, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, writer: W, ) -> Result<(), SchnorrError> { DiscreteLogInequalityProtocol::compute_challenge_contribution( @@ -326,14 +309,17 @@ mod tests { UniformRand, }; use blake2::Blake2b512; - use dock_crypto_utils::transcript::{MerlinTranscript, Transcript}; + use dock_crypto_utils::{ + commitment::PedersenCommitmentKey, + transcript::{MerlinTranscript, Transcript}, + }; type Fr = ::ScalarField; #[test] fn inequality_proof() { let mut rng = StdRng::seed_from_u64(0u64); - let comm_key = CommitmentKey::::new::(b"test"); + let comm_key = PedersenCommitmentKey::::new::(b"test"); let value = Fr::rand(&mut rng); let randomness = Fr::rand(&mut rng); let in_equal = Fr::rand(&mut rng); diff --git a/schnorr_pok/src/lib.rs b/schnorr_pok/src/lib.rs index 1076f9a7..778d7135 100644 --- a/schnorr_pok/src/lib.rs +++ b/schnorr_pok/src/lib.rs @@ -193,14 +193,12 @@ pub fn compute_random_oracle_challenge(challenge_bytes #[cfg(test)] mod tests { use super::*; - use crate::discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol}; use ark_bls12_381::Bls12_381; use ark_ec::{pairing::Pairing, VariableBaseMSM}; use ark_std::{ rand::{rngs::StdRng, SeedableRng}, UniformRand, }; - use blake2::Blake2b512; type Fr = ::ScalarField; @@ -280,51 +278,4 @@ mod tests { test_schnorr_in_group!(G1, G1Affine); test_schnorr_in_group!(G2, G2Affine); } - - #[test] - fn schnorr_single() { - let mut rng = StdRng::seed_from_u64(0u64); - - macro_rules! check { - ($group_affine:ident, $group_projective:ident) => { - let base = ::$group_projective::rand(&mut rng).into_affine(); - let witness = Fr::rand(&mut rng); - let y = base.mul_bigint(witness.into_bigint()).into_affine(); - let blinding = Fr::rand(&mut rng); - let protocol = - PokDiscreteLogProtocol::<::$group_affine>::init( - witness, blinding, &base, - ); - let mut chal_contrib_prover = vec![]; - protocol - .challenge_contribution(&base, &y, &mut chal_contrib_prover) - .unwrap(); - - test_serialization!( - PokDiscreteLogProtocol<::$group_affine>, - protocol - ); - - let challenge_prover = - compute_random_oracle_challenge::(&chal_contrib_prover); - let proof = protocol.gen_proof(&challenge_prover); - - let mut chal_contrib_verifier = vec![]; - proof - .challenge_contribution(&base, &y, &mut chal_contrib_verifier) - .unwrap(); - - let challenge_verifier = - compute_random_oracle_challenge::(&chal_contrib_verifier); - assert!(proof.verify(&y, &base, &challenge_verifier)); - assert_eq!(chal_contrib_prover, chal_contrib_verifier); - assert_eq!(challenge_prover, challenge_verifier); - - test_serialization!(PokDiscreteLog<::$group_affine>, proof); - }; - } - - check!(G1Affine, G1); - check!(G2Affine, G2); - } } diff --git a/secret_sharing_and_dkg/src/gennaro_dkg.rs b/secret_sharing_and_dkg/src/gennaro_dkg.rs index 6327a2c5..f7e279b7 100644 --- a/secret_sharing_and_dkg/src/gennaro_dkg.rs +++ b/secret_sharing_and_dkg/src/gennaro_dkg.rs @@ -9,6 +9,7 @@ use ark_ff::Zero; use ark_poly::univariate::DensePolynomial; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{collections::BTreeMap, rand::RngCore, vec::Vec, UniformRand}; +use dock_crypto_utils::commitment::PedersenCommitmentKey; use crate::{ common::{ @@ -16,7 +17,6 @@ use crate::{ }, error::SSError, feldman_vss, pedersen_dvss, pedersen_vss, - pedersen_vss::CommitmentKey, }; /// In Phase 1, each participant runs Pedersen VSS @@ -52,7 +52,7 @@ impl Phase1 { participant_id: ParticipantId, threshold: ShareId, total: ShareId, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result< ( Self, @@ -72,7 +72,7 @@ impl Phase1 { secret: GP1::ScalarField, threshold: ShareId, total: ShareId, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result< ( Self, @@ -105,7 +105,7 @@ impl Phase1 { sender_id: ParticipantId, share: VerifiableShare, commitment_coeffs: CommitmentToCoefficients, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result<(), SSError> { self.accumulator .add_received_share(sender_id, share, commitment_coeffs, comm_key)?; @@ -125,7 +125,7 @@ impl Phase1 { /// Mark Phase 1 as over and initialize Phase 2. pub fn finish>( self, - ped_comm_key: &CommitmentKey, + ped_comm_key: &PedersenCommitmentKey, fel_comm_key: &GP2, ) -> Result<(Phase2, CommitmentToCoefficients), SSError> { let id = self.self_id(); @@ -205,7 +205,6 @@ impl, GP1: AffineRepr> Phase2::new::(b"test"); + let ped_comm_key = PedersenCommitmentKey::::new::(b"test"); let fed_comm_key = ::G1Affine::rand(&mut rng); let fed_comm_key_g2 = ::G2Affine::rand(&mut rng); fn check>( rng: &mut StdRng, - ped_comm_key: &CommitmentKey, + ped_comm_key: &PedersenCommitmentKey, fed_comm_key: &GP2, ) { for (threshold, total) in vec![ diff --git a/secret_sharing_and_dkg/src/pedersen_dvss.rs b/secret_sharing_and_dkg/src/pedersen_dvss.rs index 7823dc39..279893fe 100644 --- a/secret_sharing_and_dkg/src/pedersen_dvss.rs +++ b/secret_sharing_and_dkg/src/pedersen_dvss.rs @@ -13,13 +13,13 @@ use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::Zero; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{collections::BTreeMap, vec, vec::Vec}; +use dock_crypto_utils::commitment::PedersenCommitmentKey; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; use crate::{ common::{CommitmentToCoefficients, ParticipantId, ShareId, VerifiableShare}, error::SSError, - pedersen_vss::CommitmentKey, }; /// Used by a participant to store received shares and commitment coefficients. @@ -65,7 +65,7 @@ impl SharesAccumulator { sender_id: ParticipantId, share: VerifiableShare, commitment_coeffs: CommitmentToCoefficients, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result<(), SSError> { if sender_id == self.participant_id { return Err(SSError::SenderIdSameAsReceiver( @@ -83,7 +83,7 @@ impl SharesAccumulator { /// share of the distributed secret pub fn finalize( self, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result, SSError> { // Check early that sufficient shares present let len = self.shares.len() as ShareId; @@ -122,7 +122,7 @@ impl SharesAccumulator { id: ParticipantId, share: VerifiableShare, commitment_coeffs: CommitmentToCoefficients, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result<(), SSError> { if self.participant_id != share.id { return Err(SSError::UnequalParticipantAndShareId( @@ -156,10 +156,7 @@ impl SharesAccumulator { #[cfg(test)] pub mod tests { use super::*; - use crate::{ - common::VerifiableShares, - pedersen_vss::{deal_random_secret, CommitmentKey}, - }; + use crate::{common::VerifiableShares, pedersen_vss::deal_random_secret}; use ark_ec::Group; use ark_std::rand::{rngs::StdRng, SeedableRng}; use blake2::Blake2b512; @@ -168,10 +165,10 @@ pub mod tests { #[test] fn pedersen_distributed_verifiable_secret_sharing() { let mut rng = StdRng::seed_from_u64(0u64); - let comm_key1 = CommitmentKey::::new::(b"test"); - let comm_key2 = CommitmentKey::::new::(b"test"); + let comm_key1 = PedersenCommitmentKey::::new::(b"test"); + let comm_key2 = PedersenCommitmentKey::::new::(b"test"); - fn check(rng: &mut StdRng, comm_key: &CommitmentKey) { + fn check(rng: &mut StdRng, comm_key: &PedersenCommitmentKey) { for (threshold, total) in vec![ (2, 2), (2, 3), diff --git a/secret_sharing_and_dkg/src/pedersen_vss.rs b/secret_sharing_and_dkg/src/pedersen_vss.rs index a14b225e..bd8a707f 100644 --- a/secret_sharing_and_dkg/src/pedersen_vss.rs +++ b/secret_sharing_and_dkg/src/pedersen_vss.rs @@ -11,15 +11,10 @@ use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; use ark_ff::PrimeField; use ark_poly::univariate::DensePolynomial; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{cfg_into_iter, ops::Add, rand::RngCore, vec::Vec, UniformRand}; -use digest::Digest; -use dock_crypto_utils::{ - affine_group_element_from_byte_slices, ff::powers, join, serde_utils::ArkObjectBytes, -}; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; +use ark_std::{cfg_into_iter, rand::RngCore, vec::Vec, UniformRand}; + +use dock_crypto_utils::{commitment::PedersenCommitmentKey, ff::powers}; #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -29,34 +24,6 @@ use crate::{ shamir_ss, }; -#[serde_as] -#[derive( - Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, -)] -pub struct CommitmentKey { - #[serde_as(as = "ArkObjectBytes")] - pub g: G, - #[serde_as(as = "ArkObjectBytes")] - pub h: G, -} - -impl CommitmentKey { - pub fn new(label: &[u8]) -> Self { - let (g, h) = join!( - affine_group_element_from_byte_slices!(label, b" : g"), - affine_group_element_from_byte_slices!(label, b" : h") - ); - - Self { g, h } - } - - pub fn commit(&self, s: &G::ScalarField, t: &G::ScalarField) -> G::Group { - self.g - .mul_bigint(s.into_bigint()) - .add(&self.h.mul_bigint(t.into_bigint())) - } -} - /// Generate a random secret with its shares according to Pedersen's verifiable secret sharing. /// Returns the secret, blinding, shares, Pedersen commitments to coefficients of the polynomials for /// the secret and blinding and the polynomials @@ -64,7 +31,7 @@ pub fn deal_random_secret( rng: &mut R, threshold: ShareId, total: ShareId, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result< ( G::ScalarField, @@ -88,7 +55,7 @@ pub fn deal_secret( secret: G::ScalarField, threshold: ShareId, total: ShareId, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result< ( G::ScalarField, @@ -106,7 +73,7 @@ pub fn deal_secret( // Create Pedersen commitments where each commitment commits to a coefficient of the polynomial `s_poly` and with blinding as coefficient of the polynomial `t_poly` let coeff_comms = G::Group::normalize_batch( &cfg_into_iter!(0..threshold as usize) - .map(|i| comm_key.commit(&s_poly.coeffs[i], &t_poly.coeffs[i])) + .map(|i| comm_key.commit_as_projective(&s_poly.coeffs[i], &t_poly.coeffs[i])) .collect::>(), ); @@ -134,7 +101,7 @@ impl VerifiableShare { pub fn verify>( &self, commitment_coeffs: &CommitmentToCoefficients, - comm_key: &CommitmentKey, + comm_key: &PedersenCommitmentKey, ) -> Result<(), SSError> { let len = commitment_coeffs.0.len() as ShareId; if self.threshold > len { @@ -144,7 +111,7 @@ impl VerifiableShare { // => commitment_coeffs[0] + commitment_coeffs[1]*id + commitment_coeffs[2]*{id^2} + ... commitment_coeffs[threshold-1]*{id^threshold-1} * {g*share.s + h*share.t}*-1 == 1 let powers = powers(&G::ScalarField::from(self.id as u64), self.threshold as u32); - if G::Group::msm_unchecked(&commitment_coeffs.0, &powers) + if G::Group::msm_unchecked(&commitment_coeffs.0, &powers).into() != comm_key.commit(&self.secret_share, &self.blinding_share) { return Err(SSError::InvalidShare); @@ -185,6 +152,7 @@ impl VerifiableShares { pub mod tests { use super::*; use ark_ff::One; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::rand::{rngs::StdRng, SeedableRng}; use blake2::Blake2b512; use test_utils::{test_serialization, G1, G2}; @@ -192,10 +160,10 @@ pub mod tests { #[test] fn pedersen_verifiable_secret_sharing() { let mut rng = StdRng::seed_from_u64(0u64); - let comm_key1 = CommitmentKey::::new::(b"test"); - let comm_key2 = CommitmentKey::::new::(b"test"); + let comm_key1 = PedersenCommitmentKey::::new::(b"test"); + let comm_key2 = PedersenCommitmentKey::::new::(b"test"); - fn check(rng: &mut StdRng, comm_key: &CommitmentKey) { + fn check(rng: &mut StdRng, comm_key: &PedersenCommitmentKey) { for (threshold, total) in vec![ (2, 2), (2, 3), @@ -247,8 +215,6 @@ pub mod tests { } } - test_serialization!(CommitmentKey, comm_key1); - test_serialization!(CommitmentKey, comm_key2); check(&mut rng, &comm_key1); check(&mut rng, &comm_key2); } diff --git a/short_group_sig/src/bb_sig_pok.rs b/short_group_sig/src/bb_sig_pok.rs index 32efb537..80c9256f 100644 --- a/short_group_sig/src/bb_sig_pok.rs +++ b/short_group_sig/src/bb_sig_pok.rs @@ -22,11 +22,12 @@ use ark_ec::{ }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec::Vec, UniformRand}; use dock_crypto_utils::{msm::WindowTable, randomized_pairing_check::RandomizedPairingChecker}; -use schnorr_pok::{SchnorrCommitment, SchnorrResponse}; -use schnorr_pok::discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol}; +use schnorr_pok::discrete_log::{ + PokDiscreteLog, PokDiscreteLogProtocol, PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol, +}; use zeroize::{Zeroize, ZeroizeOnDrop}; /// Protocol to prove knowledge of a BB signature in group G1 @@ -46,13 +47,13 @@ pub struct PoKOfSignatureG1Protocol { /// Protocol for proving knowledge of `beta` in `T2 = v * beta` pub sc_T2: PokDiscreteLogProtocol, /// For proving knowledge of `message` and `delta_1` in `T1 * message + u * delta_1 = 0` - pub sc_T1_x: SchnorrCommitment, + pub sc_T1_x: PokTwoDiscreteLogsProtocol, /// For proving knowledge of `message` and `delta_2` in `T2 * message + v * delta_2 = 0` - pub sc_T2_x: SchnorrCommitment, + pub sc_T2_x: PokTwoDiscreteLogsProtocol, /// For proving knowledge of `e` and `delta_3` in `T1 * e + u * delta_3 = 0` - pub sc_T1_e: SchnorrCommitment, + pub sc_T1_e: PokTwoDiscreteLogsProtocol, /// For proving knowledge of `e` and `delta_4` in `T2 * e + v * delta_4 = 0` - pub sc_T2_e: SchnorrCommitment, + pub sc_T2_e: PokTwoDiscreteLogsProtocol, /// Commitment to randomness from the 1st step of the Schnorr protocol over the pairing equation. #[zeroize(skip)] pub R_3: PairingOutput, @@ -64,14 +65,11 @@ pub struct PoKOfSignatureG1Protocol { pub delta_3: E::ScalarField, /// - e * beta pub delta_4: E::ScalarField, - pub message: E::ScalarField, - /// Scalar in the signature - pub e: E::ScalarField, } /// Proof of knowledge of a BB signature in group G1 #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] -pub struct PoKOfSignatureG1Proof { +pub struct PoKOfSignatureG1 { /// `u * alpha` pub T1: E::G1Affine, /// `v * beta` @@ -83,17 +81,13 @@ pub struct PoKOfSignatureG1Proof { /// Proof of knowledge of `beta` in `T2 = v * beta` pub sc_T2: PokDiscreteLog, /// For relation `T1 * message + u * delta_1 = 0` - pub sc_T1_x_t: E::G1Affine, - pub sc_T1_x_resp: SchnorrResponse, + pub sc_T1_x: PokTwoDiscreteLogs, /// For relation `T2 * message + v * delta_2 = 0` - pub sc_T2_x_t: E::G1Affine, - pub sc_T2_x_resp: SchnorrResponse, + pub sc_T2_x: PokTwoDiscreteLogs, /// For relation `T1 * e + u * delta_3 = 0` - pub sc_T1_e_t: E::G1Affine, - pub sc_T1_e_resp: SchnorrResponse, + pub sc_T1_e: PokTwoDiscreteLogs, /// For relation `T2 * e + v * delta_4 = 0` - pub sc_T2_e_t: E::G1Affine, - pub sc_T2_e_resp: SchnorrResponse, + pub sc_T2_e: PokTwoDiscreteLogs, /// Commitment to randomness from the 1st step of the Schnorr protocol over the pairing equation. pub R_3: PairingOutput, } @@ -139,10 +133,14 @@ impl PoKOfSignatureG1Protocol { let r_delta_4 = E::ScalarField::rand(rng); let sc_T1 = PokDiscreteLogProtocol::init(alpha, r_alpha, &proving_key.X); let sc_T2 = PokDiscreteLogProtocol::init(beta, r_beta, &proving_key.Y); - let sc_T1_x = SchnorrCommitment::new(&[T1, proving_key.X], vec![r_x, r_delta_1]); - let sc_T2_x = SchnorrCommitment::new(&[T2, proving_key.Y], vec![r_x, r_delta_2]); - let sc_T1_e = SchnorrCommitment::new(&[T1, proving_key.X], vec![r_e, r_delta_3]); - let sc_T2_e = SchnorrCommitment::new(&[T2, proving_key.Y], vec![r_e, r_delta_4]); + let sc_T1_x = + PokTwoDiscreteLogsProtocol::init(message, r_x, &T1, delta_1, r_delta_1, &proving_key.X); + let sc_T2_x = + PokTwoDiscreteLogsProtocol::init(message, r_x, &T2, delta_2, r_delta_2, &proving_key.Y); + let sc_T1_e = + PokTwoDiscreteLogsProtocol::init(e, r_e, &T1, delta_3, r_delta_3, &proving_key.X); + let sc_T2_e = + PokTwoDiscreteLogsProtocol::init(e, r_e, &T2, delta_4, r_delta_4, &proving_key.Y); let g2_prepared = E::G2Prepared::from(params.g2); let pk_0_prepared = E::G2Prepared::from(pk.0); let pk_1_prepared = E::G2Prepared::from(pk.1); @@ -178,8 +176,6 @@ impl PoKOfSignatureG1Protocol { delta_2, delta_3, delta_4, - message, - e: signature.1, } } @@ -211,31 +207,23 @@ impl PoKOfSignatureG1Protocol { pub fn gen_proof( self, challenge: &E::ScalarField, - ) -> Result, ShortGroupSigError> { + ) -> Result, ShortGroupSigError> { let sc_T1 = self.sc_T1.clone().gen_proof(challenge); let sc_T2 = self.sc_T2.clone().gen_proof(challenge); - let sc_T1_x_resp = self - .sc_T1_x - .response(&[self.message, self.delta_1], challenge)?; - let sc_T2_x_resp = self - .sc_T2_x - .response(&[self.message, self.delta_2], challenge)?; - let sc_T1_e_resp = self.sc_T1_e.response(&[self.e, self.delta_3], challenge)?; - let sc_T2_e_resp = self.sc_T2_e.response(&[self.e, self.delta_4], challenge)?; - Ok(PoKOfSignatureG1Proof { + let sc_T1_x = self.sc_T1_x.clone().gen_proof(challenge); + let sc_T2_x = self.sc_T2_x.clone().gen_proof(challenge); + let sc_T1_e = self.sc_T1_e.clone().gen_proof(challenge); + let sc_T2_e = self.sc_T2_e.clone().gen_proof(challenge); + Ok(PoKOfSignatureG1 { T1: self.T1, T2: self.T2, T3: self.T3, sc_T1, sc_T2, - sc_T1_x_t: self.sc_T1_x.t, - sc_T1_x_resp, - sc_T2_x_t: self.sc_T2_x.t, - sc_T2_x_resp, - sc_T1_e_t: self.sc_T1_e.t, - sc_T1_e_resp, - sc_T2_e_t: self.sc_T2_e.t, - sc_T2_e_resp, + sc_T1_x, + sc_T2_x, + sc_T1_e, + sc_T2_e, R_3: self.R_3, }) } @@ -276,7 +264,7 @@ impl PoKOfSignatureG1Protocol { } } -impl PoKOfSignatureG1Proof { +impl PoKOfSignatureG1 { pub fn verify( &self, challenge: &E::ScalarField, @@ -286,8 +274,8 @@ impl PoKOfSignatureG1Proof { proving_key: &ProvingKey, ) -> Result<(), ShortGroupSigError> { self.verify_except_pairings(challenge, proving_key)?; - let s_message = self.sc_T1_x_resp.get_response(0)?; - let e_message = self.sc_T1_e_resp.get_response(0)?; + let s_message = self.sc_T1_x.response1; + let e_message = self.sc_T1_e.response1; let g2_prepared = g2.into(); let PreparedPublicKeyG2(pk_0_prepared, pk_1_prepared, _) = pk.into(); let Z_table = WindowTable::new(3, proving_key.Z.into_group()); @@ -299,14 +287,12 @@ impl PoKOfSignatureG1Proof { E::G1Prepared::from( Z_table.multiply(&(self.sc_T1.response.neg() + self.sc_T2.response.neg())), ), - E::G1Prepared::from(Z_table.multiply( - &(*self.sc_T1_x_resp.get_response(1)? - + self.sc_T2_x_resp.get_response(1)?), - )), - E::G1Prepared::from(Z_table.multiply( - &(*self.sc_T1_e_resp.get_response(1)? - + self.sc_T2_e_resp.get_response(1)?), - )), + E::G1Prepared::from( + Z_table.multiply(&(self.sc_T1_x.response2 + self.sc_T2_x.response2)), + ), + E::G1Prepared::from( + Z_table.multiply(&(self.sc_T1_e.response2 + self.sc_T2_e.response2)), + ), E::G1Prepared::from(self.T3 * challenge), E::G1Prepared::from(g1.into() * challenge.neg()), ], @@ -335,8 +321,8 @@ impl PoKOfSignatureG1Proof { proving_key: &ProvingKey, pairing_checker: &mut RandomizedPairingChecker, ) -> Result<(), ShortGroupSigError> { - let s_message = self.sc_T1_x_resp.get_response(0)?; - let e_message = self.sc_T1_e_resp.get_response(0)?; + let s_message = self.sc_T1_x.response1; + let e_message = self.sc_T1_e.response1; let g2_prepared = g2.into(); let PreparedPublicKeyG2(pk_0_prepared, pk_1_prepared, _) = pk.into(); let Z_table = WindowTable::new(3, proving_key.Z.into_group()); @@ -348,15 +334,9 @@ impl PoKOfSignatureG1Proof { .multiply(&(self.sc_T1.response.neg() + self.sc_T2.response.neg())) .into(), Z_table - .multiply( - &(*self.sc_T1_x_resp.get_response(1)? - + self.sc_T2_x_resp.get_response(1)?), - ) + .multiply(&(self.sc_T1_x.response2 + self.sc_T2_x.response2)) .into(), - (Z_table.multiply( - &(*self.sc_T1_e_resp.get_response(1)? + self.sc_T2_e_resp.get_response(1)?), - )) - .into(), + (Z_table.multiply(&(self.sc_T1_e.response2 + self.sc_T2_e.response2))).into(), (self.T3 * challenge).into(), (g1.into() * challenge.neg()).into(), ], @@ -387,26 +367,42 @@ impl PoKOfSignatureG1Proof { } // Check that `message` is same in `T1 * message + u * delta_1 = 0` and `T2 * message + v * delta_2 = 0` - let s_message = self.sc_T1_x_resp.get_response(0)?; - if s_message != self.sc_T2_x_resp.get_response(0)? { + let s_message = self.sc_T1_x.response1; + if s_message != self.sc_T2_x.response1 { return Err(ShortGroupSigError::InvalidProof); } // Check that `e` is same in `T1 * e + u * delta_3 = 0` and `T2 * e + v * delta_4 = 0` - let e_message = self.sc_T1_e_resp.get_response(0)?; - if e_message != self.sc_T2_e_resp.get_response(0)? { + let e_message = self.sc_T1_e.response1; + if e_message != self.sc_T2_e.response1 { return Err(ShortGroupSigError::InvalidProof); } let zero = E::G1Affine::zero(); - self.sc_T1_x_resp - .is_valid(&[self.T1, proving_key.X], &zero, &self.sc_T1_x_t, challenge)?; - self.sc_T2_x_resp - .is_valid(&[self.T2, proving_key.Y], &zero, &self.sc_T2_x_t, challenge)?; - self.sc_T1_e_resp - .is_valid(&[self.T1, proving_key.X], &zero, &self.sc_T1_e_t, challenge)?; - self.sc_T2_e_resp - .is_valid(&[self.T2, proving_key.Y], &zero, &self.sc_T2_e_t, challenge)?; + if !self + .sc_T1_x + .verify(&zero, &self.T1, &proving_key.X, challenge) + { + return Err(ShortGroupSigError::InvalidProof); + } + if !self + .sc_T2_x + .verify(&zero, &self.T2, &proving_key.Y, challenge) + { + return Err(ShortGroupSigError::InvalidProof); + } + if !self + .sc_T1_e + .verify(&zero, &self.T1, &proving_key.X, challenge) + { + return Err(ShortGroupSigError::InvalidProof); + } + if !self + .sc_T2_e + .verify(&zero, &self.T2, &proving_key.Y, challenge) + { + return Err(ShortGroupSigError::InvalidProof); + } Ok(()) } @@ -426,21 +422,21 @@ impl PoKOfSignatureG1Proof { ¶ms.g2, &self.sc_T1.t, &self.sc_T2.t, - &self.sc_T1_x_t, - &self.sc_T2_x_t, - &self.sc_T1_e_t, - &self.sc_T2_e_t, + &self.sc_T1_x.t, + &self.sc_T2_x.t, + &self.sc_T1_e.t, + &self.sc_T2_e.t, &self.R_3, writer, ) } - pub fn get_resp_for_message(&self) -> Result<&E::ScalarField, ShortGroupSigError> { - self.sc_T1_x_resp.get_response(0).map_err(|e| e.into()) + pub fn get_resp_for_message(&self) -> &E::ScalarField { + &self.sc_T1_x.response1 } - pub fn get_resp_for_randomness(&self) -> Result<&E::ScalarField, ShortGroupSigError> { - self.sc_T1_e_resp.get_response(0).map_err(|e| e.into()) + pub fn get_resp_for_randomness(&self) -> &E::ScalarField { + &self.sc_T1_e.response1 } } @@ -555,13 +551,10 @@ mod tests { ) .unwrap(); + assert_eq!(proof1.get_resp_for_message(), proof2.get_resp_for_message()); assert_eq!( - proof1.get_resp_for_message().unwrap(), - proof2.get_resp_for_message().unwrap() - ); - assert_eq!( - proof1.get_resp_for_randomness().unwrap(), - proof2.get_resp_for_randomness().unwrap() + proof1.get_resp_for_randomness(), + proof2.get_resp_for_randomness() ); } } diff --git a/short_group_sig/src/bb_sig_pok_cdh.rs b/short_group_sig/src/bb_sig_pok_cdh.rs index e351a994..a9e2b8bc 100644 --- a/short_group_sig/src/bb_sig_pok_cdh.rs +++ b/short_group_sig/src/bb_sig_pok_cdh.rs @@ -17,10 +17,11 @@ use crate::{ use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; use ark_ff::{Field, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; -use schnorr_pok::{SchnorrCommitment, SchnorrResponse}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec::Vec, UniformRand}; -use schnorr_pok::discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol}; +use schnorr_pok::discrete_log::{ + PokDiscreteLog, PokDiscreteLogProtocol, PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol, +}; use zeroize::{Zeroize, ZeroizeOnDrop}; #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] @@ -37,14 +38,13 @@ pub struct PoKOfSignatureG1Protocol { /// `w2 * e * r1/r2` #[zeroize(skip)] pub w2_prime: E::G2Affine, - pub sc_comm_1: SchnorrCommitment, - sc_wits_1: (E::ScalarField, E::ScalarField), + pub sc_1: PokTwoDiscreteLogsProtocol, /// Protocol for proving knowledge of `e * r1 / r2` in `w2' = w2 * {e * r1 / r2}` - pub sc_comm_2: PokDiscreteLogProtocol, + pub sc_2: PokDiscreteLogProtocol, } #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] -pub struct PoKOfSignatureG1Proof { +pub struct PoKOfSignatureG1 { /// `A * r1` pub A_prime: E::G1Affine, /// `A * r2` @@ -53,8 +53,7 @@ pub struct PoKOfSignatureG1Proof { pub A_bar: E::G1Affine, /// `w2 * e * r1/r2` pub w2_prime: E::G2Affine, - pub t_1: E::G1Affine, - pub sc_resp_1: SchnorrResponse, + pub sc_1: PokTwoDiscreteLogs, pub sc_2: PokDiscreteLog, } @@ -81,20 +80,22 @@ impl PoKOfSignatureG1Protocol { let g1 = g1.into(); // A_bar = g1 * r - A' * message let A_bar = g1 * r1 + A_prime_neg * message; - let sc_comm_1 = SchnorrCommitment::new( - &[g1, A_prime_neg.into()], - vec![E::ScalarField::rand(rng), blinding], + let sc_comm_1 = PokTwoDiscreteLogsProtocol::init( + r1, + E::ScalarField::rand(rng), + &g1, + message, + blinding, + &A_prime_neg.into(), ); let sc_comm_2 = PokDiscreteLogProtocol::init(wit, E::ScalarField::rand(rng), &pk.1); - let sc_wits_1 = (r1, message); Self { A_prime: A_prime.into_affine(), A_hat: A_hat.into_affine(), A_bar: A_bar.into_affine(), w2_prime, - sc_comm_1, - sc_wits_1, - sc_comm_2, + sc_1: sc_comm_1, + sc_2: sc_comm_2, } } @@ -111,8 +112,8 @@ impl PoKOfSignatureG1Protocol { &self.w2_prime, g1, pk.1, - &self.sc_comm_1.t, - &self.sc_comm_2.t, + &self.sc_1.t, + &self.sc_2.t, writer, ) } @@ -120,18 +121,15 @@ impl PoKOfSignatureG1Protocol { pub fn gen_proof( self, challenge: &E::ScalarField, - ) -> Result, ShortGroupSigError> { - let sc_resp_1 = self - .sc_comm_1 - .response(&[self.sc_wits_1.0, self.sc_wits_1.1], challenge)?; - let sc_2 = self.sc_comm_2.clone().gen_proof(challenge); - Ok(PoKOfSignatureG1Proof { + ) -> Result, ShortGroupSigError> { + let sc_resp_1 = self.sc_1.clone().gen_proof(challenge); + let sc_2 = self.sc_2.clone().gen_proof(challenge); + Ok(PoKOfSignatureG1 { A_prime: self.A_prime, A_hat: self.A_hat, A_bar: self.A_bar, w2_prime: self.w2_prime, - t_1: self.sc_comm_1.t, - sc_resp_1, + sc_1: sc_resp_1, sc_2, }) } @@ -159,7 +157,7 @@ impl PoKOfSignatureG1Protocol { } } -impl PoKOfSignatureG1Proof { +impl PoKOfSignatureG1 { pub fn verify( &self, challenge: &E::ScalarField, @@ -173,12 +171,14 @@ impl PoKOfSignatureG1Proof { if self.w2_prime.is_zero() { return Err(ShortGroupSigError::InvalidProof); } - self.sc_resp_1.is_valid( - &[g1.into(), self.A_prime.into_group().neg().into()], + if !self.sc_1.verify( &self.A_bar, - &self.t_1, + &g1.into(), + &self.A_prime.into_group().neg().into(), challenge, - )?; + ) { + return Err(ShortGroupSigError::InvalidProof); + } let pk = pk.into(); if !self.sc_2.verify(&self.w2_prime, &pk.2, challenge) { return Err(ShortGroupSigError::InvalidProof); @@ -211,7 +211,7 @@ impl PoKOfSignatureG1Proof { &self.w2_prime, g1, pk.1, - &self.t_1, + &self.sc_1.t, &self.sc_2.t, writer, ) diff --git a/short_group_sig/src/weak_bb_sig_pok.rs b/short_group_sig/src/weak_bb_sig_pok.rs index aa43788a..c3d3e43e 100644 --- a/short_group_sig/src/weak_bb_sig_pok.rs +++ b/short_group_sig/src/weak_bb_sig_pok.rs @@ -11,11 +11,12 @@ use ark_ec::{ }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec::Vec, UniformRand}; use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; -use schnorr_pok::{SchnorrCommitment, SchnorrResponse}; -use schnorr_pok::discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol}; +use schnorr_pok::discrete_log::{ + PokDiscreteLog, PokDiscreteLogProtocol, PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol, +}; use zeroize::{Zeroize, ZeroizeOnDrop}; /// Protocol to prove knowledge of a weak-BB signature in group G1 @@ -35,23 +36,17 @@ pub struct PoKOfSignatureG1Protocol { /// Protocol for proving knowledge of `beta` in `T2 = v * beta` pub sc_T2: PokDiscreteLogProtocol, /// For proving knowledge of `message` and `delta_1` in `T1 * message + u * delta_1 = 0` - pub sc_T1_x: SchnorrCommitment, + pub sc_T1_x: PokTwoDiscreteLogsProtocol, /// For proving knowledge of `message` and `delta_2` in `T2 * message + v * delta_2 = 0` - pub sc_T2_x: SchnorrCommitment, + pub sc_T2_x: PokTwoDiscreteLogsProtocol, /// Commitment to randomness from the 1st step of the Schnorr protocol over the pairing equation. Called `R_3` in the paper #[zeroize(skip)] pub R_3: PairingOutput, - /// - message * alpha - pub delta_1: E::ScalarField, - /// - message * beta - pub delta_2: E::ScalarField, - /// Called `x` in the paper - pub message: E::ScalarField, } /// Proof of knowledge of a weak-BB signature in group G1 #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] -pub struct PoKOfSignatureG1Proof { +pub struct PoKOfSignatureG1 { /// `u * alpha` pub T1: E::G1Affine, /// `v * beta` @@ -63,11 +58,9 @@ pub struct PoKOfSignatureG1Proof { /// Proof of knowledge of `beta` in `T2 = v * beta` pub sc_T2: PokDiscreteLog, /// For relation `T1 * message + u * delta_1 = 0` - pub sc_T1_x_t: E::G1Affine, - pub sc_T1_x_resp: SchnorrResponse, + pub sc_T1_x: PokTwoDiscreteLogs, /// For relation `T2 * message + v * delta_2 = 0` - pub sc_T2_x_t: E::G1Affine, - pub sc_T2_x_resp: SchnorrResponse, + pub sc_T2_x: PokTwoDiscreteLogs, /// `R_3` from the paper pub R_3: PairingOutput, } @@ -100,8 +93,10 @@ impl PoKOfSignatureG1Protocol { let r_delta_2 = E::ScalarField::rand(rng); let sc_T1 = PokDiscreteLogProtocol::init(alpha, r_alpha, &proving_key.X); let sc_T2 = PokDiscreteLogProtocol::init(beta, r_beta, &proving_key.Y); - let sc_T1_x = SchnorrCommitment::new(&[T1, proving_key.X], vec![r_x, r_delta_1]); - let sc_T2_x = SchnorrCommitment::new(&[T2, proving_key.Y], vec![r_x, r_delta_2]); + let sc_T1_x = + PokTwoDiscreteLogsProtocol::init(message, r_x, &T1, delta_1, r_delta_1, &proving_key.X); + let sc_T2_x = + PokTwoDiscreteLogsProtocol::init(message, r_x, &T2, delta_2, r_delta_2, &proving_key.Y); let g2_prepared = E::G2Prepared::from(params.g2); // R_3 = e(T_3, g2) * r_x + e(Z, pk) * -(r_alpha + r_beta) + e(Z, g2) * (r_delta_1 + r_delta_2) let R_3 = E::multi_pairing( @@ -121,9 +116,6 @@ impl PoKOfSignatureG1Protocol { sc_T1_x, sc_T2_x, R_3, - delta_1, - delta_2, - message, } } @@ -151,25 +143,19 @@ impl PoKOfSignatureG1Protocol { pub fn gen_proof( self, challenge: &E::ScalarField, - ) -> Result, ShortGroupSigError> { + ) -> Result, ShortGroupSigError> { let sc_T1 = self.sc_T1.clone().gen_proof(challenge); let sc_T2 = self.sc_T2.clone().gen_proof(challenge); - let sc_T1_x_resp = self - .sc_T1_x - .response(&[self.message, self.delta_1], challenge)?; - let sc_T2_x_resp = self - .sc_T2_x - .response(&[self.message, self.delta_2], challenge)?; - Ok(PoKOfSignatureG1Proof { + let sc_T1_x = self.sc_T1_x.clone().gen_proof(challenge); + let sc_T2_x = self.sc_T2_x.clone().gen_proof(challenge); + Ok(PoKOfSignatureG1 { T1: self.T1, T2: self.T2, T3: self.T3, sc_T1, sc_T2, - sc_T1_x_t: self.sc_T1_x.t, - sc_T1_x_resp, - sc_T2_x_t: self.sc_T2_x.t, - sc_T2_x_resp, + sc_T1_x, + sc_T2_x, R_3: self.R_3, }) } @@ -203,7 +189,7 @@ impl PoKOfSignatureG1Protocol { } } -impl PoKOfSignatureG1Proof { +impl PoKOfSignatureG1 { pub fn verify( &self, challenge: &E::ScalarField, @@ -213,7 +199,7 @@ impl PoKOfSignatureG1Proof { proving_key: &ProvingKey, ) -> Result<(), ShortGroupSigError> { self.verify_except_pairings(challenge, proving_key)?; - let s_message = self.sc_T1_x_resp.get_response(0)?; + let s_message = self.sc_T1_x.response1; let g2_prepared = g2.into(); let pk_prepared = pk.into(); // Following is the pairing check equation from the paper converted to a single multi-pairing @@ -225,9 +211,7 @@ impl PoKOfSignatureG1Proof { proving_key.Z * (self.sc_T1.response.neg() + self.sc_T2.response.neg()), ), E::G1Prepared::from( - proving_key.Z - * (*self.sc_T1_x_resp.get_response(1)? - + self.sc_T2_x_resp.get_response(1)?), + proving_key.Z * (self.sc_T1_x.response2 + self.sc_T2_x.response2), ), E::G1Prepared::from(self.T3 * challenge), E::G1Prepared::from(g1.into() * challenge.neg()), @@ -256,16 +240,14 @@ impl PoKOfSignatureG1Proof { pairing_checker: &mut RandomizedPairingChecker, ) -> Result<(), ShortGroupSigError> { self.verify_except_pairings(challenge, proving_key)?; - let s_message = self.sc_T1_x_resp.get_response(0)?; + let s_message = self.sc_T1_x.response1; let g2_prepared = g2.into(); let pk_prepared = pk.into(); pairing_checker.add_multiple_sources_and_target( &[ (self.T3 * s_message).into(), (proving_key.Z * (self.sc_T1.response.neg() + self.sc_T2.response.neg())).into(), - (proving_key.Z - * (*self.sc_T1_x_resp.get_response(1)? + self.sc_T2_x_resp.get_response(1)?)) - .into(), + (proving_key.Z * (self.sc_T1_x.response2 + self.sc_T2_x.response2)).into(), (self.T3 * challenge).into(), (g1.into() * challenge.neg()).into(), ], @@ -293,15 +275,23 @@ impl PoKOfSignatureG1Proof { return Err(ShortGroupSigError::InvalidProof); } // Check that `message` is same in `T1 * message + u * delta_1 = 0` and `T2 * message + v * delta_2 = 0` - let s_message = self.sc_T1_x_resp.get_response(0)?; - if s_message != self.sc_T2_x_resp.get_response(0)? { + let s_message = self.sc_T1_x.response1; + if s_message != self.sc_T2_x.response1 { return Err(ShortGroupSigError::InvalidProof); } let zero = E::G1Affine::zero(); - self.sc_T1_x_resp - .is_valid(&[self.T1, proving_key.X], &zero, &self.sc_T1_x_t, challenge)?; - self.sc_T2_x_resp - .is_valid(&[self.T2, proving_key.Y], &zero, &self.sc_T2_x_t, challenge)?; + if !self + .sc_T1_x + .verify(&zero, &self.T1, &proving_key.X, challenge) + { + return Err(ShortGroupSigError::InvalidProof); + }; + if !self + .sc_T2_x + .verify(&zero, &self.T2, &proving_key.Y, challenge) + { + return Err(ShortGroupSigError::InvalidProof); + } Ok(()) } @@ -319,15 +309,15 @@ impl PoKOfSignatureG1Proof { ¶ms.g2, &self.sc_T1.t, &self.sc_T2.t, - &self.sc_T1_x_t, - &self.sc_T2_x_t, + &self.sc_T1_x.t, + &self.sc_T2_x.t, &self.R_3, writer, ) } - pub fn get_resp_for_message(&self) -> Result<&E::ScalarField, ShortGroupSigError> { - self.sc_T1_x_resp.get_response(0).map_err(|e| e.into()) + pub fn get_resp_for_message(&self) -> &E::ScalarField { + &self.sc_T1_x.response1 } } diff --git a/short_group_sig/src/weak_bb_sig_pok_cdh.rs b/short_group_sig/src/weak_bb_sig_pok_cdh.rs index aebae65d..2e7c813b 100644 --- a/short_group_sig/src/weak_bb_sig_pok_cdh.rs +++ b/short_group_sig/src/weak_bb_sig_pok_cdh.rs @@ -6,11 +6,11 @@ use crate::error::ShortGroupSigError; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; use ark_ff::{PrimeField, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec::Vec, UniformRand}; use dock_crypto_utils::{ randomized_pairing_check::RandomizedPairingChecker, serde_utils::ArkObjectBytes, }; -use schnorr_pok::{SchnorrCommitment, SchnorrResponse}; +use schnorr_pok::discrete_log::{PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -24,23 +24,19 @@ pub struct PoKOfSignatureG1Protocol { #[zeroize(skip)] pub A_bar: E::G1Affine, /// For proving relation `sigma_bar = g1 * r - sigma' * m` - pub sc_comm: SchnorrCommitment, - /// Randomness and message `(r, m)` - sc_wits: (E::ScalarField, E::ScalarField), + pub sc: PokTwoDiscreteLogsProtocol, } #[serde_as] #[derive( Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, )] -pub struct PoKOfSignatureG1Proof { +pub struct PoKOfSignatureG1 { #[serde_as(as = "ArkObjectBytes")] pub A_prime: E::G1Affine, #[serde_as(as = "ArkObjectBytes")] pub A_bar: E::G1Affine, - #[serde_as(as = "ArkObjectBytes")] - pub t: E::G1Affine, - pub sc_resp: SchnorrResponse, + pub sc: PokTwoDiscreteLogs, } impl PoKOfSignatureG1Protocol { @@ -59,16 +55,18 @@ impl PoKOfSignatureG1Protocol { let g1 = g1.into(); // A_bar = g1 * r - A_prime * m let A_bar = g1 * r + A_prime_neg * message; - let sc_comm = SchnorrCommitment::new( - &[g1, A_prime_neg.into()], - vec![E::ScalarField::rand(rng), blinding], + let sc = PokTwoDiscreteLogsProtocol::init( + r, + E::ScalarField::rand(rng), + &g1, + message, + blinding, + &A_prime_neg.into(), ); - let sc_wits = (r, message); Self { A_prime: A_prime.into_affine(), A_bar: A_bar.into_affine(), - sc_comm, - sc_wits, + sc, } } @@ -77,27 +75,18 @@ impl PoKOfSignatureG1Protocol { g1: impl Into, writer: W, ) -> Result<(), ShortGroupSigError> { - Self::compute_challenge_contribution( - &self.A_bar, - &self.A_prime, - g1, - &self.sc_comm.t, - writer, - ) + Self::compute_challenge_contribution(&self.A_bar, &self.A_prime, g1, &self.sc.t, writer) } pub fn gen_proof( self, challenge: &E::ScalarField, - ) -> Result, ShortGroupSigError> { - let sc_resp = self - .sc_comm - .response(&[self.sc_wits.0, self.sc_wits.1], challenge)?; - Ok(PoKOfSignatureG1Proof { + ) -> Result, ShortGroupSigError> { + let sc = self.sc.clone().gen_proof(challenge); + Ok(PoKOfSignatureG1 { A_prime: self.A_prime, A_bar: self.A_bar, - t: self.sc_comm.t, - sc_resp, + sc, }) } @@ -116,7 +105,7 @@ impl PoKOfSignatureG1Protocol { } } -impl PoKOfSignatureG1Proof { +impl PoKOfSignatureG1 { pub fn verify( &self, challenge: &E::ScalarField, @@ -160,12 +149,14 @@ impl PoKOfSignatureG1Proof { if self.A_prime.is_zero() { return Err(ShortGroupSigError::InvalidProof); } - self.sc_resp.is_valid( - &[g1.into(), self.A_prime.into_group().neg().into()], + if !self.sc.verify( &self.A_bar, - &self.t, + &g1.into(), + &self.A_prime.into_group().neg().into(), challenge, - )?; + ) { + return Err(ShortGroupSigError::InvalidProof); + } Ok(()) } @@ -178,13 +169,13 @@ impl PoKOfSignatureG1Proof { &self.A_bar, &self.A_prime, g1, - &self.t, + &self.sc.t, writer, ) } - pub fn get_resp_for_message(&self) -> Result<&E::ScalarField, ShortGroupSigError> { - self.sc_resp.get_response(1).map_err(|e| e.into()) + pub fn get_resp_for_message(&self) -> &E::ScalarField { + &self.sc.response2 } } diff --git a/utils/src/commitment.rs b/utils/src/commitment.rs new file mode 100644 index 00000000..cc10f6f2 --- /dev/null +++ b/utils/src/commitment.rs @@ -0,0 +1,43 @@ +use crate::{ + concat_slices, hashing_utils::affine_group_elem_from_try_and_incr, serde_utils::ArkObjectBytes, +}; +use ark_ec::AffineRepr; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::vec::Vec; +use digest::Digest; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +/// A Pedersen commitment key. The Pedersen commitment will be `g * m + h * r` with opening `(m, r)` +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct PedersenCommitmentKey { + #[serde_as(as = "ArkObjectBytes")] + pub g: G, + #[serde_as(as = "ArkObjectBytes")] + pub h: G, +} + +impl PedersenCommitmentKey { + /// Create a new commitment key + pub fn new(label: &[u8]) -> Self { + let g = affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : G"]); + let h = affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : H"]); + Self { g, h } + } + + /// Commit to a message + pub fn commit(&self, message: &G::ScalarField, randomness: &G::ScalarField) -> G { + (self.g * message + self.h * randomness).into() + } + + pub fn commit_as_projective( + &self, + message: &G::ScalarField, + randomness: &G::ScalarField, + ) -> G::Group { + self.g * message + self.h * randomness + } +} diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 373f1b35..7c151312 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -12,6 +12,7 @@ pub mod ecies; pub mod elgamal; #[macro_use] pub mod ff; +pub mod commitment; pub mod hashing_utils; pub mod iter; pub mod macros; @@ -21,5 +22,6 @@ pub mod owned_pairs; pub mod pairs; pub mod poly; pub mod randomized_pairing_check; +pub mod signature; pub mod transcript; pub mod try_iter; diff --git a/utils/src/signature.rs b/utils/src/signature.rs new file mode 100644 index 00000000..61690d25 --- /dev/null +++ b/utils/src/signature.rs @@ -0,0 +1,56 @@ +use crate::{extend_some::ExtendSome, misc::rand}; +use ark_ff::PrimeField; +use ark_std::{rand::RngCore, vec::Vec}; +use core::result::Result; + +/// Trait implemented by a signature scheme params that can sign multiple messages +pub trait MultiMessageSignatureParams { + /// Number of messages supported in the multi-message + fn supported_message_count(&self) -> usize; +} + +/// Each message can be either randomly blinded, unblinded, or blinded using supplied blinding. +/// By default, a message is blinded with random blinding. +pub enum MessageOrBlinding<'a, F: PrimeField> { + /// Message will be blinded using random blinding. + BlindMessageRandomly(&'a F), + /// Message will be revealed, and thus won't be included in PoK. + RevealMessage(&'a F), + /// Message will be blinded using the supplied blinding. + BlindMessageWithConcreteBlinding { message: &'a F, blinding: F }, +} + +impl<'a, F: PrimeField> MessageOrBlinding<'a, F> { + /// Blinds given `message` using supplied `blinding`. + pub fn blind_message_with(message: &'a F, blinding: F) -> Self { + Self::BlindMessageWithConcreteBlinding { message, blinding } + } +} + +// TODO: Document this +pub fn split_messages_and_blindings< + 'a, + R: RngCore, + F: PrimeField, + MBI: IntoIterator>, +>( + rng: &mut R, + messages_and_blindings: MBI, + params: impl MultiMessageSignatureParams, +) -> Result<(Vec, impl IntoIterator), usize> { + let (messages, ExtendSome::>(indexed_blindings)): (Vec<_>, _) = messages_and_blindings + .into_iter() + .enumerate() + .map(|(idx, msg_or_blinding)| match msg_or_blinding { + MessageOrBlinding::BlindMessageRandomly(message) => (message, (idx, rand(rng)).into()), + MessageOrBlinding::BlindMessageWithConcreteBlinding { message, blinding } => { + (message, (idx, blinding).into()) + } + MessageOrBlinding::RevealMessage(message) => (message, None), + }) + .unzip(); + let l = messages.len(); + (l == params.supported_message_count()) + .then_some((messages, indexed_blindings)) + .ok_or_else(|| l) +} diff --git a/vb_accumulator/src/kb_positive_accumulator/proofs.rs b/vb_accumulator/src/kb_positive_accumulator/proofs.rs index 6b0b4416..8e34d63e 100644 --- a/vb_accumulator/src/kb_positive_accumulator/proofs.rs +++ b/vb_accumulator/src/kb_positive_accumulator/proofs.rs @@ -11,7 +11,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{io::Write, rand::RngCore, vec::Vec, UniformRand}; use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; use short_group_sig::{ - bb_sig_pok::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}, + bb_sig_pok::{PoKOfSignatureG1, PoKOfSignatureG1Protocol}, common::ProvingKey, }; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -27,7 +27,7 @@ pub struct KBPositiveAccumulatorMembershipProofProtocol { #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct KBPositiveAccumulatorMembershipProof { - pub sig_proof: PoKOfSignatureG1Proof, + pub sig_proof: PoKOfSignatureG1, pub accum_proof: MembershipProof, } @@ -124,7 +124,7 @@ impl KBPositiveAccumulatorMembershipProof { )?; // Check that the signature's randomness is same as the non-adaptive accumulator's member - if self.sig_proof.get_resp_for_randomness()? + if self.sig_proof.get_resp_for_randomness() != self.accum_proof.get_schnorr_response_for_element() { return Err(VBAccumulatorError::MismatchBetweenSignatureAndAccumulatorValue); @@ -162,7 +162,7 @@ impl KBPositiveAccumulatorMembershipProof { )?; // Check that the signature's randomness is same as the non-adaptive accumulator's member - if self.sig_proof.get_resp_for_randomness()? + if self.sig_proof.get_resp_for_randomness() != self.accum_proof.get_schnorr_response_for_element() { return Err(VBAccumulatorError::MismatchBetweenSignatureAndAccumulatorValue); @@ -191,7 +191,7 @@ impl KBPositiveAccumulatorMembershipProof { } pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { - self.sig_proof.get_resp_for_message().unwrap() + self.sig_proof.get_resp_for_message() } } diff --git a/vb_accumulator/src/kb_positive_accumulator/proofs_cdh.rs b/vb_accumulator/src/kb_positive_accumulator/proofs_cdh.rs index b2d133ba..ae487cb6 100644 --- a/vb_accumulator/src/kb_positive_accumulator/proofs_cdh.rs +++ b/vb_accumulator/src/kb_positive_accumulator/proofs_cdh.rs @@ -13,7 +13,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{io::Write, rand::RngCore, vec::Vec, UniformRand}; use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; use short_group_sig::{ - bb_sig_pok::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}, + bb_sig_pok::{PoKOfSignatureG1, PoKOfSignatureG1Protocol}, common::ProvingKey, }; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -29,7 +29,7 @@ pub struct KBPositiveAccumulatorMembershipProofProtocol { #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct KBPositiveAccumulatorMembershipProof { - pub sig_proof: PoKOfSignatureG1Proof, + pub sig_proof: PoKOfSignatureG1, pub accum_proof: MembershipProof, } @@ -112,7 +112,7 @@ impl KBPositiveAccumulatorMembershipProof { self.accum_proof .verify(accumulator_value, challenge, pk.accum, params.accum)?; // Check that the signature's randomness is same as the non-adaptive accumulator's member - if self.sig_proof.get_resp_for_randomness()? + if self.sig_proof.get_resp_for_randomness() != self.accum_proof.get_schnorr_response_for_element() { return Err(VBAccumulatorError::MismatchBetweenSignatureAndAccumulatorValue); @@ -147,7 +147,7 @@ impl KBPositiveAccumulatorMembershipProof { pairing_checker, )?; // Check that the signature's randomness is same as the non-adaptive accumulator's member - if self.sig_proof.get_resp_for_randomness()? + if self.sig_proof.get_resp_for_randomness() != self.accum_proof.get_schnorr_response_for_element() { return Err(VBAccumulatorError::MismatchBetweenSignatureAndAccumulatorValue); @@ -171,7 +171,7 @@ impl KBPositiveAccumulatorMembershipProof { } pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { - self.sig_proof.get_resp_for_message().unwrap() + self.sig_proof.get_resp_for_message() } } diff --git a/vb_accumulator/src/proofs_cdh.rs b/vb_accumulator/src/proofs_cdh.rs index 4467aca1..eda8c136 100644 --- a/vb_accumulator/src/proofs_cdh.rs +++ b/vb_accumulator/src/proofs_cdh.rs @@ -30,7 +30,7 @@ use schnorr_pok::{ }; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use short_group_sig::weak_bb_sig_pok_cdh::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}; +use short_group_sig::weak_bb_sig_pok_cdh::{PoKOfSignatureG1, PoKOfSignatureG1Protocol}; use zeroize::{Zeroize, ZeroizeOnDrop}; /// A wrapper over the protocol for proof of knowledge of weak-BB signature. The accumulator witness is the weak-BB signature and the @@ -43,7 +43,7 @@ pub struct MembershipProofProtocol(pub PoKOfSignatureG1Protocol); Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, )] #[serde(bound = "")] -pub struct MembershipProof(pub PoKOfSignatureG1Proof); +pub struct MembershipProof(pub PoKOfSignatureG1); /// An extension over the protocol for proof of knowledge of weak-BB signature. #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] @@ -164,7 +164,7 @@ impl MembershipProof { } pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { - self.0.get_resp_for_message().unwrap() + self.0.get_resp_for_message() } } diff --git a/vb_accumulator/src/proofs_keyed_verification.rs b/vb_accumulator/src/proofs_keyed_verification.rs index 73aae41c..237825fd 100644 --- a/vb_accumulator/src/proofs_keyed_verification.rs +++ b/vb_accumulator/src/proofs_keyed_verification.rs @@ -28,7 +28,9 @@ use digest::Digest; use dock_crypto_utils::serde_utils::ArkObjectBytes; use schnorr_pok::{ compute_random_oracle_challenge, - discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol}, + discrete_log::{ + PokDiscreteLog, PokDiscreteLogProtocol, PokTwoDiscreteLogs, PokTwoDiscreteLogsProtocol, + }, SchnorrCommitment, SchnorrResponse, }; use serde::{Deserialize, Serialize}; @@ -43,8 +45,7 @@ pub struct MembershipProofProtocol { pub C_prime: G, #[zeroize(skip)] pub C_bar: G, - pub sc_comm: SchnorrCommitment, - sc_wits: (G::ScalarField, G::ScalarField), + pub sc: PokTwoDiscreteLogsProtocol, } #[serde_as] @@ -57,8 +58,7 @@ pub struct MembershipProof { #[serde_as(as = "ArkObjectBytes")] pub C_bar: G, #[serde_as(as = "ArkObjectBytes")] - pub t: G, - pub sc_resp: SchnorrResponse, + pub sc: PokTwoDiscreteLogs, } /// The part of membership proof whose verification requires knowledge of secret key. @@ -284,15 +284,18 @@ impl MembershipProofProtocol { let C_prime_neg = C_prime.neg(); let C_bar = (accumulator * l + C_prime_neg * element).into_affine(); let element_blinding = element_blinding.unwrap_or_else(|| G::ScalarField::rand(rng)); - let bases = [accumulator, C_prime_neg.into()]; - let randomness = vec![G::ScalarField::rand(rng), element_blinding]; - let sc_wits = (l, element); - let sc_comm = SchnorrCommitment::new(&bases, randomness); + let sc = PokTwoDiscreteLogsProtocol::init( + l, + G::ScalarField::rand(rng), + &accumulator, + element, + element_blinding, + &C_prime_neg.into(), + ); Self { C_prime: C_prime.into(), C_bar, - sc_comm, - sc_wits, + sc, } } @@ -305,7 +308,7 @@ impl MembershipProofProtocol { accumulator_value, &self.C_prime, &self.C_bar, - &self.sc_comm.t, + &self.sc.t, &mut writer, ) } @@ -314,14 +317,11 @@ impl MembershipProofProtocol { self, challenge: &G::ScalarField, ) -> Result, VBAccumulatorError> { - let sc_resp = self - .sc_comm - .response(&[self.sc_wits.0, self.sc_wits.1], challenge)?; + let sc = self.sc.clone().gen_proof(challenge); Ok(MembershipProof { C_prime: self.C_prime, C_bar: self.C_bar, - t: self.sc_comm.t, - sc_resp, + sc, }) } @@ -362,7 +362,7 @@ impl MembershipProof { accumulator_value, &self.C_prime, &self.C_bar, - &self.t, + &self.sc.t, &mut writer, ) } @@ -372,9 +372,14 @@ impl MembershipProof { accumulator: G, challenge: &G::ScalarField, ) -> Result<(), VBAccumulatorError> { - let bases = [accumulator, self.C_prime.into_group().neg().into()]; - self.sc_resp - .is_valid(&bases, &self.C_bar, &self.t, challenge)?; + if !self.sc.verify( + &self.C_bar, + &accumulator, + &self.C_prime.into_group().neg().into(), + challenge, + ) { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } Ok(()) } @@ -386,7 +391,7 @@ impl MembershipProof { } pub fn get_schnorr_response_for_element(&self) -> &G::ScalarField { - self.sc_resp.get_response(1).unwrap() + &self.sc.response2 } }