diff --git a/Cargo.toml b/Cargo.toml index 760cf61d..d1ee2242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ members = [ "kvac", "merlin", "bulletproofs_plus_plus", - "smc_range_proof" + "smc_range_proof", + "short_group_sig" ] resolver = "2" diff --git a/bbs_plus/src/proof.rs b/bbs_plus/src/proof.rs index e764bbfa..9da45184 100644 --- a/bbs_plus/src/proof.rs +++ b/bbs_plus/src/proof.rs @@ -116,12 +116,10 @@ pub struct PoKOfSignatureG1Protocol { #[serde_as(as = "ArkObjectBytes")] pub d: E::G1Affine, /// For proving relation `A_bar - d = A_prime * -e + h_0 * r2` - #[zeroize(skip)] pub sc_comm_1: SchnorrCommitment, #[serde_as(as = "(ArkObjectBytes, ArkObjectBytes)")] sc_wits_1: (E::ScalarField, E::ScalarField), /// 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)` - #[zeroize(skip)] pub sc_comm_2: SchnorrCommitment, #[serde_as(as = "Vec")] sc_wits_2: Vec, diff --git a/bbs_plus/src/proof_23.rs b/bbs_plus/src/proof_23.rs index 79eda954..7276845e 100644 --- a/bbs_plus/src/proof_23.rs +++ b/bbs_plus/src/proof_23.rs @@ -67,7 +67,6 @@ pub struct PoKOfSignature23G1Protocol { #[serde_as(as = "ArkObjectBytes")] pub B_bar: E::G1Affine, /// For proving relation `g1 + \sum_{i in D}(h_i*m_i)` = `sum_{j notin D}(h_j*m_j)` - #[zeroize(skip)] pub sc_comm: SchnorrCommitment, #[serde_as(as = "Vec")] sc_wits: Vec, diff --git a/bbs_plus/src/proof_23_cdl.rs b/bbs_plus/src/proof_23_cdl.rs index 64f01f53..0c05d371 100644 --- a/bbs_plus/src/proof_23_cdl.rs +++ b/bbs_plus/src/proof_23_cdl.rs @@ -73,7 +73,6 @@ pub struct PoKOfSignature23G1Protocol { #[serde_as(as = "(ArkObjectBytes, ArkObjectBytes)")] sc_wits_1: (E::ScalarField, E::ScalarField), /// For proving relation `g1 + \sum_{i in D}(h_i*m_i)` = `d*r3 + sum_{j notin D}(h_j*m_j)` - #[zeroize(skip)] pub sc_comm_2: SchnorrCommitment, #[serde_as(as = "Vec")] sc_wits_2: Vec, diff --git a/bbs_plus/src/threshold/multiplication_phase.rs b/bbs_plus/src/threshold/multiplication_phase.rs index 7d39b594..2316da55 100644 --- a/bbs_plus/src/threshold/multiplication_phase.rs +++ b/bbs_plus/src/threshold/multiplication_phase.rs @@ -9,7 +9,7 @@ use ark_std::{ vec::Vec, }; use digest::DynDigest; -use dock_crypto_utils::transcript::Merlin; +use dock_crypto_utils::transcript::MerlinTranscript; use itertools::interleave; use oblivious_transfer_protocols::{ ot_based_multiplication::{ @@ -29,7 +29,7 @@ pub struct Phase2, + pub transcripts: BTreeMap, pub ote_params: MultiplicationOTEParams, /// Map where this participant plays the role of sender, i.e Party1 pub multiplication_party1: @@ -71,7 +71,7 @@ impl assert_eq!(masked_signing_key_share.len(), masked_r.len()); let batch_size = masked_signing_key_share.len() as u32; - let mut transcripts = BTreeMap::::new(); + let mut transcripts = BTreeMap::::new(); let mut multiplication_party1 = BTreeMap::>::new(); let mut multiplication_party2 = @@ -86,7 +86,8 @@ impl interleave(masked_r.clone(), masked_signing_key_share.clone()).collect::>(); for other in others { - let mut trans = Merlin::new(b"Multiplication phase for threshold BBS and BBS+"); + let mut trans = + MerlinTranscript::new(b"Multiplication phase for threshold BBS and BBS+"); if id > other { if let Some((base_ot_choices, base_ot_keys)) = base_ot_output.receiver.remove(&other) diff --git a/bulletproofs_plus_plus/src/range_proof_arbitrary_range.rs b/bulletproofs_plus_plus/src/range_proof_arbitrary_range.rs index 6b3cdc77..004f6340 100644 --- a/bulletproofs_plus_plus/src/range_proof_arbitrary_range.rs +++ b/bulletproofs_plus_plus/src/range_proof_arbitrary_range.rs @@ -54,6 +54,28 @@ impl ProofArbitraryRange { setup_params: SetupParams, transcript: &mut impl Transcript, ) -> Result { + let (V, v) = + Self::compute_commitments_and_values(values_and_bounds, &randomness, &setup_params)?; + let prover = Prover::new_with_given_base(base, num_bits, V.clone(), v, randomness)?; + let proof = prover.prove(rng, setup_params, transcript)?; + Ok(Self { V, proof }) + } + + pub fn verify( + &self, + num_bits: u16, + setup_params: &SetupParams, + transcript: &mut impl Transcript, + ) -> Result<(), BulletproofsPlusPlusError> { + self.proof + .verify(num_bits, &self.V, setup_params, transcript) + } + + pub fn compute_commitments_and_values( + values_and_bounds: Vec<(u64, u64, u64)>, + randomness: &[G::ScalarField], + setup_params: &SetupParams, + ) -> Result<(Vec, Vec), BulletproofsPlusPlusError> { if values_and_bounds.len() * 2 != randomness.len() { return Err(BulletproofsPlusPlusError::UnexpectedLengthOfVectors( format!( @@ -85,19 +107,7 @@ impl ProofArbitraryRange { v.push(v_i - min); v.push(max - 1 - v_i); } - let prover = Prover::new_with_given_base(base, num_bits, V.clone(), v, randomness)?; - let proof = prover.prove(rng, setup_params, transcript)?; - Ok(Self { V, proof }) - } - - pub fn verify( - &self, - num_bits: u16, - setup_params: &SetupParams, - transcript: &mut impl Transcript, - ) -> Result<(), BulletproofsPlusPlusError> { - self.proof - .verify(num_bits, &self.V, setup_params, transcript) + Ok((V, v)) } pub fn num_proofs(&self) -> u32 { @@ -134,9 +144,17 @@ impl ProofArbitraryRange { self.num_proofs() as usize, )); } - let table = WindowTable::new(self.num_proofs() as usize * 2, g.into_group()); - let mut comms = Vec::with_capacity(self.num_proofs() as usize); - for i in (0..self.V.len()).step_by(2) { + Self::get_commitments_to_values_given_transformed_commitments_and_g(&self.V, bounds, g) + } + + pub fn get_commitments_to_values_given_transformed_commitments_and_g( + transformed_comms: &[G], + bounds: Vec<(u64, u64)>, + g: &G, + ) -> Result, BulletproofsPlusPlusError> { + let table = WindowTable::new(transformed_comms.len(), g.into_group()); + let mut comms = Vec::with_capacity(transformed_comms.len() / 2); + for i in (0..transformed_comms.len()).step_by(2) { let (min, max) = (bounds[i / 2].0, bounds[i / 2].1); if max <= min { return Err(BulletproofsPlusPlusError::IncorrectBounds(format!( @@ -145,10 +163,11 @@ impl ProofArbitraryRange { ))); } // `V[i]` is a commitment to `value - min` and `V[i+1]` is a commitment to `max - 1 - value`. Generate commitments - // to value by `V[i] + g * min` and `g * (max - 1) - V[i+1]` + // to `value` by `V[i] + g * min` and `g * (max - 1) - V[i+1]` comms.push(( - (self.V[i] + table.multiply(&G::ScalarField::from(min))).into_affine(), - (table.multiply(&G::ScalarField::from(max - 1)) - self.V[i + 1]).into_affine(), + (transformed_comms[i] + table.multiply(&G::ScalarField::from(min))).into_affine(), + (table.multiply(&G::ScalarField::from(max - 1)) - transformed_comms[i + 1]) + .into_affine(), )); } Ok(comms) diff --git a/coconut/README.md b/coconut/README.md index 2c95a7fa..34aea2df 100644 --- a/coconut/README.md +++ b/coconut/README.md @@ -1,4 +1,8 @@ -# Coconut + -- Threshold anonymous credentials -- Based on the paper [Security Analysis of Coconut, an Attribute-Based Credential Scheme with Threshold Issuance](https://eprint.iacr.org/2022/011) \ No newline at end of file +# Threshold anonymous credentials using Coconut + +- Based on the paper [Security Analysis of Coconut, an Attribute-Based Credential Scheme with Threshold Issuance](https://eprint.iacr.org/2022/011). +- Contains a modified implementation of PS (Pointcheval-Sanders) signature, as described in the above paper. + + diff --git a/coconut/src/helpers/mod.rs b/coconut/src/helpers/mod.rs index f659f944..88cdae22 100644 --- a/coconut/src/helpers/mod.rs +++ b/coconut/src/helpers/mod.rs @@ -14,16 +14,8 @@ use schnorr_pok::error::SchnorrError; pub mod with_schnorr_and_blindings; pub mod with_schnorr_response; -pub use iter::*; -pub use try_iter::*; pub use utils::{ - aliases::*, - extend_some::*, - iter::{self, *}, - misc::*, - owned_pairs::*, - pairs::*, - try_iter::{self, *}, + aliases::*, extend_some::*, iter::*, misc::*, owned_pairs::*, pairs::*, try_iter::*, }; pub use with_schnorr_and_blindings::*; pub use with_schnorr_response::*; diff --git a/coconut/src/lib.rs b/coconut/src/lib.rs index 4a338932..cab7bf4d 100644 --- a/coconut/src/lib.rs +++ b/coconut/src/lib.rs @@ -1,4 +1,7 @@ -//! Threshold anonymous credentials based on the paper [Security Analysis of Coconut, an Attribute-Based Credential Scheme with Threshold Issuance](https://eprint.iacr.org/2022/011). +//! # Threshold anonymous credentials using Coconut +//! +//! - Based on the paper [Security Analysis of Coconut, an Attribute-Based Credential Scheme with Threshold Issuance](https://eprint.iacr.org/2022/011). +//! - Contains a modified implementation of PS (Pointcheval-Sanders) signature, as described in the above paper. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/coconut/src/setup/keypair/secret.rs b/coconut/src/setup/keypair/secret.rs index fbd65803..072ad242 100644 --- a/coconut/src/setup/keypair/secret.rs +++ b/coconut/src/setup/keypair/secret.rs @@ -39,8 +39,8 @@ pub struct SecretKey { } impl SecretKey { - pub const X_SALT: &[u8] = b"PS-SIG-X-KEYGEN-SALT"; - pub const Y_SALT: &[u8] = b"PS-SIG-Y-KEYGEN-SALT"; + pub const X_SALT: &'static [u8] = b"PS-SIG-X-KEYGEN-SALT"; + pub const Y_SALT: &'static [u8] = b"PS-SIG-Y-KEYGEN-SALT"; /// Generates random secret key compatible with `message_count` messages. pub fn rand(rng: &mut R, message_count: u32) -> Self { diff --git a/delegatable_credentials/src/auditor.rs b/delegatable_credentials/src/auditor.rs index 3ff291b4..d4ca656a 100644 --- a/delegatable_credentials/src/auditor.rs +++ b/delegatable_credentials/src/auditor.rs @@ -41,6 +41,7 @@ pub struct Ciphertext { } impl Ciphertext { + /// Returns the ciphertext and randomness created for encryption pub fn new( rng: &mut R, upk: &E::G1Affine, diff --git a/delegatable_credentials/src/protego/show/tests.rs b/delegatable_credentials/src/protego/show/tests.rs index 27c18721..9445ae16 100644 --- a/delegatable_credentials/src/protego/show/tests.rs +++ b/delegatable_credentials/src/protego/show/tests.rs @@ -432,7 +432,7 @@ pub fn show( println!("Show time: {:?}", show_time); println!("Verify time: {:?}", verify_time); } - _ => panic!("this should never happen"), + _ => unreachable!(), } } diff --git a/legogroth16/tests/mimc.rs b/legogroth16/tests/mimc.rs index 148d3304..5828b8c8 100644 --- a/legogroth16/tests/mimc.rs +++ b/legogroth16/tests/mimc.rs @@ -5,8 +5,6 @@ variant_size_differences, stable_features, non_shorthand_field_patterns, - renamed_and_removed_lints, - private_in_public, unsafe_code )] diff --git a/merlin/Cargo.toml b/merlin/Cargo.toml index a63e056e..4f7591af 100644 --- a/merlin/Cargo.toml +++ b/merlin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dock_merlin" -version = "2.0.0" +version = "3.0.0" authors = ["Henry de Valence "] edition = "2018" readme = "README.md" @@ -28,7 +28,7 @@ rand_core = { version = "0.6", default-features = false } hex = {version = "0.4.3", default-features = false, optional = true} [dev-dependencies] -strobe-rs = "0.5" +strobe-rs = "0.8.1" curve25519-dalek = { version = "4", package = "curve25519-dalek-ng" } rand_chacha = "0.3" diff --git a/merlin/src/transcript.rs b/merlin/src/transcript.rs index 48a3828b..03b70ca9 100644 --- a/merlin/src/transcript.rs +++ b/merlin/src/transcript.rs @@ -97,6 +97,10 @@ impl Transcript { /// Protocols](https://merlin.cool/use/protocol.html) section of /// the Merlin website for details on labels. pub fn append_message(&mut self, label: &'static [u8], message: &[u8]) { + self.append_message_with_non_static_label(label, message) + } + + pub fn append_message_with_non_static_label(&mut self, label: &[u8], message: &[u8]) { let data_len = encode_usize_as_u32(message.len()); self.strobe.meta_ad(label, false); self.strobe.meta_ad(&data_len, true); @@ -176,6 +180,10 @@ impl Transcript { /// Protocols](https://merlin.cool/use/protocol.html) section of /// the Merlin website for details on labels. pub fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) { + self.challenge_bytes_with_non_static_label(label, dest) + } + + pub fn challenge_bytes_with_non_static_label(&mut self, label: &[u8], dest: &mut [u8]) { let data_len = encode_usize_as_u32(dest.len()); self.strobe.meta_ad(label, false); self.strobe.meta_ad(&data_len, true); diff --git a/proof_system/Cargo.toml b/proof_system/Cargo.toml index 8a4fb19e..ca44c54e 100644 --- a/proof_system/Cargo.toml +++ b/proof_system/Cargo.toml @@ -30,11 +30,13 @@ ark-r1cs-std.workspace = true ark-relations.workspace = true zeroize.workspace = true coconut-crypto = { version = "0.7.0", default-features = false, path = "../coconut" } -merlin = { package = "dock_merlin", version = "2.0", default-features = false, path = "../merlin" } +merlin = { package = "dock_merlin", version = "3.0.0", default-features = false, path = "../merlin" } legogroth16 = { version = "0.11.0", default-features = false, features = ["circom", "aggregation"], path = "../legogroth16" } bulletproofs_plus_plus = { version = "0.2.0", default-features = false, path = "../bulletproofs_plus_plus" } smc_range_proof = { version = "0.2.0", default-features = false, path = "../smc_range_proof" } itertools.workspace = true +aead = {version = "0.5.2", default-features = false, features = [ "alloc" ]} +chacha20poly1305 = {version = "0.10.1", default-features = false} [dev-dependencies] ark-bls12-381.workspace = true diff --git a/proof_system/src/constants.rs b/proof_system/src/constants.rs new file mode 100644 index 00000000..e306d179 --- /dev/null +++ b/proof_system/src/constants.rs @@ -0,0 +1,8 @@ +pub const COMPOSITE_PROOF_LABEL: &'static [u8; 15] = b"composite-proof"; +pub const COMPOSITE_PROOF_CHALLENGE_LABEL: &'static [u8; 25] = b"composite-proof-challenge"; +pub const NONCE_LABEL: &'static [u8; 5] = b"nonce"; +pub const CONTEXT_LABEL: &'static [u8; 7] = b"context"; +pub const BBS_PLUS_LABEL: &'static [u8; 4] = b"BBS+"; +pub const BBS_23_LABEL: &'static [u8; 5] = b"BBS23"; +pub const VB_ACCUM_MEM_LABEL: &'static [u8; 25] = b"VB-accumulator-membership"; +pub const VB_ACCUM_NON_MEM_LABEL: &'static [u8; 29] = b"VB-accumulator-non-membership"; diff --git a/proof_system/src/error.rs b/proof_system/src/error.rs index c0a5e65d..56ade7ce 100644 --- a/proof_system/src/error.rs +++ b/proof_system/src/error.rs @@ -1,10 +1,12 @@ use ark_serialize::SerializationError; use ark_std::{collections::BTreeSet, fmt::Debug, string::String, vec::Vec}; use bbs_plus::error::BBSPlusError; +use bulletproofs_plus_plus::error::BulletproofsPlusPlusError; use dock_crypto_utils::try_iter::InvalidPair; use legogroth16::{circom::CircomError, error::Error as LegoGroth16Error}; use saver::error::SaverError; use schnorr_pok::error::SchnorrError; +use smc_range_proof::prelude::SmcRangeProofError; use vb_accumulator::error::VBAccumulatorError; #[derive(Debug)] @@ -31,7 +33,7 @@ pub enum ProofSystemError { SubProtocolAlreadyInitialized(usize), SubProtocolNotReadyToGenerateProof(usize), InvalidSetupParamsIndex(usize), - TooManyCifertexts(usize), + TooManyCiphertexts(usize), NeitherParamsNorRefGiven(usize), IncompatibleBBSPlusSetupParamAtIndex(usize), IncompatiblePSSetupParamAtIndex(usize), @@ -86,9 +88,20 @@ pub enum ProofSystemError { UnsupportedValue(String), /// For an arbitrary range proof, the response of both Schnorr protocols should be same DifferentResponsesForSchnorrProtocolInBpp(usize), - BulletproofsPlusPlus(bulletproofs_plus_plus::prelude::BulletproofsPlusPlusError), - SetMembershipBasedRangeProof(smc_range_proof::prelude::SmcRangeProofError), + BulletproofsPlusPlus(BulletproofsPlusPlusError), + SetMembershipBasedRangeProof(SmcRangeProofError), SmcParamsNotProvided, + SchnorrProofContributionFailed(u32, SchnorrError), + BBSPlusProofContributionFailed(u32, BBSPlusError), + BBSProofContributionFailed(u32, BBSPlusError), + VBAccumProofContributionFailed(u32, VBAccumulatorError), + SaverProofContributionFailed(u32, SaverError), + LegoSnarkProofContributionFailed(u32, LegoGroth16Error), + PSProofContributionFailed(u32, coconut_crypto::SignaturePoKError), + BulletproofsPlusPlusProofContributionFailed(u32, BulletproofsPlusPlusError), + SmcRangeProofContributionFailed(u32, SmcRangeProofError), + DetachedVBAccumProofContributionFailed(u32, VBAccumulatorError), + IncorrectEncryptedAccumulator, } impl From for ProofSystemError { @@ -139,14 +152,14 @@ impl From for ProofSystemError { } } -impl From for ProofSystemError { - fn from(e: bulletproofs_plus_plus::prelude::BulletproofsPlusPlusError) -> Self { +impl From for ProofSystemError { + fn from(e: BulletproofsPlusPlusError) -> Self { Self::BulletproofsPlusPlus(e) } } -impl From for ProofSystemError { - fn from(e: smc_range_proof::prelude::SmcRangeProofError) -> Self { +impl From for ProofSystemError { + fn from(e: SmcRangeProofError) -> Self { Self::SetMembershipBasedRangeProof(e) } } diff --git a/proof_system/src/lib.rs b/proof_system/src/lib.rs index 6273dfc0..5d6f97e6 100644 --- a/proof_system/src/lib.rs +++ b/proof_system/src/lib.rs @@ -160,6 +160,7 @@ extern crate core; pub mod setup_params; #[macro_use] mod derived_params; +mod constants; pub mod error; mod macros; pub mod meta_statement; diff --git a/proof_system/src/meta_statement.rs b/proof_system/src/meta_statement.rs index 4517de02..86bd4b27 100644 --- a/proof_system/src/meta_statement.rs +++ b/proof_system/src/meta_statement.rs @@ -9,8 +9,6 @@ use ark_std::{ }; use serde::{Deserialize, Serialize}; -pub use serialization::*; - /// Reference to a witness described as the tuple (`statement_id`, `witness_id`) pub type WitnessRef = (usize, usize); diff --git a/proof_system/src/proof.rs b/proof_system/src/proof.rs index e8f91266..3e625bf5 100644 --- a/proof_system/src/proof.rs +++ b/proof_system/src/proof.rs @@ -16,7 +16,6 @@ pub struct AggregatedGroth16 { #[serde(bound = "")] pub struct Proof { pub statement_proofs: Vec>, - pub nonce: Option>, // TODO: Remove this skip #[serde(skip)] pub aggregated_groth16: Option>>, @@ -27,7 +26,7 @@ pub struct Proof { impl PartialEq for Proof { fn eq(&self, other: &Self) -> bool { - (self.statement_proofs == other.statement_proofs) && (self.nonce == other.nonce) + self.statement_proofs == other.statement_proofs // TODO: Add remaining } } diff --git a/proof_system/src/proof_spec.rs b/proof_system/src/proof_spec.rs index a5093af0..861964e8 100644 --- a/proof_system/src/proof_spec.rs +++ b/proof_system/src/proof_spec.rs @@ -270,7 +270,7 @@ where s.get_chunked_commitment_gens(&self.setup_params, s_idx)?, s.chunk_bit_size, ), - _ => panic!("This should never happen"), + _ => unreachable!(), }; saver_comm_keys.insert(s_idx, (comm_gens, chunk_bit_size)); } @@ -293,7 +293,7 @@ where Statement::SaverVerifier(s) => { s.get_encryption_key(&self.setup_params, s_idx)? } - _ => panic!("This should never happen"), + _ => unreachable!(), }; derived_ek_comm.on_new_statement_idx(enc_key, s_idx); @@ -310,7 +310,7 @@ where Statement::BoundCheckLegoGroth16Verifier(s) => { s.get_verifying_key(&self.setup_params, s_idx)? } - _ => panic!("This should never happen"), + _ => unreachable!(), }; derived_bound_check_lego_comm.on_new_statement_idx(verifying_key, s_idx); } @@ -323,7 +323,7 @@ where Statement::R1CSCircomVerifier(s) => { s.get_verifying_key(&self.setup_params, s_idx)? } - _ => panic!("This should never happen"), + _ => unreachable!(), }; derived_r1cs_comm.on_new_statement_idx(verifying_key, s_idx); } @@ -342,7 +342,7 @@ where Statement::BoundCheckSmcWithKVVerifier(s) => { s.get_comm_key(&self.setup_params, s_idx)? } - _ => panic!("This should never happen"), + _ => unreachable!(), }; derived_bound_check_smc_comm.on_new_statement_idx(comm_key, s_idx); } diff --git a/proof_system/src/prover.rs b/proof_system/src/prover.rs index 6c3573c2..f6416e92 100644 --- a/proof_system/src/prover.rs +++ b/proof_system/src/prover.rs @@ -14,13 +14,20 @@ use digest::Digest; use legogroth16::aggregation::srs::PreparedProverSRS; use crate::{ + constants::{ + BBS_23_LABEL, BBS_PLUS_LABEL, COMPOSITE_PROOF_CHALLENGE_LABEL, COMPOSITE_PROOF_LABEL, + CONTEXT_LABEL, NONCE_LABEL, VB_ACCUM_MEM_LABEL, VB_ACCUM_NON_MEM_LABEL, + }, meta_statement::WitnessRef, prelude::SnarkpackSRS, proof::{AggregatedGroth16, Proof}, proof_spec::ProofSpec, statement_proof::StatementProof, sub_protocols::{ - accumulator::{AccumulatorMembershipSubProtocol, AccumulatorNonMembershipSubProtocol}, + accumulator::{ + AccumulatorMembershipSubProtocol, AccumulatorNonMembershipSubProtocol, + DetachedAccumulatorMembershipSubProtocol, DetachedAccumulatorNonMembershipSubProtocol, + }, bbs_23::PoKBBSSigG1SubProtocol, bbs_plus::PoKBBSSigG1SubProtocol as PoKBBSPlusSigG1SubProtocol, bound_check_bpp::BoundCheckBppProtocol, @@ -35,7 +42,7 @@ use crate::{ }; use dock_crypto_utils::{ hashing_utils::field_elem_from_try_and_incr, - transcript::{new_merlin_transcript, Transcript}, + transcript::{MerlinTranscript, Transcript}, }; use saver::encryption::Ciphertext; @@ -158,8 +165,13 @@ where // the same public params and witness can reuse this randomness let mut commitment_randomness = BTreeMap::::new(); - // TODO: Use this for all sub-proofs and not just Bulletproofs++ - let mut transcript = new_merlin_transcript(b"composite-proof"); + let mut transcript = MerlinTranscript::new(COMPOSITE_PROOF_LABEL); + if let Some(n) = nonce.as_ref() { + transcript.append_message(NONCE_LABEL, n); + } + if let Some(ctx) = &proof_spec.context { + transcript.append_message(CONTEXT_LABEL, ctx); + } // Initialize sub-protocols for each statement for (s_idx, (statement, witness)) in proof_spec @@ -189,6 +201,8 @@ where pk, ); sp.init(rng, blindings_map, w)?; + transcript.set_label(BBS_PLUS_LABEL); + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::PoKBBSSignatureG1(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -212,6 +226,8 @@ where pk, ); sp.init(rng, blindings_map, w)?; + transcript.set_label(BBS_23_LABEL); + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::PoKBBSSignature23G1(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -230,6 +246,8 @@ where s.accumulator_value, ); sp.init(rng, blinding, w)?; + transcript.set_label(VB_ACCUM_MEM_LABEL); + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::AccumulatorMembership(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -248,6 +266,8 @@ where s.accumulator_value, ); sp.init(rng, blinding, w)?; + transcript.set_label(VB_ACCUM_NON_MEM_LABEL); + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::AccumulatorNonMembership(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -264,6 +284,7 @@ where let comm_key = s.get_commitment_key(&proof_spec.setup_params, s_idx)?; let mut sp = SchnorrProtocol::new(s_idx, comm_key, s.commitment); sp.init(rng, blindings_map, w)?; + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::PoKDiscreteLogs(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -312,6 +333,7 @@ where .unwrap(), ); + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::Saver(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -350,6 +372,7 @@ where .unwrap(), ); + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::BoundCheckLegoGroth16(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -396,6 +419,8 @@ where .last() .unwrap(), ); + + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::R1CSLegogroth16Protocol(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -415,6 +440,7 @@ where let mut sp = PSSignaturePoK::new(s_idx, &s.revealed_messages, sig_params, pk); sp.init(rng, blindings_map, w)?; + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::PSSignaturePoK(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -427,7 +453,8 @@ where let comm_key = bound_check_bpp_comm.get(s_idx).unwrap(); let mut sp = BoundCheckBppProtocol::new(s_idx, s.min, s.max, bpp_setup_params); - sp.init(rng, comm_key.as_slice(), w, blinding, &mut transcript)?; + sp.init(rng, comm_key.as_slice(), w, blinding)?; + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::BoundCheckBpp(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -441,6 +468,7 @@ where let mut sp = BoundCheckSmcProtocol::new(s_idx, s.min, s.max, params_comm_key); sp.init(rng, comm_key_as_slice, w, blinding)?; + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::BoundCheckSmc(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -458,6 +486,7 @@ where params_comm_key, ); sp.init(rng, comm_key_as_slice, w, blinding)?; + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::BoundCheckSmcWithKV(sp)); } _ => err_incompat_witness!(s_idx, s, witness), @@ -469,10 +498,42 @@ where let mut sp = InequalityProtocol::new(s_idx, s.inequal_to.clone(), &comm_key); sp.init(rng, ineq_comm.get(s_idx).unwrap().as_slice(), w, blinding)?; + sp.challenge_contribution(&mut transcript)?; sub_protocols.push(SubProtocol::Inequality(sp)); } _ => err_incompat_witness!(s_idx, s, witness), }, + Statement::DetachedAccumulatorMembershipProver(s) => match witness { + Witness::AccumulatorMembership(w) => { + let blinding = blindings.remove(&(s_idx, 0)); + let params = s.get_params(&proof_spec.setup_params, s_idx)?; + let pk = s.get_public_key(&proof_spec.setup_params, s_idx)?; + let prk = s.get_proving_key(&proof_spec.setup_params, s_idx)?; + let mut sp = + DetachedAccumulatorMembershipSubProtocol::new(s_idx, params, pk, prk); + sp.init(rng, s.accumulator_value, blinding, w)?; + transcript.set_label(VB_ACCUM_MEM_LABEL); + sp.challenge_contribution(&mut transcript)?; + sub_protocols.push(SubProtocol::DetachedAccumulatorMembership(sp)); + } + _ => err_incompat_witness!(s_idx, s, witness), + }, + Statement::DetachedAccumulatorNonMembershipProver(s) => match witness { + Witness::AccumulatorNonMembership(w) => { + let blinding = blindings.remove(&(s_idx, 0)); + let params = s.get_params(&proof_spec.setup_params, s_idx)?; + let pk = s.get_public_key(&proof_spec.setup_params, s_idx)?; + let prk = s.get_proving_key(&proof_spec.setup_params, s_idx)?; + let mut sp = DetachedAccumulatorNonMembershipSubProtocol::new( + s_idx, params, pk, prk, + ); + sp.init(rng, s.accumulator_value, blinding, w)?; + transcript.set_label(VB_ACCUM_NON_MEM_LABEL); + sp.challenge_contribution(&mut transcript)?; + sub_protocols.push(SubProtocol::DetachedAccumulatorNonMembership(sp)); + } + _ => err_incompat_witness!(s_idx, s, witness), + }, _ => return Err(ProofSystemError::InvalidStatement), } } @@ -485,27 +546,47 @@ where )); } - // Get nonce's and context's challenge contribution - let mut challenge_bytes = vec![]; - if let Some(n) = nonce.as_ref() { - challenge_bytes.extend_from_slice(n) - } - if let Some(ctx) = &proof_spec.context { - challenge_bytes.extend_from_slice(ctx); - } - - // Get each sub-protocol's challenge contribution - for p in sub_protocols.iter() { - p.challenge_contribution(&mut challenge_bytes)?; - } - // Generate the challenge - let challenge = Self::generate_challenge_from_bytes::(&challenge_bytes); + let challenge = transcript.challenge_scalar(COMPOSITE_PROOF_CHALLENGE_LABEL); // Get each sub-protocol's proof let mut statement_proofs = Vec::with_capacity(sub_protocols.len()); - for mut p in sub_protocols { - statement_proofs.push(p.gen_proof_contribution(&challenge)?); + for p in sub_protocols { + statement_proofs.push(match p { + SubProtocol::PoKBBSSignatureG1(mut sp) => sp.gen_proof_contribution(&challenge)?, + SubProtocol::AccumulatorMembership(mut sp) => { + sp.gen_proof_contribution(&challenge)? + } + SubProtocol::AccumulatorNonMembership(mut sp) => { + sp.gen_proof_contribution(&challenge)? + } + SubProtocol::PoKDiscreteLogs(mut sp) => sp.gen_proof_contribution(&challenge)?, + SubProtocol::Saver(mut sp) => sp.gen_proof_contribution(&challenge)?, + SubProtocol::BoundCheckLegoGroth16(mut sp) => { + sp.gen_proof_contribution(&challenge)? + } + SubProtocol::R1CSLegogroth16Protocol(mut sp) => { + sp.gen_proof_contribution(&challenge)? + } + SubProtocol::PSSignaturePoK(mut sp) => sp.gen_proof_contribution(&challenge)?, + SubProtocol::PoKBBSSignature23G1(mut sp) => { + sp.gen_proof_contribution(&challenge)? + } + SubProtocol::BoundCheckBpp(mut sp) => { + sp.gen_proof_contribution(rng, &challenge, &mut transcript)? + } + SubProtocol::BoundCheckSmc(mut sp) => sp.gen_proof_contribution(&challenge)?, + SubProtocol::BoundCheckSmcWithKV(mut sp) => { + sp.gen_proof_contribution(&challenge)? + } + SubProtocol::Inequality(mut sp) => sp.gen_proof_contribution(&challenge)?, + SubProtocol::DetachedAccumulatorMembership(mut sp) => { + sp.gen_proof_contribution(rng, &challenge)? + } + SubProtocol::DetachedAccumulatorNonMembership(mut sp) => { + sp.gen_proof_contribution(rng, &challenge)? + } + }); } // TODO: Revisit - aggregating after challenge generation, is this correct? @@ -527,10 +608,6 @@ where }; let prepared_srs = PreparedProverSRS::from(srs); - // TODO: Remove it and use outer transcript - let mut aggr_transcript = new_merlin_transcript(b"aggregation"); - aggr_transcript.append(b"challenge", &challenge); - if proof_spec.aggregate_groth16.is_some() { let to_aggr = proof_spec.aggregate_groth16.unwrap(); let mut proofs = vec![]; @@ -544,7 +621,7 @@ where } let ag_proof = legogroth16::aggregation::groth16::aggregate_proofs( prepared_srs.clone(), - &mut aggr_transcript, + &mut transcript, &proofs, ) .map_err(|e| ProofSystemError::LegoGroth16Error(e.into()))?; @@ -570,7 +647,7 @@ where let (ag_proof, _) = legogroth16::aggregation::legogroth16::using_groth16::aggregate_proofs( prepared_srs.clone(), - &mut aggr_transcript, + &mut transcript, &proofs, ) .map_err(|e| ProofSystemError::LegoGroth16Error(e.into()))?; @@ -585,7 +662,6 @@ where Ok(( Self { statement_proofs, - nonce, aggregated_groth16: if !aggregated_groth16.is_empty() { Some(aggregated_groth16) } else { @@ -611,10 +687,6 @@ where &self.statement_proofs } - pub fn nonce(&self) -> &Option> { - &self.nonce - } - /// Hash bytes to a field element. This is vulnerable to timing attack and is only used input /// is public anyway like when generating setup parameters or challenge pub fn generate_challenge_from_bytes(bytes: &[u8]) -> E::ScalarField { @@ -662,7 +734,6 @@ where } Self { statement_proofs, - nonce: self.nonce.clone(), aggregated_groth16: self.aggregated_groth16.clone(), aggregated_legogroth16: self.aggregated_legogroth16.clone(), } diff --git a/proof_system/src/statement/accumulator.rs b/proof_system/src/statement/accumulator.rs index c90268d0..a6756708 100644 --- a/proof_system/src/statement/accumulator.rs +++ b/proof_system/src/statement/accumulator.rs @@ -45,6 +45,58 @@ pub struct AccumulatorNonMembership { pub proving_key_ref: Option, } +macro_rules! impl_common_funcs { + ( $key_type:ident, $setup_param_variant:ident) => { + /// Get accumulator params for the statement index `s_idx` either from `self` or from given `setup_params` + pub fn get_params<'a, G: AffineRepr>( + &'a self, + setup_params: &'a [SetupParams], + st_idx: usize, + ) -> Result<&'a AccumParams, ProofSystemError> { + extract_param!( + setup_params, + &self.params, + self.params_ref, + VbAccumulatorParams, + IncompatibleAccumulatorSetupParamAtIndex, + st_idx + ) + } + + /// Get public key for the statement index `s_idx` either from `self` or from given `setup_params` + pub fn get_public_key<'a, G: AffineRepr>( + &'a self, + setup_params: &'a [SetupParams], + st_idx: usize, + ) -> Result<&'a PublicKey, ProofSystemError> { + extract_param!( + setup_params, + &self.public_key, + self.public_key_ref, + VbAccumulatorPublicKey, + IncompatibleAccumulatorSetupParamAtIndex, + st_idx + ) + } + + /// Get membership proving key for the statement index `s_idx` either from `self` or from given `setup_params` + pub fn get_proving_key<'a, G: AffineRepr>( + &'a self, + setup_params: &'a [SetupParams], + st_idx: usize, + ) -> Result<&'a $key_type, ProofSystemError> { + extract_param!( + setup_params, + &self.proving_key, + self.proving_key_ref, + $setup_param_variant, + IncompatibleAccumulatorSetupParamAtIndex, + st_idx + ) + } + }; +} + impl AccumulatorMembership { /// Create a statement by passing the accumulator params, public key and proving key directly. pub fn new_statement_from_params( @@ -82,63 +134,178 @@ impl AccumulatorMembership { }) } - /// Get accumulator params for the statement index `s_idx` either from `self` or from given `setup_params` - pub fn get_params<'a, G: AffineRepr>( - &'a self, - setup_params: &'a [SetupParams], - st_idx: usize, - ) -> Result<&'a AccumParams, ProofSystemError> { - extract_param!( - setup_params, - &self.params, - self.params_ref, - VbAccumulatorParams, - IncompatibleAccumulatorSetupParamAtIndex, - st_idx - ) + impl_common_funcs!(MembershipProvingKey, VbAccumulatorMemProvingKey); +} + +impl AccumulatorNonMembership { + pub fn new_statement_from_params( + params: AccumParams, + public_key: PublicKey, + proving_key: NonMembershipProvingKey, + accumulator_value: E::G1Affine, + ) -> Statement { + Statement::AccumulatorNonMembership(Self { + accumulator_value, + params: Some(params), + public_key: Some(public_key), + proving_key: Some(proving_key), + params_ref: None, + public_key_ref: None, + proving_key_ref: None, + }) } - /// Get publci key for the statement index `s_idx` either from `self` or from given `setup_params` - pub fn get_public_key<'a, G: AffineRepr>( - &'a self, - setup_params: &'a [SetupParams], - st_idx: usize, - ) -> Result<&'a PublicKey, ProofSystemError> { - extract_param!( - setup_params, - &self.public_key, - self.public_key_ref, - VbAccumulatorPublicKey, - IncompatibleAccumulatorSetupParamAtIndex, - st_idx - ) + pub fn new_statement_from_params_ref( + params_ref: usize, + public_key_ref: usize, + proving_key_ref: usize, + accumulator_value: E::G1Affine, + ) -> Statement { + Statement::AccumulatorNonMembership(Self { + accumulator_value, + params: None, + public_key: None, + proving_key: None, + params_ref: Some(params_ref), + public_key_ref: Some(public_key_ref), + proving_key_ref: Some(proving_key_ref), + }) } - /// Get membership proving key for the statement index `s_idx` either from `self` or from given `setup_params` - pub fn get_proving_key<'a, G: AffineRepr>( - &'a self, - setup_params: &'a [SetupParams], - st_idx: usize, - ) -> Result<&'a MembershipProvingKey, ProofSystemError> { - extract_param!( - setup_params, - &self.proving_key, - self.proving_key_ref, - VbAccumulatorMemProvingKey, - IncompatibleAccumulatorSetupParamAtIndex, - st_idx - ) + impl_common_funcs!(NonMembershipProvingKey, VbAccumulatorNonMemProvingKey); +} + +#[serde_as] +#[derive( + Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +#[serde(bound = "")] +pub struct DetachedAccumulatorMembershipProver { + #[serde_as(as = "ArkObjectBytes")] + pub accumulator_value: E::G1Affine, + pub params: Option>, + pub public_key: Option>, + pub proving_key: Option>, + pub params_ref: Option, + pub public_key_ref: Option, + pub proving_key_ref: Option, +} + +impl DetachedAccumulatorMembershipProver { + /// Create a statement by passing the accumulator params, public key and proving key directly. + pub fn new_statement_from_params( + params: AccumParams, + public_key: PublicKey, + proving_key: MembershipProvingKey, + accumulator_value: E::G1Affine, + ) -> Statement { + Statement::DetachedAccumulatorMembershipProver(Self { + accumulator_value, + params: Some(params), + public_key: Some(public_key), + proving_key: Some(proving_key), + params_ref: None, + public_key_ref: None, + proving_key_ref: None, + }) + } + + /// Create a statement by passing the indices of accumulator params, public key and proving key in `SetupParams`. + pub fn new_statement_from_params_ref( + params_ref: usize, + public_key_ref: usize, + proving_key_ref: usize, + accumulator_value: E::G1Affine, + ) -> Statement { + Statement::DetachedAccumulatorMembershipProver(Self { + accumulator_value, + params: None, + public_key: None, + proving_key: None, + params_ref: Some(params_ref), + public_key_ref: Some(public_key_ref), + proving_key_ref: Some(proving_key_ref), + }) } + + impl_common_funcs!(MembershipProvingKey, VbAccumulatorMemProvingKey); } -impl AccumulatorNonMembership { +#[serde_as] +#[derive( + Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +#[serde(bound = "")] +pub struct DetachedAccumulatorMembershipVerifier { + pub params: Option>, + pub public_key: Option>, + pub proving_key: Option>, + pub params_ref: Option, + pub public_key_ref: Option, + pub proving_key_ref: Option, +} + +impl DetachedAccumulatorMembershipVerifier { + /// Create a statement by passing the accumulator params, public key and proving key directly. + pub fn new_statement_from_params( + params: AccumParams, + public_key: PublicKey, + proving_key: MembershipProvingKey, + ) -> Statement { + Statement::DetachedAccumulatorMembershipVerifier(Self { + params: Some(params), + public_key: Some(public_key), + proving_key: Some(proving_key), + params_ref: None, + public_key_ref: None, + proving_key_ref: None, + }) + } + + /// Create a statement by passing the indices of accumulator params, public key and proving key in `SetupParams`. + pub fn new_statement_from_params_ref( + params_ref: usize, + public_key_ref: usize, + proving_key_ref: usize, + ) -> Statement { + Statement::DetachedAccumulatorMembershipVerifier(Self { + params: None, + public_key: None, + proving_key: None, + params_ref: Some(params_ref), + public_key_ref: Some(public_key_ref), + proving_key_ref: Some(proving_key_ref), + }) + } + + impl_common_funcs!(MembershipProvingKey, VbAccumulatorMemProvingKey); +} + +#[serde_as] +#[derive( + Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +#[serde(bound = "")] +pub struct DetachedAccumulatorNonMembershipProver { + #[serde_as(as = "ArkObjectBytes")] + pub accumulator_value: E::G1Affine, + pub params: Option>, + pub public_key: Option>, + pub proving_key: Option>, + pub params_ref: Option, + pub public_key_ref: Option, + pub proving_key_ref: Option, +} + +impl DetachedAccumulatorNonMembershipProver { + /// Create a statement by passing the accumulator params, public key and proving key directly. pub fn new_statement_from_params( params: AccumParams, public_key: PublicKey, proving_key: NonMembershipProvingKey, accumulator_value: E::G1Affine, ) -> Statement { - Statement::AccumulatorNonMembership(Self { + Statement::DetachedAccumulatorNonMembershipProver(Self { accumulator_value, params: Some(params), public_key: Some(public_key), @@ -149,13 +316,14 @@ impl AccumulatorNonMembership { }) } + /// Create a statement by passing the indices of accumulator params, public key and proving key in `SetupParams`. pub fn new_statement_from_params_ref( params_ref: usize, public_key_ref: usize, proving_key_ref: usize, accumulator_value: E::G1Affine, ) -> Statement { - Statement::AccumulatorNonMembership(Self { + Statement::DetachedAccumulatorNonMembershipProver(Self { accumulator_value, params: None, public_key: None, @@ -166,48 +334,55 @@ impl AccumulatorNonMembership { }) } - pub fn get_params<'a, G: AffineRepr>( - &'a self, - setup_params: &'a [SetupParams], - st_idx: usize, - ) -> Result<&'a AccumParams, ProofSystemError> { - extract_param!( - setup_params, - &self.params, - self.params_ref, - VbAccumulatorParams, - IncompatibleAccumulatorSetupParamAtIndex, - st_idx - ) - } + impl_common_funcs!(NonMembershipProvingKey, VbAccumulatorNonMemProvingKey); +} + +#[serde_as] +#[derive( + Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +#[serde(bound = "")] +pub struct DetachedAccumulatorNonMembershipVerifier { + pub params: Option>, + pub public_key: Option>, + pub proving_key: Option>, + pub params_ref: Option, + pub public_key_ref: Option, + pub proving_key_ref: Option, +} - pub fn get_public_key<'a, G: AffineRepr>( - &'a self, - setup_params: &'a [SetupParams], - st_idx: usize, - ) -> Result<&'a PublicKey, ProofSystemError> { - extract_param!( - setup_params, - &self.public_key, - self.public_key_ref, - VbAccumulatorPublicKey, - IncompatibleAccumulatorSetupParamAtIndex, - st_idx - ) +impl DetachedAccumulatorNonMembershipVerifier { + /// Create a statement by passing the accumulator params, public key and proving key directly. + pub fn new_statement_from_params( + params: AccumParams, + public_key: PublicKey, + proving_key: NonMembershipProvingKey, + ) -> Statement { + Statement::DetachedAccumulatorNonMembershipVerifier(Self { + params: Some(params), + public_key: Some(public_key), + proving_key: Some(proving_key), + params_ref: None, + public_key_ref: None, + proving_key_ref: None, + }) } - pub fn get_proving_key<'a, G: AffineRepr>( - &'a self, - setup_params: &'a [SetupParams], - st_idx: usize, - ) -> Result<&'a NonMembershipProvingKey, ProofSystemError> { - extract_param!( - setup_params, - &self.proving_key, - self.proving_key_ref, - VbAccumulatorNonMemProvingKey, - IncompatibleAccumulatorSetupParamAtIndex, - st_idx - ) + /// Create a statement by passing the indices of accumulator params, public key and proving key in `SetupParams`. + pub fn new_statement_from_params_ref( + params_ref: usize, + public_key_ref: usize, + proving_key_ref: usize, + ) -> Statement { + Statement::DetachedAccumulatorNonMembershipVerifier(Self { + params: None, + public_key: None, + proving_key: None, + params_ref: Some(params_ref), + public_key_ref: Some(public_key_ref), + proving_key_ref: Some(proving_key_ref), + }) } + + impl_common_funcs!(NonMembershipProvingKey, VbAccumulatorNonMemProvingKey); } diff --git a/proof_system/src/statement/mod.rs b/proof_system/src/statement/mod.rs index 985b37a6..2a20cc06 100644 --- a/proof_system/src/statement/mod.rs +++ b/proof_system/src/statement/mod.rs @@ -20,8 +20,6 @@ pub mod ps_signature; pub mod r1cs_legogroth16; pub mod saver; -pub use serialization::*; - /// Type of relation being proved and the public values for the relation #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(bound = "")] @@ -60,6 +58,12 @@ pub enum Statement { BoundCheckSmcWithKVVerifier(bound_check_smc_with_kv::BoundCheckSmcWithKVVerifier), /// To prove inequality of a signed message with a public value PublicInequality(inequality::PublicInequality), + DetachedAccumulatorMembershipProver(accumulator::DetachedAccumulatorMembershipProver), + DetachedAccumulatorMembershipVerifier(accumulator::DetachedAccumulatorMembershipVerifier), + DetachedAccumulatorNonMembershipProver(accumulator::DetachedAccumulatorNonMembershipProver), + DetachedAccumulatorNonMembershipVerifier( + accumulator::DetachedAccumulatorNonMembershipVerifier, + ), } /// A collection of statements @@ -115,7 +119,11 @@ macro_rules! delegate { BoundCheckSmc, BoundCheckSmcWithKVProver, BoundCheckSmcWithKVVerifier, - PublicInequality + PublicInequality, + DetachedAccumulatorMembershipProver, + DetachedAccumulatorMembershipVerifier, + DetachedAccumulatorNonMembershipProver, + DetachedAccumulatorNonMembershipVerifier : $($tt)+ } }} @@ -141,7 +149,11 @@ macro_rules! delegate_reverse { BoundCheckSmc, BoundCheckSmcWithKVProver, BoundCheckSmcWithKVVerifier, - PublicInequality + PublicInequality, + DetachedAccumulatorMembershipProver, + DetachedAccumulatorMembershipVerifier, + DetachedAccumulatorNonMembershipProver, + DetachedAccumulatorNonMembershipVerifier : $($tt)+ } diff --git a/proof_system/src/statement_proof.rs b/proof_system/src/statement_proof.rs index a86ba493..94c9eb72 100644 --- a/proof_system/src/statement_proof.rs +++ b/proof_system/src/statement_proof.rs @@ -7,7 +7,7 @@ use ark_std::{ use bbs_plus::prelude::{PoKOfSignature23G1Proof, PoKOfSignatureG1Proof}; use bulletproofs_plus_plus::prelude::ProofArbitraryRange; use coconut_crypto::SignaturePoK as PSSignaturePoK; -use dock_crypto_utils::serde_utils::*; +use dock_crypto_utils::{ecies, serde_utils::*}; use saver::encryption::Ciphertext; use schnorr_pok::SchnorrResponse; use serde::{Deserialize, Serialize}; @@ -15,7 +15,6 @@ use serde_with::serde_as; use vb_accumulator::prelude::{MembershipProof, NonMembershipProof}; use crate::error::ProofSystemError; -pub use serialization::*; /// Proof corresponding to one `Statement` #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -37,6 +36,8 @@ pub enum StatementProof { BoundCheckSmc(BoundCheckSmcProof), BoundCheckSmcWithKV(BoundCheckSmcWithKVProof), Inequality(InequalityProof), + DetachedAccumulatorMembership(DetachedAccumulatorMembershipProof), + DetachedAccumulatorNonMembership(DetachedAccumulatorNonMembershipProof), } macro_rules! delegate { @@ -58,7 +59,9 @@ macro_rules! delegate { BoundCheckBpp, BoundCheckSmc, BoundCheckSmcWithKV, - Inequality + Inequality, + DetachedAccumulatorMembership, + DetachedAccumulatorNonMembership : $($tt)+ } }}; @@ -83,7 +86,9 @@ macro_rules! delegate_reverse { BoundCheckBpp, BoundCheckSmc, BoundCheckSmcWithKV, - Inequality + Inequality, + DetachedAccumulatorMembership, + DetachedAccumulatorNonMembership : $($tt)+ } @@ -356,6 +361,40 @@ impl InequalityProof { } } +#[serde_as] +#[derive( + Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +#[serde(bound = "")] +pub struct DetachedAccumulatorMembershipProof { + #[serde_as(as = "ArkObjectBytes")] + pub accumulator: E::G1Affine, + pub accum_proof: MembershipProof, + #[serde_as(as = "ArkObjectBytes")] + pub challenge: E::ScalarField, + /// Encrypted opening + // TODO: Make constants as generic + #[serde_as(as = "ArkObjectBytes")] + pub encrypted: ecies::Encryption, +} + +#[serde_as] +#[derive( + Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +#[serde(bound = "")] +pub struct DetachedAccumulatorNonMembershipProof { + #[serde_as(as = "ArkObjectBytes")] + pub accumulator: E::G1Affine, + pub accum_proof: NonMembershipProof, + #[serde_as(as = "ArkObjectBytes")] + pub challenge: E::ScalarField, + /// Encrypted opening + // TODO: Make constants as generic + #[serde_as(as = "ArkObjectBytes")] + pub encrypted: ecies::Encryption, +} + mod serialization { use super::{ AffineRepr, CanonicalDeserialize, CanonicalSerialize, Pairing, Read, SerializationError, @@ -473,63 +512,4 @@ mod serialization { impl_serz_for_bound_check_inner!(BoundCheckSmcInnerProof); impl_serz_for_bound_check_inner!(BoundCheckSmcWithKVInnerProof); - - /*impl Valid for BoundCheckSmcInnerProof { - fn check(&self) -> Result<(), SerializationError> { - match self { - Self::CCS(c) => c.check(), - Self::CLS(c) => c.check(), - } - } - } - - impl CanonicalSerialize for BoundCheckSmcInnerProof { - fn serialize_with_mode( - &self, - mut writer: W, - compress: Compress, - ) -> Result<(), SerializationError> { - match self { - Self::CCS(c) => { - CanonicalSerialize::serialize_with_mode(&0u8, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(c, &mut writer, compress) - } - Self::CLS(c) => { - CanonicalSerialize::serialize_with_mode(&1u8, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(c, &mut writer, compress) - } - } - } - - fn serialized_size(&self, compress: Compress) -> usize { - match self { - Self::CCS(c) => 0u8.serialized_size(compress) + c.serialized_size(compress), - Self::CLS(c) => 1u8.serialized_size(compress) + c.serialized_size(compress), - } - } - } - - impl CanonicalDeserialize for BoundCheckSmcInnerProof { - fn deserialize_with_mode( - mut reader: R, - compress: Compress, - validate: Validate, - ) -> Result { - let t: u8 = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - match t { - 0u8 => Ok(Self::CCS(CanonicalDeserialize::deserialize_with_mode( - &mut reader, - compress, - validate, - )?)), - 1u8 => Ok(Self::CLS(CanonicalDeserialize::deserialize_with_mode( - &mut reader, - compress, - validate, - )?)), - _ => Err(SerializationError::InvalidData), - } - } - }*/ } diff --git a/proof_system/src/sub_protocols/accumulator.rs b/proof_system/src/sub_protocols/accumulator.rs index f07f9d09..e1cc330f 100644 --- a/proof_system/src/sub_protocols/accumulator.rs +++ b/proof_system/src/sub_protocols/accumulator.rs @@ -1,7 +1,15 @@ -use crate::{error::ProofSystemError, statement_proof::StatementProof}; -use ark_ec::{pairing::Pairing, AffineRepr}; -use ark_std::{io::Write, rand::RngCore}; -use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; +use crate::{ + error::ProofSystemError, + statement_proof::{ + DetachedAccumulatorMembershipProof, DetachedAccumulatorNonMembershipProof, StatementProof, + }, +}; +use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; + +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec, vec::Vec, UniformRand}; +use chacha20poly1305::XChaCha20Poly1305; +use dock_crypto_utils::{ecies, randomized_pairing_check::RandomizedPairingChecker}; use vb_accumulator::prelude::{ MembershipProof, MembershipProofProtocol, MembershipProvingKey, NonMembershipProof, NonMembershipProofProtocol, NonMembershipProvingKey, PreparedPublicKey, PreparedSetupParams, @@ -28,6 +36,121 @@ pub struct AccumulatorNonMembershipSubProtocol<'a, E: Pairing> { pub protocol: Option>, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DetachedAccumulatorMembershipSubProtocol<'a, E: Pairing> { + pub id: usize, + pub params: &'a AccumParams, + pub public_key: &'a PublicKey, + pub proving_key: &'a MembershipProvingKey, + pub original_accumulator_value: Option, + pub randomized_accumulator_value: Option, + pub randomizer: Option, + pub protocol: Option>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DetachedAccumulatorNonMembershipSubProtocol<'a, E: Pairing> { + pub id: usize, + pub params: &'a AccumParams, + pub public_key: &'a PublicKey, + pub proving_key: &'a NonMembershipProvingKey, + pub original_accumulator_value: Option, + pub randomized_accumulator_value: Option, + pub randomizer: Option, + pub protocol: Option>, +} + +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Opening { + pub original_accumulator: G, + pub randomizer: G::ScalarField, + pub extra: Option>, +} + +macro_rules! impl_common_funcs { + ( $wit_type:ident, $wit_protocol:ident, $proof_enum_variant:ident, $proof_typ: ident) => { + pub fn init( + &mut self, + rng: &mut R, + blinding: Option, + witness: crate::witness::$wit_type, + ) -> Result<(), ProofSystemError> { + if self.protocol.is_some() { + return Err(ProofSystemError::SubProtocolAlreadyInitialized(self.id)); + } + let protocol = $wit_protocol::init( + rng, + &witness.element, + blinding, + &witness.witness, + self.public_key, + self.params, + self.proving_key, + ); + self.protocol = Some(protocol); + Ok(()) + } + + pub fn challenge_contribution(&self, writer: W) -> Result<(), ProofSystemError> { + if self.protocol.is_none() { + return Err(ProofSystemError::SubProtocolNotReadyToGenerateChallenge( + self.id, + )); + } + self.protocol.as_ref().unwrap().challenge_contribution( + &self.accumulator_value, + self.public_key, + self.params, + self.proving_key, + writer, + )?; + Ok(()) + } + + pub fn gen_proof_contribution( + &mut self, + challenge: &E::ScalarField, + ) -> Result, ProofSystemError> { + if self.protocol.is_none() { + return Err(ProofSystemError::SubProtocolNotReadyToGenerateProof( + self.id, + )); + } + let protocol = self.protocol.take().unwrap(); + let proof = protocol.gen_proof(challenge); + Ok(StatementProof::$proof_enum_variant(proof)) + } + + pub fn verify_proof_contribution( + &self, + challenge: &E::ScalarField, + proof: &$proof_typ, + pk: impl Into>, + params: impl Into>, + pairing_checker: &mut Option>, + ) -> Result<(), ProofSystemError> { + match pairing_checker { + Some(c) => proof.verify_with_randomized_pairing_checker( + &self.accumulator_value, + challenge, + pk, + params, + self.proving_key, + c, + ), + None => proof.verify( + &self.accumulator_value, + challenge, + pk, + params, + self.proving_key, + ), + } + .map_err(|e| ProofSystemError::VBAccumProofContributionFailed(self.id as u32, e)) + } + }; +} + impl<'a, E: Pairing> AccumulatorMembershipSubProtocol<'a, E> { pub fn new( id: usize, @@ -46,25 +169,86 @@ impl<'a, E: Pairing> AccumulatorMembershipSubProtocol<'a, E> { } } + impl_common_funcs!( + Membership, + MembershipProofProtocol, + AccumulatorMembership, + MembershipProof + ); +} + +impl<'a, E: Pairing> AccumulatorNonMembershipSubProtocol<'a, E> { + pub fn new( + id: usize, + params: &'a AccumParams, + public_key: &'a PublicKey, + proving_key: &'a NonMembershipProvingKey, + accumulator_value: E::G1Affine, + ) -> Self { + Self { + id, + params, + public_key, + proving_key, + accumulator_value, + protocol: None, + } + } + + impl_common_funcs!( + NonMembership, + NonMembershipProofProtocol, + AccumulatorNonMembership, + NonMembershipProof + ); +} + +impl<'a, E: Pairing> DetachedAccumulatorMembershipSubProtocol<'a, E> { + pub fn new( + id: usize, + params: &'a AccumParams, + public_key: &'a PublicKey, + proving_key: &'a MembershipProvingKey, + ) -> Self { + Self { + id, + params, + public_key, + proving_key, + original_accumulator_value: None, + randomizer: None, + randomized_accumulator_value: None, + protocol: None, + } + } + pub fn init( &mut self, rng: &mut R, + accumulator_value: E::G1Affine, blinding: Option, witness: crate::witness::Membership, ) -> Result<(), ProofSystemError> { if self.protocol.is_some() { return Err(ProofSystemError::SubProtocolAlreadyInitialized(self.id)); } + // Randomize the accumulator and witness with the same value + let randomizer = E::ScalarField::rand(rng); + let randomized_accumulator_value = (accumulator_value * randomizer).into_affine(); + let randomized_accum_witness = witness.witness.randomize(&randomizer); let protocol = MembershipProofProtocol::init( rng, &witness.element, blinding, - &witness.witness, + &randomized_accum_witness, self.public_key, self.params, self.proving_key, ); self.protocol = Some(protocol); + self.original_accumulator_value = Some(accumulator_value); + self.randomizer = Some(randomizer); + self.randomized_accumulator_value = Some(randomized_accumulator_value); Ok(()) } @@ -75,7 +259,7 @@ impl<'a, E: Pairing> AccumulatorMembershipSubProtocol<'a, E> { )); } self.protocol.as_ref().unwrap().challenge_contribution( - &self.accumulator_value, + self.randomized_accumulator_value.as_ref().take().unwrap(), self.public_key, self.params, self.proving_key, @@ -84,8 +268,9 @@ impl<'a, E: Pairing> AccumulatorMembershipSubProtocol<'a, E> { Ok(()) } - pub fn gen_proof_contribution( + pub fn gen_proof_contribution( &mut self, + rng: &mut R, challenge: &E::ScalarField, ) -> Result, ProofSystemError> { if self.protocol.is_none() { @@ -94,53 +279,83 @@ impl<'a, E: Pairing> AccumulatorMembershipSubProtocol<'a, E> { )); } let protocol = self.protocol.take().unwrap(); - let proof = protocol.gen_proof(challenge); - Ok(StatementProof::AccumulatorMembership(proof)) + let accum_proof = protocol.gen_proof(challenge); + // Encrypt the original accumulator value and the randomizer + let opening = Opening { + original_accumulator: self.original_accumulator_value.unwrap(), + randomizer: self.randomizer.unwrap(), + extra: None, + }; + let mut opening_bytes = vec![]; + opening.serialize_compressed(&mut opening_bytes).unwrap(); + let encrypted = ecies::Encryption::encrypt::( + rng, + &opening_bytes, + &self.public_key.0, + &self.params.P_tilde, + None, + None, + ); + Ok(StatementProof::DetachedAccumulatorMembership( + DetachedAccumulatorMembershipProof { + accumulator: self.randomized_accumulator_value.unwrap(), + accum_proof, + challenge: *challenge, + encrypted, + }, + )) } pub fn verify_proof_contribution( &self, - challenge: &E::ScalarField, - proof: &MembershipProof, + proof: &DetachedAccumulatorMembershipProof, + sk: &vb_accumulator::setup::SecretKey, pk: impl Into>, params: impl Into>, - pairing_checker: &mut Option>, ) -> Result<(), ProofSystemError> { - match pairing_checker { - Some(c) => proof.verify_with_randomized_pairing_checker( - &self.accumulator_value, - challenge, - pk, - params, - self.proving_key, - c, - )?, - None => proof.verify( - &self.accumulator_value, - challenge, + // Decrypt the opening + let decrypted = proof + .encrypted + .clone() + .decrypt::(&sk.0, None, None); + let opening: Opening = + CanonicalDeserialize::deserialize_compressed(decrypted.as_slice()).unwrap(); + proof + .accum_proof + .verify( + &proof.accumulator, + &proof.challenge, pk, params, self.proving_key, - )?, + ) + .map_err(|e| { + ProofSystemError::DetachedVBAccumProofContributionFailed(self.id as u32, e) + })?; + // Check that the randomized accumulator is consistent with the original accumulator + if (opening.original_accumulator * opening.randomizer).into_affine() != proof.accumulator { + Err(ProofSystemError::IncorrectEncryptedAccumulator) + } else { + Ok(()) } - Ok(()) } } -impl<'a, E: Pairing> AccumulatorNonMembershipSubProtocol<'a, E> { +impl<'a, E: Pairing> DetachedAccumulatorNonMembershipSubProtocol<'a, E> { pub fn new( id: usize, params: &'a AccumParams, public_key: &'a PublicKey, proving_key: &'a NonMembershipProvingKey, - accumulator_value: E::G1Affine, ) -> Self { Self { id, params, public_key, proving_key, - accumulator_value, + original_accumulator_value: None, + randomizer: None, + randomized_accumulator_value: None, protocol: None, } } @@ -148,22 +363,30 @@ impl<'a, E: Pairing> AccumulatorNonMembershipSubProtocol<'a, E> { pub fn init( &mut self, rng: &mut R, + accumulator_value: E::G1Affine, blinding: Option, witness: crate::witness::NonMembership, ) -> Result<(), ProofSystemError> { if self.protocol.is_some() { return Err(ProofSystemError::SubProtocolAlreadyInitialized(self.id)); } + // Randomize the accumulator and witness with the same value + let randomizer = E::ScalarField::rand(rng); + let randomized_accumulator_value = (accumulator_value * randomizer).into_affine(); + let randomized_accum_witness = witness.witness.randomize(&randomizer); let protocol = NonMembershipProofProtocol::init( rng, &witness.element, blinding, - &witness.witness, + &randomized_accum_witness, self.public_key, self.params, self.proving_key, ); self.protocol = Some(protocol); + self.original_accumulator_value = Some(accumulator_value); + self.randomizer = Some(randomizer); + self.randomized_accumulator_value = Some(randomized_accumulator_value); Ok(()) } @@ -174,7 +397,7 @@ impl<'a, E: Pairing> AccumulatorNonMembershipSubProtocol<'a, E> { )); } self.protocol.as_ref().unwrap().challenge_contribution( - &self.accumulator_value, + self.randomized_accumulator_value.as_ref().take().unwrap(), self.public_key, self.params, self.proving_key, @@ -183,8 +406,9 @@ impl<'a, E: Pairing> AccumulatorNonMembershipSubProtocol<'a, E> { Ok(()) } - pub fn gen_proof_contribution( + pub fn gen_proof_contribution( &mut self, + rng: &mut R, challenge: &E::ScalarField, ) -> Result, ProofSystemError> { if self.protocol.is_none() { @@ -193,35 +417,64 @@ impl<'a, E: Pairing> AccumulatorNonMembershipSubProtocol<'a, E> { )); } let protocol = self.protocol.take().unwrap(); - let proof = protocol.gen_proof(challenge); - Ok(StatementProof::AccumulatorNonMembership(proof)) + let accum_proof = protocol.gen_proof(challenge); + // Encrypt the original accumulator value and the randomizer + let opening = Opening { + original_accumulator: self.original_accumulator_value.unwrap(), + randomizer: self.randomizer.unwrap(), + extra: None, + }; + let mut opening_bytes = vec![]; + opening.serialize_compressed(&mut opening_bytes).unwrap(); + let encrypted = ecies::Encryption::encrypt::( + rng, + &opening_bytes, + &self.public_key.0, + &self.params.P_tilde, + None, + None, + ); + Ok(StatementProof::DetachedAccumulatorNonMembership( + DetachedAccumulatorNonMembershipProof { + accumulator: self.randomized_accumulator_value.unwrap(), + accum_proof, + challenge: *challenge, + encrypted, + }, + )) } pub fn verify_proof_contribution( &self, - challenge: &E::ScalarField, - proof: &NonMembershipProof, + proof: &DetachedAccumulatorNonMembershipProof, + sk: &vb_accumulator::setup::SecretKey, pk: impl Into>, params: impl Into>, - pairing_checker: &mut Option>, ) -> Result<(), ProofSystemError> { - match pairing_checker { - Some(c) => proof.verify_with_randomized_pairing_checker( - &self.accumulator_value, - challenge, + // Decrypt the opening + let decrypted = proof + .encrypted + .clone() + .decrypt::(&sk.0, None, None); + let opening: Opening = + CanonicalDeserialize::deserialize_compressed(decrypted.as_slice()).unwrap(); + proof + .accum_proof + .verify( + &proof.accumulator, + &proof.challenge, pk, params, self.proving_key, - c, - )?, - None => proof.verify( - &self.accumulator_value, - challenge, - pk, - params, - self.proving_key, - )?, + ) + .map_err(|e| { + ProofSystemError::DetachedVBAccumProofContributionFailed(self.id as u32, e) + })?; + // Check that the randomized accumulator is consistent with the original accumulator + if (opening.original_accumulator * opening.randomizer).into_affine() != proof.accumulator { + Err(ProofSystemError::IncorrectEncryptedAccumulator) + } else { + Ok(()) } - Ok(()) } } diff --git a/proof_system/src/sub_protocols/bbs_23.rs b/proof_system/src/sub_protocols/bbs_23.rs index 716e7503..8d883229 100644 --- a/proof_system/src/sub_protocols/bbs_23.rs +++ b/proof_system/src/sub_protocols/bbs_23.rs @@ -2,8 +2,9 @@ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_std::{collections::BTreeMap, io::Write, rand::RngCore}; use bbs_plus::{ prelude::{ - MultiMessageSignatureParams, PoKOfSignature23G1Proof, PoKOfSignature23G1Protocol, - PreparedPublicKeyG2, PreparedSignatureParams23G1, PublicKeyG2, SignatureParams23G1, + BBSPlusError, MultiMessageSignatureParams, PoKOfSignature23G1Proof, + PoKOfSignature23G1Protocol, PreparedPublicKeyG2, PreparedSignatureParams23G1, PublicKeyG2, + SignatureParams23G1, }, proof::MessageOrBlinding, }; diff --git a/proof_system/src/sub_protocols/bbs_plus.rs b/proof_system/src/sub_protocols/bbs_plus.rs index 58ecf795..68088cff 100644 --- a/proof_system/src/sub_protocols/bbs_plus.rs +++ b/proof_system/src/sub_protocols/bbs_plus.rs @@ -1,6 +1,7 @@ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_std::{collections::BTreeMap, io::Write, rand::RngCore}; use bbs_plus::{ + error::BBSPlusError, prelude::{ MultiMessageSignatureParams, PoKOfSignatureG1Proof, PreparedPublicKeyG2, PreparedSignatureParamsG1, PublicKeyG2, SignatureParamsG1, @@ -134,7 +135,7 @@ macro_rules! impl_bbs_subprotocol { pk: impl Into>, params: impl Into<$prepared_params>, pairing_checker: &mut Option>, - ) -> Result<(), ProofSystemError> { + ) -> Result<(), BBSPlusError> { match pairing_checker { Some(c) => proof.verify_with_randomized_pairing_checker( self.revealed_messages, @@ -142,10 +143,9 @@ macro_rules! impl_bbs_subprotocol { pk, params, c, - )?, - None => proof.verify(self.revealed_messages, challenge, pk, params)?, + ), + None => proof.verify(self.revealed_messages, challenge, pk, params), } - Ok(()) } }; } diff --git a/proof_system/src/sub_protocols/bound_check_bpp.rs b/proof_system/src/sub_protocols/bound_check_bpp.rs index 2d57ab42..bdc3389f 100644 --- a/proof_system/src/sub_protocols/bound_check_bpp.rs +++ b/proof_system/src/sub_protocols/bound_check_bpp.rs @@ -6,8 +6,11 @@ use crate::{ }; use ark_ec::{pairing::Pairing, AffineRepr}; use ark_serialize::CanonicalSerialize; -use ark_std::{collections::BTreeMap, io::Write, rand::RngCore, vec, UniformRand}; -use bulletproofs_plus_plus::{prelude::ProofArbitraryRange, setup::SetupParams}; +use ark_std::{collections::BTreeMap, io::Write, rand::RngCore, vec, vec::Vec, UniformRand}; +use bulletproofs_plus_plus::{ + prelude::{ProofArbitraryRange, Prover}, + setup::SetupParams, +}; use dock_crypto_utils::transcript::Transcript; /// Runs the Bulletproofs++ protocol for proving bounds of a witness and a Schnorr protocol for proving @@ -18,7 +21,10 @@ pub struct BoundCheckBppProtocol<'a, G: AffineRepr> { pub min: u64, pub max: u64, pub setup_params: &'a SetupParams, - pub bpp_proof: Option>, + pub commitments: Option>, + pub bpp_randomness: Option>, + pub values: Option>, + // pub bpp_proof: Option>, pub sp1: Option>, pub sp2: Option>, } @@ -30,7 +36,10 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { min, max, setup_params, - bpp_proof: None, + commitments: None, + bpp_randomness: None, + values: None, + // bpp_proof: None, sp1: None, sp2: None, } @@ -42,7 +51,7 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { comm_key: &'a [G], message: G::ScalarField, blinding: Option, - transcript: &mut impl Transcript, + // transcript: &mut impl Transcript, ) -> Result<(), ProofSystemError> { if self.sp1.is_some() || self.sp2.is_some() { return Err(ProofSystemError::SubProtocolAlreadyInitialized(self.id)); @@ -51,23 +60,23 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { // blindings for the commitments in the Bulletproofs++ proof, there will be 2 Bulletproofs++ proofs, for ranges `(message - min)` and `(max - message)` let bpp_randomness = vec![G::ScalarField::rand(rng), G::ScalarField::rand(rng)]; - let proof = ProofArbitraryRange::new( - rng, - Self::get_num_bits(self.max), + let (commitments, values) = ProofArbitraryRange::compute_commitments_and_values( vec![(msg_as_u64, self.min, self.max)], - bpp_randomness.clone(), - self.setup_params.clone(), - transcript, + &bpp_randomness, + &self.setup_params, )?; - assert_eq!(proof.num_proofs(), 1); self.init_schnorr_protocol( rng, comm_key, message, blinding, (bpp_randomness[0], bpp_randomness[1]), - proof, - ) + &commitments, + )?; + self.values = Some(values); + self.commitments = Some(commitments); + self.bpp_randomness = Some(bpp_randomness); + Ok(()) } fn init_schnorr_protocol( @@ -77,7 +86,7 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { message: G::ScalarField, blinding: Option, blindings_for_bpp: (G::ScalarField, G::ScalarField), - bpp_proof: ProofArbitraryRange, + commitments: &[G], ) -> Result<(), ProofSystemError> { // blinding used to prove knowledge of message in `snark_proof.d`. The caller of this method ensures // that this will be same as the one used proving knowledge of the corresponding message in BBS+ @@ -91,13 +100,20 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { blindings.insert(0, blinding); let (r1, r2) = blindings_for_bpp; - let (comm_1, comm_2) = self.get_commitments_to_values(&bpp_proof)?; + // let (comm_1, comm_2) = self.get_commitments_to_values(&bpp_proof)?; + let (comm_1, comm_2) = + (ProofArbitraryRange::get_commitments_to_values_given_transformed_commitments_and_g( + commitments, + vec![(self.min, self.max)], + &self.setup_params.G, + )?) + .remove(0); // NOTE: value of id is dummy let mut sp1 = SchnorrProtocol::new(10000, comm_key, comm_1); let mut sp2 = SchnorrProtocol::new(10000, comm_key, comm_2); sp1.init(rng, blindings.clone(), vec![message, r1])?; sp2.init(rng, blindings, vec![message, -r2])?; - self.bpp_proof = Some(bpp_proof); + // self.bpp_proof = Some(bpp_proof); self.sp1 = Some(sp1); self.sp2 = Some(sp2); Ok(()) @@ -122,17 +138,30 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { } /// Generate responses for both the Schnorr protocols - pub fn gen_proof_contribution( + pub fn gen_proof_contribution( &mut self, + rng: &mut R, challenge: &G::ScalarField, + transcript: &mut impl Transcript, ) -> Result, ProofSystemError> { if self.sp1.is_none() || self.sp2.is_none() { return Err(ProofSystemError::SubProtocolNotReadyToGenerateProof( self.id, )); } + let commitments = self.commitments.take().unwrap(); + let prover = Prover::new( + Self::get_num_bits(self.max), + commitments.clone(), + self.values.take().unwrap(), + self.bpp_randomness.take().unwrap(), + )?; + let proof = prover.prove(rng, self.setup_params.clone(), transcript)?; Ok(StatementProof::BoundCheckBpp(BoundCheckBppProof { - bpp_proof: self.bpp_proof.take().unwrap(), + bpp_proof: ProofArbitraryRange { + proof, + V: commitments, + }, sp1: self .sp1 .take() @@ -155,7 +184,10 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { ) -> Result<(), ProofSystemError> { proof .bpp_proof - .verify(Self::get_num_bits(self.max), &self.setup_params, transcript)?; + .verify(Self::get_num_bits(self.max), &self.setup_params, transcript) + .map_err(|e| { + ProofSystemError::BulletproofsPlusPlusProofContributionFailed(self.id as u32, e) + })?; if !proof.check_schnorr_responses_consistency()? { return Err(ProofSystemError::DifferentResponsesForSchnorrProtocolInBpp( self.id, @@ -167,8 +199,10 @@ impl<'a, G: AffineRepr> BoundCheckBppProtocol<'a, G> { let sp1 = SchnorrProtocol::new(10000, comm_key, comm_1); let sp2 = SchnorrProtocol::new(10000, comm_key, comm_2); - sp1.verify_proof_contribution_as_struct(challenge, &proof.sp1)?; - sp2.verify_proof_contribution_as_struct(challenge, &proof.sp2) + sp1.verify_proof_contribution(challenge, &proof.sp1) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e))?; + sp2.verify_proof_contribution(challenge, &proof.sp2) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/sub_protocols/bound_check_legogroth16.rs b/proof_system/src/sub_protocols/bound_check_legogroth16.rs index 6c4c072f..0d639238 100644 --- a/proof_system/src/sub_protocols/bound_check_legogroth16.rs +++ b/proof_system/src/sub_protocols/bound_check_legogroth16.rs @@ -199,13 +199,16 @@ impl<'a, E: Pairing> BoundCheckLegoGrothProtocol<'a, E> { &pvk.alpha_g1_beta_g2, ); } - None => verify_proof(pvk, snark_proof, pub_inp)?, + None => verify_proof(pvk, snark_proof, pub_inp).map_err(|e| { + ProofSystemError::LegoSnarkProofContributionFailed(self.id as u32, e) + })?, } // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key, proof.snark_proof.d); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn verify_proof_contribution_using_prepared_when_aggregating_snark( @@ -216,7 +219,8 @@ impl<'a, E: Pairing> BoundCheckLegoGrothProtocol<'a, E> { ) -> Result<(), ProofSystemError> { // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key, proof.commitment); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/sub_protocols/bound_check_smc.rs b/proof_system/src/sub_protocols/bound_check_smc.rs index 6ee557f9..f921011f 100644 --- a/proof_system/src/sub_protocols/bound_check_smc.rs +++ b/proof_system/src/sub_protocols/bound_check_smc.rs @@ -188,49 +188,66 @@ impl<'a, E: Pairing> BoundCheckSmcProtocol<'a, E> { let comm_key = &self.params_and_comm_key.comm_key; match &proof.proof { BoundCheckSmcInnerProof::CCS(c) => match pairing_checker { - Some(pc) => c.verify_given_randomized_pairing_checker( - &proof.comm, - challenge, - self.min, - self.max, - comm_key, - params.params, - pc, - )?, - None => c.verify( - &proof.comm, - challenge, - self.min, - self.max, - comm_key, - params.params, - )?, + Some(pc) => c + .verify_given_randomized_pairing_checker( + &proof.comm, + challenge, + self.min, + self.max, + comm_key, + params.params, + pc, + ) + .map_err(|e| { + ProofSystemError::SmcRangeProofContributionFailed(self.id as u32, e) + })?, + None => c + .verify( + &proof.comm, + challenge, + self.min, + self.max, + comm_key, + params.params, + ) + .map_err(|e| { + ProofSystemError::SmcRangeProofContributionFailed(self.id as u32, e) + })?, }, BoundCheckSmcInnerProof::CLS(c) => match pairing_checker { - Some(pc) => c.verify_given_randomized_pairing_checker( - &proof.comm, - challenge, - self.min, - self.max, - comm_key, - params.params, - pc, - )?, - None => c.verify( - &proof.comm, - challenge, - self.min, - self.max, - comm_key, - params.params, - )?, + Some(pc) => c + .verify_given_randomized_pairing_checker( + &proof.comm, + challenge, + self.min, + self.max, + comm_key, + params.params, + pc, + ) + .map_err(|e| { + ProofSystemError::SmcRangeProofContributionFailed(self.id as u32, e) + })?, + None => c + .verify( + &proof.comm, + challenge, + self.min, + self.max, + comm_key, + params.params, + ) + .map_err(|e| { + ProofSystemError::SmcRangeProofContributionFailed(self.id as u32, e) + })?, }, } // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key_as_slice, proof.comm); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/sub_protocols/bound_check_smc_with_kv.rs b/proof_system/src/sub_protocols/bound_check_smc_with_kv.rs index d975aaa9..02a32350 100644 --- a/proof_system/src/sub_protocols/bound_check_smc_with_kv.rs +++ b/proof_system/src/sub_protocols/bound_check_smc_with_kv.rs @@ -216,30 +216,37 @@ impl<'a, E: Pairing> BoundCheckSmcWithKVProtocol<'a, E> { .ok_or(ProofSystemError::SmcParamsNotProvided)?; let comm_key = params.get_comm_key(); match &proof.proof { - BoundCheckSmcWithKVInnerProof::CCS(c) => c.verify( - &proof.comm, - challenge, - self.min, - self.max, - comm_key, - params.get_smc_params(), - ¶ms.sk, - )?, - BoundCheckSmcWithKVInnerProof::CLS(c) => c.verify( - &proof.comm, - challenge, - self.min, - self.max, - comm_key, - params.get_smc_params(), - ¶ms.sk, - )?, + BoundCheckSmcWithKVInnerProof::CCS(c) => { + c.verify( + &proof.comm, + challenge, + self.min, + self.max, + comm_key, + params.get_smc_params(), + ¶ms.sk, + ) + .map_err(|e| ProofSystemError::SmcRangeProofContributionFailed(self.id as u32, e))? + } + BoundCheckSmcWithKVInnerProof::CLS(c) => { + c.verify( + &proof.comm, + challenge, + self.min, + self.max, + comm_key, + params.get_smc_params(), + ¶ms.sk, + ) + .map_err(|e| ProofSystemError::SmcRangeProofContributionFailed(self.id as u32, e))? + } } // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key_as_slice, proof.comm); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/sub_protocols/inequality.rs b/proof_system/src/sub_protocols/inequality.rs index 1b4ec9ab..6730fbd9 100644 --- a/proof_system/src/sub_protocols/inequality.rs +++ b/proof_system/src/sub_protocols/inequality.rs @@ -137,16 +137,20 @@ impl<'a, G: AffineRepr> InequalityProtocol<'a, G> { proof: &InequalityProof, comm_key_as_slice: &[G], ) -> Result<(), ProofSystemError> { - proof.proof.verify_for_inequality_with_public_value( - &proof.comm, - &self.inequal_to, - challenge, - &self.comm_key, - )?; + proof + .proof + .verify_for_inequality_with_public_value( + &proof.comm, + &self.inequal_to, + challenge, + &self.comm_key, + ) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e))?; // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key_as_slice, proof.comm); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/sub_protocols/mod.rs b/proof_system/src/sub_protocols/mod.rs index e485a640..83497a69 100644 --- a/proof_system/src/sub_protocols/mod.rs +++ b/proof_system/src/sub_protocols/mod.rs @@ -20,15 +20,16 @@ use ark_ff::PrimeField; use ark_std::{format, io::Write}; use itertools::{EitherOrBoth, Itertools}; -use crate::{ - statement_proof::StatementProof, - sub_protocols::{ - bound_check_bpp::BoundCheckBppProtocol, - bound_check_legogroth16::BoundCheckLegoGrothProtocol, - bound_check_smc::BoundCheckSmcProtocol, - bound_check_smc_with_kv::BoundCheckSmcWithKVProtocol, inequality::InequalityProtocol, - r1cs_legogorth16::R1CSLegogroth16Protocol, +use crate::sub_protocols::{ + accumulator::{ + DetachedAccumulatorMembershipSubProtocol, DetachedAccumulatorNonMembershipSubProtocol, }, + bound_check_bpp::BoundCheckBppProtocol, + bound_check_legogroth16::BoundCheckLegoGrothProtocol, + bound_check_smc::BoundCheckSmcProtocol, + bound_check_smc_with_kv::BoundCheckSmcWithKVProtocol, + inequality::InequalityProtocol, + r1cs_legogorth16::R1CSLegogroth16Protocol, }; use accumulator::{AccumulatorMembershipSubProtocol, AccumulatorNonMembershipSubProtocol}; @@ -42,7 +43,7 @@ pub enum SubProtocol<'a, E: Pairing, G: AffineRepr> { AccumulatorNonMembership(AccumulatorNonMembershipSubProtocol<'a, E>), PoKDiscreteLogs(self::schnorr::SchnorrProtocol<'a, G>), /// For verifiable encryption using SAVER - Saver(self::saver::SaverProtocol<'a, E>), + Saver(saver::SaverProtocol<'a, E>), /// For range proof using LegoGroth16 BoundCheckLegoGroth16(BoundCheckLegoGrothProtocol<'a, E>), R1CSLegogroth16Protocol(R1CSLegogroth16Protocol<'a, E>), @@ -57,6 +58,8 @@ pub enum SubProtocol<'a, E: Pairing, G: AffineRepr> { BoundCheckSmcWithKV(BoundCheckSmcWithKVProtocol<'a, E>), /// To prove inequality of a signed message with a public value Inequality(InequalityProtocol<'a, G>), + DetachedAccumulatorMembership(DetachedAccumulatorMembershipSubProtocol<'a, E>), + DetachedAccumulatorNonMembership(DetachedAccumulatorNonMembershipSubProtocol<'a, E>), } macro_rules! delegate { @@ -75,31 +78,18 @@ macro_rules! delegate { BoundCheckBpp, BoundCheckSmc, BoundCheckSmcWithKV, - Inequality + Inequality, + DetachedAccumulatorMembership, + DetachedAccumulatorNonMembership : $($tt)+ } }}; } -pub trait ProofSubProtocol> { - fn challenge_contribution(&self, target: &mut [u8]) -> Result<(), ProofSystemError>; - fn gen_proof_contribution( - &mut self, - challenge: &E::ScalarField, - ) -> Result, ProofSystemError>; -} - impl<'a, E: Pairing, G: AffineRepr> SubProtocol<'a, E, G> { pub fn challenge_contribution(&self, writer: W) -> Result<(), ProofSystemError> { delegate!(self.challenge_contribution(writer)) } - - pub fn gen_proof_contribution( - &mut self, - challenge: &E::ScalarField, - ) -> Result, ProofSystemError> { - delegate!(self.gen_proof_contribution(challenge)) - } } /// Merges indexed messages sorted by index with indexed blindings sorted by index. diff --git a/proof_system/src/sub_protocols/ps_signature.rs b/proof_system/src/sub_protocols/ps_signature.rs index 090d712b..cd8c9cc2 100644 --- a/proof_system/src/sub_protocols/ps_signature.rs +++ b/proof_system/src/sub_protocols/ps_signature.rs @@ -130,19 +130,23 @@ impl<'a, E: Pairing> PSSignaturePoK<'a, E> { pairing_checker: &mut Option>, ) -> Result<(), ProofSystemError> { match pairing_checker { - Some(c) => proof.verify_with_randomized_pairing_checker( - challenge, - self.revealed_messages.iter().map(|(idx, msg)| (*idx, msg)), - &pk.into(), - ¶ms.into(), - c, - )?, - None => proof.verify( - challenge, - self.revealed_messages.iter().map(|(idx, msg)| (*idx, msg)), - &pk.into(), - ¶ms.into(), - )?, + Some(c) => proof + .verify_with_randomized_pairing_checker( + challenge, + self.revealed_messages.iter().map(|(idx, msg)| (*idx, msg)), + &pk.into(), + ¶ms.into(), + c, + ) + .map_err(|e| ProofSystemError::PSProofContributionFailed(self.id as u32, e))?, + None => proof + .verify( + challenge, + self.revealed_messages.iter().map(|(idx, msg)| (*idx, msg)), + &pk.into(), + ¶ms.into(), + ) + .map_err(|e| ProofSystemError::PSProofContributionFailed(self.id as u32, e))?, } Ok(()) } diff --git a/proof_system/src/sub_protocols/r1cs_legogorth16.rs b/proof_system/src/sub_protocols/r1cs_legogorth16.rs index cc441c3a..d53476da 100644 --- a/proof_system/src/sub_protocols/r1cs_legogorth16.rs +++ b/proof_system/src/sub_protocols/r1cs_legogorth16.rs @@ -184,13 +184,16 @@ impl<'a, E: Pairing> R1CSLegogroth16Protocol<'a, E> { &pvk.alpha_g1_beta_g2, ); } - None => verify_proof(pvk, &proof.snark_proof, inputs)?, + None => verify_proof(pvk, &proof.snark_proof, inputs).map_err(|e| { + ProofSystemError::LegoSnarkProofContributionFailed(self.id as u32, e) + })?, } // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key, proof.snark_proof.d); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn verify_proof_contribution_using_prepared_when_aggregating_snark( @@ -201,7 +204,8 @@ impl<'a, E: Pairing> R1CSLegogroth16Protocol<'a, E> { ) -> Result<(), ProofSystemError> { // NOTE: value of id is dummy let sp = SchnorrProtocol::new(10000, comm_key, proof.commitment); - sp.verify_proof_contribution_as_struct(challenge, &proof.sp) + sp.verify_proof_contribution(challenge, &proof.sp) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/sub_protocols/saver.rs b/proof_system/src/sub_protocols/saver.rs index 524a0ee8..f2a1fef3 100644 --- a/proof_system/src/sub_protocols/saver.rs +++ b/proof_system/src/sub_protocols/saver.rs @@ -1,6 +1,8 @@ use crate::{ error::ProofSystemError, - statement_proof::{SaverProof, SaverProofWhenAggregatingSnarks, StatementProof}, + statement_proof::{ + PedersenCommitmentProof, SaverProof, SaverProofWhenAggregatingSnarks, StatementProof, + }, sub_protocols::schnorr::SchnorrProtocol, }; use ark_ec::{ @@ -278,39 +280,53 @@ impl<'a, E: Pairing> SaverProtocol<'a, E> { &PairingOutput(pvk.alpha_g1_beta_g2), ); } - None => { - proof - .ciphertext - .verify_commitment_and_proof(&proof.snark_proof, pvk, pek, pgens)? - } + None => proof + .ciphertext + .verify_commitment_and_proof(&proof.snark_proof, pvk, pek, pgens) + .map_err(|e| ProofSystemError::SaverProofContributionFailed(self.id as u32, e))?, } - // NOTE: value of id is dummy - let sp_ciphertext = SchnorrProtocol::new(10000, ck_comm_ct, proof.ciphertext.commitment); - let sp_chunks = SchnorrProtocol::new(10000, ck_comm_chunks, proof.comm_chunks); - let sp_combined = SchnorrProtocol::new(10000, ck_comm_combined, proof.comm_combined); - - sp_ciphertext.verify_proof_contribution_as_struct(challenge, &proof.sp_ciphertext)?; - sp_chunks.verify_proof_contribution_as_struct(challenge, &proof.sp_chunks)?; - sp_combined.verify_proof_contribution_as_struct(challenge, &proof.sp_combined) + self.verify_ciphertext_and_commitment( + challenge, + &proof.ciphertext, + proof.comm_combined.clone(), + proof.comm_chunks.clone(), + &proof.sp_ciphertext, + &proof.sp_chunks, + &proof.sp_combined, + ck_comm_ct, + ck_comm_chunks, + ck_comm_combined, + ) } - pub fn verify_proof_contribution_when_aggregating_snark( + pub fn verify_ciphertext_and_commitment( &self, challenge: &E::ScalarField, - proof: &SaverProofWhenAggregatingSnarks, + ciphertext: &Ciphertext, + comm_combined: E::G1Affine, + comm_chunks: E::G1Affine, + s_pr_ciphertext: &PedersenCommitmentProof, + s_pr_chunks: &PedersenCommitmentProof, + s_pr_combined: &PedersenCommitmentProof, ck_comm_ct: &[E::G1Affine], ck_comm_chunks: &[E::G1Affine], ck_comm_combined: &[E::G1Affine], ) -> Result<(), ProofSystemError> { // NOTE: value of id is dummy - let sp_ciphertext = SchnorrProtocol::new(10000, ck_comm_ct, proof.ciphertext.commitment); - let sp_chunks = SchnorrProtocol::new(10000, ck_comm_chunks, proof.comm_chunks); - let sp_combined = SchnorrProtocol::new(10000, ck_comm_combined, proof.comm_combined); - - sp_ciphertext.verify_proof_contribution_as_struct(challenge, &proof.sp_ciphertext)?; - sp_chunks.verify_proof_contribution_as_struct(challenge, &proof.sp_chunks)?; - sp_combined.verify_proof_contribution_as_struct(challenge, &proof.sp_combined) + let sp_ciphertext = SchnorrProtocol::new(10000, ck_comm_ct, ciphertext.commitment); + let sp_chunks = SchnorrProtocol::new(10000, ck_comm_chunks, comm_chunks); + let sp_combined = SchnorrProtocol::new(10000, ck_comm_combined, comm_combined); + + sp_ciphertext + .verify_proof_contribution(challenge, s_pr_ciphertext) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e))?; + sp_chunks + .verify_proof_contribution(challenge, s_pr_chunks) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e))?; + sp_combined + .verify_proof_contribution(challenge, s_pr_combined) + .map_err(|e| ProofSystemError::SchnorrProofContributionFailed(self.id as u32, e)) } pub fn verify_ciphertext_commitments_in_batch( @@ -326,7 +342,7 @@ impl<'a, E: Pairing> SaverProtocol<'a, E> { ciphertexts .len() .try_into() - .map_err(|_| ProofSystemError::TooManyCifertexts(ciphertexts.len()))?, + .map_err(|_| ProofSystemError::TooManyCiphertexts(ciphertexts.len()))?, ); let pek = pek.into(); let pgens = pgens.into(); diff --git a/proof_system/src/sub_protocols/schnorr.rs b/proof_system/src/sub_protocols/schnorr.rs index 027528fd..246dc330 100644 --- a/proof_system/src/sub_protocols/schnorr.rs +++ b/proof_system/src/sub_protocols/schnorr.rs @@ -13,6 +13,7 @@ use crate::{ #[cfg(feature = "parallel")] use rayon::prelude::*; +use schnorr_pok::error::SchnorrError; #[derive(Clone, Debug, PartialEq, Eq)] pub struct SchnorrProtocol<'a, G: AffineRepr> { @@ -95,28 +96,14 @@ impl<'a, G: AffineRepr> SchnorrProtocol<'a, G> { Ok(PedersenCommitmentProof::new(commitment.t, responses)) } - pub fn verify_proof_contribution( - &self, - challenge: &G::ScalarField, - proof: &StatementProof, - ) -> Result<(), ProofSystemError> { - match proof { - StatementProof::PedersenCommitment(p) => { - self.verify_proof_contribution_as_struct(challenge, p) - } - _ => Err(ProofSystemError::ProofIncompatibleWithSchnorrProtocol), - } - } - - pub fn verify_proof_contribution_as_struct( + pub fn verify_proof_contribution( &self, challenge: &G::ScalarField, proof: &PedersenCommitmentProof, - ) -> Result<(), ProofSystemError> { + ) -> Result<(), SchnorrError> { proof .response .is_valid(self.commitment_key, &self.commitment, &proof.t, challenge) - .map_err(|e| e.into()) } pub fn compute_challenge_contribution( diff --git a/proof_system/src/verifier.rs b/proof_system/src/verifier.rs index 4d435629..5965e6fd 100644 --- a/proof_system/src/verifier.rs +++ b/proof_system/src/verifier.rs @@ -1,4 +1,8 @@ use crate::{ + constants::{ + BBS_23_LABEL, BBS_PLUS_LABEL, COMPOSITE_PROOF_CHALLENGE_LABEL, COMPOSITE_PROOF_LABEL, + CONTEXT_LABEL, NONCE_LABEL, VB_ACCUM_MEM_LABEL, VB_ACCUM_NON_MEM_LABEL, + }, error::ProofSystemError, proof::Proof, proof_spec::{ProofSpec, SnarkpackSRS}, @@ -26,7 +30,7 @@ use bbs_plus::prelude::MultiMessageSignatureParams; use digest::Digest; use dock_crypto_utils::{ randomized_pairing_check::RandomizedPairingChecker, - transcript::{new_merlin_transcript, Transcript}, + transcript::{MerlinTranscript, Transcript}, }; use saver::encryption::Ciphertext; @@ -49,7 +53,7 @@ macro_rules! err_incompat_proof { } macro_rules! check_resp_for_equalities { - ($witness_equalities:ident, $s_idx: ident, $p: ident, $func_name: ident, $self: ident, $responses_for_equalities: ident) => { + ($witness_equalities:ident, $s_idx: ident, $p: expr, $func_name: ident, $self: ident, $responses_for_equalities: ident) => { for i in 0..$witness_equalities.len() { // Check witness equalities for this statement. As there is only 1 witness // of interest, its index is always 0 @@ -68,7 +72,7 @@ macro_rules! check_resp_for_equalities { } macro_rules! check_resp_for_equalities_with_err { - ($witness_equalities:ident, $s_idx: ident, $p: ident, $func_name: ident, $self: ident, $responses_for_equalities: ident) => { + ($witness_equalities:ident, $s_idx: ident, $p: expr, $func_name: ident, $self: ident, $responses_for_equalities: ident) => { for i in 0..$witness_equalities.len() { // Check witness equalities for this statement. As there is only 1 witness // of interest, its index is always 0 @@ -126,8 +130,7 @@ where )); } - // TODO: Use this for all sub-proofs and not just Bulletproofs++ - let mut transcript = new_merlin_transcript(b"composite-proof"); + let mut transcript = MerlinTranscript::new(COMPOSITE_PROOF_LABEL); // TODO: Check SNARK SRSs compatible when aggregating and statement proof compatible with proof spec when aggregating @@ -202,12 +205,11 @@ where vec![None; witness_equalities.len()]; // Get nonce's and context's challenge contribution - let mut challenge_bytes = vec![]; if let Some(n) = nonce.as_ref() { - challenge_bytes.extend_from_slice(n) + transcript.append_message(NONCE_LABEL, n); } if let Some(ctx) = &proof_spec.context { - challenge_bytes.extend_from_slice(ctx); + transcript.append_message(CONTEXT_LABEL, ctx); } // Get challenge contribution for each statement and check if response is equal for all witnesses. @@ -239,10 +241,11 @@ where } } } + transcript.set_label(BBS_PLUS_LABEL); p.challenge_contribution( &s.revealed_messages, sig_params, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -267,10 +270,11 @@ where } } } + transcript.set_label(BBS_23_LABEL); p.challenge_contribution( &s.revealed_messages, sig_params, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -288,12 +292,13 @@ where let params = s.get_params(&proof_spec.setup_params, s_idx)?; let pk = s.get_public_key(&proof_spec.setup_params, s_idx)?; let prk = s.get_proving_key(&proof_spec.setup_params, s_idx)?; + transcript.set_label(VB_ACCUM_MEM_LABEL); p.challenge_contribution( &s.accumulator_value, pk, params, prk, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -311,12 +316,13 @@ where let params = s.get_params(&proof_spec.setup_params, s_idx)?; let pk = s.get_public_key(&proof_spec.setup_params, s_idx)?; let prk = s.get_proving_key(&proof_spec.setup_params, s_idx)?; + transcript.set_label(VB_ACCUM_NON_MEM_LABEL); p.challenge_contribution( &s.accumulator_value, pk, params, prk, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -344,7 +350,7 @@ where comm_key, &s.commitment, &p.t, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -366,7 +372,7 @@ where &cc_keys.0, &cc_keys.1, p, - &mut challenge_bytes, + &mut transcript, )?; } StatementProof::SaverWithAggregation(p) => { @@ -385,7 +391,7 @@ where &cc_keys.0, &cc_keys.1, p, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -405,7 +411,7 @@ where BoundCheckLegoGrothProtocol::compute_challenge_contribution( comm_key, p, - &mut challenge_bytes, + &mut transcript, )?; } StatementProof::BoundCheckLegoGroth16WithAggregation(p) => { @@ -422,7 +428,7 @@ where BoundCheckLegoGrothProtocol::compute_challenge_contribution_when_aggregating_snark( comm_key, p, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -449,7 +455,7 @@ where R1CSLegogroth16Protocol::compute_challenge_contribution( r1cs_comm_keys.get(s_idx).unwrap(), p, - &mut challenge_bytes, + &mut transcript, )?; } StatementProof::R1CSLegoGroth16WithAggregation(p) => { @@ -471,7 +477,7 @@ where R1CSLegogroth16Protocol::compute_challenge_contribution_when_aggregating_snark( r1cs_comm_keys.get(s_idx).unwrap(), p, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -502,7 +508,7 @@ where } } } - p.challenge_contribution(&mut challenge_bytes, pk, sig_params)?; + p.challenge_contribution(&mut transcript, pk, sig_params)?; } _ => err_incompat_proof!(s_idx, s, proof), }, @@ -523,7 +529,7 @@ where s.max, comm_key.as_slice(), p, - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -544,7 +550,7 @@ where comm_key_slice.as_slice(), p, derived_smc_param.get(s_idx).unwrap().clone(), - &mut challenge_bytes, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -565,7 +571,7 @@ where comm_key_slice.as_slice(), p, s.get_params_and_comm_key_and_sk(&proof_spec.setup_params, s_idx)?, - &mut challenge_bytes, + &mut transcript, )? } _ => err_incompat_proof!(s_idx, s, proof), @@ -587,7 +593,55 @@ where p, &s.inequal_to, s.get_comm_key(&proof_spec.setup_params, s_idx)?, - &mut challenge_bytes, + &mut transcript, + )?; + } + _ => err_incompat_proof!(s_idx, s, proof), + }, + Statement::DetachedAccumulatorMembershipVerifier(s) => match proof { + StatementProof::DetachedAccumulatorMembership(p) => { + check_resp_for_equalities!( + witness_equalities, + s_idx, + p.accum_proof, + get_schnorr_response_for_element, + Self, + responses_for_equalities + ); + let params = s.get_params(&proof_spec.setup_params, s_idx)?; + let pk = s.get_public_key(&proof_spec.setup_params, s_idx)?; + let prk = s.get_proving_key(&proof_spec.setup_params, s_idx)?; + transcript.set_label(VB_ACCUM_MEM_LABEL); + p.accum_proof.challenge_contribution( + &p.accumulator, + pk, + params, + prk, + &mut transcript, + )?; + } + _ => err_incompat_proof!(s_idx, s, proof), + }, + Statement::DetachedAccumulatorNonMembershipVerifier(s) => match proof { + StatementProof::DetachedAccumulatorNonMembership(p) => { + check_resp_for_equalities!( + witness_equalities, + s_idx, + p.accum_proof, + get_schnorr_response_for_element, + Self, + responses_for_equalities + ); + let params = s.get_params(&proof_spec.setup_params, s_idx)?; + let pk = s.get_public_key(&proof_spec.setup_params, s_idx)?; + let prk = s.get_proving_key(&proof_spec.setup_params, s_idx)?; + transcript.set_label(VB_ACCUM_NON_MEM_LABEL); + p.accum_proof.challenge_contribution( + &p.accumulator, + pk, + params, + prk, + &mut transcript, )?; } _ => err_incompat_proof!(s_idx, s, proof), @@ -612,7 +666,7 @@ where } // Verifier independently generates challenge - let challenge = Self::generate_challenge_from_bytes::(&challenge_bytes); + let challenge = transcript.challenge_scalar(COMPOSITE_PROOF_CHALLENGE_LABEL); // Verify the proof for each statement for (s_idx, (statement, proof)) in proof_spec @@ -639,7 +693,10 @@ where derived_bbs_pk.get(s_idx).unwrap().clone(), derived_bbs_plus_param.get(s_idx).unwrap().clone(), &mut pairing_checker, - )? + ) + .map_err(|e| { + ProofSystemError::BBSPlusProofContributionFailed(s_idx as u32, e) + })? } _ => err_incompat_proof!(s_idx, s, proof), }, @@ -659,7 +716,10 @@ where derived_bbs_pk.get(s_idx).unwrap().clone(), derived_bbs_param.get(s_idx).unwrap().clone(), &mut pairing_checker, - )? + ) + .map_err(|e| { + ProofSystemError::BBSProofContributionFailed(s_idx as u32, e) + })? } _ => err_incompat_proof!(s_idx, s, proof), }, @@ -708,10 +768,12 @@ where _ => err_incompat_proof!(s_idx, s, proof), }, Statement::PedersenCommitment(s) => match proof { - StatementProof::PedersenCommitment(ref _p) => { + StatementProof::PedersenCommitment(ref p) => { let comm_key = s.get_commitment_key(&proof_spec.setup_params, s_idx)?; let sp = SchnorrProtocol::new(s_idx, comm_key, s.commitment); - sp.verify_proof_contribution(&challenge, &proof)? + sp.verify_proof_contribution(&challenge, p).map_err(|e| { + ProofSystemError::SchnorrProofContributionFailed(s_idx as u32, e) + })? } _ => err_incompat_proof!(s_idx, s, proof), }, @@ -749,9 +811,14 @@ where ProofSystemError::InvalidStatementProofIndex(s_idx) })?; agg_saver[*agg_idx].push(saver_proof.ciphertext.clone()); - sp.verify_proof_contribution_when_aggregating_snark( + sp.verify_ciphertext_and_commitment( &challenge, - saver_proof, + &saver_proof.ciphertext, + saver_proof.comm_combined.clone(), + saver_proof.comm_chunks.clone(), + &saver_proof.sp_ciphertext, + &saver_proof.sp_chunks, + &saver_proof.sp_combined, ek_comm_key, &cc_keys.0, &cc_keys.1, @@ -918,6 +985,8 @@ where } _ => err_incompat_proof!(s_idx, s, proof), }, + Statement::DetachedAccumulatorMembershipVerifier(_s) => (), + Statement::DetachedAccumulatorNonMembershipVerifier(_s) => (), _ => return Err(ProofSystemError::InvalidStatement), } } @@ -930,9 +999,6 @@ where _ => return Err(ProofSystemError::SnarckpackSrsNotProvided), }; - let mut transcript = new_merlin_transcript(b"aggregation"); - transcript.append(b"challenge", &challenge); - if let Some(to_aggregate) = proof_spec.aggregate_groth16 { if let Some(aggr_proofs) = self.aggregated_groth16 { if to_aggregate.len() != aggr_proofs.len() { diff --git a/proof_system/src/witness.rs b/proof_system/src/witness.rs index 0a7cff0c..d0f43e77 100644 --- a/proof_system/src/witness.rs +++ b/proof_system/src/witness.rs @@ -12,7 +12,6 @@ use vb_accumulator::witness::{MembershipWitness, NonMembershipWitness}; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::error::ProofSystemError; -pub use serialization::*; /// Secret data that the prover will prove knowledge of, this data is known only to the prover #[serde_as] diff --git a/proof_system/tests/bbs_plus_and_accumulator.rs b/proof_system/tests/bbs_plus_and_accumulator.rs index 9b9e2c44..108acb63 100644 --- a/proof_system/tests/bbs_plus_and_accumulator.rs +++ b/proof_system/tests/bbs_plus_and_accumulator.rs @@ -20,6 +20,8 @@ use proof_system::{ accumulator::{ AccumulatorMembership as AccumulatorMembershipStmt, AccumulatorNonMembership as AccumulatorNonMembershipStmt, + DetachedAccumulatorMembershipProver, DetachedAccumulatorMembershipVerifier, + DetachedAccumulatorNonMembershipProver, DetachedAccumulatorNonMembershipVerifier, }, bbs_23::PoKBBSSignature23G1 as PoKSignatureBBS23G1Stmt, bbs_plus::PoKBBSSignatureG1 as PoKSignatureBBSG1Stmt, @@ -27,6 +29,10 @@ use proof_system::{ ped_comm::PedersenCommitment as PedersenCommitmentStmt, Statements, }, + statement_proof::StatementProof, + sub_protocols::accumulator::{ + DetachedAccumulatorMembershipSubProtocol, DetachedAccumulatorNonMembershipSubProtocol, + }, witness::{ Membership as MembershipWit, NonMembership as NonMembershipWit, PoKBBSSignature23G1 as PoKSignatureBBS23G1Wit, PoKBBSSignatureG1 as PoKSignatureBBSG1Wit, @@ -1869,3 +1875,706 @@ fn proof_spec_validation() { let ps_3 = ProofSpec::new(statements_3, meta_statements_3, vec![], None); assert!(ps_3.validate().is_err()); } + +#[test] +fn detached_accumulator() { + // Prove knowledge of BBS+ signature and one of the message's membership and non-membership in accumulators + let mut rng = StdRng::seed_from_u64(0u64); + + let msg_count = 6; + let (msgs, sig_params, sig_keypair, sig) = bbs_plus_sig_setup(&mut rng, msg_count as u32); + + let max = 10; + let (pos_accum_params, pos_accum_keypair, mut pos_accumulator, mut pos_state) = + setup_positive_accum(&mut rng); + let mem_prk = MembershipProvingKey::generate_using_rng(&mut rng); + + // Message with index `accum_member_1_idx` is added in the positive accumulator + let accum_member_1_idx = 1; + let accum_member_1 = msgs[accum_member_1_idx]; + + pos_accumulator = pos_accumulator + .add( + accum_member_1, + &pos_accum_keypair.secret_key, + &mut pos_state, + ) + .unwrap(); + let mem_1_wit = pos_accumulator + .get_membership_witness(&accum_member_1, &pos_accum_keypair.secret_key, &pos_state) + .unwrap(); + assert!(pos_accumulator.verify_membership( + &accum_member_1, + &mem_1_wit, + &pos_accum_keypair.public_key, + &pos_accum_params + )); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorMembershipProver::new_statement_from_params( + pos_accum_params.clone(), + pos_accum_keypair.public_key.clone(), + mem_prk.clone(), + *pos_accumulator.value(), + ), + ); + + // Create meta statement describing that message in the signature at index `accum_member_1_idx` is + // same as the accumulator member + let mut meta_statements = MetaStatements::new(); + meta_statements.add_witness_equality(EqualWitnesses( + vec![ + (0, accum_member_1_idx), + (1, 0), // Since accumulator (non)membership has only one (for applications) which is the (non)member, that witness is at index 0. + ] + .into_iter() + .collect::>(), + )); + + test_serialization!(Statements, statements); + test_serialization!(MetaStatements, meta_statements); + + let context = Some(b"test".to_vec()); + let proof_spec = ProofSpec::new( + statements.clone(), + meta_statements.clone(), + vec![], + context.clone(), + ); + proof_spec.validate().unwrap(); + + test_serialization!(ProofSpec, proof_spec); + + let mut witnesses = Witnesses::new(); + witnesses.add(PoKSignatureBBSG1Wit::new_as_witness( + sig.clone(), + msgs.clone().into_iter().enumerate().collect(), + )); + witnesses.add(MembershipWit::new_as_witness( + accum_member_1, + mem_1_wit.clone(), + )); + test_serialization!(Witnesses, witnesses); + + let nonce = Some(b"test-nonce".to_vec()); + + let proof = ProofG1::new::( + &mut rng, + proof_spec.clone(), + witnesses.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap() + .0; + + test_serialization!(ProofG1, proof); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorMembershipVerifier::new_statement_from_params( + pos_accum_params.clone(), + pos_accum_keypair.public_key.clone(), + mem_prk.clone(), + ), + ); + let proof_spec = ProofSpec::new(statements.clone(), meta_statements, vec![], context.clone()); + + let start = Instant::now(); + proof + .clone() + .verify::( + &mut rng, + proof_spec.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap(); + println!( + "Time to verify proof with a BBS+ signature and positive accumulator membership: {:?}", + start.elapsed() + ); + + match &proof.statement_proofs[1] { + StatementProof::DetachedAccumulatorMembership(p) => { + let sp = DetachedAccumulatorMembershipSubProtocol::new( + 1, + &pos_accum_params, + &pos_accum_keypair.public_key, + &mem_prk, + ); + sp.verify_proof_contribution( + p, + &pos_accum_keypair.secret_key, + pos_accum_keypair.public_key.clone(), + pos_accum_params.clone(), + ) + .unwrap(); + } + _ => assert!(false, "Needed a detached accumulator proof"), + } + + /*// Wrong witness reference fails to verify + let mut meta_statements_incorrect = MetaStatements::new(); + meta_statements_incorrect.add_witness_equality(EqualWitnesses( + vec![(0, 0), (1, 0)] + .into_iter() + .collect::>(), + )); + let proof_spec_incorrect = ProofSpec::new( + statements.clone(), + meta_statements_incorrect, + vec![], + context.clone(), + ); + let proof = ProofG1::new::( + &mut rng, + proof_spec_incorrect.clone(), + witnesses, + nonce.clone(), + Default::default(), + ) + .unwrap() + .0; + + assert!(proof + .clone() + .verify::( + &mut rng, + proof_spec_incorrect.clone(), + nonce.clone(), + Default::default() + ) + .is_err()); + assert!(proof + .verify::( + &mut rng, + proof_spec_incorrect, + nonce.clone(), + VerifierConfig { + use_lazy_randomized_pairing_checks: Some(false), + }, + ) + .is_err()); + + // Non-member fails to verify + let mut witnesses_incorrect = Witnesses::new(); + witnesses_incorrect.add(PoKSignatureBBSG1Wit::new_as_witness( + sig.clone(), + msgs.clone().into_iter().enumerate().collect(), + )); + witnesses_incorrect.add(Witness::AccumulatorMembership(MembershipWit { + element: msgs[2], // 2nd message from BBS+ sig in accumulator + witness: mem_1_wit.clone(), + })); + let mut meta_statements = MetaStatements::new(); + meta_statements.add_witness_equality(EqualWitnesses( + vec![ + (0, 2), // 2nd message from BBS+ sig in accumulator + (1, 0), + ] + .into_iter() + .collect::>(), + )); + let proof_spec = ProofSpec::new(statements, meta_statements, vec![], context.clone()); + proof_spec.validate().unwrap(); + let proof = ProofG1::new::( + &mut rng, + proof_spec.clone(), + witnesses_incorrect, + nonce.clone(), + Default::default(), + ) + .unwrap() + .0; + assert!(proof + .clone() + .verify::( + &mut rng, + proof_spec.clone(), + nonce.clone(), + Default::default() + ) + .is_err()); + assert!(proof + .verify::( + &mut rng, + proof_spec, + nonce.clone(), + VerifierConfig { + use_lazy_randomized_pairing_checks: Some(false), + }, + ) + .is_err());*/ + + // Prove knowledge of signature and membership of message with index `accum_member_2_idx` in universal accumulator + let accum_member_2_idx = 2; + let accum_member_2 = msgs[accum_member_2_idx]; + let (uni_accum_params, uni_accum_keypair, mut uni_accumulator, initial_elements, mut uni_state) = + setup_universal_accum(&mut rng, max); + let non_mem_prk = NonMembershipProvingKey::generate_using_rng(&mut rng); + let derived_mem_prk = non_mem_prk.derive_membership_proving_key(); + + uni_accumulator = uni_accumulator + .add( + accum_member_2, + &uni_accum_keypair.secret_key, + &initial_elements, + &mut uni_state, + ) + .unwrap(); + let mem_2_wit = uni_accumulator + .get_membership_witness(&accum_member_2, &uni_accum_keypair.secret_key, &uni_state) + .unwrap(); + assert!(uni_accumulator.verify_membership( + &accum_member_2, + &mem_2_wit, + &uni_accum_keypair.public_key, + &uni_accum_params + )); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorMembershipProver::new_statement_from_params( + uni_accum_params.clone(), + uni_accum_keypair.public_key.clone(), + derived_mem_prk.clone(), + *uni_accumulator.value(), + ), + ); + + let mut witnesses = Witnesses::new(); + witnesses.add(PoKSignatureBBSG1Wit::new_as_witness( + sig.clone(), + msgs.clone().into_iter().enumerate().collect(), + )); + witnesses.add(Witness::AccumulatorMembership(MembershipWit { + element: accum_member_2, + witness: mem_2_wit.clone(), + })); + + let mut meta_statements = MetaStatements::new(); + meta_statements.add_witness_equality(EqualWitnesses( + vec![(0, accum_member_2_idx), (1, 0)] + .into_iter() + .collect::>(), + )); + + test_serialization!(Statements, statements); + test_serialization!(MetaStatements, meta_statements); + test_serialization!(Witnesses, witnesses); + + let proof_spec = ProofSpec::new( + statements.clone(), + meta_statements.clone(), + vec![], + context.clone(), + ); + proof_spec.validate().unwrap(); + + test_serialization!(ProofSpec, proof_spec); + + let proof = ProofG1::new::( + &mut rng, + proof_spec.clone(), + witnesses.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap() + .0; + + test_serialization!(ProofG1, proof); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorMembershipVerifier::new_statement_from_params( + uni_accum_params.clone(), + uni_accum_keypair.public_key.clone(), + derived_mem_prk.clone(), + ), + ); + let proof_spec = ProofSpec::new(statements.clone(), meta_statements, vec![], context.clone()); + + let start = Instant::now(); + proof + .clone() + .verify::( + &mut rng, + proof_spec.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap(); + println!( + "Time to verify proof with a BBS+ signature and universal accumulator membership: {:?}", + start.elapsed() + ); + + match &proof.statement_proofs[1] { + StatementProof::DetachedAccumulatorMembership(p) => { + let sp = DetachedAccumulatorMembershipSubProtocol::new( + 1, + &uni_accum_params, + &uni_accum_keypair.public_key, + &derived_mem_prk, + ); + sp.verify_proof_contribution( + p, + &uni_accum_keypair.secret_key, + uni_accum_keypair.public_key.clone(), + uni_accum_params.clone(), + ) + .unwrap(); + } + _ => assert!(false, "Needed a detached accumulator proof"), + } + + // Prove knowledge of signature and non-membership of message with index `accum_non_member_idx` in universal accumulator + let accum_non_member_idx = 3; + let accum_non_member = msgs[accum_non_member_idx]; + let non_mem_wit = uni_accumulator + .get_non_membership_witness( + &accum_non_member, + &uni_accum_keypair.secret_key, + &uni_state, + &uni_accum_params, + ) + .unwrap(); + assert!(uni_accumulator.verify_non_membership( + &accum_non_member, + &non_mem_wit, + &uni_accum_keypair.public_key, + &uni_accum_params + )); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorNonMembershipProver::new_statement_from_params( + uni_accum_params.clone(), + uni_accum_keypair.public_key.clone(), + non_mem_prk.clone(), + *uni_accumulator.value(), + ), + ); + + let mut witnesses = Witnesses::new(); + witnesses.add(PoKSignatureBBSG1Wit::new_as_witness( + sig.clone(), + msgs.clone().into_iter().enumerate().collect(), + )); + witnesses.add(Witness::AccumulatorNonMembership(NonMembershipWit { + element: accum_non_member, + witness: non_mem_wit.clone(), + })); + + let mut meta_statements = MetaStatements::new(); + meta_statements.add_witness_equality(EqualWitnesses( + vec![(0, accum_non_member_idx), (1, 0)] + .into_iter() + .collect::>(), + )); + + test_serialization!(Statements, statements); + test_serialization!(MetaStatements, meta_statements); + test_serialization!(Witnesses, witnesses); + + let proof_spec = ProofSpec::new( + statements.clone(), + meta_statements.clone(), + vec![], + context.clone(), + ); + proof_spec.validate().unwrap(); + + test_serialization!(ProofSpec, proof_spec); + + let proof = ProofG1::new::( + &mut rng, + proof_spec.clone(), + witnesses.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap() + .0; + + test_serialization!(ProofG1, proof); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorNonMembershipVerifier::new_statement_from_params( + uni_accum_params.clone(), + uni_accum_keypair.public_key.clone(), + non_mem_prk.clone(), + ), + ); + + let proof_spec = ProofSpec::new(statements.clone(), meta_statements, vec![], context.clone()); + proof_spec.validate().unwrap(); + + let start = Instant::now(); + proof + .clone() + .verify::( + &mut rng, + proof_spec.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap(); + println!( + "Time to verify proof with a BBS+ signature and universal accumulator non-membership: {:?}", + start.elapsed() + ); + + match &proof.statement_proofs[1] { + StatementProof::DetachedAccumulatorNonMembership(p) => { + let sp = DetachedAccumulatorNonMembershipSubProtocol::new( + 1, + &uni_accum_params, + &uni_accum_keypair.public_key, + &non_mem_prk, + ); + sp.verify_proof_contribution( + p, + &uni_accum_keypair.secret_key, + uni_accum_keypair.public_key.clone(), + uni_accum_params.clone(), + ) + .unwrap(); + } + _ => assert!(false, "Needed a detached accumulator proof"), + } + + // Prove knowledge of signature and + // - membership of message with index `accum_member_1_idx` in positive accumulator + // - membership of message with index `accum_member_2_idx` in universal accumulator + // - non-membership of message with index `accum_non_member_idx` in universal accumulator + let mut all_setup_params = vec![]; + all_setup_params.push(SetupParams::VbAccumulatorParams(uni_accum_params.clone())); + all_setup_params.push(SetupParams::VbAccumulatorPublicKey( + uni_accum_keypair.public_key.clone(), + )); + all_setup_params.push(SetupParams::VbAccumulatorMemProvingKey( + derived_mem_prk.clone(), + )); + all_setup_params.push(SetupParams::VbAccumulatorNonMemProvingKey( + non_mem_prk.clone(), + )); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params.clone(), + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorMembershipProver::new_statement_from_params( + pos_accum_params.clone(), + pos_accum_keypair.public_key.clone(), + mem_prk.clone(), + *pos_accumulator.value(), + ), + ); + statements.add( + DetachedAccumulatorMembershipProver::new_statement_from_params_ref( + 0, + 1, + 2, + *uni_accumulator.value(), + ), + ); + statements.add( + DetachedAccumulatorNonMembershipProver::new_statement_from_params_ref( + 0, + 1, + 3, + *uni_accumulator.value(), + ), + ); + + let mut meta_statements = MetaStatements::new(); + meta_statements.add_witness_equality(EqualWitnesses( + vec![(0, accum_member_1_idx), (1, 0)] + .into_iter() + .collect::>(), + )); + meta_statements.add_witness_equality(EqualWitnesses( + vec![(0, accum_member_2_idx), (2, 0)] + .into_iter() + .collect::>(), + )); + meta_statements.add_witness_equality(EqualWitnesses( + vec![(0, accum_non_member_idx), (3, 0)] + .into_iter() + .collect::>(), + )); + + test_serialization!(Statements, statements); + test_serialization!(MetaStatements, meta_statements); + + let mut witnesses = Witnesses::new(); + witnesses.add(PoKSignatureBBSG1Wit::new_as_witness( + sig, + msgs.into_iter().enumerate().collect(), + )); + witnesses.add(Witness::AccumulatorMembership(MembershipWit { + element: accum_member_1, + witness: mem_1_wit, + })); + witnesses.add(Witness::AccumulatorMembership(MembershipWit { + element: accum_member_2, + witness: mem_2_wit, + })); + witnesses.add(Witness::AccumulatorNonMembership(NonMembershipWit { + element: accum_non_member, + witness: non_mem_wit, + })); + + test_serialization!(Witnesses, witnesses); + + let proof_spec = ProofSpec::new( + statements.clone(), + meta_statements.clone(), + all_setup_params.clone(), + context.clone(), + ); + proof_spec.validate().unwrap(); + + test_serialization!(ProofSpec, proof_spec); + + let proof = ProofG1::new::( + &mut rng, + proof_spec.clone(), + witnesses.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap() + .0; + + test_serialization!(ProofG1, proof); + + let mut statements = Statements::new(); + statements.add(PoKSignatureBBSG1Stmt::new_statement_from_params( + sig_params, + sig_keypair.public_key.clone(), + BTreeMap::new(), + )); + statements.add( + DetachedAccumulatorMembershipVerifier::new_statement_from_params( + pos_accum_params.clone(), + pos_accum_keypair.public_key.clone(), + mem_prk.clone(), + ), + ); + statements.add(DetachedAccumulatorMembershipVerifier::new_statement_from_params_ref(0, 1, 2)); + statements + .add(DetachedAccumulatorNonMembershipVerifier::new_statement_from_params_ref(0, 1, 3)); + + let proof_spec = ProofSpec::new( + statements.clone(), + meta_statements, + all_setup_params, + context, + ); + + let start = Instant::now(); + proof + .clone() + .verify::( + &mut rng, + proof_spec.clone(), + nonce.clone(), + Default::default(), + ) + .unwrap(); + println!("Time to verify proof with a BBS+ signature and 3 accumulator membership and non-membership checks: {:?}", start.elapsed()); + + match &proof.statement_proofs[1] { + StatementProof::DetachedAccumulatorMembership(p) => { + let sp = DetachedAccumulatorMembershipSubProtocol::new( + 1, + &pos_accum_params, + &pos_accum_keypair.public_key, + &mem_prk, + ); + sp.verify_proof_contribution( + p, + &pos_accum_keypair.secret_key, + pos_accum_keypair.public_key.clone(), + pos_accum_params.clone(), + ) + .unwrap(); + } + _ => assert!(false, "Needed a detached accumulator proof"), + } + match &proof.statement_proofs[2] { + StatementProof::DetachedAccumulatorMembership(p) => { + let sp = DetachedAccumulatorMembershipSubProtocol::new( + 2, + &uni_accum_params, + &uni_accum_keypair.public_key, + &derived_mem_prk, + ); + sp.verify_proof_contribution( + p, + &uni_accum_keypair.secret_key, + uni_accum_keypair.public_key.clone(), + uni_accum_params.clone(), + ) + .unwrap(); + } + _ => assert!(false, "Needed a detached accumulator proof"), + } + match &proof.statement_proofs[3] { + StatementProof::DetachedAccumulatorNonMembership(p) => { + let sp = DetachedAccumulatorNonMembershipSubProtocol::new( + 3, + &uni_accum_params, + &uni_accum_keypair.public_key, + &non_mem_prk, + ); + sp.verify_proof_contribution( + p, + &uni_accum_keypair.secret_key, + uni_accum_keypair.public_key.clone(), + uni_accum_params.clone(), + ) + .unwrap(); + } + _ => assert!(false, "Needed a detached accumulator proof"), + } +} diff --git a/saver/Cargo.toml b/saver/Cargo.toml index b190d3e4..102af3f1 100644 --- a/saver/Cargo.toml +++ b/saver/Cargo.toml @@ -22,7 +22,7 @@ serde.workspace = true serde_with.workspace = true zeroize.workspace = true legogroth16 = { version = "0.11.0", default-features = false, features = ["aggregation"], path = "../legogroth16" } -merlin = { package = "dock_merlin", version = "2.0", default-features = false, path = "../merlin" } +merlin = { package = "dock_merlin", version = "3.0.0", default-features = false, path = "../merlin" } [dev-dependencies] blake2.workspace = true diff --git a/schnorr_pok/src/inequality.rs b/schnorr_pok/src/inequality.rs index 5d385484..303c0357 100644 --- a/schnorr_pok/src/inequality.rs +++ b/schnorr_pok/src/inequality.rs @@ -321,7 +321,6 @@ impl InequalityProof { #[cfg(test)] mod tests { use super::*; - use crate::compute_random_oracle_challenge; use ark_bls12_381::{Bls12_381, G1Affine}; use ark_ec::pairing::Pairing; use ark_std::{ @@ -329,6 +328,7 @@ mod tests { UniformRand, }; use blake2::Blake2b512; + use dock_crypto_utils::transcript::{MerlinTranscript, Transcript}; type Fr = ::ScalarField; @@ -350,19 +350,31 @@ mod tests { ) .unwrap(); - let mut bytes = vec![]; + let mut prover_transcript = MerlinTranscript::new(b"test"); protocol - .challenge_contribution_for_public_inequality(&comm, &in_equal, &comm_key, &mut bytes) + .challenge_contribution_for_public_inequality( + &comm, + &in_equal, + &comm_key, + &mut prover_transcript, + ) .unwrap(); - let challenge_prover = compute_random_oracle_challenge::(&bytes); + let challenge_prover = prover_transcript.challenge_scalar(b"chal"); let proof = protocol.gen_proof(&challenge_prover).unwrap(); - let mut bytes = vec![]; + let mut verifier_transcript = MerlinTranscript::new(b"test"); proof - .challenge_contribution_for_public_inequality(&comm, &in_equal, &comm_key, &mut bytes) + .challenge_contribution_for_public_inequality( + &comm, + &in_equal, + &comm_key, + &mut verifier_transcript, + ) .unwrap(); - let challenge_verifier = compute_random_oracle_challenge::(&bytes); + let challenge_verifier = verifier_transcript.challenge_scalar(b"chal"); + + assert_eq!(challenge_prover, challenge_verifier); proof .verify_for_inequality_with_public_value( @@ -385,19 +397,29 @@ mod tests { ) .unwrap(); - let mut bytes = vec![]; + let mut prover_transcript = MerlinTranscript::new(b"test1"); protocol - .challenge_contribution_for_committed_inequality(&comm, &comm2, &comm_key, &mut bytes) + .challenge_contribution_for_committed_inequality( + &comm, + &comm2, + &comm_key, + &mut prover_transcript, + ) .unwrap(); - let challenge_prover = compute_random_oracle_challenge::(&bytes); + let challenge_prover = prover_transcript.challenge_scalar(b"chal"); let proof = protocol.gen_proof(&challenge_prover).unwrap(); - let mut bytes = vec![]; + let mut verifier_transcript = MerlinTranscript::new(b"test1"); proof - .challenge_contribution_for_committed_inequality(&comm, &comm2, &comm_key, &mut bytes) + .challenge_contribution_for_committed_inequality( + &comm, + &comm2, + &comm_key, + &mut verifier_transcript, + ) .unwrap(); - let challenge_verifier = compute_random_oracle_challenge::(&bytes); + let challenge_verifier = verifier_transcript.challenge_scalar(b"chal"); proof .verify_for_inequality_with_committed_value( diff --git a/schnorr_pok/src/lib.rs b/schnorr_pok/src/lib.rs index b8fbcf63..9123d498 100644 --- a/schnorr_pok/src/lib.rs +++ b/schnorr_pok/src/lib.rs @@ -286,7 +286,7 @@ macro_rules! impl_proof_of_knowledge_of_discrete_log { $protocol_name::compute_challenge_contribution(base, y, &self.t, writer) } - /// base*response - y*challenge == t + /// `base*response - y*challenge == t` pub fn verify(&self, y: &G, base: &G, challenge: &G::ScalarField) -> bool { let mut expected = base.mul_bigint(self.response.into_bigint()); expected -= y.mul_bigint(challenge.into_bigint()); diff --git a/secret_sharing_and_dkg/src/pedersen_vss.rs b/secret_sharing_and_dkg/src/pedersen_vss.rs index 4a19a642..a14b225e 100644 --- a/secret_sharing_and_dkg/src/pedersen_vss.rs +++ b/secret_sharing_and_dkg/src/pedersen_vss.rs @@ -247,6 +247,8 @@ 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/Cargo.toml b/short_group_sig/Cargo.toml new file mode 100644 index 00000000..72ddeb09 --- /dev/null +++ b/short_group_sig/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "short_group_sig" +version = "0.1.0" +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +ark-serialize.workspace = true +ark-ff.workspace = true +ark-ec.workspace = true +ark-std.workspace = true +ark-poly.workspace = true +digest.workspace = true +rayon = {workspace = true, optional = true} +serde.workspace = true +serde_with.workspace = true +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" } + +[dev-dependencies] +blake2.workspace = true +ark-bls12-381.workspace = true +serde_json = "1.0" +rmp-serde = "1.0" + +[features] +default = [ "parallel" ] +std = [ "ark-ff/std", "ark-ec/std", "ark-poly/std", "ark-std/std", "ark-serialize/std", "dock_crypto_utils/std", "schnorr_pok/std", "serde/std"] +print-trace = [ "ark-std/print-trace", "dock_crypto_utils/print-trace" ] +parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-poly/parallel", "ark-std/parallel", "rayon", "dock_crypto_utils/parallel", "schnorr_pok/parallel" ] \ No newline at end of file diff --git a/short_group_sig/README.md b/short_group_sig/README.md new file mode 100644 index 00000000..d09467a2 --- /dev/null +++ b/short_group_sig/README.md @@ -0,0 +1,10 @@ + + +# Short group signatures + +1. BB and Weak-BB signatures and proof of knowledge of weak-BB signature as described in the paper [Short Signatures Without Random Oracles](https://eprint.iacr.org/2004/171) +2. Proof of knowledge of BB signature adapted from the paper [Proof-of-Knowledge of Representation of Committed Value and Its Applications](https://link.springer.com/chapter/10.1007/978-3-642-14081-5_22) +3. An optimized implementation of proof of knowledge of weak-BB signature taken from the paper [Scalable Revocation Scheme for Anonymous Credentials Based on n-times Unlinkable Proofs](http://library.usc.edu.ph/ACM/SIGSAC%202017/wpes/p123.pdf). This does not require the prover to do pairings +4. Similar to weak-BB, proof of knowledge of BB signature that does not require the prover to do pairings. + + diff --git a/short_group_sig/src/bb_sig.rs b/short_group_sig/src/bb_sig.rs new file mode 100644 index 00000000..6ca14c92 --- /dev/null +++ b/short_group_sig/src/bb_sig.rs @@ -0,0 +1,204 @@ +//! BB signature + +use crate::{ + common::{SignatureParams, SignatureParamsWithPairing}, + error::ShortGroupSigError, +}; +use ark_ec::{pairing::Pairing, AffineRepr}; +use ark_ff::{ + field_hashers::{DefaultFieldHasher, HashToField}, + Field, PrimeField, Zero, +}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use digest::DynDigest; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Secret key used by the signer to sign messages +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Zeroize, ZeroizeOnDrop, +)] +pub struct SecretKey(pub F, pub F); + +/// Public key used to verify signatures +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct PublicKeyG2(pub ::G2Affine, pub ::G2Affine); + +impl SecretKey { + pub fn new(rng: &mut R) -> Self { + Self(F::rand(rng), F::rand(rng)) + } +} + +impl PublicKeyG2 +where + E: Pairing, +{ + pub fn generate_using_secret_key( + secret_key: &SecretKey, + params: &SignatureParams, + ) -> Self { + Self( + (params.g2 * secret_key.0).into(), + (params.g2 * secret_key.1).into(), + ) + } + + /// Public key shouldn't be 0. A verifier on receiving this must first check that its + /// valid and only then use it for any signature or proof of knowledge of signature verification. + pub fn is_valid(&self) -> bool { + !(self.0.is_zero() || self.1.is_zero()) + } +} + +#[derive( + Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Zeroize, ZeroizeOnDrop, +)] +pub struct SignatureG1(pub E::G1Affine, pub E::ScalarField); + +impl SignatureG1 { + /// Create a new signature + pub fn new( + rng: &mut R, + message: &E::ScalarField, + sk: &SecretKey, + params: &SignatureParams, + ) -> Self { + let mut r = E::ScalarField::rand(rng); + while r == ((sk.0 + message) * sk.1.inverse().unwrap()).neg() { + r = E::ScalarField::rand(rng) + } + Self::new_given_randomness(message, r, sk, params) + } + + /// Create a new deterministic signature. The randomness in the signature comes from a PRF applied on the message + /// and the secret key. + pub fn new_deterministic( + message: &E::ScalarField, + sk: &SecretKey, + params: &SignatureParams, + ) -> Self { + let randomness = Self::generate_random_for_message::(message, sk); + Self::new_given_randomness(message, randomness, sk, params) + } + + /// Create a new signature with the provided randomness + pub fn new_given_randomness( + message: &E::ScalarField, + randomness: E::ScalarField, + sk: &SecretKey, + params: &SignatureParams, + ) -> Self { + Self( + (params.g1 * ((sk.0 + message + sk.1 * randomness).inverse().unwrap())).into(), + randomness, + ) + } + + pub fn verify( + &self, + message: &E::ScalarField, + pk: &PublicKeyG2, + params: &SignatureParams, + ) -> Result<(), ShortGroupSigError> { + if !self.is_non_zero() { + return Err(ShortGroupSigError::ZeroSignature); + } + // Check e(sig, v1 + g2*m + v2*r) == e(g1, g2) => e(g1, g2) - e(sig, v1 + g2*m + v2*r) == 0 => e(g1, g2) + e(sig, -(v1 + g2*m + v2*r)) == 0 + // gm = -g2*m - v1 - v2*r + let gm = params.g2 * message.neg() - pk.0 - pk.1 * self.1; + if !E::multi_pairing( + [E::G1Prepared::from(self.0), E::G1Prepared::from(params.g1)], + [E::G2Prepared::from(gm), E::G2Prepared::from(params.g2)], + ) + .is_zero() + { + return Err(ShortGroupSigError::InvalidSignature); + } + Ok(()) + } + + pub fn verify_given_sig_params_with_pairing( + &self, + message: &E::ScalarField, + pk: &PublicKeyG2, + params: &SignatureParamsWithPairing, + ) -> Result<(), ShortGroupSigError> { + if !self.is_non_zero() { + return Err(ShortGroupSigError::ZeroSignature); + } + // Check e(sig, v1 + g2*m + v2*r) == e(g1, g2) + // gm = g2*m + g2*x + let gm = params.g2 * message + pk.0 + pk.1 * self.1; + if E::pairing(E::G1Prepared::from(self.0), E::G2Prepared::from(gm)) != params.g1g2 { + return Err(ShortGroupSigError::InvalidSignature); + } + Ok(()) + } + + pub fn is_non_zero(&self) -> bool { + !(self.0.is_zero() || self.1.is_zero()) + } + + /// Generate randomness to be used in the signature for a given message + pub fn generate_random_for_message( + message: &E::ScalarField, + secret_key: &SecretKey, + ) -> E::ScalarField { + prf::(message, secret_key) + } +} + +/// A PRF (PseudoRandom Function) with key as the signing key. The PRF is computed as `H(sk||message)` where `H` is hash +/// function that outputs a finite field element +pub fn prf( + message: &F, + secret_key: &SecretKey, +) -> F { + let hasher = as HashToField>::new(b"BB-SIG-RANDOMNESS"); + // bytes = sk.0||sk.1||msg + let mut bytes = vec![]; + secret_key.0.serialize_compressed(&mut bytes).unwrap(); + secret_key.1.serialize_compressed(&mut bytes).unwrap(); + message.serialize_compressed(&mut bytes).unwrap(); + let r = hasher.hash_to_field(&bytes, 1).pop().unwrap(); + bytes.zeroize(); + r +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + + #[test] + fn signature_verification() { + let mut rng = StdRng::seed_from_u64(0u64); + + let params = SignatureParams::::generate_using_rng(&mut rng); + let params_with_pairing = SignatureParamsWithPairing::::from(params.clone()); + let sk = SecretKey::new(&mut rng); + let pk = PublicKeyG2::generate_using_secret_key(&sk, ¶ms); + + let message = Fr::rand(&mut rng); + let sig = SignatureG1::new(&mut rng, &message, &sk, ¶ms); + sig.verify(&message, &pk, ¶ms).unwrap(); + sig.verify_given_sig_params_with_pairing(&message, &pk, ¶ms_with_pairing) + .unwrap(); + + let sig1 = SignatureG1::new(&mut rng, &message, &sk, ¶ms); + sig1.verify(&message, &pk, ¶ms).unwrap(); + assert_ne!(sig, sig1); + + let sig2 = SignatureG1::new_deterministic::(&message, &sk, ¶ms); + let sig3 = SignatureG1::new_deterministic::(&message, &sk, ¶ms); + sig2.verify(&message, &pk, ¶ms).unwrap(); + sig3.verify(&message, &pk, ¶ms).unwrap(); + assert_eq!(sig2, sig3); + } +} diff --git a/short_group_sig/src/bb_sig_pok.rs b/short_group_sig/src/bb_sig_pok.rs new file mode 100644 index 00000000..ca55edf5 --- /dev/null +++ b/short_group_sig/src/bb_sig_pok.rs @@ -0,0 +1,480 @@ +//! Proof of knowledge of BB signature. Adapted from the construction in section 4.2 of the paper [Proof-of-Knowledge of Representation of Committed Value and Its Applications](https://link.springer.com/chapter/10.1007/978-3-642-14081-5_22) +//! Specifically the adaptation is of `SPK_1` of construction `pi_m` in section 4.2 as following: +//! For BB signature, secret key = `(x, y)`, public key = `(w1=g2*x, w2=g2*y)`, message = `m` and signature = `(A = g*{1/{m + x + e*y}}, e)` +//! As part of setup params, generators `u`, `v` and `h` og group G1 exist. +//! 1. Pick random `alpha` and `beta` from `Z_p`. +//! 2. Create `delta_1 = -m * alpha, delta_2 = -m * beta, delta_3 = -e * alpha, delta_4 = -e * beta, T1 = u * alpha, T2 = v * alpha, T3 = A * alpha + h * (alpha + beta)`. +//! 3. Now the prover proves the following 5 relations +//! a. `T1*m + u*delta_1 = 0` +//! b. `T2*m + v*delta_2 = 0` +//! c. `T1*e + u*delta_3 = 0` +//! d. `T2*e + v*delta_4 = 0` +//! e. `e(T3, g2)*m + e(T3, w2)*e + e(h, w1)*{alpha + beta} + e(h, g2)*{delta_1 + delta_2} + e(h, w2)*{delta_3 + delta_4} = e(g1, g2) - e(T3, w1)` + +use crate::{ + bb_sig::{PublicKeyG2, SignatureG1}, + common::{ProvingKey, SignatureParams}, + error::ShortGroupSigError, +}; +use ark_ec::{ + pairing::{Pairing, PairingOutput}, + AffineRepr, CurveGroup, +}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use dock_crypto_utils::{msm::WindowTable, serde_utils::ArkObjectBytes}; +use schnorr_pok::{ + error::SchnorrError, impl_proof_of_knowledge_of_discrete_log, SchnorrCommitment, + SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +impl_proof_of_knowledge_of_discrete_log!(RandomnessKnowledgeProtocol, RandomnessKnowledgeProof); + +/// Protocol to prove knowledge of a BB signature in group G1 +#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] +pub struct PoKOfSignatureG1Protocol { + /// `u * alpha` + #[zeroize(skip)] + pub T1: E::G1Affine, + /// `v * beta` + #[zeroize(skip)] + pub T2: E::G1Affine, + /// `A + h * (alpha + beta)` + #[zeroize(skip)] + pub T3: E::G1Affine, + /// Protocol for proving knowledge of `alpha` in `T1 = u * alpha` + pub sc_T1: RandomnessKnowledgeProtocol, + /// Protocol for proving knowledge of `beta` in `T2 = v * beta` + pub sc_T2: RandomnessKnowledgeProtocol, + /// For proving knowledge of `message` and `delta_1` in `T1 * message + u * delta_1 = 0` + pub sc_T1_x: SchnorrCommitment, + /// For proving knowledge of `message` and `delta_2` in `T2 * message + v * delta_2 = 0` + pub sc_T2_x: SchnorrCommitment, + /// For proving knowledge of `e` and `delta_3` in `T1 * e + u * delta_3 = 0` + pub sc_T1_e: SchnorrCommitment, + /// For proving knowledge of `e` and `delta_4` in `T2 * e + v * delta_4 = 0` + pub sc_T2_e: SchnorrCommitment, + /// Commitment to randomness from the 1st step of the Schnorr protocol over the pairing equation. + #[zeroize(skip)] + pub R_3: PairingOutput, + /// - message * alpha + pub delta_1: E::ScalarField, + /// - message * beta + pub delta_2: E::ScalarField, + /// - e * alpha + 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 { + /// `u * alpha` + pub T1: E::G1Affine, + /// `v * beta` + pub T2: E::G1Affine, + /// `A + h * (alpha + beta)` + pub T3: E::G1Affine, + /// Proof of knowledge of `alpha` in `T1 = u * alpha` + pub sc_T1: RandomnessKnowledgeProof, + /// Proof of knowledge of `beta` in `T2 = v * beta` + pub sc_T2: RandomnessKnowledgeProof, + /// For relation `T1 * message + u * delta_1 = 0` + pub sc_T1_x_t: E::G1Affine, + pub sc_T1_x_resp: SchnorrResponse, + /// For relation `T2 * message + v * delta_2 = 0` + pub sc_T2_x_t: E::G1Affine, + pub sc_T2_x_resp: SchnorrResponse, + /// For relation `T1 * e + u * delta_3 = 0` + pub sc_T1_e_t: E::G1Affine, + pub sc_T1_e_resp: SchnorrResponse, + /// For relation `T2 * e + v * delta_4 = 0` + pub sc_T2_e_t: E::G1Affine, + pub sc_T2_e_resp: SchnorrResponse, + /// Commitment to randomness from the 1st step of the Schnorr protocol over the pairing equation. + pub R_3: PairingOutput, +} + +impl PoKOfSignatureG1Protocol { + pub fn init( + rng: &mut R, + signature: &SignatureG1, + message: E::ScalarField, + message_blinding: Option, + randomness_blinding: Option, + pk: &PublicKeyG2, + params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result { + let A = signature.0; + let e = signature.1; + let alpha = E::ScalarField::rand(rng); + let beta = E::ScalarField::rand(rng); + // - message * alpha + let delta_1 = (message * alpha).neg(); + // - message * beta + let delta_2 = (message * beta).neg(); + // - e * alpha + let delta_3 = (e * alpha).neg(); + // - e * beta + let delta_4 = (e * beta).neg(); + + let T1 = (proving_key.X * alpha).into_affine(); + let T2 = (proving_key.Y * beta).into_affine(); + let Z_table = WindowTable::new(4, proving_key.Z.into_group()); + + let T3 = A + Z_table.multiply(&(alpha + beta)); + // blinding for message + let r_x = message_blinding.unwrap_or_else(|| E::ScalarField::rand(rng)); + // blinding for e + let r_e = randomness_blinding.unwrap_or_else(|| E::ScalarField::rand(rng)); + let r_alpha = E::ScalarField::rand(rng); + let r_beta = E::ScalarField::rand(rng); + let r_delta_1 = E::ScalarField::rand(rng); + let r_delta_2 = E::ScalarField::rand(rng); + let r_delta_3 = E::ScalarField::rand(rng); + let r_delta_4 = E::ScalarField::rand(rng); + let sc_T1 = RandomnessKnowledgeProtocol::init(alpha, r_alpha, &proving_key.X); + let sc_T2 = RandomnessKnowledgeProtocol::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 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); + // R_3 = e(T_3, g2) * r_x + e(T_3, w2) * r_e + e(Z, w1) * -(r_alpha + r_beta) + e(Z, g2) * (r_delta_1 + r_delta_2) + e(Z, w2) * (r_delta_3 + r_delta_4) + let R_3 = E::multi_pairing( + [ + E::G1Prepared::from(T3 * r_x), + E::G1Prepared::from(T3 * r_e), + E::G1Prepared::from(Z_table.multiply(&(r_alpha.neg() + r_beta.neg()))), + E::G1Prepared::from(Z_table.multiply(&(r_delta_1 + r_delta_2))), + E::G1Prepared::from(Z_table.multiply(&(r_delta_3 + r_delta_4))), + ], + [ + g2_prepared.clone(), + pk_1_prepared.clone(), + pk_0_prepared, + g2_prepared, + pk_1_prepared, + ], + ); + Ok(Self { + T1, + T2, + T3: T3.into(), + sc_T1, + sc_T2, + sc_T1_x, + sc_T2_x, + sc_T1_e, + sc_T2_e, + R_3, + delta_1, + delta_2, + delta_3, + delta_4, + message, + e: signature.1, + }) + } + + pub fn challenge_contribution( + &self, + pk: &PublicKeyG2, + params: &SignatureParams, + proving_key: &ProvingKey, + writer: W, + ) -> Result<(), ShortGroupSigError> { + Self::compute_challenge_contribution( + &self.T1, + &self.T2, + &self.T3, + &proving_key, + pk, + ¶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.R_3, + writer, + ) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> 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 { + 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, + R_3: self.R_3, + }) + } + + pub fn compute_challenge_contribution( + T1: &E::G1Affine, + T2: &E::G1Affine, + T3: &E::G1Affine, + proving_key: &ProvingKey, + pk: &PublicKeyG2, + g2: &E::G2Affine, + t_T1: &E::G1Affine, + t_T2: &E::G1Affine, + t_T1_x: &E::G1Affine, + t_T2_x: &E::G1Affine, + t_T1_e: &E::G1Affine, + t_T2_e: &E::G1Affine, + R3: &PairingOutput, + mut writer: W, + ) -> Result<(), ShortGroupSigError> { + T1.serialize_compressed(&mut writer)?; + T2.serialize_compressed(&mut writer)?; + T3.serialize_compressed(&mut writer)?; + proving_key.X.serialize_compressed(&mut writer)?; + proving_key.Y.serialize_compressed(&mut writer)?; + proving_key.Z.serialize_compressed(&mut writer)?; + pk.0.serialize_compressed(&mut writer)?; + pk.1.serialize_compressed(&mut writer)?; + g2.serialize_compressed(&mut writer)?; + t_T1.serialize_compressed(&mut writer)?; + t_T2.serialize_compressed(&mut writer)?; + t_T1_x.serialize_compressed(&mut writer)?; + t_T2_x.serialize_compressed(&mut writer)?; + t_T1_e.serialize_compressed(&mut writer)?; + t_T2_e.serialize_compressed(&mut writer)?; + R3.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl PoKOfSignatureG1Proof { + pub fn verify( + &self, + challenge: &E::ScalarField, + pk: &PublicKeyG2, + params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result<(), ShortGroupSigError> { + if !self.sc_T1.verify(&self.T1, &proving_key.X, challenge) { + return Err(ShortGroupSigError::InvalidProof); + } + if !self.sc_T2.verify(&self.T2, &proving_key.Y, challenge) { + 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)? { + 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)? { + 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)?; + 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); + let Z_table = WindowTable::new(3, proving_key.Z.into_group()); + if self.R_3 + != E::multi_pairing( + [ + E::G1Prepared::from(self.T3 * s_message), + E::G1Prepared::from(self.T3 * e_message), + 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(self.T3 * challenge), + E::G1Prepared::from(params.g1 * challenge.neg()), + ], + [ + g2_prepared.clone(), + pk_1_prepared.clone(), + pk_0_prepared.clone(), + g2_prepared.clone(), + pk_1_prepared, + pk_0_prepared, + g2_prepared, + ], + ) + { + return Err(ShortGroupSigError::InvalidProof); + } + Ok(()) + } + + pub fn challenge_contribution( + &self, + pk: &PublicKeyG2, + params: &SignatureParams, + proving_key: &ProvingKey, + writer: W, + ) -> Result<(), ShortGroupSigError> { + PoKOfSignatureG1Protocol::::compute_challenge_contribution( + &self.T1, + &self.T2, + &self.T3, + &proving_key, + pk, + ¶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.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_randomness(&self) -> Result<&E::ScalarField, ShortGroupSigError> { + self.sc_T1_e_resp.get_response(0).map_err(|e| e.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bb_sig::SecretKey; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn proof_of_knowledge_of_signature() { + let mut rng = StdRng::seed_from_u64(0u64); + let params = SignatureParams::::new::(b"test-params"); + let prk = ProvingKey::::generate_using_hash::(b"test-proving-key"); + + let sk = SecretKey::new(&mut rng); + let pk = PublicKeyG2::generate_using_secret_key(&sk, ¶ms); + let message = Fr::rand(&mut rng); + let sig = SignatureG1::new(&mut rng, &message, &sk, ¶ms); + sig.verify(&message, &pk, ¶ms).unwrap(); + + let protocol = + PoKOfSignatureG1Protocol::init(&mut rng, &sig, message, None, None, &pk, ¶ms, &prk) + .unwrap(); + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(&pk, ¶ms, &prk, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(&pk, ¶ms, &prk, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + assert_eq!(challenge_prover, challenge_verifier); + proof + .verify(&challenge_verifier, &pk, ¶ms, &prk) + .unwrap(); + + let msg_blinding = Fr::rand(&mut rng); + let rand_blinding = Fr::rand(&mut rng); + let same_challenge = Fr::rand(&mut rng); + + let protocol1 = PoKOfSignatureG1Protocol::init( + &mut rng, + &sig, + message, + Some(msg_blinding), + Some(rand_blinding), + &pk, + ¶ms, + &prk, + ) + .unwrap(); + let proof1 = protocol1.gen_proof(&same_challenge).unwrap(); + proof1.verify(&same_challenge, &pk, ¶ms, &prk).unwrap(); + + let protocol2 = PoKOfSignatureG1Protocol::init( + &mut rng, + &sig, + message, + Some(msg_blinding), + Some(rand_blinding), + &pk, + ¶ms, + &prk, + ) + .unwrap(); + let proof2 = protocol2.gen_proof(&same_challenge).unwrap(); + proof2.verify(&same_challenge, &pk, ¶ms, &prk).unwrap(); + + 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() + ); + } +} diff --git a/short_group_sig/src/bb_sig_pok_alt.rs b/short_group_sig/src/bb_sig_pok_alt.rs new file mode 100644 index 00000000..de88f5b1 --- /dev/null +++ b/short_group_sig/src/bb_sig_pok_alt.rs @@ -0,0 +1,276 @@ +//! Proof of knowledge of BB signature. This is not published in any paper but is an adaptation of similar protocol for proving +//! knowledge of weak-BB signature. The advantage of this variation is that the prover does not need to compute any pairings. +//! Following is a description +//! For BB signature, secret key = `(x, y)`, public key = `(w1=g2*x, w2=g2*y)`, message = `m` and signature = `(A = g*{1/{m + x + e*y}}, e)` +//! As part of setup params, generators `u`, `v` and `h` og group G1 exist. +//! 1. Pick random `r1` and `r2` from `Z_p`. +//! 2. Create `A' = A * r1, A_hat = A * r2, A_bar = g1 * r - A' * m, w2' = w2 * e * r1/r2`. Note that `A_bar = A*{{x + e*y}*r1} = A'*{x + e*y}`. +//! 3. Prover creates proof `pi_1` to prove knowledge of `m` and `r1` in `A_bar`, i.e. `pi_1 = SPK{(m, r1): A_bar = g1 * r - A' * m}`. +//! 4. Prover creates proof `pi_2` to prove knowledge of `e*r1/r2` in `w2'`, i.e. `pi_2 = SPK{(e*r1/r2): w2' = w2 * {e*r1/r2}}`. +//! 5. Prover sends `A', A_hat, A_bar, w2'`, proofs `pi_1, pi_2` to verifier. +//! 6. Verifier checks `pi_1, pi_2`, and `A'` and `w2'` are not zero, and the relation `e(A_bar, g2) = e(A', w1) * e(A_hat, w2')` + +use crate::{ + bb_sig::{PublicKeyG2, SignatureG1}, + common::SignatureParams, + error::ShortGroupSigError, +}; +use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; +use ark_ff::{Field, PrimeField, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use dock_crypto_utils::serde_utils::ArkObjectBytes; +use schnorr_pok::{ + error::SchnorrError, impl_proof_of_knowledge_of_discrete_log, SchnorrCommitment, + SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +impl_proof_of_knowledge_of_discrete_log!(RandomnessKnowledgeProtocol, RandomnessKnowledgeProof); + +#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] +pub struct PoKOfSignatureG1Protocol { + /// `A * r1` + #[zeroize(skip)] + pub A_prime: E::G1Affine, + /// `A * r2` + #[zeroize(skip)] + pub A_hat: E::G1Affine, + /// `g1 * r - A' * m` + #[zeroize(skip)] + pub A_bar: E::G1Affine, + /// `w2 * e * r1/r2` + #[zeroize(skip)] + pub w2_prime: E::G2Affine, + pub sc_comm_1: SchnorrCommitment, + sc_wits_1: (E::ScalarField, E::ScalarField), + /// Protocol for proving knowledge of `e * r1 / r2` in `w2' = w2 * {e * r1 / r2}` + pub sc_comm_2: RandomnessKnowledgeProtocol, +} + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct PoKOfSignatureG1Proof { + /// `A * r1` + pub A_prime: E::G1Affine, + /// `A * r2` + pub A_hat: E::G1Affine, + /// `g1 * r - A' * m` + 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_2: RandomnessKnowledgeProof, +} + +impl PoKOfSignatureG1Protocol { + pub fn init( + rng: &mut R, + signature: &SignatureG1, + message: E::ScalarField, + blinding: Option, + pk: &PublicKeyG2, + g1: impl Into, + ) -> Result { + let A = signature.0; + let e = signature.1; + let r1 = E::ScalarField::rand(rng); + let r2 = E::ScalarField::rand(rng); + let blinding = blinding.unwrap_or_else(|| E::ScalarField::rand(rng)); + let A_prime = A * r1; + let A_hat = A * r2; + let A_prime_neg = A_prime.neg(); + // wit = e * r1 / r2 + let wit = e * r1 * r2.inverse().unwrap(); + let w2_prime = (pk.1 * wit).into(); + 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_2 = RandomnessKnowledgeProtocol::init(wit, E::ScalarField::rand(rng), &pk.1); + let sc_wits_1 = (r1, message); + Ok(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, + }) + } + + pub fn challenge_contribution( + &self, + pk: &PublicKeyG2, + g1: impl Into, + writer: W, + ) -> Result<(), ShortGroupSigError> { + Self::compute_challenge_contribution( + &self.A_bar, + &self.A_prime, + &self.A_hat, + &self.w2_prime, + g1, + pk.1, + &self.sc_comm_1.t, + &self.sc_comm_2.t, + writer, + ) + } + + 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 { + 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_2, + }) + } + + pub fn compute_challenge_contribution( + A_bar: &E::G1Affine, + A_prime: &E::G1Affine, + A_hat: &E::G1Affine, + pk_prime: &E::G2Affine, + g1: impl Into, + g2: impl Into, + t_1: &E::G1Affine, + t_2: &E::G2Affine, + mut writer: W, + ) -> Result<(), ShortGroupSigError> { + A_bar.serialize_compressed(&mut writer)?; + A_prime.serialize_compressed(&mut writer)?; + A_hat.serialize_compressed(&mut writer)?; + pk_prime.serialize_compressed(&mut writer)?; + g1.into().serialize_compressed(&mut writer)?; + g2.into().serialize_compressed(&mut writer)?; + t_1.serialize_compressed(&mut writer)?; + t_2.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl PoKOfSignatureG1Proof { + pub fn verify( + &self, + challenge: &E::ScalarField, + pk: &PublicKeyG2, + params: &SignatureParams, + ) -> Result<(), ShortGroupSigError> { + if self.A_prime.is_zero() { + return Err(ShortGroupSigError::InvalidProof); + } + if self.w2_prime.is_zero() { + return Err(ShortGroupSigError::InvalidProof); + } + self.sc_resp_1.is_valid( + &[params.g1, self.A_prime.into_group().neg().into()], + &self.A_bar, + &self.t_1, + challenge, + )?; + if !self.sc_2.verify(&self.w2_prime, &pk.1, challenge) { + return Err(ShortGroupSigError::InvalidProof); + } + if !E::multi_pairing( + [ + E::G1Prepared::from(-(self.A_bar.into_group())), + E::G1Prepared::from(self.A_prime), + E::G1Prepared::from(self.A_hat), + ], + [ + E::G2Prepared::from(params.g2), + E::G2Prepared::from(pk.0), + E::G2Prepared::from(self.w2_prime), + ], + ) + .is_zero() + { + return Err(ShortGroupSigError::InvalidProof); + } + Ok(()) + } + + pub fn challenge_contribution( + &self, + pk: &PublicKeyG2, + g1: impl Into, + writer: W, + ) -> Result<(), ShortGroupSigError> { + PoKOfSignatureG1Protocol::::compute_challenge_contribution( + &self.A_bar, + &self.A_prime, + &self.A_hat, + &self.w2_prime, + g1, + pk.1, + &self.t_1, + &self.sc_2.t, + writer, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + bb_sig::{PublicKeyG2, SecretKey, SignatureG1}, + common::SignatureParams, + }; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn proof_of_knowledge_of_signature() { + let mut rng = StdRng::seed_from_u64(0u64); + let params = SignatureParams::::new::(b"test-params"); + + let sk = SecretKey::new(&mut rng); + let pk = PublicKeyG2::generate_using_secret_key(&sk, ¶ms); + let message = Fr::rand(&mut rng); + let sig = SignatureG1::new(&mut rng, &message, &sk, ¶ms); + + let protocol = PoKOfSignatureG1Protocol::::init( + &mut rng, &sig, message, None, &pk, params.g1, + ) + .unwrap(); + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(&pk, params.g1, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(&pk, params.g1, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + proof.verify(&challenge_verifier, &pk, ¶ms).unwrap(); + } +} diff --git a/short_group_sig/src/common.rs b/short_group_sig/src/common.rs new file mode 100644 index 00000000..96e93b63 --- /dev/null +++ b/short_group_sig/src/common.rs @@ -0,0 +1,131 @@ +use ark_ec::{ + pairing::{Pairing, PairingOutput}, + AffineRepr, +}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec::Vec, UniformRand}; +use digest::Digest; +use dock_crypto_utils::{ + affine_group_element_from_byte_slices, concat_slices, + hashing_utils::affine_group_elem_from_try_and_incr, serde_utils::ArkObjectBytes, +}; +use schnorr_pok::{error::SchnorrError, SchnorrChallengeContributor}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +/// Public parameters for creating and verifying BB signatures +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct SignatureParams { + pub g1: E::G1Affine, + pub g2: E::G2Affine, +} + +/// `SignatureParams` with pre-computation done for protocols more efficient +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct SignatureParamsWithPairing { + pub g1: E::G1Affine, + pub g2: E::G2Affine, + pub g2_prepared: E::G2Prepared, + /// pairing e(g1, g2) + pub g1g2: PairingOutput, +} + +impl SignatureParams { + pub fn new(label: &[u8]) -> Self { + let g1 = + affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : g1"]); + let g2 = + affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : g2"]); + Self { g1, g2 } + } + + pub fn generate_using_rng(rng: &mut R) -> Self { + Self { + g1: E::G1::rand(rng).into(), + g2: E::G2::rand(rng).into(), + } + } + + pub fn is_valid(&self) -> bool { + !(self.g1.is_zero() || self.g2.is_zero()) + } +} + +impl From> for SignatureParamsWithPairing { + fn from(params: SignatureParams) -> Self { + let g1g2 = E::pairing(params.g1, params.g2); + Self { + g1: params.g1, + g2: params.g2, + g2_prepared: E::G2Prepared::from(params.g2), + g1g2, + } + } +} + +impl AsRef for SignatureParams { + fn as_ref(&self) -> &E::G1Affine { + &self.g1 + } +} + +impl AsRef for SignatureParamsWithPairing { + fn as_ref(&self) -> &E::G1Affine { + &self.g1 + } +} + +/// The public parameters used during the proof of knowledge of signature are called `ProvingKey`. These are mutually +/// agreed upon by the prover and verifier and can be same or different between different provers and verifiers +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct ProvingKey { + /// Called `u` in the paper + #[serde_as(as = "ArkObjectBytes")] + pub X: G, + /// Called `v` in the paper + #[serde_as(as = "ArkObjectBytes")] + pub Y: G, + /// Called `h` in the paper + #[serde_as(as = "ArkObjectBytes")] + pub Z: G, +} + +impl ProvingKey { + /// Generate using a random number generator + pub fn generate_using_rng(rng: &mut R) -> ProvingKey { + ProvingKey { + X: G::Group::rand(rng).into(), + Y: G::Group::rand(rng).into(), + Z: G::Group::rand(rng).into(), + } + } + + /// Generate by hashing known strings + pub fn generate_using_hash(label: &[u8]) -> ProvingKey { + // 3 G1 elements + ProvingKey { + X: affine_group_element_from_byte_slices![label, b" : X"], + Y: affine_group_element_from_byte_slices![label, b" : Y"], + Z: affine_group_element_from_byte_slices![label, b" : Z"], + } + } +} + +impl AsRef> for ProvingKey { + fn as_ref(&self) -> &ProvingKey { + &self + } +} + +impl SchnorrChallengeContributor for ProvingKey { + fn challenge_contribution(&self, mut writer: W) -> Result<(), SchnorrError> { + self.X.serialize_compressed(&mut writer)?; + self.Y.serialize_compressed(&mut writer)?; + self.Z + .serialize_compressed(&mut writer) + .map_err(|e| e.into()) + } +} diff --git a/short_group_sig/src/error.rs b/short_group_sig/src/error.rs new file mode 100644 index 00000000..ce1af337 --- /dev/null +++ b/short_group_sig/src/error.rs @@ -0,0 +1,26 @@ +use ark_serialize::SerializationError; +use dock_crypto_utils::serde_utils::ArkSerializationError; +use schnorr_pok::error::SchnorrError; +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub enum ShortGroupSigError { + ZeroSignature, + InvalidSignature, + SchnorrError(SchnorrError), + #[serde(with = "ArkSerializationError")] + Serialization(SerializationError), + InvalidProof, +} + +impl From for ShortGroupSigError { + fn from(e: SchnorrError) -> Self { + Self::SchnorrError(e) + } +} + +impl From for ShortGroupSigError { + fn from(e: SerializationError) -> Self { + Self::Serialization(e) + } +} diff --git a/short_group_sig/src/lib.rs b/short_group_sig/src/lib.rs new file mode 100644 index 00000000..7d3991a0 --- /dev/null +++ b/short_group_sig/src/lib.rs @@ -0,0 +1,20 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(non_snake_case)] + +//! # Short group signatures +//! +//! 1. BB and Weak-BB signatures and proof of knowledge of weak-BB signature as described in the paper [Short Signatures Without Random Oracles](https://eprint.iacr.org/2004/171) +//! 2. Proof of knowledge of BB signature adapted from the paper [Proof-of-Knowledge of Representation of Committed Value and Its Applications](https://link.springer.com/chapter/10.1007/978-3-642-14081-5_22) +//! 3. An optimized implementation of proof of knowledge of weak-BB signature taken from the paper [Scalable Revocation Scheme for Anonymous Credentials Based on n-times Unlinkable Proofs](http://library.usc.edu.ph/ACM/SIGSAC%202017/wpes/p123.pdf). This does not require the prover to do pairings +//! 4. Similar to weak-BB, proof of knowledge of BB signature that does not require the prover to do pairings. + +extern crate alloc; + +pub mod bb_sig; +pub mod bb_sig_pok; +pub mod bb_sig_pok_alt; +pub mod common; +pub mod error; +pub mod weak_bb_sig; +pub mod weak_bb_sig_pok; +pub mod weak_bb_sig_pok_alt; diff --git a/smc_range_proof/src/bb_sig.rs b/short_group_sig/src/weak_bb_sig.rs similarity index 52% rename from smc_range_proof/src/bb_sig.rs rename to short_group_sig/src/weak_bb_sig.rs index ae929711..1068b898 100644 --- a/smc_range_proof/src/bb_sig.rs +++ b/short_group_sig/src/weak_bb_sig.rs @@ -1,39 +1,16 @@ -//! BB signature +//! Weak BB signature -use crate::error::SmcRangeProofError; -use ark_ec::{ - pairing::{Pairing, PairingOutput}, - AffineRepr, +use crate::{ + common::{SignatureParams, SignatureParamsWithPairing}, + error::ShortGroupSigError, }; +use ark_ec::{pairing::Pairing, AffineRepr}; use ark_ff::{Field, PrimeField, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{ - cfg_into_iter, collections::BTreeSet, ops::Neg, rand::RngCore, vec::Vec, UniformRand, -}; -use digest::Digest; +use ark_std::{rand::RngCore, vec::Vec}; +use core::ops::Neg; use zeroize::{Zeroize, ZeroizeOnDrop}; -use dock_crypto_utils::{concat_slices, hashing_utils::affine_group_elem_from_try_and_incr}; -#[cfg(feature = "parallel")] -use rayon::prelude::*; - -/// Public parameters for creating and verifying BB signatures -#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] -pub struct SignatureParams { - pub g1: E::G1Affine, - pub g2: E::G2Affine, -} - -/// `SignatureParams` with pre-computation done for protocols more efficient -#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] -pub struct SignatureParamsWithPairing { - pub g1: E::G1Affine, - pub g2: E::G2Affine, - pub g2_prepared: E::G2Prepared, - /// pairing e(g1, g2) - pub g1g2: PairingOutput, -} - /// Secret key used by the signer to sign messages #[derive( Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Zeroize, ZeroizeOnDrop, @@ -44,36 +21,12 @@ pub struct SecretKey(pub F); #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct PublicKeyG2(pub ::G2Affine); -impl SignatureParams { - pub fn new(label: &[u8]) -> Self { - let g1 = - affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : g1"]); - let g2 = - affine_group_elem_from_try_and_incr::(&concat_slices![label, b" : g2"]); - Self { g1, g2 } - } - - pub fn generate_using_rng(rng: &mut R) -> Self { - Self { - g1: E::G1::rand(rng).into(), - g2: E::G2::rand(rng).into(), - } - } - - pub fn is_valid(&self) -> bool { - !(self.g1.is_zero() || self.g2.is_zero()) - } -} +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct PreparedPublicKeyG2(pub E::G2Prepared); -impl From> for SignatureParamsWithPairing { - fn from(params: SignatureParams) -> Self { - let g1g2 = E::pairing(params.g1, params.g2); - Self { - g1: params.g1, - g2: params.g2, - g2_prepared: E::G2Prepared::from(params.g2), - g1g2, - } +impl From> for PreparedPublicKeyG2 { + fn from(pk: PublicKeyG2) -> Self { + Self(E::G2Prepared::from(pk.0)) } } @@ -81,25 +34,6 @@ impl SecretKey { pub fn new(rng: &mut R) -> Self { Self(F::rand(rng)) } - - /// Generate secret key which should not be in `0, -1, 2, .. -(base-1)` because during range proof using - /// base `base`, signature is created over `0, 1, 2, ... base-1` - pub fn new_for_base(rng: &mut R, base: u16) -> Self { - let mut sk = F::rand(rng); - let neg_bases = cfg_into_iter!(0..base) - .map(|b| F::from(b).neg()) - .collect::>(); - while neg_bases.contains(&sk) { - sk = F::rand(rng) - } - Self(sk) - } - - pub fn is_valid_for_base(&self, base: u16) -> bool { - let neg_bases = (0..base).map(|b| F::from(b).neg()); - let position = neg_bases.into_iter().position(|b| b == self.0); - position.is_none() - } } impl PublicKeyG2 @@ -140,9 +74,9 @@ impl SignatureG1 { message: &E::ScalarField, pk: &PublicKeyG2, params: &SignatureParams, - ) -> Result<(), SmcRangeProofError> { + ) -> Result<(), ShortGroupSigError> { if !self.is_non_zero() { - return Err(SmcRangeProofError::ZeroSignature); + return Err(ShortGroupSigError::ZeroSignature); } // Check e(sig, pk + g2*m) == e(g1, g2) => e(g1, g2) - e(sig, pk + g2*m) == 0 => e(g1, g2) + e(sig, -(pk + g2*m)) == 0 // gm = -g2*m - g2*x @@ -153,7 +87,7 @@ impl SignatureG1 { ) .is_zero() { - return Err(SmcRangeProofError::InvalidSignature); + return Err(ShortGroupSigError::InvalidSignature); } Ok(()) } @@ -163,15 +97,15 @@ impl SignatureG1 { message: &E::ScalarField, pk: &PublicKeyG2, params: &SignatureParamsWithPairing, - ) -> Result<(), SmcRangeProofError> { + ) -> Result<(), ShortGroupSigError> { if !self.is_non_zero() { - return Err(SmcRangeProofError::ZeroSignature); + return Err(ShortGroupSigError::ZeroSignature); } // Check e(sig, pk + g2*m) == e(g1, g2) // gm = g2*m + g2*x let gm = params.g2 * message + pk.0; if E::pairing(E::G1Prepared::from(self.0), E::G2Prepared::from(gm)) != params.g1g2 { - return Err(SmcRangeProofError::InvalidSignature); + return Err(ShortGroupSigError::InvalidSignature); } Ok(()) } @@ -181,6 +115,12 @@ impl SignatureG1 { } } +impl AsRef for SignatureG1 { + fn as_ref(&self) -> &E::G1Affine { + &self.0 + } +} + #[cfg(test)] mod tests { use super::*; @@ -190,25 +130,6 @@ mod tests { UniformRand, }; - #[test] - fn secret_key_validation() { - let mut rng = StdRng::seed_from_u64(0u64); - for base in [2, 4, 8, 16, 32, 64] { - SecretKey::::new_for_base(&mut rng, base); - } - - // Create secret key as negative of a base - let sk = SecretKey(Fr::from(32).neg()); - - for base in [2, 4, 8, 16] { - assert!(sk.is_valid_for_base(base)); - } - - for base in [33, 64, 128] { - assert!(!sk.is_valid_for_base(base)); - } - } - #[test] fn signature_verification() { let mut rng = StdRng::seed_from_u64(0u64); diff --git a/short_group_sig/src/weak_bb_sig_pok.rs b/short_group_sig/src/weak_bb_sig_pok.rs new file mode 100644 index 00000000..40c4d87c --- /dev/null +++ b/short_group_sig/src/weak_bb_sig_pok.rs @@ -0,0 +1,338 @@ +//! Proof of knowledge of weak-BB signature. Implements the protocol described in section 4 of the paper [Short Group Signatures](https://eprint.iacr.org/2004/174) + +use crate::{ + common::{ProvingKey, SignatureParams}, + error::ShortGroupSigError, + weak_bb_sig::{PublicKeyG2, SignatureG1}, +}; +use ark_ec::{ + pairing::{Pairing, PairingOutput}, + AffineRepr, CurveGroup, +}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use dock_crypto_utils::serde_utils::ArkObjectBytes; +use schnorr_pok::{ + error::SchnorrError, impl_proof_of_knowledge_of_discrete_log, SchnorrCommitment, + SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +impl_proof_of_knowledge_of_discrete_log!(RandomnessKnowledgeProtocol, RandomnessKnowledgeProof); + +/// Protocol to prove knowledge of a weak-BB signature in group G1 +#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] +pub struct PoKOfSignatureG1Protocol { + /// `u * alpha` + #[zeroize(skip)] + pub T1: E::G1Affine, + /// `v * beta` + #[zeroize(skip)] + pub T2: E::G1Affine, + /// `A + h * (alpha + beta)` + #[zeroize(skip)] + pub T3: E::G1Affine, + /// Protocol for proving knowledge of `alpha` in `T1 = u * alpha` + pub sc_T1: RandomnessKnowledgeProtocol, + /// Protocol for proving knowledge of `beta` in `T2 = v * beta` + pub sc_T2: RandomnessKnowledgeProtocol, + /// For proving knowledge of `message` and `delta_1` in `T1 * message + u * delta_1 = 0` + pub sc_T1_x: SchnorrCommitment, + /// For proving knowledge of `message` and `delta_2` in `T2 * message + v * delta_2 = 0` + pub sc_T2_x: SchnorrCommitment, + /// 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 { + /// `u * alpha` + pub T1: E::G1Affine, + /// `v * beta` + pub T2: E::G1Affine, + /// `A + h * (alpha + beta)` + pub T3: E::G1Affine, + /// Proof of knowledge of `alpha` in `T1 = u * alpha` + pub sc_T1: RandomnessKnowledgeProof, + /// Proof of knowledge of `beta` in `T2 = v * beta` + pub sc_T2: RandomnessKnowledgeProof, + /// For relation `T1 * message + u * delta_1 = 0` + pub sc_T1_x_t: E::G1Affine, + pub sc_T1_x_resp: SchnorrResponse, + /// For relation `T2 * message + v * delta_2 = 0` + pub sc_T2_x_t: E::G1Affine, + pub sc_T2_x_resp: SchnorrResponse, + /// `R_3` from the paper + pub R_3: PairingOutput, +} + +impl PoKOfSignatureG1Protocol { + pub fn init( + rng: &mut R, + signature: &SignatureG1, + message: E::ScalarField, + blinding: Option, + pk: &PublicKeyG2, + params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result { + let A = signature.0; + let alpha = E::ScalarField::rand(rng); + let beta = E::ScalarField::rand(rng); + // - message * alpha + let delta_1 = (message * alpha).neg(); + // - message * beta + let delta_2 = (message * beta).neg(); + let T1 = (proving_key.X * alpha).into_affine(); + let T2 = (proving_key.Y * beta).into_affine(); + let T3 = A + proving_key.Z * (alpha + beta); + // blinding for message + let r_x = blinding.unwrap_or_else(|| E::ScalarField::rand(rng)); + let r_alpha = E::ScalarField::rand(rng); + let r_beta = E::ScalarField::rand(rng); + let r_delta_1 = E::ScalarField::rand(rng); + let r_delta_2 = E::ScalarField::rand(rng); + let sc_T1 = RandomnessKnowledgeProtocol::init(alpha, r_alpha, &proving_key.X); + let sc_T2 = RandomnessKnowledgeProtocol::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 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( + [ + E::G1Prepared::from(T3 * r_x), + E::G1Prepared::from(proving_key.Z * (r_alpha.neg() + r_beta.neg())), + E::G1Prepared::from(proving_key.Z * (r_delta_1 + r_delta_2)), + ], + [g2_prepared.clone(), E::G2Prepared::from(pk.0), g2_prepared], + ); + Ok(Self { + T1, + T2, + T3: T3.into(), + sc_T1, + sc_T2, + sc_T1_x, + sc_T2_x, + R_3, + delta_1, + delta_2, + message, + }) + } + + pub fn challenge_contribution( + &self, + params: &SignatureParams, + proving_key: &ProvingKey, + writer: W, + ) -> Result<(), ShortGroupSigError> { + Self::compute_challenge_contribution( + &self.T1, + &self.T2, + &self.T3, + &proving_key, + ¶ms.g2, + &self.sc_T1.t, + &self.sc_T2.t, + &self.sc_T1_x.t, + &self.sc_T2_x.t, + &self.R_3, + writer, + ) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> 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 { + 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, + R_3: self.R_3, + }) + } + + pub fn compute_challenge_contribution( + T1: &E::G1Affine, + T2: &E::G1Affine, + T3: &E::G1Affine, + proving_key: &ProvingKey, + g2: &E::G2Affine, + t_T1: &E::G1Affine, + t_T2: &E::G1Affine, + t_T1_x: &E::G1Affine, + t_T2_x: &E::G1Affine, + R3: &PairingOutput, + mut writer: W, + ) -> Result<(), ShortGroupSigError> { + T1.serialize_compressed(&mut writer)?; + T2.serialize_compressed(&mut writer)?; + T3.serialize_compressed(&mut writer)?; + proving_key.X.serialize_compressed(&mut writer)?; + proving_key.Y.serialize_compressed(&mut writer)?; + proving_key.Z.serialize_compressed(&mut writer)?; + g2.serialize_compressed(&mut writer)?; + t_T1.serialize_compressed(&mut writer)?; + t_T2.serialize_compressed(&mut writer)?; + t_T1_x.serialize_compressed(&mut writer)?; + t_T2_x.serialize_compressed(&mut writer)?; + R3.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl PoKOfSignatureG1Proof { + pub fn verify( + &self, + challenge: &E::ScalarField, + pk: &PublicKeyG2, + params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result<(), ShortGroupSigError> { + if !self.sc_T1.verify(&self.T1, &proving_key.X, challenge) { + return Err(ShortGroupSigError::InvalidProof); + } + if !self.sc_T2.verify(&self.T2, &proving_key.Y, challenge) { + 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)? { + 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)?; + let g2_prepared = E::G2Prepared::from(params.g2); + let pk_prepared = E::G2Prepared::from(pk.0); + // Following is the pairing check equation from the paper converted to a single multi-pairing + if self.R_3 + != E::multi_pairing( + [ + E::G1Prepared::from(self.T3 * s_message), + E::G1Prepared::from( + 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)?), + ), + E::G1Prepared::from(self.T3 * challenge), + E::G1Prepared::from(params.g1 * challenge.neg()), + ], + [ + g2_prepared.clone(), + pk_prepared.clone(), + g2_prepared.clone(), + pk_prepared, + g2_prepared, + ], + ) + { + return Err(ShortGroupSigError::InvalidProof); + } + Ok(()) + } + + pub fn challenge_contribution( + &self, + params: &SignatureParams, + proving_key: &ProvingKey, + writer: W, + ) -> Result<(), ShortGroupSigError> { + PoKOfSignatureG1Protocol::::compute_challenge_contribution( + &self.T1, + &self.T2, + &self.T3, + &proving_key, + ¶ms.g2, + &self.sc_T1.t, + &self.sc_T2.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()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::weak_bb_sig::SecretKey; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn proof_of_knowledge_of_signature() { + let mut rng = StdRng::seed_from_u64(0u64); + let params = SignatureParams::::new::(b"test-params"); + let prk = ProvingKey::::generate_using_hash::(b"test-proving-key"); + + let sk = SecretKey::new(&mut rng); + let pk = PublicKeyG2::generate_using_secret_key(&sk, ¶ms); + let message = Fr::rand(&mut rng); + let sig = SignatureG1::new(&message, &sk, ¶ms); + + let protocol = + PoKOfSignatureG1Protocol::init(&mut rng, &sig, message, None, &pk, ¶ms, &prk) + .unwrap(); + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(¶ms, &prk, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(¶ms, &prk, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + assert_eq!(challenge_prover, challenge_verifier); + proof + .verify(&challenge_verifier, &pk, ¶ms, &prk) + .unwrap(); + } +} diff --git a/short_group_sig/src/weak_bb_sig_pok_alt.rs b/short_group_sig/src/weak_bb_sig_pok_alt.rs new file mode 100644 index 00000000..23392fab --- /dev/null +++ b/short_group_sig/src/weak_bb_sig_pok_alt.rs @@ -0,0 +1,247 @@ +//! Proof of knowledge of weak-BB signature as described in the paper [Scalable Revocation Scheme for Anonymous Credentials Based on n-times Unlinkable Proofs](http://library.usc.edu.ph/ACM/SIGSAC%202017/wpes/p123.pdf) +//! The advantage of this variation is that the prover does not need to compute any pairings +// TODO: Add proof of correctness (should i really call proof of correctness as this makes the proof/simulation happen), i.e. a tuple (G, G*x) and proof that x is the secret key + +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 dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; +use schnorr_pok::{SchnorrCommitment, SchnorrResponse}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] +pub struct PoKOfSignatureG1Protocol { + /// Randomized signature. Called `sigma'` in the paper + #[zeroize(skip)] + pub A_prime: E::G1Affine, + /// Called `sigma_bar` in the paper + #[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), +} + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct PoKOfSignatureG1Proof { + pub A_prime: E::G1Affine, + pub A_bar: E::G1Affine, + pub t: E::G1Affine, + pub sc_resp: SchnorrResponse, +} + +impl PoKOfSignatureG1Protocol { + pub fn init( + rng: &mut R, + signature: impl AsRef, + message: E::ScalarField, + blinding: Option, + g1: impl Into, + ) -> Result { + let r = E::ScalarField::rand(rng); + let blinding = blinding.unwrap_or_else(|| E::ScalarField::rand(rng)); + // A * r + let A_prime = signature.as_ref().mul_bigint(r.into_bigint()); + let A_prime_neg = A_prime.neg(); + 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_wits = (r, message); + Ok(Self { + A_prime: A_prime.into_affine(), + A_bar: A_bar.into_affine(), + sc_comm, + sc_wits, + }) + } + + pub fn challenge_contribution( + &self, + g1: impl Into, + writer: W, + ) -> Result<(), ShortGroupSigError> { + Self::compute_challenge_contribution( + &self.A_bar, + &self.A_prime, + g1, + &self.sc_comm.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 { + A_prime: self.A_prime, + A_bar: self.A_bar, + t: self.sc_comm.t, + sc_resp, + }) + } + + pub fn compute_challenge_contribution( + A_prime: &E::G1Affine, + A_bar: &E::G1Affine, + g1: impl Into, + t: &E::G1Affine, + mut writer: W, + ) -> Result<(), ShortGroupSigError> { + A_bar.serialize_compressed(&mut writer)?; + A_prime.serialize_compressed(&mut writer)?; + g1.into().serialize_compressed(&mut writer)?; + t.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl PoKOfSignatureG1Proof { + pub fn verify( + &self, + challenge: &E::ScalarField, + pk: impl Into, + g1: impl Into, + g2: impl Into, + ) -> Result<(), ShortGroupSigError> { + if self.A_prime.is_zero() { + return Err(ShortGroupSigError::InvalidProof); + } + self.sc_resp.is_valid( + &[g1.into(), self.A_prime.into_group().neg().into()], + &self.A_bar, + &self.t, + challenge, + )?; + if !E::multi_pairing( + [ + E::G1Prepared::from(self.A_bar), + E::G1Prepared::from(-(self.A_prime.into_group())), + ], + [g2.into(), pk.into()], + ) + .is_zero() + { + return Err(ShortGroupSigError::InvalidProof); + } + Ok(()) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + challenge: &E::ScalarField, + pk: impl Into, + g1: impl Into, + g2: impl Into, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), ShortGroupSigError> { + if self.A_prime.is_zero() { + return Err(ShortGroupSigError::InvalidProof); + } + self.sc_resp.is_valid( + &[g1.into(), self.A_prime.into_group().neg().into()], + &self.A_bar, + &self.t, + challenge, + )?; + pairing_checker.add_sources(&self.A_prime, pk.into(), &self.A_bar, g2); + Ok(()) + } + + pub fn challenge_contribution( + &self, + g1: impl Into, + writer: W, + ) -> Result<(), ShortGroupSigError> { + PoKOfSignatureG1Protocol::::compute_challenge_contribution( + &self.A_bar, + &self.A_prime, + g1, + &self.t, + writer, + ) + } + + pub fn get_resp_for_message(&self) -> Result<&E::ScalarField, ShortGroupSigError> { + self.sc_resp.get_response(1).map_err(|e| e.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + common::SignatureParams, + weak_bb_sig::{PreparedPublicKeyG2, PublicKeyG2, SecretKey, SignatureG1}, + }; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn proof_of_knowledge_of_signature() { + let mut rng = StdRng::seed_from_u64(0u64); + let params = SignatureParams::::new::(b"test-params"); + + let sk = SecretKey::new(&mut rng); + let pk = PublicKeyG2::generate_using_secret_key(&sk, ¶ms); + let prepared_pk = PreparedPublicKeyG2::from(pk.clone()); + let message = Fr::rand(&mut rng); + let sig = SignatureG1::new(&message, &sk, ¶ms); + + let protocol = + PoKOfSignatureG1Protocol::::init(&mut rng, sig, message, None, params.g1) + .unwrap(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(params.g1, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(params.g1, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + assert_eq!(challenge_prover, challenge_verifier); + proof + .verify( + &challenge_verifier, + prepared_pk.0.clone(), + params.g1, + params.g2, + ) + .unwrap(); + + let mut pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); + proof + .verify_with_randomized_pairing_checker( + &challenge_verifier, + prepared_pk.0, + params.g1, + params.g2, + &mut pairing_checker, + ) + .unwrap(); + assert!(pairing_checker.verify()); + } +} diff --git a/smc_range_proof/Cargo.toml b/smc_range_proof/Cargo.toml index 9a452ba7..94a04b3e 100644 --- a/smc_range_proof/Cargo.toml +++ b/smc_range_proof/Cargo.toml @@ -15,6 +15,7 @@ ark-serialize.workspace = true digest.workspace = true zeroize.workspace = true dock_crypto_utils = { version = "0.16.0", default-features = false, path = "../utils" } +short_group_sig = { version = "0.1.0", default-features = false, path = "../short_group_sig" } rayon = {workspace = true, optional = true} [dev-dependencies] @@ -24,5 +25,5 @@ schnorr_pok = { version = "0.16.0", default-features = false, path = "../schnorr [features] default = [ "parallel"] -std = [ "ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std", "dock_crypto_utils/std"] -parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-std/parallel", "rayon", "dock_crypto_utils/parallel"] \ No newline at end of file +std = [ "ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std", "dock_crypto_utils/std", "short_group_sig/std"] +parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-std/parallel", "rayon", "dock_crypto_utils/parallel", "short_group_sig/parallel"] \ No newline at end of file diff --git a/smc_range_proof/README.md b/smc_range_proof/README.md index 47d03c73..1e3167dc 100644 --- a/smc_range_proof/README.md +++ b/smc_range_proof/README.md @@ -11,7 +11,7 @@ where `u` is the base and the upper bound is a power of the base. [Code](src/ccs 5. Implements the Keyed-Verification of the above protocols where the verifier knows the secret key of the BB sig. This makes the proof generation and verification more efficient by removing the need for pairings. This idea is taken from this PhD. thesis. -Above protocols use a pairing based signature called the [BB signature](src/bb_sig.rs). +Above protocols use a pairing based signature called the Weak BB signature. References: diff --git a/smc_range_proof/src/ccs_range_proof/kv_arbitrary_range.rs b/smc_range_proof/src/ccs_range_proof/kv_arbitrary_range.rs index c3e65ee7..f184f2ec 100644 --- a/smc_range_proof/src/ccs_range_proof/kv_arbitrary_range.rs +++ b/smc_range_proof/src/ccs_range_proof/kv_arbitrary_range.rs @@ -2,19 +2,18 @@ //! secret key of the BB-sig use crate::{ - bb_sig::SecretKey, ccs_set_membership::setup::SetMembershipCheckParams, - common::MemberCommitmentKey, error::SmcRangeProofError, + ccs_set_membership::setup::SetMembershipCheckParams, common::MemberCommitmentKey, + error::SmcRangeProofError, }; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; +use crate::ccs_range_proof::util::{check_commitment_for_arbitrary_range, find_l_greater_than}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{cfg_into_iter, format, io::Write, rand::RngCore, vec::Vec, UniformRand}; -use dock_crypto_utils::misc::n_rand; - -use crate::ccs_range_proof::util::{check_commitment_for_arbitrary_range, find_l_greater_than}; -use dock_crypto_utils::msm::multiply_field_elems_with_same_group_elem; +use dock_crypto_utils::{misc::n_rand, msm::multiply_field_elems_with_same_group_elem}; #[cfg(feature = "parallel")] use rayon::prelude::*; +use short_group_sig::weak_bb_sig::SecretKey; use crate::common::padded_base_n_digits_as_field_elements; diff --git a/smc_range_proof/src/ccs_range_proof/kv_perfect_range.rs b/smc_range_proof/src/ccs_range_proof/kv_perfect_range.rs index b68a8ac5..4ee871b5 100644 --- a/smc_range_proof/src/ccs_range_proof/kv_perfect_range.rs +++ b/smc_range_proof/src/ccs_range_proof/kv_perfect_range.rs @@ -2,14 +2,15 @@ //! secret key of the BB-sig use crate::{ - bb_sig::SecretKey, ccs_set_membership::setup::SetMembershipCheckParams, - common::MemberCommitmentKey, error::SmcRangeProofError, + ccs_set_membership::setup::SetMembershipCheckParams, common::MemberCommitmentKey, + error::SmcRangeProofError, }; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{cfg_into_iter, io::Write, rand::RngCore, vec::Vec, UniformRand}; use dock_crypto_utils::{misc::n_rand, msm::multiply_field_elems_with_same_group_elem}; +use short_group_sig::weak_bb_sig::SecretKey; use crate::common::padded_base_n_digits_as_field_elements; diff --git a/smc_range_proof/src/ccs_set_membership/kv_single.rs b/smc_range_proof/src/ccs_set_membership/kv_single.rs index 7c48874a..846e10ad 100644 --- a/smc_range_proof/src/ccs_set_membership/kv_single.rs +++ b/smc_range_proof/src/ccs_set_membership/kv_single.rs @@ -2,12 +2,13 @@ //! the secret key for BB sig use crate::{ - bb_sig::SecretKey, ccs_set_membership::setup::SetMembershipCheckParams, - common::MemberCommitmentKey, error::SmcRangeProofError, + ccs_set_membership::setup::SetMembershipCheckParams, common::MemberCommitmentKey, + error::SmcRangeProofError, }; use ark_ec::{pairing::Pairing, CurveGroup}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{io::Write, rand::RngCore, vec::Vec, UniformRand}; +use short_group_sig::weak_bb_sig::SecretKey; #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct SetMembershipCheckWithKVProtocol { @@ -156,7 +157,7 @@ impl SetMembershipCheckWithKVProof { #[cfg(test)] mod tests { use super::*; - use crate::{bb_sig::SignatureParams, ccs_set_membership::setup::SetMembershipCheckParams}; + use crate::ccs_set_membership::setup::SetMembershipCheckParams; use ark_bls12_381::{Bls12_381, Fr}; use ark_std::{ rand::{rngs::StdRng, SeedableRng}, @@ -165,6 +166,7 @@ mod tests { use blake2::Blake2b512; use dock_crypto_utils::misc::n_rand; use schnorr_pok::compute_random_oracle_challenge; + use short_group_sig::common::SignatureParams; #[test] fn membership_check() { diff --git a/smc_range_proof/src/ccs_set_membership/setup.rs b/smc_range_proof/src/ccs_set_membership/setup.rs index 0789433b..cb000b50 100644 --- a/smc_range_proof/src/ccs_set_membership/setup.rs +++ b/smc_range_proof/src/ccs_set_membership/setup.rs @@ -1,13 +1,15 @@ -use crate::{ - bb_sig::{PublicKeyG2, SecretKey, SignatureG1, SignatureParams, SignatureParamsWithPairing}, - error::SmcRangeProofError, -}; +use crate::error::SmcRangeProofError; use ark_ec::{pairing::Pairing, AffineRepr}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{cfg_into_iter, cfg_iter, io::Write, rand::RngCore, vec::Vec}; use digest::Digest; use dock_crypto_utils::msm::multiply_field_elems_with_same_group_elem; +use short_group_sig::{ + common::{SignatureParams, SignatureParamsWithPairing}, + weak_bb_sig::{PublicKeyG2, SecretKey, SignatureG1}, +}; +use crate::common::generate_secret_key_for_base; #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -113,6 +115,14 @@ impl SetMembershipCheckParams { sig_params: SignatureParams, ) -> (Self, SecretKey) { let sk = SecretKey::new(rng); + Self::new_given_sig_params_and_secret_key(set, sig_params, sk) + } + + pub fn new_given_sig_params_and_secret_key( + set: Vec, + sig_params: SignatureParams, + sk: SecretKey, + ) -> (Self, SecretKey) { let pk = PublicKeyG2::generate_using_secret_key(&sk, &sig_params); let sigs = cfg_iter!(set) .map(|i| SignatureG1::new(i, &sk, &sig_params)) @@ -137,7 +147,8 @@ impl SetMembershipCheckParams { let set = cfg_into_iter!(0..base) .map(|i| E::ScalarField::from(i)) .collect(); - Self::new_given_sig_params(rng, set, sig_params) + let sk = generate_secret_key_for_base::(rng, base); + Self::new_given_sig_params_and_secret_key(set, sig_params, sk) } /// Verify each signature in the params diff --git a/smc_range_proof/src/ccs_set_membership/single_member.rs b/smc_range_proof/src/ccs_set_membership/single_member.rs index 48f5f8dc..d8902e8d 100644 --- a/smc_range_proof/src/ccs_set_membership/single_member.rs +++ b/smc_range_proof/src/ccs_set_membership/single_member.rs @@ -168,11 +168,8 @@ impl SetMembershipCheckProof { #[cfg(test)] mod tests { use super::*; - use crate::{ - bb_sig::SignatureParams, - ccs_set_membership::setup::{ - SetMembershipCheckParams, SetMembershipCheckParamsWithPairing, - }, + use crate::ccs_set_membership::setup::{ + SetMembershipCheckParams, SetMembershipCheckParamsWithPairing, }; use ark_bls12_381::{Bls12_381, Fr}; use ark_std::{ @@ -182,6 +179,7 @@ mod tests { use blake2::Blake2b512; use dock_crypto_utils::misc::n_rand; use schnorr_pok::compute_random_oracle_challenge; + use short_group_sig::common::SignatureParams; #[test] fn membership_check() { diff --git a/smc_range_proof/src/cls_range_proof/kv_range_proof.rs b/smc_range_proof/src/cls_range_proof/kv_range_proof.rs index 4445b9fc..168757b2 100644 --- a/smc_range_proof/src/cls_range_proof/kv_range_proof.rs +++ b/smc_range_proof/src/cls_range_proof/kv_range_proof.rs @@ -11,8 +11,8 @@ use crate::{ }; use dock_crypto_utils::misc::n_rand; -use crate::prelude::SecretKey; use dock_crypto_utils::{ff::inner_product, msm::multiply_field_elems_with_same_group_elem}; +use short_group_sig::weak_bb_sig::SecretKey; #[cfg(feature = "parallel")] use rayon::prelude::*; diff --git a/smc_range_proof/src/common.rs b/smc_range_proof/src/common.rs index 1a2f5b4e..a97c0e54 100644 --- a/smc_range_proof/src/common.rs +++ b/smc_range_proof/src/common.rs @@ -2,7 +2,9 @@ use ark_ec::AffineRepr; use ark_ff::PrimeField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{rand::RngCore, vec::Vec}; +use ark_std::{ + cfg_into_iter, collections::BTreeSet, ops::Neg, rand::RngCore, vec::Vec, UniformRand, +}; use digest::Digest; use dock_crypto_utils::{ concat_slices, @@ -11,6 +13,14 @@ use dock_crypto_utils::{ misc::rand, }; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +pub use short_group_sig::{ + common::{SignatureParams, SignatureParamsWithPairing}, + weak_bb_sig::{PublicKeyG2, SecretKey, SignatureG1}, +}; + /// Commitment key to commit the set member #[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct MemberCommitmentKey { @@ -100,6 +110,26 @@ macro_rules! randomize_sigs { }}; } +pub fn generate_secret_key_for_base( + rng: &mut R, + base: u16, +) -> SecretKey { + let mut sk = F::rand(rng); + let neg_bases = cfg_into_iter!(0..base) + .map(|b| F::from(b).neg()) + .collect::>(); + while neg_bases.contains(&sk) { + sk = F::rand(rng) + } + SecretKey(sk) +} + +pub fn is_secret_key_valid_for_base(sk: &SecretKey, base: u16) -> bool { + let neg_bases = (0..base).map(|b| F::from(b).neg()); + let position = neg_bases.into_iter().position(|b| b == sk.0); + position.is_none() +} + #[cfg(test)] mod tests { use super::*; @@ -134,4 +164,23 @@ mod tests { let comm_d = comm_key.commit_decomposed(base, &digits, &randomness); assert_eq!(comm, comm_d) } + + #[test] + fn secret_key_validation() { + let mut rng = StdRng::seed_from_u64(0u64); + for base in [2, 4, 8, 16, 32, 64] { + generate_secret_key_for_base::(&mut rng, base); + } + + // Create secret key as negative of a base + let sk = SecretKey(Fr::from(32).neg()); + + for base in [2, 4, 8, 16] { + assert!(is_secret_key_valid_for_base(&sk, base)); + } + + for base in [33, 64, 128] { + assert!(!is_secret_key_valid_for_base(&sk, base)); + } + } } diff --git a/smc_range_proof/src/error.rs b/smc_range_proof/src/error.rs index 0feb19e6..655a2938 100644 --- a/smc_range_proof/src/error.rs +++ b/smc_range_proof/src/error.rs @@ -1,5 +1,6 @@ use ark_serialize::SerializationError; use ark_std::string::String; +use short_group_sig::error::ShortGroupSigError; #[derive(Debug)] pub enum SmcRangeProofError { @@ -13,6 +14,7 @@ pub enum SmcRangeProofError { InvalidRange(u64, u16), IncorrectBounds(String), Serialization(SerializationError), + ShortGroupSig(ShortGroupSigError), } impl From for SmcRangeProofError { @@ -20,3 +22,9 @@ impl From for SmcRangeProofError { Self::Serialization(e) } } + +impl From for SmcRangeProofError { + fn from(e: ShortGroupSigError) -> Self { + Self::ShortGroupSig(e) + } +} diff --git a/smc_range_proof/src/lib.rs b/smc_range_proof/src/lib.rs index 1459943b..f865f740 100644 --- a/smc_range_proof/src/lib.rs +++ b/smc_range_proof/src/lib.rs @@ -10,7 +10,7 @@ //! 5. Implements the Keyed-Verification of the above protocols where the verifier knows the secret key of the BB sig. This makes //! the proof generation and verification more efficient by removing the need for pairings. This idea is taken from this PhD. thesis. //! -//! Above protocols use a pairing based signature called the [BB signature](src/bb_sig.rs). +//! Above protocols use a pairing based signature called the Weak BB signature. //! //! References: //! @@ -20,7 +20,6 @@ #[macro_use] pub mod common; -pub mod bb_sig; pub mod ccs_range_proof; pub mod ccs_set_membership; mod cls_range_proof; @@ -28,7 +27,6 @@ pub mod error; pub mod prelude { pub use crate::{ - bb_sig::{PublicKeyG2, SecretKey, SignatureParams, SignatureParamsWithPairing}, ccs_range_proof::{ CCSArbitraryRangeProof, CCSArbitraryRangeProofProtocol, CCSArbitraryRangeProofWithKVProtocol, CCSArbitraryRangeWithKVProof, @@ -39,7 +37,10 @@ pub mod prelude { cls_range_proof::{ CLSRangeProof, CLSRangeProofProtocol, CLSRangeProofWithKV, CLSRangeProofWithKVProtocol, }, - common::MemberCommitmentKey, + common::{ + MemberCommitmentKey, PublicKeyG2, SecretKey, SignatureG1, SignatureParams, + SignatureParamsWithPairing, + }, error::SmcRangeProofError, }; } diff --git a/utils/Cargo.toml b/utils/Cargo.toml index c1cdb837..162fba04 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -22,17 +22,21 @@ digest.workspace = true serde.workspace = true serde_with.workspace = true rayon = {workspace = true, optional = true} -merlin = { package = "dock_merlin", version = "2.0", default-features = false, path = "../merlin" } +merlin = { package = "dock_merlin", version = "3.0.0", default-features = false, path = "../merlin" } itertools.workspace = true num = { version = "0.4.1", default-features = false } +hkdf = {version = "0.12.3", default-features = false} +sha2 = {version = "0.10.8", default-features = false} +aead = {version = "0.5.2", default-features = false, features = [ "alloc" ]} [dev-dependencies] blake2.workspace = true ark-bls12-381.workspace = true +chacha20poly1305 = {version = "0.10.1", default-features = false} [features] default = ["parallel"] -std = ["ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std", "serde/std", "ark-poly/std", "merlin/std", "num/std"] +std = ["ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std", "serde/std", "ark-poly/std", "merlin/std", "num/std", "hkdf/std", "sha2/std", "chacha20poly1305/std"] print-trace = ["ark-std/print-trace"] parallel = ["std", "ark-ff/parallel", "ark-ec/parallel", "ark-poly/parallel", "ark-std/parallel", "rayon"] #with-serde = ["serde", "serde_with"] \ No newline at end of file diff --git a/utils/src/ecies.rs b/utils/src/ecies.rs new file mode 100644 index 00000000..4b5fb38a --- /dev/null +++ b/utils/src/ecies.rs @@ -0,0 +1,136 @@ +//! Elliptic Curve Integrated Encryption Scheme (ECIES) + +use crate::elgamal::keygen; +use aead::{generic_array::GenericArray, Aead, KeyInit}; +use ark_ec::AffineRepr; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{rand::RngCore, vec, vec::Vec}; +// use digest::{ +// core_api::{BlockSizeUser, CoreProxy}, +// Digest, FixedOutputReset, HashMarker, OutputSizeUser, +// }; +use hkdf::Hkdf; +use sha2::Sha256; + +/*pub trait Hash: +Default ++ HashMarker ++ OutputSizeUser> ++ BlockSizeUser ++ FixedOutputReset ++ CoreProxy ++ Clone + where + ::Core: ProxyHash, + <::Core as BlockSizeUser>::BlockSize: IsLess, + Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, +{ +}*/ + +// TODO: Make hash function generic + +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Encryption { + pub ephemeral_pk: G, + pub nonce: [u8; NONCE_BYTE_SIZE], + pub ciphertext: Vec, +} + +impl + Encryption +{ + pub fn encrypt( + rng: &mut R, + msg: &[u8], + other_pk: &G, + gen: &G, + salt: Option<&[u8]>, + info: Option<&[u8]>, + ) -> Self { + let (sk, pk) = keygen::(rng, gen); + let shared_secret = *other_pk * sk.0; + let mut shared_secret_bytes = vec![]; + shared_secret + .serialize_compressed(&mut shared_secret_bytes) + .unwrap(); + let hk = Hkdf::::new(salt, &shared_secret_bytes); + let mut sym_key = [0u8; KEY_BYTE_SIZE]; + // TODO: Fix unwrap + hk.expand(info.unwrap_or_else(|| &[]), &mut sym_key) + .unwrap(); + let mut nonce = [0u8; NONCE_BYTE_SIZE]; + rng.fill_bytes(&mut nonce); + let cipher = A::new(GenericArray::from_slice(&sym_key)); + // TODO: Fix unwrap + let ciphertext = cipher + .encrypt(GenericArray::from_slice(&nonce), msg) + .unwrap(); + Self { + ciphertext, + nonce, + ephemeral_pk: pk.0, + } + } + + pub fn decrypt( + self, + sk: &G::ScalarField, + salt: Option<&[u8]>, + info: Option<&[u8]>, + ) -> Vec { + let shared_secret = self.ephemeral_pk * sk; + let mut shared_secret_bytes = vec![]; + shared_secret + .serialize_compressed(&mut shared_secret_bytes) + .unwrap(); + let hk = Hkdf::::new(salt, &shared_secret_bytes); + let mut sym_key = [0u8; KEY_BYTE_SIZE]; + // TODO: Fix unwrap + hk.expand(info.unwrap_or_else(|| &[]), &mut sym_key) + .unwrap(); + let cipher = A::new(GenericArray::from_slice(&sym_key)); + // TODO: Fix unwrap + cipher + .decrypt( + &GenericArray::from_slice(&self.nonce), + self.ciphertext.as_ref(), + ) + .unwrap() + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bls12_381::{G1Affine, G2Affine}; + use ark_ec::CurveGroup; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use chacha20poly1305::XChaCha20Poly1305; + + #[test] + fn encrypt_decrypt() { + let mut rng = StdRng::seed_from_u64(0u64); + + fn check(rng: &mut StdRng) { + let gen = G::Group::rand(rng).into_affine(); + let (sk, pk) = keygen(rng, &gen); + let mut msg = vec![]; + let r = G::ScalarField::rand(rng); + r.serialize_compressed(&mut msg).unwrap(); + let enc = Encryption::::encrypt::<_, XChaCha20Poly1305>( + rng, &msg, &pk.0, &gen, None, None, + ); + let decrypted = enc.decrypt::(&sk.0, None, None); + assert_eq!(msg, decrypted); + let decrypted_r: G::ScalarField = + CanonicalDeserialize::deserialize_compressed(&decrypted[..]).unwrap(); + assert_eq!(decrypted_r, r); + } + + check::(&mut rng); + check::(&mut rng); + } +} diff --git a/utils/src/elgamal.rs b/utils/src/elgamal.rs new file mode 100644 index 00000000..8ccba02b --- /dev/null +++ b/utils/src/elgamal.rs @@ -0,0 +1,97 @@ +//! Elgamal encryption + +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ops::Neg, rand::RngCore, vec::Vec, UniformRand}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +#[derive( + Clone, Debug, PartialEq, Eq, Zeroize, ZeroizeOnDrop, CanonicalSerialize, CanonicalDeserialize, +)] +pub struct SecretKey(pub F); + +#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)] +pub struct PublicKey(pub G); + +impl SecretKey { + pub fn new(rng: &mut R) -> Self { + Self(F::rand(rng)) + } +} + +impl PublicKey { + pub fn new(secret_key: &SecretKey, gen: &G) -> Self { + Self(gen.mul_bigint(secret_key.0.into_bigint()).into_affine()) + } +} + +pub fn keygen( + rng: &mut R, + gen: &G, +) -> (SecretKey, PublicKey) { + let sk = SecretKey::new(rng); + let pk = PublicKey::new(&sk, gen); + (sk, pk) +} + +/// Elgamal encryption of a group element `m` +#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Ciphertext { + /// `m + r * pk` + pub enc1: G, + /// Ephemeral public key `r * gen` + pub enc2: G, +} + +impl Ciphertext { + /// Returns the ciphertext and randomness created for encryption + pub fn new( + rng: &mut R, + msg: &G, + public_key: &G, + gen: &G, + ) -> (Self, G::ScalarField) { + let alpha = G::ScalarField::rand(rng); + let alpha_bi = alpha.into_bigint(); + let enc1 = (public_key.mul_bigint(alpha_bi) + msg).into_affine(); + ( + Self { + enc1, + enc2: gen.mul_bigint(alpha_bi).into_affine(), + }, + alpha, + ) + } + + pub fn decrypt(&self, secret_key: &G::ScalarField) -> G { + (self.enc2.mul(secret_key).neg() + self.enc1).into_affine() + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bls12_381::{G1Affine, G2Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + + #[test] + fn encrypt_decrypt() { + let mut rng = StdRng::seed_from_u64(0u64); + + fn check(rng: &mut StdRng) { + let gen = G::Group::rand(rng).into_affine(); + let (sk, pk) = keygen(rng, &gen); + + let msg = G::Group::rand(rng).into_affine(); + let (ciphertext, _) = Ciphertext::new(rng, &msg, &pk.0, &gen); + assert_eq!(ciphertext.decrypt(&sk.0), msg); + } + + check::(&mut rng); + check::(&mut rng); + } +} diff --git a/utils/src/ff.rs b/utils/src/ff.rs index 573136ee..9a3034b8 100644 --- a/utils/src/ff.rs +++ b/utils/src/ff.rs @@ -4,21 +4,25 @@ use ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, rand::Rng, vec::Vec}; #[cfg(feature = "parallel")] use rayon::prelude::*; -/// Inner product of 2 vectors `a` and `b` -pub fn inner_product(a: &[F], b: &[F]) -> F { - let size = a.len().min(b.len()); +#[macro_export] +macro_rules! cfg_iter_sum { + ($iter: expr, $initial: tt) => {{ + #[cfg(feature = "parallel")] + let result = $iter.reduce($initial, |a, b| a + b); - #[cfg(feature = "parallel")] - let sum = cfg_into_iter!(0..size) - .map(|i| a[i] * b[i]) - .reduce(|| F::zero(), |accum, v| accum + v); + #[cfg(not(feature = "parallel"))] + let result = $iter.fold($initial(), |a, b| a + b); - #[cfg(not(feature = "parallel"))] - let sum = (0..size) - .map(|i| a[i] * b[i]) - .fold(F::zero(), |accum, v| accum + v); + result + }}; +} - sum +/// Inner product of 2 vectors `a` and `b` +pub fn inner_product(a: &[F], b: &[F]) -> F { + let size = a.len().min(b.len()); + let product = cfg_into_iter!(0..size).map(|i| a[i] * b[i]); + let zero = F::zero; + cfg_iter_sum!(product, zero) } /// Hadamard product of two vectors of scalars @@ -49,17 +53,10 @@ pub fn weighted_inner_product(a: &[F], b: &[F], w: &F) -> F { let mut weights = powers(w, size as u32 + 1); weights.remove(0); - #[cfg(feature = "parallel")] - let sum = cfg_into_iter!(0..size) - .map(|i| a[i] * b[i] * weights[i]) - .reduce(|| F::zero(), |accum, v| accum + v); - - #[cfg(not(feature = "parallel"))] - let sum = (0..size) - .map(|i| a[i] * b[i] * weights[i]) - .fold(F::zero(), |accum, v| accum + v); + let product = cfg_into_iter!(0..size).map(|i| a[i] * b[i] * weights[i]); - sum + let zero = F::zero; + cfg_iter_sum!(product, zero) } /// Weighted inner product of the vector `n` with itself. Calculated as `\sum_{i=0}(n_i * n_i * w^{i+1})` diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 9b70ccf8..373f1b35 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -1,12 +1,16 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +extern crate core; pub mod aliases; pub mod extend_some; // TODO: Feature gate this #[macro_use] pub mod serde_utils; +pub mod ecies; +pub mod elgamal; +#[macro_use] pub mod ff; pub mod hashing_utils; pub mod iter; diff --git a/utils/src/msm.rs b/utils/src/msm.rs index bc85307a..13bb206b 100644 --- a/utils/src/msm.rs +++ b/utils/src/msm.rs @@ -69,7 +69,7 @@ pub mod tests { use ark_ec::{ pairing::Pairing, scalar_mul::wnaf::WnafContext, AffineRepr, CurveGroup, VariableBaseMSM, }; - use ark_ff::PrimeField; + use ark_ff::{PrimeField, Zero}; use ark_std::{ cfg_iter, rand::{rngs::StdRng, SeedableRng}, @@ -81,6 +81,38 @@ pub mod tests { type Fr = ::ScalarField; type G1 = ::G1; + type G2 = ::G2; + + #[test] + fn temp() { + let mut rng = StdRng::seed_from_u64(0u64); + let g1 = G1::rand(&mut rng); + let g2 = G2::rand(&mut rng); + fn get_bytes_g1(g: &G1, compressed: bool) -> Vec { + let mut b = vec![]; + if compressed { + g.serialize_compressed(&mut b).unwrap(); + } else { + g.serialize_uncompressed(&mut b).unwrap(); + } + return b; + } + fn get_bytes_g2(g: &G2, compressed: bool) -> Vec { + let mut b = vec![]; + if compressed { + g.serialize_compressed(&mut b).unwrap(); + } else { + g.serialize_uncompressed(&mut b).unwrap(); + } + return b; + } + println!("g1 compressed {:?}", get_bytes_g1(&g1, true)); + println!("g1 uncompressed {:?}", get_bytes_g1(&g1, false)); + println!("g2 compressed {:?}", get_bytes_g2(&g2, true)); + println!("g2 uncompressed {:?}", get_bytes_g2(&g2, false)); + println!("g1 zero compressed {:?}", get_bytes_g1(&G1::zero(), true)); + println!("g2 zero compressed {:?}", get_bytes_g2(&G2::zero(), true)); + } #[test] fn timing_ark_ops() { diff --git a/utils/src/poly.rs b/utils/src/poly.rs index 03c14aa9..c1aaf656 100644 --- a/utils/src/poly.rs +++ b/utils/src/poly.rs @@ -1,6 +1,6 @@ use ark_ff::{PrimeField, Zero}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; -use ark_std::{cfg_into_iter, vec::Vec}; +use ark_std::{cfg_into_iter, vec, vec::Vec}; #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -30,31 +30,26 @@ pub fn multiply_many_polys(polys: Vec>) -> Den .reduce(|a, b| multiply_poly(&a, &b)) .unwrap(); + let one = || DensePolynomial::from_coefficients_vec(vec![F::one()]); #[cfg(feature = "parallel")] - let r = polys.into_par_iter().reduce( - || DensePolynomial::from_coefficients_vec(vec![F::one()]), - |a, b| multiply_poly(&a, &b), - ); + let r = polys + .into_par_iter() + .reduce(one, |a, b| multiply_poly(&a, &b)); r } /// Given a vector of polynomials `polys` and scalars `coeffs`, return their inner product `polys[0] * coeffs[0] + polys[1] * coeffs[1] + ...` pub fn inner_product_poly( - polys: Vec>, + polys: &[DensePolynomial], coeffs: Vec, ) -> DensePolynomial { let product = cfg_into_iter!(coeffs) .zip(cfg_into_iter!(polys)) - .map(|(f, p)| &p * f); - - #[cfg(feature = "parallel")] - let sum = product.reduce(DensePolynomial::zero, |a, b| a + b); - - #[cfg(not(feature = "parallel"))] - let sum = product.fold(DensePolynomial::zero(), |a, b| a + b); + .map(|(f, p)| p * f); - sum + let zero = DensePolynomial::zero; + cfg_iter_sum!(product, zero) } /// Create a polynomial from given `roots` as `(x-roots[0])*(x-roots[1])*(x-roots[2])*..` @@ -64,19 +59,10 @@ pub fn poly_from_roots(roots: &[F]) -> DensePolynomial { } // [(x-roots[0]), (x-roots[1]), (x-roots[2]), ..., (x-roots[last])] - - #[cfg(not(feature = "parallel"))] - let x_i = roots - .iter() + let terms = cfg_into_iter!(roots) .map(|i| DensePolynomial::from_coefficients_slice(&[-*i, F::one()])) .collect::>(); - #[cfg(feature = "parallel")] - let x_i = roots - .par_iter() - .map(|i| DensePolynomial::from_coefficients_slice(&[-*i, F::one()])) - .collect(); - // Product (x-roots[0]) * (x-roots[1]) * (x-roots[2]) * ... * (x-roots[last]) - multiply_many_polys(x_i) + multiply_many_polys(terms) } diff --git a/utils/src/transcript.rs b/utils/src/transcript.rs index e8c98101..ef24154e 100644 --- a/utils/src/transcript.rs +++ b/utils/src/transcript.rs @@ -1,12 +1,39 @@ use ark_ec::AffineRepr; use ark_ff::fields::Field; -use ark_serialize::CanonicalSerialize; -use ark_std::{vec, vec::Vec}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ + io::{Result as ArkResult, Write}, + vec, + vec::Vec, +}; pub use merlin::Transcript as Merlin; +use serde::{Deserialize, Serialize}; +use zeroize::{Zeroize, ZeroizeOnDrop}; /// must be specific to the application. -pub fn new_merlin_transcript(label: &'static [u8]) -> impl Transcript + Clone { - Merlin::new(label) +pub fn new_merlin_transcript(label: &'static [u8]) -> impl Transcript + Clone + Write { + MerlinTranscript::new(label) +} + +#[derive( + Clone, Zeroize, ZeroizeOnDrop, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct MerlinTranscript { + merlin: Merlin, + next_label: Vec, +} + +impl MerlinTranscript { + pub fn new(label: &'static [u8]) -> Self { + Self { + merlin: Merlin::new(label), + next_label: b"".to_vec(), + } + } + + pub fn set_label(&mut self, label: &'static [u8]) { + self.next_label = label.to_vec(); + } } /// Transcript is the application level transcript to derive the challenges @@ -14,31 +41,126 @@ pub fn new_merlin_transcript(label: &'static [u8]) -> impl Transcript + Clone { /// prover/verifier so that the transcript can be fed with any other data first. /// Taken from pub trait Transcript { - fn append(&mut self, label: &'static [u8], point: &S); + fn append(&mut self, label: &'static [u8], element: &S); + fn append_without_static_label(&mut self, label: &[u8], element: &S); fn append_message(&mut self, label: &'static [u8], bytes: &[u8]); + fn append_message_without_static_label(&mut self, label: &[u8], bytes: &[u8]); fn challenge_scalar(&mut self, label: &'static [u8]) -> F; + fn challenge_scalar_without_static_label(&mut self, label: &[u8]) -> F; fn challenge_scalars(&mut self, label: &'static [u8], count: usize) -> Vec; + fn challenge_scalars_without_static_label( + &mut self, + label: &[u8], + count: usize, + ) -> Vec; fn challenge_group_elem(&mut self, label: &'static [u8]) -> G; + fn challenge_group_elem_without_static_label(&mut self, label: &[u8]) -> G; } -impl Transcript for Merlin { +impl Transcript for MerlinTranscript { + fn append_without_static_label(&mut self, label: &[u8], element: &S) { + let mut buff: Vec = vec![0; element.compressed_size()]; + element + .serialize_compressed(&mut buff) + .expect("serialization failed"); + self.merlin + .append_message_with_non_static_label(label, &buff); + } + + fn append_message_without_static_label(&mut self, label: &[u8], bytes: &[u8]) { + self.merlin + .append_message_with_non_static_label(label, bytes) + } + + fn challenge_scalar_without_static_label(&mut self, label: &[u8]) -> F { + // Reduce a double-width scalar to ensure a uniform distribution + // TODO: It assumes 32 byte field element. Make it generic + let mut buf = [0; 64]; + self.merlin + .challenge_bytes_with_non_static_label(label, &mut buf); + let mut counter = 0; + loop { + let c = F::from_random_bytes(&buf); + if let Some(chal) = c { + if let Some(c_inv) = chal.inverse() { + return c_inv; + } + } + + buf[0] = counter; + counter += 1; + self.merlin + .challenge_bytes_with_non_static_label(label, &mut buf); + } + } + + fn challenge_scalars_without_static_label( + &mut self, + label: &[u8], + count: usize, + ) -> Vec { + // Reduce a double-width scalar to ensure a uniform distribution + // TODO: It assumes 32 byte field element. Make it generic + let mut buf = vec![0; count * 64]; + self.merlin + .challenge_bytes_with_non_static_label(label, &mut buf); + let mut out = Vec::with_capacity(count); + for i in 0..count { + let mut counter = 0; + let start = i * 64; + let end = (i + 1) * 64; + loop { + let c = F::from_random_bytes(&buf[start..end]); + if let Some(chal) = c { + if let Some(c_inv) = chal.inverse() { + out.push(c_inv); + break; + } + } + buf[start] = counter; + counter += 1; + self.merlin + .challenge_bytes_with_non_static_label(label, &mut buf[start..end]); + } + } + out + } + + fn challenge_group_elem_without_static_label(&mut self, label: &[u8]) -> G { + let mut buf = [0; 64]; + self.merlin + .challenge_bytes_with_non_static_label(label, &mut buf); + let mut counter = 0; + loop { + let c = G::from_random_bytes(&buf); + if let Some(chal) = c { + return chal; + } + + buf[0] = counter; + counter += 1; + self.merlin + .challenge_bytes_with_non_static_label(label, &mut buf); + } + } + fn append(&mut self, label: &'static [u8], element: &S) { let mut buff: Vec = vec![0; element.compressed_size()]; element .serialize_compressed(&mut buff) .expect("serialization failed"); - self.append_message(label, &buff); + self.merlin.append_message(label, &buff); } fn append_message(&mut self, label: &'static [u8], bytes: &[u8]) { - self.append_message(label, bytes) + self.merlin.append_message(label, bytes) } fn challenge_scalar(&mut self, label: &'static [u8]) -> F { // Reduce a double-width scalar to ensure a uniform distribution // TODO: It assumes 32 byte field element. Make it generic let mut buf = [0; 64]; - self.challenge_bytes(label, &mut buf); + self.merlin.challenge_bytes(label, &mut buf); let mut counter = 0; loop { let c = F::from_random_bytes(&buf); @@ -50,7 +172,7 @@ impl Transcript for Merlin { buf[0] = counter; counter += 1; - self.challenge_bytes(label, &mut buf); + self.merlin.challenge_bytes(label, &mut buf); } } @@ -58,7 +180,7 @@ impl Transcript for Merlin { // Reduce a double-width scalar to ensure a uniform distribution // TODO: It assumes 32 byte field element. Make it generic let mut buf = vec![0; count * 64]; - self.challenge_bytes(label, &mut buf); + self.merlin.challenge_bytes(label, &mut buf); let mut out = Vec::with_capacity(count); for i in 0..count { let mut counter = 0; @@ -74,7 +196,7 @@ impl Transcript for Merlin { } buf[start] = counter; counter += 1; - self.challenge_bytes(label, &mut buf[start..end]); + self.merlin.challenge_bytes(label, &mut buf[start..end]); } } out @@ -82,7 +204,7 @@ impl Transcript for Merlin { fn challenge_group_elem(&mut self, label: &'static [u8]) -> G { let mut buf = [0; 64]; - self.challenge_bytes(label, &mut buf); + self.merlin.challenge_bytes(label, &mut buf); let mut counter = 0; loop { let c = G::from_random_bytes(&buf); @@ -92,11 +214,24 @@ impl Transcript for Merlin { buf[0] = counter; counter += 1; - self.challenge_bytes(label, &mut buf); + self.merlin.challenge_bytes(label, &mut buf); } } } +impl Write for MerlinTranscript { + fn write(&mut self, data: &[u8]) -> ArkResult { + self.merlin + .append_message_with_non_static_label(&self.next_label, data); + Ok(data.len()) + } + + #[inline] + fn flush(&mut self) -> ArkResult<()> { + Ok(()) + } +} + // TODO: Impl Write trait for Merlin // TODO: Support domain-separator function that adds a label to transcript. One approach is to have MerlinTranscript struct // that has a mutable field called write_label set which is used in call to `append_message` diff --git a/vb_accumulator/Cargo.toml b/vb_accumulator/Cargo.toml index 95226730..4df47072 100644 --- a/vb_accumulator/Cargo.toml +++ b/vb_accumulator/Cargo.toml @@ -24,6 +24,7 @@ serde_with.workspace = true zeroize.workspace = true schnorr_pok = { version = "0.16.0", default-features = false, path = "../schnorr_pok" } dock_crypto_utils = { version = "0.16.0", default-features = false, path = "../utils" } +short_group_sig = { version = "0.1.0", default-features = false, path = "../short_group_sig" } [dev-dependencies] blake2.workspace = true @@ -33,6 +34,6 @@ rmp-serde = "1.0" [features] default = [ "parallel" ] -std = [ "ark-ff/std", "ark-ec/std", "ark-poly/std", "ark-std/std", "ark-serialize/std", "schnorr_pok/std", "dock_crypto_utils/std", "serde/std"] +std = [ "ark-ff/std", "ark-ec/std", "ark-poly/std", "ark-std/std", "ark-serialize/std", "schnorr_pok/std", "dock_crypto_utils/std", "serde/std", "short_group_sig/std"] print-trace = [ "ark-std/print-trace", "schnorr_pok/print-trace", "dock_crypto_utils/print-trace" ] -parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-poly/parallel", "ark-std/parallel", "rayon", "schnorr_pok/parallel", "dock_crypto_utils/parallel" ] \ No newline at end of file +parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-poly/parallel", "ark-std/parallel", "rayon", "schnorr_pok/parallel", "dock_crypto_utils/parallel", "short_group_sig/parallel"] \ No newline at end of file diff --git a/vb_accumulator/README.md b/vb_accumulator/README.md index 68a11429..9920ad4e 100644 --- a/vb_accumulator/README.md +++ b/vb_accumulator/README.md @@ -1,7 +1,10 @@ -# vb_accumulator + +# Accumulators based on bilinear map (pairings) + +## vb_accumulator Dynamic Positive and Universal accumulators according to the paper: [Dynamic Universal Accumulator with Batch Update over Bilinear Groups](https://eprint.iacr.org/2020/777) -Provides +Implements - a dynamic positive accumulator [`PositiveAccumulator`], that supports membership proofs. - a dynamic universal accumulator [`UniversalAccumulator`], that supports membership and non-membership proofs. - a zero knowledge proof of membership and non-membership in the accumulators with [`ProofProtocol`]. @@ -17,13 +20,26 @@ Most of the update logic is in the trait [`Witness`] which is implemented by bot and [`NonMembershipWitness`]. The implementation tries to use the same variable names as the paper and thus violate Rust's naming conventions at places. -[`Accumulator`]: crate::positive::Accumulator -[`PositiveAccumulator`]: crate::positive::PositiveAccumulator -[`UniversalAccumulator`]: crate::universal::UniversalAccumulator -[`MembershipWitness`]: crate::witness::MembershipWitness -[`NonMembershipWitness`]: crate::witness::NonMembershipWitness -[`Witness`]: crate::witness::Witness -[`Omega`]: crate::batch_utils::Omega -[`ProofProtocol`]: crate::proofs::ProofProtocol +## kb_accumulator +Dynamic Positive and Universal accumulators according to the paper: [Efficient Constructions of Pairing Based Accumulators](https://eprint.iacr.org/2021/638) +Implements +- a dynamic positive accumulator [`KBPositiveAccumulator`], that supports membership proofs. Based on construction 2 in the paper +- a dynamic universal accumulator [`KBUniversalAccumulator`], that supports membership and non-membership proofs. Based on construction 3 in the paper +- zero knowledge proofs of membership and non-membership in the accumulators + +Allows batch updates to the accumulator and the witness using the techniques from `vb_accumulator` + +The implementation uses type-3 pairings compared to type-1 in the paper. + +[`Accumulator`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/positive/trait.Accumulator.html +[`PositiveAccumulator`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/positive/struct.PositiveAccumulator.html +[`UniversalAccumulator`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/universal/struct.UniversalAccumulator.html +[`MembershipWitness`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/witness/struct.MembershipWitness.html +[`NonMembershipWitness`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/witness/struct.NonMembershipWitness.html +[`Witness`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/witness/trait.Witness.html +[`Omega`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/batch_utils/struct.Omega.html +[`ProofProtocol`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/proofs/trait.ProofProtocol.html +[`KBPositiveAccumulator`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/kb_positive_accumulator/adaptive_accumulator/struct.KBPositiveAccumulator.html +[`KBUniversalAccumulator`]: https://docs.rs/vb_accumulator/latest/vb_accumulator/kb_universal_accumulator/accumulator/struct.KBUniversalAccumulator.html -License: Apache-2.0 + diff --git a/vb_accumulator/src/batch_utils.rs b/vb_accumulator/src/batch_utils.rs index d69afc26..fbc58425 100644 --- a/vb_accumulator/src/batch_utils.rs +++ b/vb_accumulator/src/batch_utils.rs @@ -5,31 +5,38 @@ use crate::setup::SecretKey; use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; -use ark_ff::{batch_inversion, PrimeField, Zero}; +use ark_ff::{batch_inversion, One, PrimeField, Zero}; use ark_poly::{ polynomial::{univariate::DensePolynomial, DenseUVPolynomial}, Polynomial, }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ - cfg_iter, + cfg_into_iter, cfg_iter, cfg_iter_mut, fmt::Debug, iter::{IntoIterator, Iterator}, + ops::Neg, vec, vec::Vec, }; +use digest::DynDigest; use dock_crypto_utils::{ + cfg_iter_sum, msm::multiply_field_elems_with_same_group_elem, poly::{inner_product_poly, multiply_many_polys, multiply_poly}, serde_utils::*, }; +use short_group_sig::bb_sig::SecretKey as BBSigSecretKey; use serde::{Deserialize, Serialize}; use serde_with::serde_as; +use dock_crypto_utils::ff::inner_product; #[cfg(feature = "parallel")] use rayon::prelude::*; +use short_group_sig::bb_sig::prf; + /// Create a polynomial with given points in `updates` as: /// `(updates[0]-x) * (updates[1]-x) * (updates[2] - x)...(updates[last] - x)` fn poly_from_given_updates(updates: &[F]) -> DensePolynomial { @@ -38,22 +45,14 @@ fn poly_from_given_updates(updates: &[F]) -> DensePolynomial { } let minus_one = -F::one(); + // [(updates[0]-x), (updates[1]-x), (updates[2] - x), ..., (updates[last] - x)] - #[cfg(not(feature = "parallel"))] - let x_i = updates - .iter() + let terms = cfg_into_iter!(updates) .map(|i| DensePolynomial::from_coefficients_slice(&[*i, minus_one])) .collect::>(); - #[cfg(feature = "parallel")] - let x_i = updates - .par_iter() - .map(|i| DensePolynomial::from_coefficients_slice(&[*i, minus_one])) - .collect(); - // Product (updates[0]-x) * (updates[1]-x) * (updates[2] - x)...(updates[last] - x) - - multiply_many_polys(x_i) + multiply_many_polys(terms) // Note: Using multiply operator from ark-poly is orders of magnitude slower than naive multiplication // x_i.into_iter().reduce(|a, b| &a * &b).unwrap() } @@ -135,7 +134,7 @@ where ); } - let sum = inner_product_poly(polys, factors); + let sum = inner_product_poly(&polys, factors); Self(sum) } @@ -198,6 +197,37 @@ where .reduce(F::zero(), |a, b| a + b)*/ } + /// Evaluation of polynomial at multiple values without creating the polynomial as the variables are already known. + pub fn eval_direct_on_batch(additions: &[F], alpha: &F, x: &[F]) -> Vec { + let n = additions.len(); + let m = x.len(); + if n == 0 { + return vec![F::zero(); m]; + } + if n == 1 { + return vec![F::one(); m]; + } + // Compute products (y_0+alpha), (y_0+alpha)*(y_1+alpha), .. etc by memoization + let mut factors = vec![F::one(); n]; + let mut polys = vec![vec![F::one(); n]; m]; + for s in 1..n { + factors[s] = factors[s - 1] * (additions[s - 1] + alpha); + cfg_iter_mut!(polys).enumerate().for_each(|(j, polys_j)| { + polys_j[n - 1 - s] = polys_j[n - s] * (additions[n - s] - x[j]) + }); + } + cfg_into_iter!(polys) + .map(|poly| { + /*factors + .iter() + .zip(poly.into_iter()) + .map(|(f, p)| p * f) + .fold(F::zero(), |a, b| a + b)*/ + inner_product(&factors, &poly) + }) + .collect() + } + /// Evaluation of polynomial without creating the polynomial as the variables are already known. /// Slower than `Self::eval_direct` but uses less memory at the cost of recomputing /// products of field elements @@ -261,7 +291,7 @@ where ); } - let sum = inner_product_poly(polys, factors); + let sum = inner_product_poly(&polys, factors); Self(sum) } @@ -327,6 +357,38 @@ where .reduce(|| F::zero(), |a, b| a + b)*/ } + pub fn eval_direct_on_batch(removals: &[F], alpha: &F, x: &[F]) -> Vec { + let n = removals.len(); + let m = x.len(); + if n == 0 { + return vec![F::zero(); m]; + } + // Compute 1/(removals[i]+alpha) for all i + let mut y_plus_alpha_inv = removals.iter().map(|y| *y + *alpha).collect::>(); + batch_inversion(&mut y_plus_alpha_inv); + + // Compute products by memoization: 1/(y_0+alpha), 1/(y_0+alpha)*1/(y_1+alpha), ...., 1/(y_0+alpha)*1/(y_1+alpha)*...*1/(y_{n-1}+alpha) + let mut factors = vec![F::one(); n]; + let mut polys = vec![vec![F::one(); n]; m]; + factors[0] = y_plus_alpha_inv[0]; + for s in 1..n { + factors[s] = factors[s - 1] * y_plus_alpha_inv[s]; + cfg_iter_mut!(polys) + .enumerate() + .for_each(|(j, polys_j)| polys_j[s] = polys_j[s - 1] * (removals[s - 1] - x[j])); + } + cfg_into_iter!(polys) + .map(|poly| { + factors + .iter() + .zip(poly.into_iter()) + .map(|(f, p)| p * f) + .fold(F::zero(), |a, b| a + b) + // inner_product(&factors, &poly) + }) + .collect() + } + /// Evaluation of polynomial without creating the polynomial as the variables are already known. /// Slower than `Self::eval_direct` but uses less memory at the cost of recomputing /// products of field elements @@ -349,6 +411,10 @@ where .fold(F::zero(), |a, b| a + b) } + pub fn get_coefficients(&self) -> &[F] { + &self.0.coeffs + } + fn compute_factor(s: usize, removals: &[F], alpha: &F) -> F { (0..s + 1) .map(|i| removals[i] + *alpha) @@ -364,9 +430,6 @@ where { /// Generate polynomial `v_{A,D}(x)`, given `y_A` as `additions`, `y_D` as `removals` and the secret key `alpha` pub fn generate(additions: &[F], removals: &[F], alpha: &F) -> Self { - if additions.is_empty() && removals.is_empty() { - return Self(DensePolynomial::zero()); - } let mut p = Poly_v_A::generate(additions, alpha).0; if !removals.is_empty() { p = &p @@ -389,6 +452,18 @@ where e } + pub fn eval_direct_on_batch(additions: &[F], removals: &[F], alpha: &F, x: &[F]) -> Vec { + let f = Self::compute_factor(additions, alpha); + let mut a = Poly_v_A::eval_direct_on_batch(additions, alpha, x); + if !removals.is_empty() { + let b = Poly_v_D::eval_direct_on_batch(removals, alpha, x); + cfg_iter_mut!(a) + .enumerate() + .for_each(|(i, a_i)| *a_i = *a_i - (b[i] * f)); + } + a + } + pub fn get_coefficients(&self) -> &[F] { &self.0.coeffs } @@ -414,6 +489,8 @@ where { /// Create new `Omega` after `additions` are added and `removals` are removed from `old_accumulator`. /// Note that `old_accumulator` is the accumulated value before the updates were made. + /// Returns `c_0 * V, c_1 * V, ..., c_n * V` where `V` is the accumulator before the update and `c_i` are the coefficients of + /// the polynomial `v_AD` pub fn new( additions: &[G::ScalarField], removals: &[G::ScalarField], @@ -422,11 +499,150 @@ where ) -> Self { let poly = Poly_v_AD::generate(additions, removals, &sk.0); let coeffs = poly.get_coefficients(); - Omega(G::Group::normalize_batch( + Self(G::Group::normalize_batch( &multiply_field_elems_with_same_group_elem(old_accumulator.into_group(), coeffs), )) } + /// Create `Omega` for KB positive accumulator after `removals` are removed from `old_accumulator`. + /// Returns `c_0 * -V, c_1 * -V, ..., c_n * -V` where `V` is the accumulator before the update and `c_i` are the coefficients of + /// the polynomial `v_D`. As this accumulator does not change on additions, only polynomial `v_D` is generated. + pub fn new_for_kb_positive_accumulator( + removals: &[G::ScalarField], + old_accumulator: &G, + sk: &SecretKey, + sig_sk: &BBSigSecretKey, + ) -> Self { + let accum_members = cfg_into_iter!(removals) + .map(|r| prf::(r, sig_sk)) + .collect::>(); + let poly = Poly_v_D::generate(&accum_members, &sk.0); + let coeffs = poly.get_coefficients(); + Self(G::Group::normalize_batch( + &multiply_field_elems_with_same_group_elem(old_accumulator.into_group().neg(), coeffs), + )) + } + + /// Create 2 `Omega`s for KB universal accumulator. As this accumulator comprises of 2 positive accumulators, this + /// returns 2 `Omega`s, one for each of those accumulators + pub fn new_for_kb_universal_accumulator( + additions: &[G::ScalarField], + removals: &[G::ScalarField], + old_mem_accumulator: &G, + old_non_mem_accumulator: &G, + sk: &SecretKey, + ) -> (Self, Self) { + let m = additions.len(); + let n = removals.len(); + let alpha = &sk.0; + + // mem_add_poly and mem_rem_poly are used to create v_A and v_D for the membership accumulator + + // (additions[0] + alpha), (additions[0] + alpha)*(additions[1] + alpha), ..., (additions[0] + alpha)*(additions[1] + alpha)*...(additions[m-1] + alpha) + let mut factors_add = vec![G::ScalarField::one(); m]; + // (additions[1] - x)*(additions[2] - x)*...(additions[m-1] - x), (additions[2] - x)*(additions[3] - x)*...(additions[m-1] - x), .., 1. For v_A polynomial for membership accumulator + let mut mem_add_poly = + vec![DensePolynomial::from_coefficients_vec(vec![G::ScalarField::one()]); m]; + // 1, (additions[0] - x), (additions[0] - x)*(additions[1] - x), ..., (additions[0] - x)*(additions[1] - x)*...(additions[m-2] - x). For v_D polynomial for non-membership accumulator + let mut non_mem_rem_poly = + vec![DensePolynomial::from_coefficients_vec(vec![G::ScalarField::one()]); m]; + + // (removals[0] + alpha), (removals[0] + alpha)*(removals[1] + alpha), ..., (removals[0] + alpha)*(removals[1] + alpha)*...(removals[n-1] + alpha) + let mut factors_rem = vec![G::ScalarField::one(); n]; + // 1, (removals[0] - x), (removals[0] - x)*(removals[1] - x), ..., (removals[0] - x)*(removals[1] - x)*...(removals[n-2] - x). For v_D polynomial for membership accumulator + let mut mem_rem_poly = + vec![DensePolynomial::from_coefficients_vec(vec![G::ScalarField::one()]); n]; + // (removals[1] - x)*(removals[2] - x)*...(removals[n-1] - x), (removals[2] - x)*(removals[3] - x)*...(removals[n-1] - x), .., 1. For v_A polynomial for non-membership accumulator + let mut non_mem_add_poly = + vec![DensePolynomial::from_coefficients_vec(vec![G::ScalarField::one()]); n]; + + let minus_1 = -G::ScalarField::one(); + + if !additions.is_empty() { + factors_add[0] = additions[0] + alpha; + } + if !removals.is_empty() { + factors_rem[0] = removals[0] + alpha; + } + + for s in 1..m { + factors_add[s] = factors_add[s - 1] * (additions[s] + alpha); + mem_add_poly[m - s - 1] = multiply_poly( + &mem_add_poly[m - s], + &DensePolynomial::from_coefficients_vec(vec![additions[m - s], minus_1]), + ); + non_mem_rem_poly[s] = multiply_poly( + &non_mem_rem_poly[s - 1], + &DensePolynomial::from_coefficients_vec(vec![additions[s - 1], minus_1]), + ); + } + for s in 1..n { + factors_rem[s] = factors_rem[s - 1] * (removals[s] + alpha); + non_mem_add_poly[n - s - 1] = multiply_poly( + &non_mem_add_poly[n - s], + &DensePolynomial::from_coefficients_vec(vec![removals[n - s], minus_1]), + ); + mem_rem_poly[s] = multiply_poly( + &mem_rem_poly[s - 1], + &DensePolynomial::from_coefficients_vec(vec![removals[s - 1], minus_1]), + ); + } + + // 1/(additions[0] + alpha), 1/(additions[0] + alpha)*(additions[1] + alpha), ..., 1/(additions[0] + alpha)*(additions[1] + alpha)*...(additions[m-1] + alpha) + let mut factors_add_inv = factors_add.clone(); + batch_inversion(&mut factors_add_inv); + // 1/(removals[0] + alpha), 1/(removals[0] + alpha)*(removals[1] + alpha), ..., 1/(removals[0] + alpha)*(removals[1] + alpha)*...(removals[n-1] + alpha) + let mut factors_rem_inv = factors_rem.clone(); + batch_inversion(&mut factors_rem_inv); + + let one = G::ScalarField::one(); + let zero = DensePolynomial::zero; + + // 1*mem_add_poly[0] + factors_add[0]*mem_add_poly[1] + ... + factors_add[m-2]*mem_add_poly[m-1] + let mem_poly_v_A = cfg_into_iter!(0..m) + .map(|i| if i == 0 { &one } else { &factors_add[i - 1] }) + .zip(cfg_iter!(mem_add_poly)) + .map(|(f, p)| p * *f); + let mem_poly_v_A = cfg_iter_sum!(mem_poly_v_A, zero); + + // 1*non_mem_add_poly[0] + factors_rem[0]*non_mem_add_poly[1] + ... + factors_rem[n-2]*non_mem_add_poly[n-1] + let non_mem_poly_v_A = cfg_into_iter!(0..n) + .map(|i| if i == 0 { &one } else { &factors_rem[i - 1] }) + .zip(cfg_iter!(non_mem_add_poly)) + .map(|(f, p)| p * *f); + let non_mem_poly_v_A = cfg_iter_sum!(non_mem_poly_v_A, zero); + + let mem_poly_v_D = inner_product_poly(&mem_rem_poly, factors_rem_inv); + + let non_mem_poly_v_D = inner_product_poly(&non_mem_rem_poly, factors_add_inv); + + // mem_poly_v_AD = mem_poly_v_A - mem_poly_v_AD*(additions[0] + alpha)*(additions[1] + alpha)*...(additions[m-1] + alpha) + let mut mem_poly_v_AD = mem_poly_v_A; + if !removals.is_empty() { + mem_poly_v_AD = &mem_poly_v_AD - &(&mem_poly_v_D * factors_add[m - 1]); + } + let omega_mem = Self(G::Group::normalize_batch( + &multiply_field_elems_with_same_group_elem( + old_mem_accumulator.into_group(), + &mem_poly_v_AD.coeffs, + ), + )); + + // non_mem_poly_v_AD = non_mem_poly_v_AD - non_mem_poly_v_AD*(removals[0] + alpha)*(removals[1] + alpha)*...(removals[n-1] + alpha) + let mut non_mem_poly_v_AD = non_mem_poly_v_A; + if !additions.is_empty() { + non_mem_poly_v_AD = &non_mem_poly_v_AD - &(&non_mem_poly_v_D * factors_rem[n - 1]); + } + let omega_non_mem = Self(G::Group::normalize_batch( + &multiply_field_elems_with_same_group_elem( + old_non_mem_accumulator.into_group(), + &non_mem_poly_v_AD.coeffs, + ), + )); + + (omega_mem, omega_non_mem) + } + /// Inner product of powers of `y`, i.e. the element for which witness needs to be updated and `omega` /// Used by the (non)member to update its witness without the knowledge of secret key. pub fn inner_product_with_scaled_powers_of_y( @@ -497,10 +713,39 @@ where // * 1/d_D(x) let y_omega_ip = omega.inner_product_with_scaled_powers_of_y(element, &d_D_inv); - println!("y_omega_ip={}", y_omega_ip); - println!("V_prime={}", V_prime); assert_eq!(V_prime, y_omega_ip); } + + #[cfg(test)] + /// Test function to check if generated correctly. + pub(crate) fn check_for_kb_positive_accumulator( + removals: &[G::ScalarField], + element: &G::ScalarField, + old_accumulator: &G, + sk: &SecretKey, + sig_sk: &BBSigSecretKey, + ) { + use ark_ff::Field; + + let removed_members = cfg_into_iter!(removals) + .map(|r| prf::(r, sig_sk)) + .collect::>(); + let member = prf::(element, sig_sk); + let v_D = Poly_v_D::eval_direct(&removed_members, &sk.0, &member); + let d_D_inv = Poly_d::eval_direct(&removed_members, &member) + .inverse() + .unwrap(); + + let mut V_prime = old_accumulator.into_group(); + V_prime *= v_D * d_D_inv; + + let omega = + Self::new_for_kb_positive_accumulator::(removals, old_accumulator, sk, sig_sk); + // * 1/d_D(x) + let y_omega_ip = omega.inner_product_with_scaled_powers_of_y(&member, &d_D_inv); + + assert_eq!(V_prime, y_omega_ip.neg()); + } } #[cfg(test)] @@ -523,7 +768,11 @@ mod tests { let mut rng = StdRng::seed_from_u64(0u64); let updates = (0..100).map(|_| Fr::rand(&mut rng)).collect::>(); + let batch_size = 10; let x = Fr::rand(&mut rng); + let x_vec = (0..batch_size) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); let poly_d = Poly_d::generate(&updates); assert_eq!(Poly_d::eval_direct(&updates, &x), poly_d.eval(&x)); @@ -565,6 +814,12 @@ mod tests { Fr::zero() ); assert_eq!(Poly_v_A::generate(&[], &alpha).eval(&x), Fr::zero()); + assert_eq!( + Poly_v_A::eval_direct_on_batch(&updates, &alpha, &x_vec), + (0..batch_size) + .map(|i| Poly_v_A::eval_direct(&updates, &alpha, &x_vec[i])) + .collect::>(), + ); let poly_v_D = Poly_v_D::generate(&updates, &alpha); assert_eq!( @@ -589,14 +844,46 @@ mod tests { Fr::zero() ); assert_eq!(Poly_v_D::generate(&[], &alpha).eval(&x), Fr::zero()); + assert_eq!( + Poly_v_D::eval_direct_on_batch(&updates, &alpha, &x_vec), + (0..batch_size) + .map(|i| Poly_v_D::eval_direct(&updates, &alpha, &x_vec[i])) + .collect::>(), + ); for &i in &[100, 70, 50, 40, 35, 20, 10, 7, 1, 0] { let updates_1 = (0..i).map(|_| Fr::rand(&mut rng)).collect::>(); + + let start = Instant::now(); let poly_v_AD = Poly_v_AD::generate(&updates, &updates_1, &alpha); - assert_eq!( - Poly_v_AD::eval_direct(&updates, &updates_1, &alpha, &x), - poly_v_AD.eval(&x) + println!( + "For {} additions and {} removals, Poly_v_AD::generates takes {:?}", + updates.len(), + updates_1.len(), + start.elapsed() ); + + let start = Instant::now(); + let expected = Poly_v_AD::eval_direct(&updates, &updates_1, &alpha, &x); + println!( + "For {} additions and {} removals, Poly_v_AD::eval_direct takes {:?}", + updates.len(), + updates_1.len(), + start.elapsed() + ); + assert_eq!(expected, poly_v_AD.eval(&x)); + + let start = Instant::now(); + let r1 = Poly_v_AD::eval_direct_on_batch(&updates, &updates_1, &alpha, &x_vec); + println!("For {} additions and {} removals and a batch of {}, Poly_v_AD::eval_direct_on_batch takes {:?}", updates.len(), updates_1.len(), x_vec.len(), start.elapsed()); + + let start = Instant::now(); + let r2 = (0..batch_size) + .map(|i| Poly_v_AD::eval_direct(&updates, &updates_1, &alpha, &x_vec[i])) + .collect::>(); + println!("For {} additions and {} removals and a batch of {}, Poly_v_AD::eval_direct takes {:?}", updates.len(), updates_1.len(), x_vec.len(), start.elapsed()); + + assert_eq!(r1, r2); } macro_rules! test_poly_time { @@ -623,6 +910,19 @@ mod tests { println!("For {} updates, {}::generates takes {:?} with memoization and {:?} without memoization", $count, $name, poly_gen_mem_time, poly_gen_time); println!("For {} updates, {}::eval_direct takes {:?} with memoization and {:?} without memoization", $count, $name, poly_eval_mem_time, poly_eval_time); + + let start = Instant::now(); + let a = $poly::eval_direct_on_batch(&$updates, &$alpha, &x_vec); + let eval_batch_time = start.elapsed(); + + let start = Instant::now(); + let b = (0..batch_size).map(|i| $poly::eval_direct(&$updates, &$alpha, &x_vec[i])).collect::>(); + let eval_single_time = start.elapsed(); + + assert_eq!(a, b); + + println!("For {} updates and a batch of {}, {}::eval_direct_on_batch takes {:?}", $count, batch_size, $name, eval_batch_time); + println!("For {} updates and a batch of {}, {}::eval_direct takes {:?}", $count, batch_size, $name, eval_single_time); } } diff --git a/vb_accumulator/src/error.rs b/vb_accumulator/src/error.rs index 5ee14d92..4679ad2a 100644 --- a/vb_accumulator/src/error.rs +++ b/vb_accumulator/src/error.rs @@ -7,6 +7,7 @@ use ark_std::fmt::Debug; use dock_crypto_utils::serde_utils::ArkSerializationError; use schnorr_pok::error::SchnorrError; use serde::Serialize; +use short_group_sig::error::ShortGroupSigError; #[derive(Debug, Serialize)] pub enum VBAccumulatorError { @@ -33,6 +34,12 @@ pub enum VBAccumulatorError { #[serde(with = "ArkSerializationError")] Serialization(SerializationError), SchnorrError(SchnorrError), + InvalidMembershipCorrectnessProof, + InvalidNonMembershipCorrectnessProof, + IncorrectRandomizedWitness, + InvalidWitness, + ShortGroupSigError(ShortGroupSigError), + MismatchBetweenSignatureAndAccumulatorValue, } impl From for VBAccumulatorError { @@ -46,3 +53,9 @@ impl From for VBAccumulatorError { Self::Serialization(e) } } + +impl From for VBAccumulatorError { + fn from(e: ShortGroupSigError) -> Self { + Self::ShortGroupSigError(e) + } +} diff --git a/vb_accumulator/src/kb_positive_accumulator/adaptive_accumulator.rs b/vb_accumulator/src/kb_positive_accumulator/adaptive_accumulator.rs new file mode 100644 index 00000000..fd15b6af --- /dev/null +++ b/vb_accumulator/src/kb_positive_accumulator/adaptive_accumulator.rs @@ -0,0 +1,539 @@ +//! An adaptive positive accumulator contructed from a non-adaptive accumulator and BB signature scheme. Adding an element to the accumulator +//! involves creating a BB signature over that element and adding the randomness from the signature in the non-adaptive accumulator. +//! This adaptive accumulator's witness comprises of the BB signature and the witness in the non-adaptive accumulator. Adding to +//! this accumulator does not change its value but removing does. Same for existing witnesses, it does not change on addition but removal + +use crate::{ + error::VBAccumulatorError, + kb_positive_accumulator::non_adaptive_accumulator::NonAdaptivePositiveAccumulator, + persistence::State, + prelude::{PublicKey, SecretKey, SetupParams}, +}; +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{cfg_into_iter, cfg_iter, rand::RngCore, vec::Vec}; +use digest::DynDigest; +use dock_crypto_utils::serde_utils::ArkObjectBytes; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use short_group_sig::{ + bb_sig::{PublicKeyG2 as BBSigPublicKey, SecretKey as BBSigSecretKey, SignatureG1 as BBSig}, + common::SignatureParams as BBSigParams, +}; + +use crate::positive::Accumulator; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +use crate::kb_positive_accumulator::witness::KBPositiveAccumulatorWitness; + +/// A dynamic positive accumulator +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct KBPositiveAccumulator( + #[serde_as(as = "ArkObjectBytes")] pub NonAdaptivePositiveAccumulator, +); + +impl KBPositiveAccumulator { + pub fn initialize(rng: &mut R, params_gen: impl AsRef) -> Self { + Self(NonAdaptivePositiveAccumulator::initialize(rng, params_gen)) + } + + /// Add an element to the accumulator. Returns the membership witness of that element + pub fn add( + &self, + element: &E::ScalarField, + sk: &SecretKey, + state: &mut dyn State, + sig_sk: &BBSigSecretKey, + params: &BBSigParams, + ) -> Result, VBAccumulatorError> { + let signature = BBSig::new_deterministic::(element, sig_sk, params); + self.0.add(signature.1.clone(), state)?; + let accum_witness = self.0.get_membership_witness(&signature.1, sk, state)?; + Ok(KBPositiveAccumulatorWitness { + signature, + accum_witness, + }) + } + + /// Removes an element from the accumulator. Returns the new value of the accumulator + pub fn remove( + &self, + element: &E::ScalarField, + sk: &SecretKey, + state: &mut dyn State, + sig_sk: &BBSigSecretKey, + ) -> Result { + let member = Self::accumulator_member::(element, sig_sk); + let new = self.0.remove(&member, sk, state)?; + Ok(Self(new)) + } + + /// Add a batch of elements to the accumulator. Returns the membership witness of that batch + pub fn add_batch( + &self, + elements: Vec, + sk: &SecretKey, + state: &mut dyn State, + sig_sk: &BBSigSecretKey, + params: &BBSigParams, + ) -> Result>, VBAccumulatorError> { + let sigs = cfg_into_iter!(elements) + .map(|e| BBSig::new_deterministic::(&e, sig_sk, params)) + .collect::>(); + let members = cfg_iter!(sigs).map(|s| s.1).collect::>(); + let w = self.0.compute_membership_witness_for_batch(&members, sk); + self.0.add_batch(members, state)?; + let wits = cfg_into_iter!(sigs) + .zip(cfg_into_iter!(w)) + .map(|(signature, accum_witness)| KBPositiveAccumulatorWitness { + signature, + accum_witness, + }) + .collect::>(); + Ok(wits) + } + + /// Removes a batch of elements from the accumulator. Returns the new value of the accumulator + pub fn remove_batch( + &self, + elements: &[E::ScalarField], + sk: &SecretKey, + state: &mut dyn State, + sig_sk: &BBSigSecretKey, + ) -> Result { + let members = cfg_into_iter!(elements) + .map(|element| Self::accumulator_member::(element, sig_sk)) + .collect::>(); + let new = self.0.remove_batch(&members, sk, state)?; + Ok(Self(new)) + } + + /// Add and removes batches of elements. Returns the new accumulator value and the membership witnesses of the added batch. + pub fn batch_updates( + &self, + additions: Vec, + removals: &[E::ScalarField], + sk: &SecretKey, + state: &mut dyn State, + sig_sk: &BBSigSecretKey, + params: &BBSigParams, + ) -> Result<(Self, Vec>), VBAccumulatorError> { + let sigs = cfg_into_iter!(additions) + .map(|e| BBSig::new_deterministic::(&e, sig_sk, params)) + .collect::>(); + let additions = cfg_iter!(sigs).map(|s| s.1).collect::>(); + let removals = cfg_into_iter!(removals) + .map(|element| Self::accumulator_member::(element, sig_sk)) + .collect::>(); + let new = KBPositiveAccumulator(self.0.remove_batch(&removals, sk, state)?); + let w = new.0.compute_membership_witness_for_batch(&additions, sk); + self.0.add_batch(additions, state)?; + let wits = cfg_into_iter!(sigs) + .zip(cfg_into_iter!(w)) + .map(|(signature, accum_witness)| KBPositiveAccumulatorWitness { + signature, + accum_witness, + }) + .collect::>(); + Ok((new, wits)) + } + + /// Get membership witness of an element + pub fn get_witness( + &self, + member: &E::ScalarField, + sk: &SecretKey, + state: &dyn State, + sig_sk: &BBSigSecretKey, + sig_params: &BBSigParams, + ) -> Result, VBAccumulatorError> { + let signature = BBSig::new_deterministic::(member, sig_sk, sig_params); + let accum_witness = self.0.get_membership_witness(&signature.1, sk, state)?; + Ok(KBPositiveAccumulatorWitness { + signature, + accum_witness, + }) + } + + pub fn get_witnesses_for_batch( + &self, + members: &[E::ScalarField], + sk: &SecretKey, + state: &dyn State, + sig_sk: &BBSigSecretKey, + sig_params: &BBSigParams, + ) -> Result>, VBAccumulatorError> { + let sigs = cfg_into_iter!(members) + .map(|e| BBSig::new_deterministic::(e, sig_sk, sig_params)) + .collect::>(); + let members = cfg_iter!(sigs).map(|s| s.1).collect::>(); + let w = self + .0 + .get_membership_witnesses_for_batch(&members, sk, state)?; + let wits = cfg_into_iter!(sigs) + .zip(cfg_into_iter!(w)) + .map(|(signature, accum_witness)| KBPositiveAccumulatorWitness { + signature, + accum_witness, + }) + .collect::>(); + Ok(wits) + } + + pub fn verify_membership( + &self, + member: &E::ScalarField, + witness: &KBPositiveAccumulatorWitness, + pk: &PublicKey, + params: &SetupParams, + sig_pk: &BBSigPublicKey, + sig_params: &BBSigParams, + ) -> Result<(), VBAccumulatorError> { + witness.signature.verify(member, sig_pk, sig_params)?; + self.0 + .verify_membership( + witness.get_accumulator_member(), + &witness.accum_witness, + pk, + params, + ) + .then(|| ()) + .ok_or(VBAccumulatorError::InvalidWitness) + } + + pub fn value(&self) -> &E::G1Affine { + &self.0 .0 + } + + pub fn from_accumulated(accumulated: E::G1Affine) -> Self { + Self(NonAdaptivePositiveAccumulator(accumulated)) + } + + /// The value corresponding to `element` that is added to the non-adaptive accumulator + pub fn accumulator_member( + element: &E::ScalarField, + sk: &BBSigSecretKey, + ) -> E::ScalarField { + BBSig::::generate_random_for_message::(element, sk) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::{persistence::test::*, setup::Keypair, test_serialization}; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_ec::AffineRepr; + use ark_ff::Field; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + + pub fn setup_kb_positive_accum( + rng: &mut StdRng, + ) -> ( + BBSigParams, + BBSigSecretKey, + BBSigPublicKey, + SetupParams, + Keypair, + KBPositiveAccumulator, + InMemoryState, + ) { + let sig_params = BBSigParams::::generate_using_rng(rng); + let sk = BBSigSecretKey::new(rng); + let pk = BBSigPublicKey::generate_using_secret_key(&sk, &sig_params); + + let params = SetupParams::::generate_using_rng(rng); + let keypair = Keypair::::generate_using_rng(rng, ¶ms); + let accumulator = KBPositiveAccumulator::initialize(rng, ¶ms); + let state = InMemoryState::new(); + (sig_params, sk, pk, params, keypair, accumulator, state) + } + + #[test] + fn membership() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, mut accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + test_serialization!(KBPositiveAccumulator, accumulator); + + let count = 10; + let mut elems = vec![]; + for _ in 0..count { + let elem = Fr::rand(&mut rng); + assert!(accumulator + .get_witness::(&elem, &keypair.secret_key, &state, &sk, &sig_params) + .is_err()); + + let accum_member = + KBPositiveAccumulator::::accumulator_member::(&elem, &sk); + assert!(!state.has(&accum_member)); + let wit = accumulator + .add::(&elem, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + assert!(state.has(&accum_member)); + + assert!(accumulator + .add::(&elem, &keypair.secret_key, &mut state, &sk, &sig_params) + .is_err()); + + let m_wit = accumulator + .get_witness::(&elem, &keypair.secret_key, &state, &sk, &sig_params) + .unwrap(); + let mut expected_V = m_wit.accum_witness.0.into_group(); + expected_V *= accum_member + keypair.secret_key.0; + assert_eq!(expected_V, *accumulator.value()); + + assert_eq!(m_wit, wit); + + let verification_accumulator = + KBPositiveAccumulator::from_accumulated(*accumulator.value()); + verification_accumulator + .verify_membership( + &elem, + &m_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + + elems.push(elem); + } + + for elem in elems { + let accum_member = + KBPositiveAccumulator::::accumulator_member::(&elem, &sk); + assert!(state.has(&accum_member)); + let old_accum = accumulator.value().clone(); + accumulator = accumulator + .remove::(&elem, &keypair.secret_key, &mut state, &sk) + .unwrap(); + assert_eq!( + old_accum * (keypair.secret_key.0 + accum_member).inverse().unwrap(), + *accumulator.value() + ); + assert!(!state.has(&elem)); + assert!(accumulator + .get_witness::(&elem, &keypair.secret_key, &state, &sk, &sig_params) + .is_err()) + } + } + + #[test] + fn batch_update_and_membership() { + // Tests batch updates to accumulator and batch membership witness generation + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, mut accumulator_1, mut state_1) = + setup_kb_positive_accum(&mut rng); + + // Create more accumulators to compare. Same elements will be added and removed from them as accumulator_1 + let mut accumulator_2 = accumulator_1.clone(); + let mut state_2 = InMemoryState::::new(); + let mut accumulator_3 = accumulator_1.clone(); + let mut state_3 = InMemoryState::::new(); + let mut accumulator_4 = accumulator_1.clone(); + let mut state_4 = InMemoryState::::new(); + + let additions: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let removals: Vec = vec![0, 1, 6, 9].into_iter().map(|i| additions[i]).collect(); + + let mut old: ::G1Affine = *accumulator_1.value(); + // Add one by one + for i in 0..additions.len() { + let elem = additions[i]; + accumulator_1 + .add::(&elem, &keypair.secret_key, &mut state_1, &sk, &sig_params) + .unwrap(); + } + + // Adding does not change accumulator + assert_eq!(*accumulator_1.value(), old); + + // Add as a batch + let wits = accumulator_2 + .add_batch::( + additions.clone(), + &keypair.secret_key, + &mut state_2, + &sk, + &sig_params, + ) + .unwrap(); + assert_eq!(*accumulator_1.value(), *accumulator_2.value()); + assert_eq!(state_1.db, state_2.db); + + for i in 0..additions.len() { + accumulator_2 + .verify_membership( + &additions[i], + &wits[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + // Remove one by one + for i in 0..removals.len() { + accumulator_1 = accumulator_1 + .remove::(&removals[i], &keypair.secret_key, &mut state_1, &sk) + .unwrap(); + } + + assert_ne!(*accumulator_1.value(), *accumulator_2.value()); + + // Remove as a batch + accumulator_2 = accumulator_2 + .remove_batch::(&removals, &keypair.secret_key, &mut state_2, &sk) + .unwrap(); + assert_eq!(*accumulator_1.value(), *accumulator_2.value()); + assert_eq!(state_1.db, state_2.db); + + // Need to make `accumulator_3` same as `accumulator_1` and `accumulator_2` by doing batch addition and removal simultaneously. + // To do the removals, first they need to be added to the accumulator and the additions elements need to be adjusted. + let mut new_additions = additions.clone(); + for e in removals.iter() { + accumulator_3 + .add::(e, &keypair.secret_key, &mut state_3, &sk, &sig_params) + .unwrap(); + new_additions.retain(|&x| x != *e); + } + + assert_ne!(*accumulator_1.value(), *accumulator_3.value()); + assert_ne!(*accumulator_2.value(), *accumulator_3.value()); + + // Add and remove in a single call as a batch + let upd = accumulator_3 + .batch_updates::( + new_additions.clone(), + &removals, + &keypair.secret_key, + &mut state_3, + &sk, + &sig_params, + ) + .unwrap(); + accumulator_3 = upd.0; + assert_eq!(*accumulator_1.value(), *accumulator_3.value()); + assert_eq!(*accumulator_2.value(), *accumulator_3.value()); + assert_eq!(state_1.db, state_3.db); + assert_eq!(state_2.db, state_3.db); + + for i in 0..new_additions.len() { + accumulator_3 + .verify_membership( + &new_additions[i], + &upd.1[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + let verification_accumulator = + KBPositiveAccumulator::from_accumulated(*accumulator_3.value()); + let witnesses = accumulator_3 + .get_witnesses_for_batch::( + &new_additions, + &keypair.secret_key, + &state_3, + &sk, + &sig_params, + ) + .unwrap(); + for i in 0..new_additions.len() { + verification_accumulator + .verify_membership( + &new_additions[i], + &witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + // Add a batch + old = *accumulator_4.value(); + let upd = accumulator_4 + .batch_updates::( + additions.clone(), + &[], + &keypair.secret_key, + &mut state_4, + &sk, + &sig_params, + ) + .unwrap(); + accumulator_4 = upd.0; + assert_eq!(old, *accumulator_4.value()); + for i in 0..additions.len() { + accumulator_4 + .verify_membership( + &additions[i], + &upd.1[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + // Remove a batch + let upd = accumulator_4 + .batch_updates::( + vec![], + &removals, + &keypair.secret_key, + &mut state_4, + &sk, + &sig_params, + ) + .unwrap(); + accumulator_4 = upd.0; + assert_eq!(upd.1.len(), 0); + // Effect should be same as that of adding and removing them together + assert_eq!(*accumulator_1.value(), *accumulator_4.value()); + assert_eq!(state_1.db, state_4.db); + + let verification_accumulator = + KBPositiveAccumulator::from_accumulated(*accumulator_4.value()); + let witnesses = accumulator_4 + .get_witnesses_for_batch::( + &new_additions, + &keypair.secret_key, + &state_4, + &sk, + &sig_params, + ) + .unwrap(); + for i in 0..new_additions.len() { + verification_accumulator + .verify_membership( + &new_additions[i], + &witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + } +} diff --git a/vb_accumulator/src/kb_positive_accumulator/mod.rs b/vb_accumulator/src/kb_positive_accumulator/mod.rs new file mode 100644 index 00000000..cb9d1c52 --- /dev/null +++ b/vb_accumulator/src/kb_positive_accumulator/mod.rs @@ -0,0 +1,10 @@ +//! A dynamic positive accumulator based on construction 2, Fig. 2 in the paper [Efficient Constructions of Pairing Based Accumulators](https://eprint.iacr.org/2021/638) + +pub mod adaptive_accumulator; +pub mod non_adaptive_accumulator; +pub mod proofs; +pub mod witness; + +pub mod proofs_alt; + +pub use adaptive_accumulator::KBPositiveAccumulator; diff --git a/vb_accumulator/src/kb_positive_accumulator/non_adaptive_accumulator.rs b/vb_accumulator/src/kb_positive_accumulator/non_adaptive_accumulator.rs new file mode 100644 index 00000000..4e471482 --- /dev/null +++ b/vb_accumulator/src/kb_positive_accumulator/non_adaptive_accumulator.rs @@ -0,0 +1,304 @@ +//! A non-adaptive accumulator + +use crate::{ + error::VBAccumulatorError, + persistence::State, + prelude::{Accumulator, SecretKey}, +}; +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{rand::RngCore, vec::Vec, UniformRand}; +use dock_crypto_utils::serde_utils::ArkObjectBytes; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct NonAdaptivePositiveAccumulator( + #[serde_as(as = "ArkObjectBytes")] pub E::G1Affine, +); + +impl Accumulator for NonAdaptivePositiveAccumulator { + fn value(&self) -> &E::G1Affine { + &self.0 + } + + fn from_accumulated(accumulated: E::G1Affine) -> Self { + NonAdaptivePositiveAccumulator(accumulated) + } +} + +impl NonAdaptivePositiveAccumulator { + pub fn initialize(rng: &mut R, params_gen: impl AsRef) -> Self { + let u = E::ScalarField::rand(rng); + Self((*params_gen.as_ref() * u).into()) + } + + pub fn add( + &self, + element: E::ScalarField, + state: &mut dyn State, + ) -> Result<(), VBAccumulatorError> { + self.check_before_add(&element, state)?; + state.add(element); + Ok(()) + } + + pub fn remove( + &self, + element: &E::ScalarField, + sk: &SecretKey, + state: &mut dyn State, + ) -> Result { + let (_, acc_pub) = self._remove(element, sk, state)?; + Ok(Self(acc_pub)) + } + + pub fn add_batch( + &self, + elements: Vec, + state: &mut dyn State, + ) -> Result<(), VBAccumulatorError> { + for element in elements.iter() { + self.check_before_add(element, state)?; + } + for element in elements { + state.add(element); + } + Ok(()) + } + + pub fn remove_batch( + &self, + elements: &[E::ScalarField], + sk: &SecretKey, + state: &mut dyn State, + ) -> Result { + let (_, acc_pub) = self._remove_batch(elements, sk, state)?; + Ok(Self(acc_pub)) + } + + pub fn batch_updates( + &self, + additions: Vec, + removals: &[E::ScalarField], + sk: &SecretKey, + state: &mut dyn State, + ) -> Result { + for element in additions.iter() { + self.check_before_add(element, state)?; + } + for element in removals { + self.check_before_remove(element, state)?; + } + let (_, acc_pub) = self._compute_new_post_remove_batch(removals, sk); + for element in additions { + state.add(element); + } + for element in removals { + state.remove(element); + } + Ok(NonAdaptivePositiveAccumulator(acc_pub)) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::{persistence::test::*, prelude::SetupParams, setup::Keypair, test_serialization}; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_ec::AffineRepr; + use ark_ff::Field; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + + #[test] + fn membership() { + // Test to check membership in accumulator + let mut rng = StdRng::seed_from_u64(0u64); + let params = SetupParams::::generate_using_rng(&mut rng); + let keypair = Keypair::::generate_using_rng(&mut rng, ¶ms); + let mut accumulator = NonAdaptivePositiveAccumulator::initialize(&mut rng, ¶ms); + let mut state = InMemoryState::new(); + test_serialization!(NonAdaptivePositiveAccumulator, accumulator); + + let count = 100; + let mut elems = vec![]; + for _ in 0..count { + let elem = Fr::rand(&mut rng); + assert!(accumulator + .get_membership_witness(&elem, &keypair.secret_key, &state) + .is_err()); + + assert!(!state.has(&elem)); + let old_accum = accumulator.value().clone(); + accumulator.add(elem.clone(), &mut state).unwrap(); + assert_eq!(old_accum, *accumulator.value()); + assert!(state.has(&elem)); + + assert!(accumulator.add(elem, &mut state).is_err()); + + let m_wit = accumulator + .get_membership_witness(&elem, &keypair.secret_key, &state) + .unwrap(); + let mut expected_V = m_wit.0.into_group(); + expected_V *= elem + keypair.secret_key.0; + assert_eq!(expected_V, *accumulator.value()); + + let verification_accumulator = + NonAdaptivePositiveAccumulator::from_accumulated(*accumulator.value()); + assert!(verification_accumulator.verify_membership( + &elem, + &m_wit, + &keypair.public_key, + ¶ms + )); + + elems.push(elem); + } + + for elem in elems { + assert!(state.has(&elem)); + let old_accum = accumulator.value().clone(); + accumulator = accumulator + .remove(&elem, &keypair.secret_key, &mut state) + .unwrap(); + assert_eq!( + old_accum * (keypair.secret_key.0 + elem).inverse().unwrap(), + *accumulator.value() + ); + assert!(!state.has(&elem)); + assert!(accumulator + .get_membership_witness(&elem, &keypair.secret_key, &state) + .is_err()) + } + } + + #[test] + fn batch_update_and_membership() { + // Tests batch updates to accumulator and batch membership witness generation + let mut rng = StdRng::seed_from_u64(0u64); + + let params = SetupParams::::generate_using_rng(&mut rng); + let keypair = Keypair::::generate_using_rng(&mut rng, ¶ms); + let mut accumulator_1 = + NonAdaptivePositiveAccumulator::::initialize(&mut rng, ¶ms); + let mut state_1 = InMemoryState::new(); + + // Create more accumulators to compare. Same elements will be added and removed from them as accumulator_1 + let mut accumulator_2 = accumulator_1.clone(); + let mut state_2 = InMemoryState::::new(); + let mut accumulator_3 = accumulator_1.clone(); + let mut state_3 = InMemoryState::::new(); + let mut accumulator_4 = accumulator_1.clone(); + let mut state_4 = InMemoryState::::new(); + + let additions: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let removals: Vec = vec![0, 1, 6, 9].into_iter().map(|i| additions[i]).collect(); + + let mut old: ::G1Affine = *accumulator_1.value(); + // Add one by one + for i in 0..additions.len() { + let elem = additions[i]; + accumulator_1.add(elem, &mut state_1).unwrap(); + } + + // Adding does not change accumulator + assert_eq!(*accumulator_1.value(), old); + + // Add as a batch + accumulator_2 + .add_batch(additions.clone(), &mut state_2) + .unwrap(); + assert_eq!(*accumulator_1.value(), *accumulator_2.value()); + assert_eq!(state_1.db, state_2.db); + + // Remove one by one + for i in 0..removals.len() { + accumulator_1 = accumulator_1 + .remove(&removals[i], &keypair.secret_key, &mut state_1) + .unwrap(); + } + + assert_ne!(*accumulator_1.value(), *accumulator_2.value()); + + // Remove as a batch + accumulator_2 = accumulator_2 + .remove_batch(&removals, &keypair.secret_key, &mut state_2) + .unwrap(); + assert_eq!(*accumulator_1.value(), *accumulator_2.value()); + assert_eq!(state_1.db, state_2.db); + + // Need to make `accumulator_3` same as `accumulator_1` and `accumulator_2` by doing batch addition and removal simultaneously. + // To do the removals, first they need to be added to the accumulator and the additions elements need to be adjusted. + let mut new_additions = additions.clone(); + for e in removals.iter() { + accumulator_3.add(*e, &mut state_3).unwrap(); + new_additions.retain(|&x| x != *e); + } + + assert_ne!(*accumulator_1.value(), *accumulator_3.value()); + assert_ne!(*accumulator_2.value(), *accumulator_3.value()); + + // Add and remove in a single call as a batch + accumulator_3 = accumulator_3 + .batch_updates( + new_additions.clone(), + &removals, + &keypair.secret_key, + &mut state_3, + ) + .unwrap(); + assert_eq!(*accumulator_1.value(), *accumulator_3.value()); + assert_eq!(*accumulator_2.value(), *accumulator_3.value()); + assert_eq!(state_1.db, state_3.db); + assert_eq!(state_2.db, state_3.db); + + let verification_accumulator = + NonAdaptivePositiveAccumulator::from_accumulated(*accumulator_3.value()); + let witnesses = accumulator_3 + .get_membership_witnesses_for_batch(&new_additions, &keypair.secret_key, &state_3) + .unwrap(); + for i in 0..new_additions.len() { + assert!(verification_accumulator.verify_membership( + &new_additions[i], + &witnesses[i], + &keypair.public_key, + ¶ms + )); + } + + // Add a batch + old = *accumulator_4.value(); + accumulator_4 = accumulator_4 + .batch_updates(additions, &[], &keypair.secret_key, &mut state_4) + .unwrap(); + assert_eq!(old, *accumulator_4.value()); + + // Remove a batch + accumulator_4 = accumulator_4 + .batch_updates(vec![], &removals, &keypair.secret_key, &mut state_4) + .unwrap(); + // Effect should be same as that of adding and removing them together + assert_eq!(*accumulator_1.value(), *accumulator_4.value()); + assert_eq!(state_1.db, state_4.db); + + let verification_accumulator = + NonAdaptivePositiveAccumulator::from_accumulated(*accumulator_4.value()); + let witnesses = accumulator_4 + .get_membership_witnesses_for_batch(&new_additions, &keypair.secret_key, &state_4) + .unwrap(); + for i in 0..new_additions.len() { + assert!(verification_accumulator.verify_membership( + &new_additions[i], + &witnesses[i], + &keypair.public_key, + ¶ms + )); + } + } +} diff --git a/vb_accumulator/src/kb_positive_accumulator/proofs.rs b/vb_accumulator/src/kb_positive_accumulator/proofs.rs new file mode 100644 index 00000000..b8c019f6 --- /dev/null +++ b/vb_accumulator/src/kb_positive_accumulator/proofs.rs @@ -0,0 +1,271 @@ +use crate::{ + error::VBAccumulatorError, + kb_positive_accumulator::witness::KBPositiveAccumulatorWitness, + prelude::{PreparedPublicKey, PreparedSetupParams}, + proofs::{MembershipProof, MembershipProofProtocol}, + setup::{PublicKey, SetupParams}, +}; +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec::Vec, UniformRand}; +use short_group_sig::{ + bb_sig::PublicKeyG2 as BBPubKey, + bb_sig_pok::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}, + common::{ProvingKey, SignatureParams}, +}; + +/// Protocol for proving knowledge of the accumulator member and the corresponding witness. This runs 2 protocols, one to prove +/// knowledge of a BB signature and the other to prove knowledge in a non-adaptive accumulator which essentially is a protocol +/// for proving knowledge of a weak-BB signature. +pub struct KBPositiveAccumulatorMembershipProofProtocol { + pub sig_protocol: PoKOfSignatureG1Protocol, + pub accum_protocol: MembershipProofProtocol, +} + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBPositiveAccumulatorMembershipProof { + pub sig_proof: PoKOfSignatureG1Proof, + pub accum_proof: MembershipProof, +} + +impl KBPositiveAccumulatorMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: E::ScalarField, + element_blinding: Option, + witness: &KBPositiveAccumulatorWitness, + pk: &PublicKey, + params: &SetupParams, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result { + let accum_member_blinding = E::ScalarField::rand(rng); + let sig_protocol = PoKOfSignatureG1Protocol::init( + rng, + &witness.signature, + element, + element_blinding, + Some(accum_member_blinding), + sig_pk, + sig_params, + proving_key, + )?; + + let accum_protocol = MembershipProofProtocol::init( + rng, + witness.get_accumulator_member(), + Some(accum_member_blinding), + &witness.accum_witness, + pk, + params, + proving_key, + ); + Ok(Self { + sig_protocol, + accum_protocol, + }) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + pk: &PublicKey, + params: &SetupParams, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + self.sig_protocol + .challenge_contribution(sig_pk, sig_params, proving_key, &mut writer)?; + self.accum_protocol.challenge_contribution( + accumulator_value, + pk, + params, + proving_key, + &mut writer, + )?; + Ok(()) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + let sig_proof = self.sig_protocol.gen_proof(challenge)?; + let accum_proof = self.accum_protocol.gen_proof(challenge); + Ok(KBPositiveAccumulatorMembershipProof { + sig_proof, + accum_proof, + }) + } +} + +impl KBPositiveAccumulatorMembershipProof { + pub fn verify( + &self, + accumulator_value: &E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result<(), VBAccumulatorError> { + self.sig_proof + .verify(challenge, sig_pk, sig_params, proving_key)?; + + self.accum_proof + .verify(accumulator_value, challenge, pk, params, proving_key)?; + + // Check that the signature's randomness is same as the non-adaptive accumulator's member + if self.sig_proof.get_resp_for_randomness()? + != self.accum_proof.get_schnorr_response_for_element() + { + return Err(VBAccumulatorError::MismatchBetweenSignatureAndAccumulatorValue); + } + + Ok(()) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + pk: &PublicKey, + params: &SetupParams, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + self.sig_proof + .challenge_contribution(sig_pk, sig_params, proving_key, &mut writer)?; + self.accum_proof.challenge_contribution( + accumulator_value, + pk, + params, + proving_key, + &mut writer, + )?; + Ok(()) + } + + pub fn get_schnorr_response_for_element(&self) -> Result<&E::ScalarField, VBAccumulatorError> { + self.sig_proof.get_resp_for_message().map_err(|e| e.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{Duration, Instant}; + + use crate::kb_positive_accumulator::adaptive_accumulator::tests::setup_kb_positive_accum; + use ark_bls12_381::{Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn membership_proof() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + let _prepared_params = PreparedSetupParams::from(params.clone()); + let _prepared_pk = PreparedPublicKey::from(keypair.public_key.clone()); + let prk = ProvingKey::::generate_using_hash::(b"test-proving-key"); + + let mut members = vec![]; + let mut mem_witnesses = vec![]; + let count = 10; + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + let wit = accumulator + .add::(&elem, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + members.push(elem); + mem_witnesses.push(wit); + } + + let mut proof_create_duration = Duration::default(); + let mut proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = KBPositiveAccumulatorMembershipProofProtocol::init( + &mut rng, + members[i], + None, + &mem_witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + &prk, + ) + .unwrap(); + proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution( + accumulator.value(), + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + &prk, + &mut chal_bytes_prover, + ) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let start = Instant::now(); + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution( + accumulator.value(), + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + &prk, + &mut chal_bytes_verifier, + ) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + assert_eq!(challenge_prover, challenge_verifier); + proof + .verify( + accumulator.value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &pk, + &sig_params, + &prk, + ) + .unwrap(); + proof_verif_duration += start.elapsed(); + } + + println!( + "Time to create {} membership proofs is {:?}", + count, proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, proof_verif_duration + ); + } +} diff --git a/vb_accumulator/src/kb_positive_accumulator/proofs_alt.rs b/vb_accumulator/src/kb_positive_accumulator/proofs_alt.rs new file mode 100644 index 00000000..939f4408 --- /dev/null +++ b/vb_accumulator/src/kb_positive_accumulator/proofs_alt.rs @@ -0,0 +1,246 @@ +//! More efficient than proofs described in proofs.rs + +use crate::{ + error::VBAccumulatorError, + kb_positive_accumulator::witness::KBPositiveAccumulatorWitness, + prelude::{PreparedPublicKey, PreparedSetupParams}, + proofs_alt::{MembershipProof, MembershipProofProtocol}, +}; +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec::Vec, UniformRand}; +use short_group_sig::{ + bb_sig::PublicKeyG2 as BBPubKey, + bb_sig_pok::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}, + common::{ProvingKey, SignatureParams}, +}; + +/// Protocol for proving knowledge of the accumulator member and the corresponding witness. This runs 2 protocols, one to prove +/// knowledge of a BB signature and the other to prove knowledge in a non-adaptive accumulator which essentially is a protocol +/// for proving knowledge of a weak-BB signature. +pub struct KBPositiveAccumulatorMembershipProofProtocol { + pub sig_protocol: PoKOfSignatureG1Protocol, + pub accum_protocol: MembershipProofProtocol, +} + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBPositiveAccumulatorMembershipProof { + pub sig_proof: PoKOfSignatureG1Proof, + pub accum_proof: MembershipProof, +} + +impl KBPositiveAccumulatorMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: E::ScalarField, + element_blinding: Option, + witness: &KBPositiveAccumulatorWitness, + accumulator_value: E::G1Affine, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result { + let accum_member_blinding = E::ScalarField::rand(rng); + let sig_protocol = PoKOfSignatureG1Protocol::init( + rng, + &witness.signature, + element, + element_blinding, + Some(accum_member_blinding), + sig_pk, + sig_params, + proving_key, + )?; + let accum_protocol = MembershipProofProtocol::init( + rng, + *witness.get_accumulator_member(), + Some(accum_member_blinding), + accumulator_value, + &witness.accum_witness, + )?; + Ok(Self { + sig_protocol, + accum_protocol, + }) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + self.sig_protocol + .challenge_contribution(sig_pk, sig_params, proving_key, &mut writer)?; + self.accum_protocol + .challenge_contribution(accumulator_value, &mut writer)?; + Ok(()) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + let sig_proof = self.sig_protocol.gen_proof(challenge)?; + let accum_proof = self.accum_protocol.gen_proof(challenge)?; + Ok(KBPositiveAccumulatorMembershipProof { + sig_proof, + accum_proof, + }) + } +} + +impl KBPositiveAccumulatorMembershipProof { + pub fn verify( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + ) -> Result<(), VBAccumulatorError> { + self.sig_proof + .verify(challenge, sig_pk, sig_params, proving_key)?; + self.accum_proof + .verify(accumulator_value, challenge, pk, params)?; + // Check that the signature's randomness is same as the non-adaptive accumulator's member + if self.sig_proof.get_resp_for_randomness()? + != self.accum_proof.get_schnorr_response_for_element() + { + return Err(VBAccumulatorError::MismatchBetweenSignatureAndAccumulatorValue); + } + Ok(()) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + sig_pk: &BBPubKey, + sig_params: &SignatureParams, + proving_key: &ProvingKey, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + self.sig_proof + .challenge_contribution(sig_pk, sig_params, proving_key, &mut writer)?; + self.accum_proof + .challenge_contribution(accumulator_value, &mut writer)?; + Ok(()) + } + + pub fn get_schnorr_response_for_element(&self) -> Result<&E::ScalarField, VBAccumulatorError> { + self.sig_proof.get_resp_for_message().map_err(|e| e.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{Duration, Instant}; + + use crate::kb_positive_accumulator::adaptive_accumulator::tests::setup_kb_positive_accum; + use ark_bls12_381::{Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn membership_proof() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + let _prepared_params = PreparedSetupParams::from(params.clone()); + let _prepared_pk = PreparedPublicKey::from(keypair.public_key.clone()); + let prk = ProvingKey::::generate_using_hash::(b"test-proving-key"); + + let mut members = vec![]; + let mut mem_witnesses = vec![]; + let count = 10; + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + let wit = accumulator + .add::(&elem, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + members.push(elem); + mem_witnesses.push(wit); + } + + let mut proof_create_duration = Duration::default(); + let mut proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = KBPositiveAccumulatorMembershipProofProtocol::init( + &mut rng, + members[i], + None, + &mem_witnesses[i], + *accumulator.value(), + &pk, + &sig_params, + &prk, + ) + .unwrap(); + proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution( + *accumulator.value(), + &pk, + &sig_params, + &prk, + &mut chal_bytes_prover, + ) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let start = Instant::now(); + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution( + *accumulator.value(), + &pk, + &sig_params, + &prk, + &mut chal_bytes_verifier, + ) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + assert_eq!(challenge_prover, challenge_verifier); + proof + .verify( + *accumulator.value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &pk, + &sig_params, + &prk, + ) + .unwrap(); + proof_verif_duration += start.elapsed(); + } + + println!( + "Time to create {} membership proofs is {:?}", + count, proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, proof_verif_duration + ); + } +} diff --git a/vb_accumulator/src/kb_positive_accumulator/witness.rs b/vb_accumulator/src/kb_positive_accumulator/witness.rs new file mode 100644 index 00000000..63582d19 --- /dev/null +++ b/vb_accumulator/src/kb_positive_accumulator/witness.rs @@ -0,0 +1,1010 @@ +use crate::{ + error::VBAccumulatorError, + prelude::SecretKey, + witness::{MembershipWitness, Witness}, +}; +use ark_ec::{pairing::Pairing}; + +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{cfg_into_iter, vec::Vec}; +use short_group_sig::bb_sig::SignatureG1 as BBSig; + +use crate::batch_utils::{Omega}; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +/// Membership witness in for the positive accumulator +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBPositiveAccumulatorWitness { + /// The BB signature on the member + pub signature: BBSig, + /// The membership witness in the non-adaptive accumulator + pub accum_witness: MembershipWitness, +} + +impl KBPositiveAccumulatorWitness { + /// Get the member in the non-adaptive accumulator + pub fn get_accumulator_member(&self) -> &E::ScalarField { + &self.signature.1 + } + + /// Update witness after removal of an element from accumulator. The removed element is expected to be passed as members + /// of the non-adaptive accumulator + pub fn update_after_removal( + &self, + removal: &E::ScalarField, + new_accumulator: &E::G1Affine, + ) -> Result { + let new_wit = self.accum_witness.update_after_removal( + self.get_accumulator_member(), + removal, + new_accumulator, + )?; + Ok(Self { + signature: self.signature.clone(), + accum_witness: new_wit, + }) + } + + pub fn update_using_secret_key_after_batch_removals( + removals: &[E::ScalarField], + old_witnesses: &[KBPositiveAccumulatorWitness], + old_accumulator: &E::G1Affine, + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> { + let members = cfg_into_iter!(old_witnesses) + .map(|w| *w.get_accumulator_member()) + .collect::>(); + let old_accum_wits = cfg_into_iter!(old_witnesses) + .map(|w| w.accum_witness.0) + .collect::>(); + let (_, new_wits) = + MembershipWitness::compute_update_using_secret_key_after_batch_removals( + removals, + &members, + &old_accum_wits, + old_accumulator, + sk, + )?; + Ok(cfg_into_iter!(new_wits) + .zip(cfg_into_iter!(old_witnesses)) + .map(|(aw, w)| KBPositiveAccumulatorWitness { + signature: w.signature.clone(), + accum_witness: MembershipWitness(aw), + }) + .collect::>()) + } + + pub fn update_using_public_info_after_batch_updates( + &self, + removals: &[E::ScalarField], + omega: &Omega, + ) -> Result { + let (_, new_wit) = + MembershipWitness::::compute_update_using_public_info_after_batch_updates( + &[], + removals, + omega, + self.get_accumulator_member(), + &self.accum_witness.0, + )?; + Ok(KBPositiveAccumulatorWitness { + signature: self.signature.clone(), + accum_witness: MembershipWitness(new_wit), + }) + } + + pub fn update_using_public_info_after_multiple_batch_updates( + &self, + removals_and_omegas: Vec<(&[E::ScalarField], &Omega)>, + ) -> Result { + if removals_and_omegas.len() == 1 { + return self.update_using_public_info_after_batch_updates( + removals_and_omegas[0].0, + removals_and_omegas[0].1, + ); + } + let mut removals = Vec::with_capacity(removals_and_omegas.len()); + let mut omegas = Vec::with_capacity(removals_and_omegas.len()); + for (r, omega) in removals_and_omegas { + removals.push(r); + omegas.push(omega); + } + let (_, new_wit) = MembershipWitness::compute_update_for_multiple_batches(Vec::new(), removals, omegas, self.get_accumulator_member(), &self.accum_witness.0)?; + Ok(KBPositiveAccumulatorWitness { + signature: self.signature.clone(), + accum_witness: MembershipWitness(new_wit), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{Duration, Instant}; + + use crate::{ + kb_positive_accumulator::adaptive_accumulator::{ + tests::setup_kb_positive_accum, KBPositiveAccumulator, + }, + persistence::State, + setup::{Keypair, SetupParams}, + }; + use ark_bls12_381::{Bls12_381, Fr}; + + use ark_std::{ + cfg_iter, + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use short_group_sig::{ + bb_sig::{PublicKeyG2 as BBSigPublicKey, SecretKey as BBSigSecretKey}, + common::SignatureParams as BBSigParams, + }; + + #[test] + fn single_membership_witness_update() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, mut accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + + let mut members = vec![]; + let mut witnesses = vec![]; + let count = 10; + + let mut update_post_remove_duration = Duration::default(); + let mut update_post_remove_counter = 0; + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + let wit = accumulator + .add::(&elem, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + accumulator + .verify_membership(&elem, &wit, &keypair.public_key, ¶ms, &pk, &sig_params) + .unwrap(); + members.push(elem); + witnesses.push(wit); + } + + // Remove an existing element, update witness of an existing member and check that the new witness is valid + let mut i = count - 1; + loop { + let new_accumulator = accumulator + .remove::(&members[i], &keypair.secret_key, &mut state, &sk) + .unwrap(); + let mut j = i; + while j > 0 { + // Update witness of each element before i, going backwards + assert!(new_accumulator + .verify_membership( + &members[j - 1], + &witnesses[j - 1], + &keypair.public_key, + ¶ms, + &pk, + &sig_params + ) + .is_err()); + + let start = Instant::now(); + let new_wit = witnesses[j - 1] + .update_after_removal( + witnesses[i].get_accumulator_member(), + new_accumulator.value(), + ) + .unwrap(); + update_post_remove_duration += start.elapsed(); + update_post_remove_counter += 1; + + new_accumulator + .verify_membership( + &members[j - 1], + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + witnesses[j - 1] = new_wit; + j -= 1; + } + accumulator = new_accumulator; + if i == 0 { + break; + } + i -= 1; + } + + println!( + "KB Positive Accumulator: Single update witness time after {} removals {:?}", + update_post_remove_counter, update_post_remove_duration + ); + } + + #[test] + fn batch_updates_witnesses() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + + let additions_1: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let additions_2: Vec = (0..5).map(|_| Fr::rand(&mut rng)).collect(); + let additions_3: Vec = (0..5).map(|_| Fr::rand(&mut rng)).collect(); + let remove_indices = vec![0, 1, 6, 9]; + let removals = remove_indices + .iter() + .map(|i| additions_1[*i]) + .collect::>(); + + // Add elements in `additions_1`, compute witnesses for them, then add `additions_2` + let wits = accumulator + .add_batch::( + additions_1.clone(), + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + let witnesses_1 = accumulator + .get_witnesses_for_batch::( + &additions_1, + &keypair.secret_key, + &state, + &sk, + &sig_params, + ) + .unwrap(); + assert_eq!(wits, witnesses_1); + for i in 0..witnesses_1.len() { + accumulator + .verify_membership( + &additions_1[i], + &witnesses_1[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + let witnesses_2 = accumulator + .add_batch::( + additions_2.clone(), + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + for i in 0..witnesses_2.len() { + accumulator + .verify_membership( + &additions_2[i], + &witnesses_2[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + let removed_members = remove_indices + .iter() + .map(|i| *witnesses_1[*i].get_accumulator_member()) + .collect::>(); + + for k in 0..removed_members.len() { + assert!(state.has(&removed_members[k])); + } + for k in 0..witnesses_2.len() { + assert!(state.has(witnesses_2[k].get_accumulator_member())); + } + + // Remove elements in `removals` and update witnesses for `additions_2` + let accumulator_2 = accumulator + .remove_batch::(&removals, &keypair.secret_key, &mut state, &sk) + .unwrap(); + for i in 0..witnesses_1.len() { + assert!(accumulator_2 + .verify_membership( + &additions_1[i], + &witnesses_1[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params + ) + .is_err()); + } + for i in 0..witnesses_2.len() { + assert!(accumulator_2 + .verify_membership( + &additions_2[i], + &witnesses_2[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params + ) + .is_err()); + } + + for k in 0..removed_members.len() { + assert!(!state.has(&removed_members[k])); + } + for k in 0..witnesses_2.len() { + assert!(state.has(witnesses_2[k].get_accumulator_member())); + } + + let new_wits = KBPositiveAccumulatorWitness::update_using_secret_key_after_batch_removals( + &removed_members, + &witnesses_2, + accumulator.value(), + &keypair.secret_key, + ) + .unwrap(); + assert_eq!(new_wits.len(), witnesses_2.len()); + for i in 0..new_wits.len() { + accumulator_2 + .verify_membership( + &additions_2[i], + &new_wits[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + // Compute membership witness for elements remaining from `additions_1`, remove elements in `additions_2`, add elements in `addition_3` + // and update witnesses for the remaining elements + let mut remaining = additions_1; + for e in removals { + remaining.retain(|&x| x != e); + } + + let witnesses_3 = accumulator_2 + .get_witnesses_for_batch::( + &remaining, + &keypair.secret_key, + &state, + &sk, + &sig_params, + ) + .unwrap(); + for i in 0..witnesses_3.len() { + accumulator_2 + .verify_membership( + &remaining[i], + &witnesses_3[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + let accumulator_2_cloned = accumulator_2.clone(); + let mut state_cloned = state.clone(); + + /// Update an accumulator with a batch of updates, update existing witnesses of given elements and check that new witnesses are valid + fn check_batch_witness_update_using_secret_key( + current_accm: &KBPositiveAccumulator, + additions: Vec, + removals: &[Fr], + elements: &[Fr], + old_witnesses: &[KBPositiveAccumulatorWitness], + keypair: &Keypair, + params: &SetupParams, + state: &mut dyn State, + sig_sk: &BBSigSecretKey, + sig_pk: &BBSigPublicKey, + sig_params: &BBSigParams, + ) -> ( + KBPositiveAccumulator, + Vec>, + ) { + let (accumulator_new, _wits) = current_accm + .batch_updates::( + additions.clone(), + removals, + &keypair.secret_key, + state, + sig_sk, + sig_params, + ) + .unwrap(); + if !removals.is_empty() { + for i in 0..old_witnesses.len() { + assert!(accumulator_new + .verify_membership( + &elements[i], + &old_witnesses[i], + &keypair.public_key, + params, + sig_pk, + sig_params + ) + .is_err()); + } + } + + let removed_members = cfg_into_iter!(removals) + .map(|r| { + KBPositiveAccumulator::::accumulator_member::(r, sig_sk) + }) + .collect::>(); + let new_witnesses = + KBPositiveAccumulatorWitness::update_using_secret_key_after_batch_removals( + &removed_members, + old_witnesses, + current_accm.value(), + &keypair.secret_key, + ) + .unwrap(); + assert_eq!(new_witnesses.len(), old_witnesses.len()); + for i in 0..new_witnesses.len() { + accumulator_new + .verify_membership( + &elements[i], + &new_witnesses[i], + &keypair.public_key, + params, + sig_pk, + sig_params, + ) + .unwrap(); + } + (accumulator_new, new_witnesses) + } + + let (accumulator_4, witnesses_4) = check_batch_witness_update_using_secret_key( + &accumulator_2, + additions_3.clone(), + &additions_2, + &remaining, + &witnesses_3, + &keypair, + ¶ms, + &mut state, + &sk, + &pk, + &sig_params, + ); + let verification_accumulator_4 = + KBPositiveAccumulator::::from_accumulated(*accumulator_4.value()); + + let (accumulator_4_new, witnesses_6) = check_batch_witness_update_using_secret_key( + &accumulator_2_cloned, + additions_3.clone(), + &[], + &remaining, + &witnesses_3, + &keypair, + ¶ms, + &mut state_cloned, + &sk, + &pk, + &sig_params, + ); + let _verification_accumulator_4_new = + KBPositiveAccumulator::::from_accumulated(*accumulator_4_new.value()); + + let (_accumulator_5_new, _) = check_batch_witness_update_using_secret_key( + &accumulator_4_new, + vec![], + &additions_2, + &remaining, + &witnesses_6, + &keypair, + ¶ms, + &mut state_cloned, + &sk, + &pk, + &sig_params, + ); + + // Public updates to witnesses - each one in `remaining` updates his witness using publicly published info from manager + let omega = Omega::new_for_kb_positive_accumulator::( + &additions_2, + accumulator_2.value(), + &keypair.secret_key, + &sk, + ); + + let removed_members = cfg_into_iter!(additions_2.as_slice()) + .map(|r| KBPositiveAccumulator::::accumulator_member::(r, &sk)) + .collect::>(); + + for k in 0..additions_2.len() { + assert_eq!(removed_members[k], *witnesses_2[k].get_accumulator_member()); + } + + for i in 0..remaining.len() { + Omega::check_for_kb_positive_accumulator::( + &additions_2, + &remaining[i], + accumulator_2.value(), + &keypair.secret_key, + &sk, + ); + let new_wit = witnesses_3[i] + .update_using_public_info_after_batch_updates(&removed_members, &omega) + .unwrap(); + assert_eq!(witnesses_4[i], new_wit); + verification_accumulator_4 + .verify_membership( + &remaining[i], + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + } + + #[test] + fn update_witnesses_after_multiple_batch_updates() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + + let mut members = vec![]; + for _ in 0..10 { + let elem = Fr::rand(&mut rng); + accumulator + .add::(&elem, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + members.push(elem) + } + + let witnesses = accumulator + .get_witnesses_for_batch::( + &members, + &keypair.secret_key, + &state, + &sk, + &sig_params, + ) + .unwrap(); + for i in 0..10 { + accumulator + .verify_membership( + &members[i], + &witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + let additions_1: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let additions_2: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let additions_3: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let removals_1: Vec = vec![0, 1, 6, 9] + .into_iter() + .map(|i| additions_1[i]) + .collect(); + let removed_members_1 = cfg_iter!(removals_1) + .map(|r| KBPositiveAccumulator::::accumulator_member::(r, &sk)) + .collect::>(); + let removals_2: Vec = vec![0, 1, 6, 9] + .into_iter() + .map(|i| additions_2[i]) + .collect(); + let removed_members_2 = cfg_iter!(removals_2) + .map(|r| KBPositiveAccumulator::::accumulator_member::(r, &sk)) + .collect::>(); + let removals_3: Vec = vec![0, 1, 6, 9] + .into_iter() + .map(|i| additions_3[i]) + .collect(); + let removed_members_3 = cfg_iter!(removals_3) + .map(|r| KBPositiveAccumulator::::accumulator_member::(r, &sk)) + .collect::>(); + + accumulator + .add_batch::( + additions_1.clone(), + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + let accumulator_1 = accumulator + .remove_batch::(&removals_1, &keypair.secret_key, &mut state, &sk) + .unwrap(); + for i in 0..witnesses.len() { + assert!(accumulator_1 + .verify_membership( + &members[i], + &witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params + ) + .is_err()); + } + let omega_1 = Omega::new_for_kb_positive_accumulator::( + &removals_1, + accumulator.value(), + &keypair.secret_key, + &sk, + ); + + for (i, wit) in witnesses.iter().enumerate() { + let new_wit = wit + .update_using_public_info_after_multiple_batch_updates(vec![( + removed_members_1.as_slice(), + &omega_1, + )]) + .unwrap(); + accumulator_1 + .verify_membership( + &members[i], + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + accumulator_1 + .add_batch::( + additions_2.clone(), + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + let accumulator_2 = accumulator_1 + .remove_batch::(&removals_2, &keypair.secret_key, &mut state, &sk) + .unwrap(); + for i in 0..witnesses.len() { + assert!(accumulator_2 + .verify_membership( + &members[i], + &witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params + ) + .is_err()); + } + let omega_2 = Omega::new_for_kb_positive_accumulator::( + &removals_2, + accumulator_1.value(), + &keypair.secret_key, + &sk, + ); + + for (i, wit) in witnesses.iter().enumerate() { + let new_wit = wit + .update_using_public_info_after_multiple_batch_updates(vec![ + (removed_members_1.as_slice(), &omega_1), + (removed_members_2.as_slice(), &omega_2), + ]) + .unwrap(); + accumulator_2 + .verify_membership( + &members[i], + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + accumulator_2 + .add_batch::( + additions_3.clone(), + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + let accumulator_3 = accumulator_2 + .remove_batch::(&removals_3, &keypair.secret_key, &mut state, &sk) + .unwrap(); + for i in 0..witnesses.len() { + assert!(accumulator_3 + .verify_membership( + &members[i], + &witnesses[i], + &keypair.public_key, + ¶ms, + &pk, + &sig_params + ) + .is_err()); + } + let omega_3 = Omega::new_for_kb_positive_accumulator::( + &removals_3, + accumulator_2.value(), + &keypair.secret_key, + &sk, + ); + + for (i, wit) in witnesses.into_iter().enumerate() { + let new_wit = wit + .update_using_public_info_after_multiple_batch_updates(vec![ + (removed_members_1.as_slice(), &omega_1), + (removed_members_2.as_slice(), &omega_2), + (removed_members_3.as_slice(), &omega_3), + ]) + .unwrap(); + accumulator_3 + .verify_membership( + &members[i], + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + } + + fn multiple_batches_check( + member: &Fr, + initial_additions: Vec, + additions: Vec>, + removals: Vec>, + ) { + let mut rng = StdRng::seed_from_u64(0u64); + + let (sig_params, sk, pk, params, keypair, mut accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + + accumulator + .add_batch::( + initial_additions, + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + + let mut omegas = vec![]; + let mut removed_members = vec![]; + + // Witness that will be updated with multiple batches + let wit = accumulator + .get_witness::(member, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + + // This witness is updated with only 1 batch in each iteration of the loop below + let mut wit_temp = wit.clone(); + + for i in 0..additions.len() { + let omega = Omega::new_for_kb_positive_accumulator::( + &removals[i], + accumulator.value(), + &keypair.secret_key, + &sk, + ); + let new = accumulator + .batch_updates::( + additions[i].clone(), + &removals[i], + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + accumulator = new.0; + let removed = cfg_into_iter!(removals[i].as_slice()) + .map(|r| { + KBPositiveAccumulator::::accumulator_member::(r, &sk) + }) + .collect::>(); + wit_temp = wit_temp + .update_using_public_info_after_batch_updates(&removed, &omega) + .unwrap(); + accumulator + .verify_membership( + member, + &wit_temp, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + omegas.push(omega); + removed_members.push(removed); + } + + let mut updates_and_omegas = vec![]; + for i in 0..additions.len() { + updates_and_omegas.push((removed_members[i].as_slice(), &omegas[i])); + } + + let new_wit = wit + .update_using_public_info_after_multiple_batch_updates(updates_and_omegas) + .unwrap(); + + accumulator + .verify_membership( + member, + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + #[test] + fn update_witnesses_after_multiple_batch_updates_1() { + let mut rng = StdRng::seed_from_u64(0u64); + let e0 = Fr::rand(&mut rng); + let e1 = Fr::rand(&mut rng); + let e2 = Fr::rand(&mut rng); + let e3 = Fr::rand(&mut rng); + let e4 = Fr::rand(&mut rng); + let e5 = Fr::rand(&mut rng); + let e6 = Fr::rand(&mut rng); + let e7 = Fr::rand(&mut rng); + let e8 = Fr::rand(&mut rng); + let e9 = Fr::rand(&mut rng); + + let initial_additions = vec![e0, e1, e2]; + let additions = vec![vec![e3, e4], vec![e5, e6], vec![e7, e8, e9]]; + let removals = vec![vec![e0, e1], vec![e3], vec![e4]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4], vec![e5, e6], vec![e7, e8, e9]]; + let removals = vec![vec![e0, e1], vec![e3], vec![]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4], vec![e5, e6], vec![e7, e8, e9]]; + let removals = vec![vec![e0, e1], vec![], vec![]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4], vec![e5, e6], vec![e7, e8, e9]]; + let removals = vec![vec![e0, e1], vec![], vec![e3, e4, e5]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4], vec![e5, e6], vec![e7, e8]]; + let removals = vec![vec![e0, e1], vec![e3], vec![e4]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4], vec![e5, e6, e7]]; + let removals = vec![vec![e0, e1], vec![e3]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4], vec![e5, e6, e7]]; + let removals = vec![vec![e0, e1], vec![]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4, e5, e6, e7, e8, e9], vec![], vec![]]; + let removals = vec![vec![e0], vec![], vec![e1, e3, e4, e5]]; + multiple_batches_check(&e2, initial_additions.clone(), additions, removals); + + let additions = vec![vec![e3, e4, e5, e6, e7, e8, e9], vec![], vec![], vec![]]; + let removals = vec![vec![e0], vec![], vec![e1, e3, e4, e5], vec![e6, e7, e8, e9]]; + multiple_batches_check(&e2, initial_additions, additions, removals); + } + + #[test] + fn update_witnesses_after_multiple_batch_updates_2() { + let mut rng = StdRng::seed_from_u64(0u64); + let (sig_params, sk, pk, params, keypair, mut accumulator, mut state) = + setup_kb_positive_accum(&mut rng); + let e0 = Fr::rand(&mut rng); + + let elements: Vec = (0..12).map(|_| Fr::rand(&mut rng)).collect(); + + accumulator + .add_batch::( + vec![e0, elements[0], elements[1]], + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + + let wit = accumulator + .get_witness::(&e0, &keypair.secret_key, &mut state, &sk, &sig_params) + .unwrap(); + + let mut wit_temp = wit.clone(); + + let mut omegas = vec![]; + let mut additions = vec![]; + let mut removals = vec![]; + let mut removed_members = vec![]; + for i in (2..10).step_by(2) { + additions.push(vec![elements[i], elements[i + 1]]); + removals.push(vec![elements[i - 2], elements[i - 1]]); + removed_members.push( + cfg_into_iter!(removals.last().unwrap()) + .map(|r| { + KBPositiveAccumulator::::accumulator_member::(r, &sk) + }) + .collect::>(), + ); + let omega = Omega::new_for_kb_positive_accumulator::( + removals.last().unwrap(), + accumulator.value(), + &keypair.secret_key, + &sk, + ); + omegas.push(omega); + let new = accumulator + .batch_updates::( + additions.last().unwrap().clone(), + removals.last().unwrap(), + &keypair.secret_key, + &mut state, + &sk, + &sig_params, + ) + .unwrap(); + accumulator = new.0; + wit_temp = wit_temp + .update_using_public_info_after_batch_updates( + removed_members.last().unwrap(), + omegas.last().unwrap(), + ) + .unwrap(); + accumulator + .verify_membership( + &e0, + &wit_temp, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } + + let new_wit = wit + .update_using_public_info_after_multiple_batch_updates(vec![ + (&removed_members[0], &omegas[0]), + (&removed_members[1], &omegas[1]), + (&removed_members[2], &omegas[2]), + (&removed_members[3], &omegas[3]), + ]) + .unwrap(); + + accumulator + .verify_membership( + &e0, + &new_wit, + &keypair.public_key, + ¶ms, + &pk, + &sig_params, + ) + .unwrap(); + } +} diff --git a/vb_accumulator/src/kb_universal_accumulator/accumulator.rs b/vb_accumulator/src/kb_universal_accumulator/accumulator.rs new file mode 100644 index 00000000..9aa08f98 --- /dev/null +++ b/vb_accumulator/src/kb_universal_accumulator/accumulator.rs @@ -0,0 +1,520 @@ +//! A universal accumulator contructed from 2 positive accumulators where one accumulator accumulates all the members, say *Acc_M*, +//! and the other accumulates all the non-members, say *Acc_N*. Thus in an empty accumulator, all possible elements, called +//! the *domain* are present in the accumulator *Acc_N*. Adding an element to the accumulator results in adding the element to *Acc_M* and +//! removing it from *Acc_N* and removing an element results in adding it to *Acc_N* and removing from *Acc_M*. A membership +//! witness is a membership witness in *Acc_M* and a non-membership witness is a membership witness in *Acc_N* + +use crate::{ + batch_utils::Poly_d, + error::VBAccumulatorError, + kb_universal_accumulator::witness::{ + KBUniversalAccumulatorMembershipWitness, KBUniversalAccumulatorNonMembershipWitness, + }, + persistence::State, + positive::{Accumulator, PositiveAccumulator}, + setup::{PublicKey, SecretKey, SetupParams}, +}; +use ark_ec::pairing::Pairing; +use ark_ff::{Field, One}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::vec::Vec; + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulator { + /// The accumulator accumulating all the members + pub mem: PositiveAccumulator, + /// The accumulator accumulating all the non-members + pub non_mem: PositiveAccumulator, +} + +impl KBUniversalAccumulator { + /// Initialize a new accumulator. `domain` is the set of all possible accumulator members. Initialization includes adding + /// the `domain` to the accumulator accumulating all non-members + pub fn initialize( + params_gen: impl AsRef, + sk: &SecretKey, + domain: Vec, + non_mem_state: &mut dyn State, + ) -> Result { + let mem = PositiveAccumulator::initialize(params_gen); + let mut non_mem = mem.clone(); + non_mem = non_mem.add_batch(domain, sk, non_mem_state)?; + Ok(Self { mem, non_mem }) + } + + pub fn initialize_given_initialized_non_members_accumulator( + params_gen: impl AsRef, + non_mem: PositiveAccumulator, + ) -> Self { + let mem = PositiveAccumulator::initialize(params_gen); + Self { mem, non_mem } + } + + /// Add new elements to an already initialized accumulator that were not part of its `domain`. + pub fn extend_domain( + &self, + sk: &SecretKey, + new_elements: Vec, + non_mem_state: &mut dyn State, + ) -> Result { + let mut new = self.clone(); + new.non_mem = new.non_mem.add_batch(new_elements, sk, non_mem_state)?; + Ok(new) + } + + /// Add an element to the accumulator updating both the internal accumulators. + pub fn add( + &self, + element: E::ScalarField, + sk: &SecretKey, + mem_state: &mut dyn State, + non_mem_state: &mut dyn State, + ) -> Result { + let mut new = self.clone(); + // Remove from non-membership accumulator + new.non_mem = new.non_mem.remove(&element, sk, non_mem_state)?; + // Add to membership accumulator + new.mem = new.mem.add(element, sk, mem_state)?; + Ok(new) + } + + /// Remove an element from the accumulator updating both the internal accumulators. + pub fn remove( + &self, + element: E::ScalarField, + sk: &SecretKey, + mem_state: &mut dyn State, + non_mem_state: &mut dyn State, + ) -> Result { + let mut new = self.clone(); + new.mem = new.mem.remove(&element, sk, mem_state)?; + new.non_mem = new.non_mem.add(element, sk, non_mem_state)?; + Ok(new) + } + + pub fn add_batch( + &self, + elements: Vec, + sk: &SecretKey, + mem_state: &mut dyn State, + non_mem_state: &mut dyn State, + ) -> Result { + for element in &elements { + self.non_mem.check_before_remove(element, non_mem_state)?; + self.mem.check_before_add(element, mem_state)?; + } + let mut new = self.clone(); + let update = Poly_d::::eval_direct(&elements, &-sk.0); + let update_inv = update.inverse().unwrap(); + for element in elements { + non_mem_state.remove(&element); + mem_state.add(element); + } + new.mem = PositiveAccumulator((*new.mem.value() * update).into()); + new.non_mem = PositiveAccumulator((*new.non_mem.value() * update_inv).into()); + Ok(new) + } + + pub fn remove_batch( + &self, + elements: Vec, + sk: &SecretKey, + mem_state: &mut dyn State, + non_mem_state: &mut dyn State, + ) -> Result { + for element in &elements { + self.mem.check_before_remove(element, mem_state)?; + self.non_mem.check_before_add(element, non_mem_state)?; + } + let mut new = self.clone(); + let update = Poly_d::::eval_direct(&elements, &-sk.0); + let update_inv = update.inverse().unwrap(); + for element in elements { + mem_state.remove(&element); + non_mem_state.add(element); + } + new.non_mem = PositiveAccumulator((*new.non_mem.value() * update).into()); + new.mem = PositiveAccumulator((*new.mem.value() * update_inv).into()); + Ok(new) + } + + pub fn batch_updates( + &self, + additions: Vec, + removals: Vec, + sk: &SecretKey, + mem_state: &mut dyn State, + non_mem_state: &mut dyn State, + ) -> Result { + for element in &additions { + self.mem.check_before_add(element, mem_state)?; + self.non_mem.check_before_remove(element, non_mem_state)?; + } + for element in &removals { + self.mem.check_before_remove(element, mem_state)?; + self.non_mem.check_before_add(element, non_mem_state)?; + } + + let mut new = self.clone(); + let update_add = if !additions.is_empty() { + Poly_d::::eval_direct(&additions, &-sk.0) + } else { + E::ScalarField::one() + }; + let update_rem = if !removals.is_empty() { + Poly_d::::eval_direct(&removals, &-sk.0) + } else { + E::ScalarField::one() + }; + let update_mem = update_add * update_rem.inverse().unwrap(); + + for element in additions { + non_mem_state.remove(&element); + mem_state.add(element); + } + for element in removals { + mem_state.remove(&element); + non_mem_state.add(element); + } + new.mem = PositiveAccumulator((*new.mem.value() * update_mem).into()); + new.non_mem = + PositiveAccumulator((*new.non_mem.value() * update_mem.inverse().unwrap()).into()); + Ok(new) + } + + pub fn get_membership_witness( + &self, + member: &E::ScalarField, + sk: &SecretKey, + mem_state: &dyn State, + ) -> Result, VBAccumulatorError> { + self.mem + .get_membership_witness(member, sk, mem_state) + .map(|w| w.into()) + } + + pub fn get_membership_witnesses_for_batch( + &self, + members: &[E::ScalarField], + sk: &SecretKey, + mem_state: &dyn State, + ) -> Result>, VBAccumulatorError> { + self.mem + .get_membership_witnesses_for_batch(members, sk, mem_state) + .map(|ws| ws.into_iter().map(|w| w.into()).collect()) + } + + pub fn get_non_membership_witness( + &self, + non_member: &E::ScalarField, + sk: &SecretKey, + non_mem_state: &dyn State, + ) -> Result, VBAccumulatorError> { + self.non_mem + .get_membership_witness(non_member, sk, non_mem_state) + .map(|w| w.into()) + } + + pub fn get_non_membership_witnesses_for_batch( + &self, + non_members: &[E::ScalarField], + sk: &SecretKey, + non_mem_state: &dyn State, + ) -> Result>, VBAccumulatorError> + { + self.non_mem + .get_membership_witnesses_for_batch(non_members, sk, non_mem_state) + .map(|ws| ws.into_iter().map(|w| w.into()).collect()) + } + + pub fn verify_membership( + &self, + member: &E::ScalarField, + witness: &KBUniversalAccumulatorMembershipWitness, + pk: &PublicKey, + params: &SetupParams, + ) -> bool { + self.mem.verify_membership(member, &witness.0, pk, params) + } + + pub fn verify_non_membership( + &self, + non_member: &E::ScalarField, + witness: &KBUniversalAccumulatorNonMembershipWitness, + pk: &PublicKey, + params: &SetupParams, + ) -> bool { + self.non_mem + .verify_membership(non_member, &witness.0, pk, params) + } + + pub fn mem_value(&self) -> &E::G1Affine { + self.mem.value() + } + + pub fn non_mem_value(&self) -> &E::G1Affine { + self.non_mem.value() + } + + pub fn value(&self) -> (&E::G1Affine, &E::G1Affine) { + (self.mem.value(), self.non_mem.value()) + } + + pub fn from_accumulated( + mem_accumulated: E::G1Affine, + non_mem_accumulated: E::G1Affine, + ) -> Self { + Self { + mem: PositiveAccumulator::from_accumulated(mem_accumulated), + non_mem: PositiveAccumulator::from_accumulated(non_mem_accumulated), + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use std::time::{Duration, Instant}; + + use ark_bls12_381::{Bls12_381, Fr}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + + use crate::{persistence::test::*, setup::Keypair}; + + pub fn setup_kb_universal_accum( + rng: &mut StdRng, + size: usize, + ) -> ( + SetupParams, + Keypair, + KBUniversalAccumulator, + Vec, + InMemoryState, + InMemoryState, + ) { + let params = SetupParams::::generate_using_rng(rng); + let keypair = Keypair::::generate_using_rng(rng, ¶ms); + + let domain = (0..size).map(|_| Fr::rand(rng)).collect::>(); + let mem_state = InMemoryState::new(); + let mut non_mem_state = InMemoryState::new(); + let accumulator = KBUniversalAccumulator::initialize( + ¶ms, + &keypair.secret_key, + domain.clone(), + &mut non_mem_state, + ) + .unwrap(); + ( + params, + keypair, + accumulator, + domain, + mem_state, + non_mem_state, + ) + } + + #[test] + fn membership_non_membership() { + // Test to check membership and non-membership in accumulator + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator, domain, mut mem_state, mut non_mem_state) = + setup_kb_universal_accum(&mut rng, max); + + let mut total_mem_check_time = Duration::default(); + let mut total_non_mem_check_time = Duration::default(); + let count = max; + for i in 0..count { + let elem = domain[i].clone(); + assert!(accumulator + .get_membership_witness(&elem, &keypair.secret_key, &mem_state) + .is_err()); + + let mut start = Instant::now(); + let nm_wit = accumulator + .get_non_membership_witness(&elem, &keypair.secret_key, &non_mem_state) + .unwrap(); + assert!(accumulator.verify_non_membership( + &elem, + &nm_wit, + &keypair.public_key, + ¶ms + )); + total_non_mem_check_time += start.elapsed(); + + assert!(!mem_state.has(&elem)); + assert!(non_mem_state.has(&elem)); + + accumulator = accumulator + .add( + elem, + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + + assert!(mem_state.has(&elem)); + assert!(!non_mem_state.has(&elem)); + + let m_wit = accumulator + .get_membership_witness(&elem, &keypair.secret_key, &mem_state) + .unwrap(); + + start = Instant::now(); + assert!(accumulator.verify_membership(&elem, &m_wit, &keypair.public_key, ¶ms)); + total_mem_check_time += start.elapsed(); + } + + println!( + "Total time to verify {} individual memberships {:?}", + count, total_mem_check_time + ); + println!( + "Total time to verify {} individual non-memberships {:?}", + count, total_non_mem_check_time + ); + } + + #[test] + fn batch_update_and_membership() { + // Tests batch updates and batch membership witness generation + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator_1, domain, mut mem_state, mut non_mem_state) = + setup_kb_universal_accum(&mut rng, max); + + // Create more accumulators to compare. Same elements will be added and removed from them as accumulator_1 + let mut accumulator_2: KBUniversalAccumulator = accumulator_1.clone(); + let mut state_2_mem = mem_state.clone(); + let mut state_2_non_mem = non_mem_state.clone(); + + let mut accumulator_3: KBUniversalAccumulator = accumulator_1.clone(); + let mut state_3_mem = mem_state.clone(); + let mut state_3_non_mem = non_mem_state.clone(); + + let additions: Vec = (0..10).map(|i| domain[i]).collect(); + let removals: Vec = vec![0, 1, 6, 9].into_iter().map(|i| additions[i]).collect(); + + // Add one by one + for i in 0..additions.len() { + let elem = additions[i]; + accumulator_1 = accumulator_1 + .add( + elem, + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + } + + // Add as a batch + accumulator_2 = accumulator_2 + .add_batch( + additions.clone(), + &keypair.secret_key, + &mut state_2_mem, + &mut state_2_non_mem, + ) + .unwrap(); + assert_eq!(accumulator_1.value(), accumulator_2.value()); + assert_eq!(mem_state.db, state_2_mem.db); + assert_eq!(non_mem_state.db, state_2_non_mem.db); + + // Remove one by one + for i in 0..removals.len() { + accumulator_1 = accumulator_1 + .remove( + removals[i], + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + } + + // Remove as a batch + accumulator_2 = accumulator_2 + .remove_batch( + removals.clone(), + &keypair.secret_key, + &mut state_2_mem, + &mut state_2_non_mem, + ) + .unwrap(); + assert_eq!(accumulator_1.value(), accumulator_2.value()); + assert_eq!(mem_state.db, state_2_mem.db); + assert_eq!(non_mem_state.db, state_2_non_mem.db); + + // Need to make `accumulator_3` same as `accumulator_1` and `accumulator_2` by doing batch addition and removal simultaneously. + // To do the removals, first they need to be added to the accumulator and the additions elements need to be adjusted. + let mut new_additions = additions; + for e in removals.iter() { + accumulator_3 = accumulator_3 + .add( + *e, + &keypair.secret_key, + &mut state_3_mem, + &mut state_3_non_mem, + ) + .unwrap(); + new_additions.retain(|&x| x != *e); + } + + assert_ne!(accumulator_1.value(), accumulator_3.value()); + assert_ne!(accumulator_2.value(), accumulator_3.value()); + + // Add and remove as a batch + accumulator_3 = accumulator_3 + .batch_updates( + new_additions.clone(), + removals.clone(), + &keypair.secret_key, + &mut state_3_mem, + &mut state_3_non_mem, + ) + .unwrap(); + assert_eq!(accumulator_1.value(), accumulator_3.value()); + assert_eq!(accumulator_2.value(), accumulator_3.value()); + + assert_eq!(mem_state.db, state_2_mem.db); + assert_eq!(non_mem_state.db, state_2_non_mem.db); + assert_eq!(mem_state.db, state_3_mem.db); + assert_eq!(non_mem_state.db, state_3_non_mem.db); + + let mem_witnesses = accumulator_3 + .get_membership_witnesses_for_batch(&new_additions, &keypair.secret_key, &state_3_mem) + .unwrap(); + for i in 0..new_additions.len() { + assert!(accumulator_3.verify_membership( + &new_additions[i], + &mem_witnesses[i], + &keypair.public_key, + ¶ms + )); + } + let npn_mem_witnesses = accumulator_3 + .get_non_membership_witnesses_for_batch( + &removals, + &keypair.secret_key, + &state_3_non_mem, + ) + .unwrap(); + for i in 0..removals.len() { + assert!(accumulator_3.verify_non_membership( + &removals[i], + &npn_mem_witnesses[i], + &keypair.public_key, + ¶ms + )); + } + } +} diff --git a/vb_accumulator/src/kb_universal_accumulator/mod.rs b/vb_accumulator/src/kb_universal_accumulator/mod.rs new file mode 100644 index 00000000..85a2f152 --- /dev/null +++ b/vb_accumulator/src/kb_universal_accumulator/mod.rs @@ -0,0 +1,9 @@ +//! Universal dynamic accumulator defined in section 6, Fig 3 of the paper [Efficient Constructions of Pairing Based Accumulators](https://eprint.iacr.org/2021/638) + +pub mod accumulator; +pub mod proofs; +pub mod proofs_alt; +pub mod proofs_keyed_verification; +pub mod witness; + +pub use accumulator::KBUniversalAccumulator; diff --git a/vb_accumulator/src/kb_universal_accumulator/proofs.rs b/vb_accumulator/src/kb_universal_accumulator/proofs.rs new file mode 100644 index 00000000..8f477751 --- /dev/null +++ b/vb_accumulator/src/kb_universal_accumulator/proofs.rs @@ -0,0 +1,528 @@ +//! As the KB universal accumulator is made of 2 positive accumulators, its proof of membership and non-membership is the proof +//! of membership in positive accumulator + +use crate::{ + error::VBAccumulatorError, + kb_universal_accumulator::witness::{ + KBUniversalAccumulatorMembershipWitness, KBUniversalAccumulatorNonMembershipWitness, + }, + prelude::{PreparedPublicKey, PreparedSetupParams}, + proofs::{MembershipProof, MembershipProofProtocol}, + setup::{PublicKey, SetupParams}, +}; +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec::Vec}; + +use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; +use short_group_sig::common::ProvingKey; + +pub struct KBUniversalAccumulatorMembershipProofProtocol( + pub MembershipProofProtocol, +); + +pub struct KBUniversalAccumulatorNonMembershipProofProtocol( + pub MembershipProofProtocol, +); + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulatorMembershipProof(pub MembershipProof); + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulatorNonMembershipProof(pub MembershipProof); + +impl KBUniversalAccumulatorMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: &E::ScalarField, + element_blinding: Option, + witness: &KBUniversalAccumulatorMembershipWitness, + pk: &PublicKey, + params: &SetupParams, + prk: impl AsRef>, + ) -> Self { + Self(MembershipProofProtocol::init( + rng, + element, + element_blinding, + &witness.0, + pk, + params, + prk, + )) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + pk: &PublicKey, + params: &SetupParams, + prk: impl AsRef>, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0 + .challenge_contribution(accumulator_value, pk, params, prk, writer) + } + + pub fn gen_proof(self, challenge: &E::ScalarField) -> KBUniversalAccumulatorMembershipProof { + KBUniversalAccumulatorMembershipProof(self.0.gen_proof(challenge)) + } +} + +impl KBUniversalAccumulatorMembershipProof { + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + pk: &PublicKey, + params: &SetupParams, + prk: impl AsRef>, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0 + .challenge_contribution(accumulator_value, pk, params, prk, writer) + } + + pub fn verify( + &self, + accumulator_value: &E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + prk: impl AsRef>, + ) -> Result<(), VBAccumulatorError> { + self.0.verify(accumulator_value, challenge, pk, params, prk) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + accumulator_value: &E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + prk: impl AsRef>, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), VBAccumulatorError> { + self.0.verify_with_randomized_pairing_checker( + accumulator_value, + challenge, + pk, + params, + prk, + pairing_checker, + ) + } + + pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { + self.0.get_schnorr_response_for_element() + } +} + +impl KBUniversalAccumulatorNonMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: &E::ScalarField, + element_blinding: Option, + witness: &KBUniversalAccumulatorNonMembershipWitness, + pk: &PublicKey, + params: &SetupParams, + prk: impl AsRef>, + ) -> Self { + Self(MembershipProofProtocol::init( + rng, + element, + element_blinding, + &witness.0, + pk, + params, + prk, + )) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + pk: &PublicKey, + params: &SetupParams, + prk: impl AsRef>, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0 + .challenge_contribution(accumulator_value, pk, params, prk, writer) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> KBUniversalAccumulatorNonMembershipProof { + KBUniversalAccumulatorNonMembershipProof(self.0.gen_proof(challenge)) + } +} + +impl KBUniversalAccumulatorNonMembershipProof { + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + pk: &PublicKey, + params: &SetupParams, + prk: impl AsRef>, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0 + .challenge_contribution(accumulator_value, pk, params, prk, writer) + } + + pub fn verify( + &self, + accumulator_value: &E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + prk: impl AsRef>, + ) -> Result<(), VBAccumulatorError> { + self.0.verify(accumulator_value, challenge, pk, params, prk) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + accumulator_value: &E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + prk: impl AsRef>, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), VBAccumulatorError> { + self.0.verify_with_randomized_pairing_checker( + accumulator_value, + challenge, + pk, + params, + prk, + pairing_checker, + ) + } + + pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { + self.0.get_schnorr_response_for_element() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::kb_universal_accumulator::accumulator::tests::setup_kb_universal_accum; + use ark_bls12_381::Fr; + use ark_std::rand::{rngs::StdRng, SeedableRng}; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + use std::time::{Duration, Instant}; + + #[test] + fn membership_non_membership_proof() { + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator, domain, mut mem_state, mut non_mem_state) = + setup_kb_universal_accum(&mut rng, max); + let prk = ProvingKey::generate_using_rng(&mut rng); + let prepared_params = PreparedSetupParams::from(params.clone()); + let prepared_pk = PreparedPublicKey::from(keypair.public_key.clone()); + + let mut members = vec![]; + let mut non_members = vec![]; + let mut mem_witnesses = vec![]; + let mut non_mem_witnesses = vec![]; + let count = 10; + + for i in 0..count { + let elem = domain[i]; + accumulator = accumulator + .add( + elem, + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + members.push(elem); + non_members.push(domain[count + i]) + } + + for i in 0..count { + let w = accumulator + .get_membership_witness(&members[i], &keypair.secret_key, &mem_state) + .unwrap(); + assert!(accumulator.verify_membership(&members[i], &w, &keypair.public_key, ¶ms)); + mem_witnesses.push(w); + + let w = accumulator + .get_non_membership_witness( + &non_members[i], + &keypair.secret_key, + &mut non_mem_state, + ) + .unwrap(); + assert!(accumulator.verify_non_membership( + &non_members[i], + &w, + &keypair.public_key, + ¶ms + )); + non_mem_witnesses.push(w); + } + + let mut mem_proof_create_duration = Duration::default(); + let mut mem_proof_verif_duration = Duration::default(); + let mut mem_proof_verif_with_prepared_duration = Duration::default(); + let mut mem_proof_verif_with_rand_pair_check_duration = Duration::default(); + let mut mem_proof_verif_with_prepared_and_rand_pair_check_duration = Duration::default(); + let mut non_mem_proof_create_duration = Duration::default(); + let mut non_mem_proof_verif_duration = Duration::default(); + let mut non_mem_proof_verif_with_prepared_duration = Duration::default(); + let mut non_mem_proof_verif_with_rand_pair_check_duration = Duration::default(); + let mut non_mem_proof_verif_with_prepared_and_rand_pair_check_duration = + Duration::default(); + + let mut mem_pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); + let mut non_mem_pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); + + for i in 0..count { + let start = Instant::now(); + let protocol = KBUniversalAccumulatorMembershipProofProtocol::init( + &mut rng, + &members[i], + None, + &mem_witnesses[i], + &keypair.public_key, + ¶ms, + &prk, + ); + mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution( + accumulator.mem_value(), + &keypair.public_key, + ¶ms, + &prk, + &mut chal_bytes_prover, + ) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover); + mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution( + accumulator.mem_value(), + &keypair.public_key, + ¶ms, + &prk, + &mut chal_bytes_verifier, + ) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + accumulator.mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &prk, + ) + .unwrap(); + mem_proof_verif_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify( + accumulator.mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + &prk, + ) + .unwrap(); + mem_proof_verif_with_prepared_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + accumulator.mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &prk, + &mut mem_pairing_checker, + ) + .unwrap(); + mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + accumulator.mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + &prk, + &mut mem_pairing_checker, + ) + .unwrap(); + mem_proof_verif_with_prepared_and_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + let protocol = KBUniversalAccumulatorNonMembershipProofProtocol::init( + &mut rng, + &non_members[i], + None, + &non_mem_witnesses[i], + &keypair.public_key, + ¶ms, + &prk, + ); + non_mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution( + accumulator.non_mem_value(), + &keypair.public_key, + ¶ms, + &prk, + &mut chal_bytes_prover, + ) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover); + non_mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution( + accumulator.non_mem_value(), + &keypair.public_key, + ¶ms, + &prk, + &mut chal_bytes_verifier, + ) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + accumulator.non_mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &prk, + ) + .unwrap(); + non_mem_proof_verif_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify( + accumulator.non_mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + &prk, + ) + .unwrap(); + non_mem_proof_verif_with_prepared_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + accumulator.non_mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &prk, + &mut non_mem_pairing_checker, + ) + .unwrap(); + non_mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + accumulator.non_mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + &prk, + &mut non_mem_pairing_checker, + ) + .unwrap(); + non_mem_proof_verif_with_prepared_and_rand_pair_check_duration += start.elapsed(); + } + + let start = Instant::now(); + assert!(mem_pairing_checker.verify()); + mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + assert!(non_mem_pairing_checker.verify()); + non_mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + println!( + "Time to create {} membership proofs is {:?}", + count, mem_proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, mem_proof_verif_duration + ); + println!( + "Time to verify {} membership proofs using prepared params is {:?}", + count, mem_proof_verif_with_prepared_duration + ); + println!( + "Time to verify {} membership proofs using randomized pairing checker is {:?}", + count, mem_proof_verif_with_rand_pair_check_duration + ); + println!( + "Time to verify {} membership proofs using prepared params and randomized pairing checker is {:?}", + count, mem_proof_verif_with_prepared_and_rand_pair_check_duration + ); + + println!( + "Time to create {} non-membership proofs is {:?}", + count, non_mem_proof_create_duration + ); + println!( + "Time to verify {} non-membership proofs is {:?}", + count, non_mem_proof_verif_duration + ); + println!( + "Time to verify {} non-membership proofs using prepared params is {:?}", + count, non_mem_proof_verif_with_prepared_duration + ); + println!( + "Time to verify {} non-membership proofs using randomized pairing checker is {:?}", + count, non_mem_proof_verif_with_rand_pair_check_duration + ); + println!( + "Time to verify {} non-membership proofs using prepared params and randomized pairing checker is {:?}", + count, non_mem_proof_verif_with_prepared_and_rand_pair_check_duration + ); + } +} diff --git a/vb_accumulator/src/kb_universal_accumulator/proofs_alt.rs b/vb_accumulator/src/kb_universal_accumulator/proofs_alt.rs new file mode 100644 index 00000000..845554ed --- /dev/null +++ b/vb_accumulator/src/kb_universal_accumulator/proofs_alt.rs @@ -0,0 +1,466 @@ +//! More efficient than proofs described in proofs.rs + +use crate::{ + error::VBAccumulatorError, + kb_universal_accumulator::witness::{ + KBUniversalAccumulatorMembershipWitness, KBUniversalAccumulatorNonMembershipWitness, + }, + prelude::{PreparedPublicKey, PreparedSetupParams}, + proofs_alt::{MembershipProof, MembershipProofProtocol}, +}; +use ark_ec::pairing::Pairing; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec::Vec}; +use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker; + +pub struct KBUniversalAccumulatorMembershipProofProtocol( + pub MembershipProofProtocol, +); + +pub struct KBUniversalAccumulatorNonMembershipProofProtocol( + pub MembershipProofProtocol, +); + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulatorMembershipProof(pub MembershipProof); + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulatorNonMembershipProof(pub MembershipProof); + +impl KBUniversalAccumulatorMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: E::ScalarField, + element_blinding: Option, + accumulator_value: E::G1Affine, + witness: &KBUniversalAccumulatorMembershipWitness, + ) -> Result { + Ok(Self(MembershipProofProtocol::init( + rng, + element, + element_blinding, + accumulator_value, + &witness.0, + )?)) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(KBUniversalAccumulatorMembershipProof( + self.0.gen_proof(challenge)?, + )) + } +} + +impl KBUniversalAccumulatorMembershipProof { + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn verify( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + ) -> Result<(), VBAccumulatorError> { + self.0.verify(accumulator_value, challenge, pk, params) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), VBAccumulatorError> { + self.0.verify_with_randomized_pairing_checker( + accumulator_value, + challenge, + pk, + params, + pairing_checker, + ) + } + + pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { + self.0.get_schnorr_response_for_element() + } +} + +impl KBUniversalAccumulatorNonMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: E::ScalarField, + element_blinding: Option, + accumulator_value: E::G1Affine, + witness: &KBUniversalAccumulatorNonMembershipWitness, + ) -> Result { + Ok(Self(MembershipProofProtocol::init( + rng, + element, + element_blinding, + accumulator_value, + &witness.0, + )?)) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(KBUniversalAccumulatorNonMembershipProof( + self.0.gen_proof(challenge)?, + )) + } +} + +impl KBUniversalAccumulatorNonMembershipProof { + pub fn verify( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + ) -> Result<(), VBAccumulatorError> { + self.0.verify(accumulator_value, challenge, pk, params) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), VBAccumulatorError> { + self.0.verify_with_randomized_pairing_checker( + accumulator_value, + challenge, + pk, + params, + pairing_checker, + ) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { + self.0.get_schnorr_response_for_element() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::kb_universal_accumulator::accumulator::tests::setup_kb_universal_accum; + use ark_bls12_381::Fr; + use ark_std::rand::{rngs::StdRng, SeedableRng}; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + use std::time::{Duration, Instant}; + + #[test] + fn membership_non_membership_proof() { + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator, domain, mut mem_state, mut non_mem_state) = + setup_kb_universal_accum(&mut rng, max); + let prepared_params = PreparedSetupParams::from(params.clone()); + let prepared_pk = PreparedPublicKey::from(keypair.public_key.clone()); + + let mut members = vec![]; + let mut non_members = vec![]; + let mut mem_witnesses = vec![]; + let mut non_mem_witnesses = vec![]; + let count = 10; + + for i in 0..count { + let elem = domain[i]; + accumulator = accumulator + .add( + elem, + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + members.push(elem); + non_members.push(domain[count + i]) + } + + for i in 0..count { + let w = accumulator + .get_membership_witness(&members[i], &keypair.secret_key, &mem_state) + .unwrap(); + assert!(accumulator.verify_membership(&members[i], &w, &keypair.public_key, ¶ms)); + mem_witnesses.push(w); + + let w = accumulator + .get_non_membership_witness( + &non_members[i], + &keypair.secret_key, + &mut non_mem_state, + ) + .unwrap(); + assert!(accumulator.verify_non_membership( + &non_members[i], + &w, + &keypair.public_key, + ¶ms + )); + non_mem_witnesses.push(w); + } + + let mut mem_proof_create_duration = Duration::default(); + let mut mem_proof_verif_duration = Duration::default(); + let mut mem_proof_verif_with_prepared_duration = Duration::default(); + let mut mem_proof_verif_with_rand_pair_check_duration = Duration::default(); + let mut mem_proof_verif_with_prepared_and_rand_pair_check_duration = Duration::default(); + let mut non_mem_proof_create_duration = Duration::default(); + let mut non_mem_proof_verif_duration = Duration::default(); + let mut non_mem_proof_verif_with_prepared_duration = Duration::default(); + let mut non_mem_proof_verif_with_rand_pair_check_duration = Duration::default(); + let mut non_mem_proof_verif_with_prepared_and_rand_pair_check_duration = + Duration::default(); + + let mut mem_pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); + let mut non_mem_pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); + + for i in 0..count { + let start = Instant::now(); + let protocol = KBUniversalAccumulatorMembershipProofProtocol::init( + &mut rng, + members[i], + None, + *accumulator.mem_value(), + &mem_witnesses[i], + ) + .unwrap(); + mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(*accumulator.mem_value(), &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(*accumulator.mem_value(), &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + *accumulator.mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + ) + .unwrap(); + mem_proof_verif_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify( + *accumulator.mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + ) + .unwrap(); + mem_proof_verif_with_prepared_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + *accumulator.mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &mut mem_pairing_checker, + ) + .unwrap(); + mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + *accumulator.mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + &mut mem_pairing_checker, + ) + .unwrap(); + mem_proof_verif_with_prepared_and_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + let protocol = KBUniversalAccumulatorNonMembershipProofProtocol::init( + &mut rng, + non_members[i], + None, + *accumulator.non_mem_value(), + &non_mem_witnesses[i], + ) + .unwrap(); + non_mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(*accumulator.non_mem_value(), &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + non_mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(*accumulator.non_mem_value(), &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + *accumulator.non_mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + ) + .unwrap(); + non_mem_proof_verif_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify( + *accumulator.non_mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + ) + .unwrap(); + non_mem_proof_verif_with_prepared_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + *accumulator.non_mem_value(), + &challenge_verifier, + keypair.public_key.clone(), + params.clone(), + &mut non_mem_pairing_checker, + ) + .unwrap(); + non_mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + proof + .verify_with_randomized_pairing_checker( + *accumulator.non_mem_value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + &mut non_mem_pairing_checker, + ) + .unwrap(); + non_mem_proof_verif_with_prepared_and_rand_pair_check_duration += start.elapsed(); + } + + let start = Instant::now(); + assert!(mem_pairing_checker.verify()); + mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + let start = Instant::now(); + assert!(non_mem_pairing_checker.verify()); + non_mem_proof_verif_with_rand_pair_check_duration += start.elapsed(); + + println!( + "Time to create {} membership proofs is {:?}", + count, mem_proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, mem_proof_verif_duration + ); + println!( + "Time to verify {} membership proofs using prepared params is {:?}", + count, mem_proof_verif_with_prepared_duration + ); + println!( + "Time to verify {} membership proofs using randomized pairing checker is {:?}", + count, mem_proof_verif_with_rand_pair_check_duration + ); + println!( + "Time to verify {} membership proofs using prepared params and randomized pairing checker is {:?}", + count, mem_proof_verif_with_prepared_and_rand_pair_check_duration + ); + + println!( + "Time to create {} non-membership proofs is {:?}", + count, non_mem_proof_create_duration + ); + println!( + "Time to verify {} non-membership proofs is {:?}", + count, non_mem_proof_verif_duration + ); + println!( + "Time to verify {} non-membership proofs using prepared params is {:?}", + count, non_mem_proof_verif_with_prepared_duration + ); + println!( + "Time to verify {} non-membership proofs using randomized pairing checker is {:?}", + count, non_mem_proof_verif_with_rand_pair_check_duration + ); + println!( + "Time to verify {} non-membership proofs using prepared params and randomized pairing checker is {:?}", + count, non_mem_proof_verif_with_prepared_and_rand_pair_check_duration + ); + } +} diff --git a/vb_accumulator/src/kb_universal_accumulator/proofs_keyed_verification.rs b/vb_accumulator/src/kb_universal_accumulator/proofs_keyed_verification.rs new file mode 100644 index 00000000..19a17420 --- /dev/null +++ b/vb_accumulator/src/kb_universal_accumulator/proofs_keyed_verification.rs @@ -0,0 +1,421 @@ +//! Proofs of membership and non-membership with keyed-verification, i.e. the verifier needs to know the secret key to verify the proofs. +//! These are essentially keyed-verification proofs of knowledge of weak-BB signature. + +use crate::{ + error::VBAccumulatorError, + kb_universal_accumulator::witness::{ + KBUniversalAccumulatorMembershipWitness, KBUniversalAccumulatorNonMembershipWitness, + }, + prelude::SecretKey, + proofs_keyed_verification::{ + DelegatedMembershipProof, MembershipProof, MembershipProofProtocol, + }, +}; +use ark_ec::AffineRepr; + +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, rand::RngCore, vec::Vec}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +#[derive( + Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop, CanonicalSerialize, CanonicalDeserialize, +)] +pub struct KBUniversalAccumulatorMembershipProofProtocol( + pub MembershipProofProtocol, +); + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct KBUniversalAccumulatorMembershipProof(pub MembershipProof); + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct KBUniversalAccumulatorDelegatedMembershipProof( + pub DelegatedMembershipProof, +); + +#[derive( + Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop, CanonicalSerialize, CanonicalDeserialize, +)] +pub struct KBUniversalAccumulatorNonMembershipProofProtocol( + pub MembershipProofProtocol, +); + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct KBUniversalAccumulatorNonMembershipProof(pub MembershipProof); + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct KBUniversalAccumulatorDelegatedNonMembershipProof( + pub DelegatedMembershipProof, +); + +impl KBUniversalAccumulatorMembershipProofProtocol { + /// Initialize a membership proof protocol. + pub fn init( + rng: &mut R, + element: G::ScalarField, + element_blinding: Option, + witness: &KBUniversalAccumulatorMembershipWitness, + accumulator: G, + ) -> Self { + Self(MembershipProofProtocol::init( + rng, + element, + element_blinding, + &witness.0, + accumulator, + )) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn gen_proof( + self, + challenge: &G::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(KBUniversalAccumulatorMembershipProof( + self.0.clone().gen_proof(challenge)?, + )) + } +} + +impl KBUniversalAccumulatorMembershipProof { + pub fn verify( + &self, + accumulator: G, + secret_key: &SecretKey, + challenge: &G::ScalarField, + ) -> Result<(), VBAccumulatorError> { + self.0.verify(accumulator, secret_key, challenge) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn verify_schnorr_proof( + &self, + accumulator: G, + challenge: &G::ScalarField, + ) -> Result<(), VBAccumulatorError> { + self.0.verify_schnorr_proof(accumulator, challenge) + } + + pub fn to_delegated_proof(&self) -> KBUniversalAccumulatorDelegatedMembershipProof { + KBUniversalAccumulatorDelegatedMembershipProof(self.0.to_delegated_proof()) + } + + pub fn get_schnorr_response_for_element(&self) -> &G::ScalarField { + self.0.get_schnorr_response_for_element() + } +} + +impl KBUniversalAccumulatorNonMembershipProofProtocol { + /// Initialize a membership proof protocol. + pub fn init( + rng: &mut R, + element: G::ScalarField, + element_blinding: Option, + witness: &KBUniversalAccumulatorNonMembershipWitness, + accumulator: G, + ) -> Self { + Self(MembershipProofProtocol::init( + rng, + element, + element_blinding, + &witness.0, + accumulator, + )) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn gen_proof( + self, + challenge: &G::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(KBUniversalAccumulatorNonMembershipProof( + self.0.clone().gen_proof(challenge)?, + )) + } +} + +impl KBUniversalAccumulatorNonMembershipProof { + pub fn verify( + &self, + accumulator: G, + secret_key: &SecretKey, + challenge: &G::ScalarField, + ) -> Result<(), VBAccumulatorError> { + self.0.verify(accumulator, secret_key, challenge) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer) + } + + pub fn verify_schnorr_proof( + &self, + accumulator: G, + challenge: &G::ScalarField, + ) -> Result<(), VBAccumulatorError> { + self.0.verify_schnorr_proof(accumulator, challenge) + } + + pub fn to_delegated_proof(&self) -> KBUniversalAccumulatorDelegatedNonMembershipProof { + KBUniversalAccumulatorDelegatedNonMembershipProof(self.0.to_delegated_proof()) + } + + pub fn get_schnorr_response_for_element(&self) -> &G::ScalarField { + self.0.get_schnorr_response_for_element() + } +} + +impl KBUniversalAccumulatorDelegatedMembershipProof { + pub fn verify(&self, secret_key: &SecretKey) -> Result<(), VBAccumulatorError> { + self.0.verify(secret_key) + } +} + +impl KBUniversalAccumulatorDelegatedNonMembershipProof { + pub fn verify(&self, secret_key: &SecretKey) -> Result<(), VBAccumulatorError> { + self.0.verify(secret_key) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + kb_universal_accumulator::accumulator::KBUniversalAccumulator, + persistence::test::InMemoryState, + setup_keyed_verification::{PublicKey, SetupParams}, + }; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + use std::time::{Duration, Instant}; + + pub fn setup_uni_accum( + rng: &mut StdRng, + max: u64, + ) -> ( + SetupParams, + SecretKey, + PublicKey, + KBUniversalAccumulator, + Vec, + InMemoryState, + InMemoryState, + ) { + let params = SetupParams::::new::(b"test"); + let seed = [0, 1, 2, 10, 11]; + let secret_key = SecretKey::generate_using_seed::(&seed); + let public_key = PublicKey::new_from_secret_key(&secret_key, ¶ms); + + let domain = (0..max).map(|_| Fr::rand(rng)).collect::>(); + let mem_state = InMemoryState::new(); + let mut non_mem_state = InMemoryState::new(); + let accumulator = KBUniversalAccumulator::initialize( + ¶ms, + &secret_key, + domain.clone(), + &mut non_mem_state, + ) + .unwrap(); + ( + params, + secret_key, + public_key, + accumulator, + domain, + mem_state, + non_mem_state, + ) + } + #[test] + fn membership_non_membership_proof() { + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (_, secret_key, _, mut accumulator, domain, mut mem_state, mut non_mem_state) = + setup_uni_accum(&mut rng, max); + + let mut members = vec![]; + let mut non_members = vec![]; + let mut mem_witnesses = vec![]; + let mut non_mem_witnesses = vec![]; + let count = 10; + + for i in 0..count { + let elem = domain[i]; + accumulator = accumulator + .add(elem, &secret_key, &mut mem_state, &mut non_mem_state) + .unwrap(); + members.push(elem); + non_members.push(domain[count + i]) + } + + for i in 0..count { + let w = accumulator + .get_membership_witness(&members[i], &secret_key, &mem_state) + .unwrap(); + mem_witnesses.push(w); + + let w = accumulator + .get_non_membership_witness(&non_members[i], &secret_key, &mut non_mem_state) + .unwrap(); + non_mem_witnesses.push(w); + } + + let mut mem_proof_create_duration = Duration::default(); + let mut mem_proof_verif_duration = Duration::default(); + let mut non_mem_proof_create_duration = Duration::default(); + let mut non_mem_proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = KBUniversalAccumulatorMembershipProofProtocol::init( + &mut rng, + members[i].clone(), + None, + &mem_witnesses[i], + accumulator.mem_value().clone(), + ); + mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(accumulator.mem_value(), &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(accumulator.mem_value(), &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + accumulator.mem_value().clone(), + &secret_key, + &challenge_verifier, + ) + .unwrap(); + mem_proof_verif_duration += start.elapsed(); + + proof + .verify_schnorr_proof(accumulator.mem_value().clone(), &challenge_verifier) + .unwrap(); + let delegated_proof = proof.to_delegated_proof(); + delegated_proof.verify(&secret_key).unwrap(); + + let start = Instant::now(); + let protocol = KBUniversalAccumulatorNonMembershipProofProtocol::init( + &mut rng, + non_members[i].clone(), + None, + &non_mem_witnesses[i], + accumulator.non_mem_value().clone(), + ); + non_mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(accumulator.non_mem_value(), &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + non_mem_proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(accumulator.non_mem_value(), &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + accumulator.non_mem_value().clone(), + &secret_key, + &challenge_verifier, + ) + .unwrap(); + non_mem_proof_verif_duration += start.elapsed(); + + proof + .verify_schnorr_proof(accumulator.non_mem_value().clone(), &challenge_verifier) + .unwrap(); + let delegated_proof = proof.to_delegated_proof(); + delegated_proof.verify(&secret_key).unwrap(); + } + + println!( + "Time to create {} membership proofs is {:?}", + count, mem_proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, mem_proof_verif_duration + ); + println!( + "Time to create {} non-membership proofs is {:?}", + count, non_mem_proof_create_duration + ); + println!( + "Time to verify {} non-membership proofs is {:?}", + count, non_mem_proof_verif_duration + ); + } +} diff --git a/vb_accumulator/src/kb_universal_accumulator/witness.rs b/vb_accumulator/src/kb_universal_accumulator/witness.rs new file mode 100644 index 00000000..f10d6c37 --- /dev/null +++ b/vb_accumulator/src/kb_universal_accumulator/witness.rs @@ -0,0 +1,1007 @@ +use crate::{ + error::VBAccumulatorError, + kb_universal_accumulator::accumulator::KBUniversalAccumulator, + positive::Accumulator, + prelude::SecretKey, + witness::{MembershipWitness, Witness}, +}; +use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; +use ark_ff::{batch_inversion, One, PrimeField, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{cfg_into_iter, cfg_iter, vec, vec::Vec}; + +use crate::prelude::Omega; +use dock_crypto_utils::msm::WindowTable; + +use dock_crypto_utils::cfg_iter_sum; +use dock_crypto_utils::ff::inner_product; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulatorMembershipWitness(pub MembershipWitness); + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct KBUniversalAccumulatorNonMembershipWitness(pub MembershipWitness); + +impl From> for KBUniversalAccumulatorMembershipWitness { + fn from(w: MembershipWitness) -> Self { + KBUniversalAccumulatorMembershipWitness(w) + } +} + +impl From for KBUniversalAccumulatorMembershipWitness { + fn from(w: G) -> Self { + KBUniversalAccumulatorMembershipWitness(MembershipWitness(w)) + } +} + +impl From> for KBUniversalAccumulatorNonMembershipWitness { + fn from(w: MembershipWitness) -> Self { + KBUniversalAccumulatorNonMembershipWitness(w) + } +} + +impl From for KBUniversalAccumulatorNonMembershipWitness { + fn from(w: G) -> Self { + KBUniversalAccumulatorNonMembershipWitness(MembershipWitness(w)) + } +} + +// Any change to accumulator changes for membership and non-membership witness + +impl KBUniversalAccumulator { + /// Update the membership witness on adding an element. Call this on the accumulator before update + pub fn update_mem_wit_on_addition( + &self, + wit: &KBUniversalAccumulatorMembershipWitness, + member: &E::ScalarField, + addition: &E::ScalarField, + ) -> KBUniversalAccumulatorMembershipWitness { + wit.0 + .update_after_addition(member, addition, self.mem.value()) + .into() + } + + /// Update the membership witness on removal of an element. Call this on the accumulator after update + pub fn update_mem_wit_on_removal( + &self, + wit: &KBUniversalAccumulatorMembershipWitness, + member: &E::ScalarField, + removal: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(wit + .0 + .update_after_removal(member, removal, self.mem.value())? + .into()) + } + + /// Update the membership witnesses on addition of a batch of elements. Call this on the accumulator before update + pub fn update_mem_wit_using_secret_key_on_batch_additions( + &self, + additions: &[E::ScalarField], + members: &[E::ScalarField], + old_witnesses: &[KBUniversalAccumulatorMembershipWitness], + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> { + let old: Vec = cfg_iter!(old_witnesses).map(|w| w.0 .0).collect(); + let (_, new) = MembershipWitness::::compute_update_using_secret_key_after_batch_additions(additions, members, &old, &self.mem.0, sk)?; + Ok(cfg_into_iter!(new) + .map(|w| MembershipWitness(w).into()) + .collect()) + } + + /// Update the membership witnesses on removal of a batch of elements. Call this on the accumulator before update + pub fn update_mem_wit_using_secret_key_on_batch_removals( + &self, + removals: &[E::ScalarField], + members: &[E::ScalarField], + old_witnesses: &[KBUniversalAccumulatorMembershipWitness], + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> { + let old: Vec = cfg_iter!(old_witnesses).map(|w| w.0 .0).collect(); + let (_, new) = + MembershipWitness::::compute_update_using_secret_key_after_batch_removals( + removals, + members, + &old, + &self.mem.0, + sk, + )?; + Ok(cfg_into_iter!(new) + .map(|w| MembershipWitness(w).into()) + .collect()) + } + + /// Update the membership witnesses on addition and removal of a batch of elements. Call this on the accumulator before update + pub fn update_mem_wit_using_secret_key_on_batch_updates( + &self, + additions: &[E::ScalarField], + removals: &[E::ScalarField], + members: &[E::ScalarField], + old_witnesses: &[KBUniversalAccumulatorMembershipWitness], + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> { + let old: Vec = cfg_iter!(old_witnesses).map(|w| w.0 .0).collect(); + let (_, new) = + MembershipWitness::::compute_update_using_secret_key_after_batch_updates( + additions, + removals, + members, + &old, + &self.mem.0, + sk, + )?; + Ok(cfg_into_iter!(new) + .map(|w| MembershipWitness(w).into()) + .collect()) + } + + /// Update the non-membership witness on adding an element. Call this on the accumulator after update + pub fn update_non_mem_wit_on_addition( + &self, + wit: &KBUniversalAccumulatorNonMembershipWitness, + non_member: &E::ScalarField, + addition: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(wit + .0 + .update_after_removal(non_member, addition, self.non_mem.value())? + .into()) + } + + /// Update the non-membership witness on removal of an element. Call this on the accumulator before update + pub fn update_non_mem_wit_on_removal( + &self, + wit: &KBUniversalAccumulatorNonMembershipWitness, + non_member: &E::ScalarField, + removal: &E::ScalarField, + ) -> KBUniversalAccumulatorNonMembershipWitness { + wit.0 + .update_after_addition(non_member, removal, self.non_mem.value()) + .into() + } + + /// Update the non-membership witnesses on addition of a batch of elements. Call this on the accumulator before update + pub fn update_non_mem_wit_using_secret_key_on_batch_additions( + &self, + additions: &[E::ScalarField], + non_members: &[E::ScalarField], + old_witnesses: &[KBUniversalAccumulatorNonMembershipWitness], + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> + { + let old: Vec = cfg_iter!(old_witnesses).map(|w| w.0 .0).collect(); + let (_, new) = + MembershipWitness::::compute_update_using_secret_key_after_batch_removals( + additions, + non_members, + &old, + &self.non_mem.0, + sk, + )?; + Ok(cfg_into_iter!(new) + .map(|w| MembershipWitness(w).into()) + .collect()) + } + + /// Update the non-membership witnesses on removal of a batch of elements. Call this on the accumulator before update + pub fn update_non_mem_wit_using_secret_key_on_batch_removals( + &self, + removals: &[E::ScalarField], + non_members: &[E::ScalarField], + old_witnesses: &[KBUniversalAccumulatorNonMembershipWitness], + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> + { + let old: Vec = cfg_iter!(old_witnesses).map(|w| w.0 .0).collect(); + let (_, new) = + MembershipWitness::::compute_update_using_secret_key_after_batch_additions( + removals, + non_members, + &old, + &self.non_mem.0, + sk, + )?; + Ok(cfg_into_iter!(new) + .map(|w| MembershipWitness(w).into()) + .collect()) + } + + /// Update the non-membership witnesses on addition and removal of a batch of elements. Call this on the accumulator before update + pub fn update_non_mem_wit_using_secret_key_on_batch_updates( + &self, + additions: &[E::ScalarField], + removals: &[E::ScalarField], + non_members: &[E::ScalarField], + old_witnesses: &[KBUniversalAccumulatorNonMembershipWitness], + sk: &SecretKey, + ) -> Result>, VBAccumulatorError> + { + let old: Vec = cfg_iter!(old_witnesses).map(|w| w.0 .0).collect(); + let (_, new) = + MembershipWitness::::compute_update_using_secret_key_after_batch_updates( + removals, + additions, + non_members, + &old, + &self.non_mem.0, + sk, + )?; + Ok(cfg_into_iter!(new) + .map(|w| MembershipWitness(w).into()) + .collect()) + } + + /// Call this on the accumulator before update + pub fn generate_omega_for_membership_witnesses( + &self, + additions: &[E::ScalarField], + removals: &[E::ScalarField], + sk: &SecretKey, + ) -> Omega { + Omega::new(additions, removals, self.mem.value(), sk) + } + + /// Call this on the accumulator before update + pub fn generate_omega_for_non_membership_witnesses( + &self, + additions: &[E::ScalarField], + removals: &[E::ScalarField], + sk: &SecretKey, + ) -> Omega { + Omega::new(removals, additions, self.non_mem.value(), sk) + } + + /// Update both membership and non-membership witnesses in a single call. Call this on the accumulator before update + pub fn update_both_wit_using_secret_key_on_batch_updates( + &self, + additions: &[E::ScalarField], + removals: &[E::ScalarField], + members: &[E::ScalarField], + old_mem_witnesses: &[KBUniversalAccumulatorMembershipWitness], + non_members: &[E::ScalarField], + old_non_mem_witnesses: &[KBUniversalAccumulatorNonMembershipWitness], + sk: &SecretKey, + ) -> Result< + ( + Vec>, + Vec>, + ), + VBAccumulatorError, + > { + if members.len() != old_mem_witnesses.len() { + return Err(VBAccumulatorError::NeedSameNoOfElementsAndWitnesses); + } + if non_members.len() != old_non_mem_witnesses.len() { + return Err(VBAccumulatorError::NeedSameNoOfElementsAndWitnesses); + } + let m = additions.len(); + let n = removals.len(); + let p = members.len(); + let q = non_members.len(); + let alpha = &sk.0; + + // (additions[0] + alpha), (additions[0] + alpha)*(additions[1] + alpha), ..., (additions[0] + alpha)*(additions[1] + alpha)*...(additions[m-1] + alpha) + let mut factors_add = vec![E::ScalarField::one(); m]; + // (removals[0] + alpha), (removals[0] + alpha)*(removals[1] + alpha), ..., (removals[0] + alpha)*(removals[1] + alpha)*...(removals[n-1] + alpha) + let mut factors_rem = vec![E::ScalarField::one(); n]; + // For each of the p members, mem_add_poly[i] = (additions[1] - members[i])*(additions[2] - members[i])*...(additions[m-1] - members[i]), (additions[2] - members[i])*(additions[3] - members[i])*...(additions[m-1] - members[i]), .., 1 + let mut mem_add_poly = vec![vec![E::ScalarField::one(); m]; p]; + // For each of the p members, mem_rem_poly[i] = 1, (removals[0] - members[i]), (removals[0] - x)*(removals[1] - members[i]), ..., (removals[0] - members[i])*(removals[1] - members[i])*...(removals[n-2] - members[i]) + let mut mem_rem_poly = vec![vec![E::ScalarField::one(); n]; p]; + let mut non_mem_add_poly = vec![vec![E::ScalarField::one(); n]; q]; + let mut non_mem_rem_poly = vec![vec![E::ScalarField::one(); m]; q]; + + if !additions.is_empty() { + factors_add[0] = additions[0] + alpha; + } + if !removals.is_empty() { + factors_rem[0] = removals[0] + alpha; + } + + for s in 1..m { + factors_add[s] = factors_add[s - 1] * (additions[s] + alpha); + for j in 0..p { + mem_add_poly[j][m - 1 - s] = + mem_add_poly[j][m - s] * (additions[m - s] - members[j]); + } + for j in 0..q { + non_mem_rem_poly[j][s] = + non_mem_rem_poly[j][s - 1] * (additions[s - 1] - non_members[j]); + } + } + for s in 1..n { + factors_rem[s] = factors_rem[s - 1] * (removals[s] + alpha); + for j in 0..q { + non_mem_add_poly[j][n - 1 - s] = + non_mem_add_poly[j][n - s] * (removals[n - s] - non_members[j]); + } + for j in 0..p { + mem_rem_poly[j][s] = mem_rem_poly[j][s - 1] * (removals[s - 1] - members[j]); + } + } + + // 1/(additions[0] + alpha), 1/(additions[0] + alpha)*(additions[1] + alpha), ..., 1/(additions[0] + alpha)*(additions[1] + alpha)*...(additions[m-1] + alpha) + let mut factors_add_inv = factors_add.clone(); + batch_inversion(&mut factors_add_inv); + // 1/(removals[0] + alpha), 1/(removals[0] + alpha)*(removals[1] + alpha), ..., 1/(removals[0] + alpha)*(removals[1] + alpha)*...(removals[n-1] + alpha) + let mut factors_rem_inv = factors_rem.clone(); + batch_inversion(&mut factors_rem_inv); + + let (mem_d_A, mut mem_d_D): (Vec<_>, Vec<_>) = cfg_into_iter!(0..p) + .map(|i| { + ( + mem_add_poly[i][0] * (additions[0] - members[i]), + mem_rem_poly[i][n - 1] * (removals[n - 1] - members[i]), + ) + }) + .unzip(); + let (non_mem_d_A, mut non_mem_d_D): (Vec<_>, Vec<_>) = cfg_into_iter!(0..q) + .map(|i| { + ( + non_mem_add_poly[i][0] * (removals[0] - non_members[i]), + non_mem_rem_poly[i][m - 1] * (additions[m - 1] - non_members[i]), + ) + }) + .unzip(); + + batch_inversion(&mut mem_d_D); + batch_inversion(&mut non_mem_d_D); + + let one = E::ScalarField::one(); + let zero = E::ScalarField::zero; + + let mem_v_AD = cfg_into_iter!(0..p) + .map(|j| { + // 1*mem_add_poly[0] + factors_add[0]*mem_add_poly[1] + ... + factors_add[m-2]*mem_add_poly[m-1] + let mem_poly_v_A = cfg_into_iter!(0..m) + .map(|i| if i == 0 { &one } else { &factors_add[i - 1] }) + .zip(cfg_iter!(mem_add_poly[j])) + .map(|(f, p)| *p * *f); + let mem_poly_v_A = cfg_iter_sum!(mem_poly_v_A, zero); + + let mem_poly_v_D = inner_product(&factors_rem_inv, &mem_rem_poly[j]); + + mem_poly_v_A - (mem_poly_v_D * factors_add[m - 1]) + }) + .collect::>(); + + let non_mem_v_AD = cfg_into_iter!(0..q) + .map(|j| { + // 1*non_mem_add_poly[0] + factors_rem[0]*non_mem_add_poly[1] + ... + factors_rem[n-2]*non_mem_add_poly[n-1] + let non_mem_poly_v_A = cfg_into_iter!(0..n) + .map(|i| if i == 0 { &one } else { &factors_rem[i - 1] }) + .zip(cfg_iter!(non_mem_add_poly[j])) + .map(|(f, p)| *p * *f); + let non_mem_poly_v_A = cfg_iter_sum!(non_mem_poly_v_A, zero); + + let non_mem_poly_v_D = inner_product(&factors_add_inv, &non_mem_rem_poly[j]); + + non_mem_poly_v_A - (non_mem_poly_v_D * factors_rem[n - 1]) + }) + .collect::>(); + + let mem_table = WindowTable::new(members.len(), self.mem.value().into_group()); + let non_mem_table = WindowTable::new(non_members.len(), self.non_mem.value().into_group()); + + let new_mem_wits = cfg_into_iter!(mem_d_A) + .zip(cfg_into_iter!(mem_d_D)) + .zip(cfg_into_iter!(mem_v_AD)) + .enumerate() + .map(|(i, ((d_A_i, d_D_inv), v))| { + let d_A_times_d_D_inv = d_A_i * d_D_inv; + let v_d_inv = v * d_D_inv; + // d_A_i/d_D * C + v_{A,D}/d_D * V + let r = old_mem_witnesses[i] + .0 + .0 + .mul_bigint(d_A_times_d_D_inv.into_bigint()) + + mem_table.multiply(&v_d_inv); + r + }) + .collect::>(); + + let new_non_mem_wits = cfg_into_iter!(non_mem_d_A) + .zip(cfg_into_iter!(non_mem_d_D)) + .zip(cfg_into_iter!(non_mem_v_AD)) + .enumerate() + .map(|(i, ((d_A_i, d_D_inv), v))| { + let d_A_times_d_D_inv = d_A_i * d_D_inv; + let v_d_inv = v * d_D_inv; + // d_A_i/d_D * C + v_{A,D}/d_D * V + let r = old_non_mem_witnesses[i] + .0 + .0 + .mul_bigint(d_A_times_d_D_inv.into_bigint()) + + non_mem_table.multiply(&v_d_inv); + r + }) + .collect::>(); + + let new_mem_wits = cfg_into_iter!(E::G1::normalize_batch(&new_mem_wits)) + .map(|w| w.into()) + .collect::>>(); + let new_non_mem_wits = cfg_into_iter!(E::G1::normalize_batch(&new_non_mem_wits)) + .map(|w| w.into()) + .collect::>>(); + Ok((new_mem_wits, new_non_mem_wits)) + } + + /// Call this on the accumulator before update + pub fn generate_omega_for_both_witnesses( + &self, + additions: &[E::ScalarField], + removals: &[E::ScalarField], + sk: &SecretKey, + ) -> (Omega, Omega) { + Omega::new_for_kb_universal_accumulator( + additions, + removals, + &self.mem.value(), + &self.non_mem.value(), + sk, + ) + } +} + +impl KBUniversalAccumulatorMembershipWitness { + pub fn update_using_public_info_after_batch_updates( + &self, + additions: &[G::ScalarField], + removals: &[G::ScalarField], + omega: &Omega, + member: &G::ScalarField, + ) -> Result { + let (_, new) = MembershipWitness::compute_update_using_public_info_after_batch_updates( + additions, removals, omega, member, &self.0 .0, + )?; + Ok(Self(MembershipWitness(new))) + } + + pub fn update_using_public_info_after_multiple_batch_updates( + &self, + updates_and_omegas: Vec<(&[G::ScalarField], &[G::ScalarField], &Omega)>, + member: &G::ScalarField, + ) -> Result { + let (_, new) = + MembershipWitness::compute_update_using_public_info_after_multiple_batch_updates( + updates_and_omegas, + member, + &self.0 .0, + )?; + Ok(Self(MembershipWitness(new))) + } +} + +impl KBUniversalAccumulatorNonMembershipWitness { + pub fn update_using_public_info_after_batch_updates( + &self, + additions: &[G::ScalarField], + removals: &[G::ScalarField], + omega: &Omega, + member: &G::ScalarField, + ) -> Result { + let (_, new) = MembershipWitness::compute_update_using_public_info_after_batch_updates( + removals, additions, omega, member, &self.0 .0, + )?; + Ok(Self(MembershipWitness(new))) + } + + pub fn update_using_public_info_after_multiple_batch_updates( + &self, + updates_and_omegas: Vec<(&[G::ScalarField], &[G::ScalarField], &Omega)>, + member: &G::ScalarField, + ) -> Result { + let (_, new) = + MembershipWitness::compute_update_using_public_info_after_multiple_batch_updates( + cfg_into_iter!(updates_and_omegas) + .map(|(a, r, o)| (r, a, o)) + .collect(), + member, + &self.0 .0, + )?; + Ok(Self(MembershipWitness(new))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::kb_universal_accumulator::accumulator::tests::setup_kb_universal_accum; + use ark_bls12_381::Fr; + use ark_std::rand::{prelude::StdRng, SeedableRng}; + use std::time::{Duration, Instant}; + + #[test] + fn single_witness_update_kb_universal_accumulator() { + // Test to update non-membership witness after single addition or removal + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator, domain, mut mem_state, mut non_mem_state) = + setup_kb_universal_accum(&mut rng, max); + + let mut non_members = vec![]; + let mut non_membership_witnesses = vec![]; + + let n = 10; + for i in 0..n { + let elem = domain[i].clone(); + let wit = accumulator + .get_non_membership_witness(&elem, &keypair.secret_key, &non_mem_state) + .unwrap(); + assert!(accumulator.verify_non_membership(&elem, &wit, &keypair.public_key, ¶ms)); + non_members.push(elem); + non_membership_witnesses.push(wit); + } + + let mut update_m_post_add_duration = Duration::default(); + let mut update_m_post_add_counter = 0; + let mut update_m_post_remove_duration = Duration::default(); + let mut update_m_post_remove_counter = 0; + let mut update_nm_post_add_duration = Duration::default(); + let mut update_nm_post_add_counter = 0; + let mut update_nm_post_remove_duration = Duration::default(); + let mut update_nm_post_remove_counter = 0; + + let mut members = vec![]; + let mut membership_witnesses = vec![]; + + // Add a new element, update witness of non-member and check that the new witness is valid + for i in 0..n { + let elem = domain[n + i].clone(); + let new_accumulator = accumulator + .add( + elem, + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + members.push(elem); + membership_witnesses.push( + new_accumulator + .get_membership_witness(&elem, &keypair.secret_key, &mem_state) + .unwrap(), + ); + + for j in 0..n { + assert!(!new_accumulator.verify_non_membership( + &non_members[j], + &non_membership_witnesses[j], + &keypair.public_key, + ¶ms + )); + + let start = Instant::now(); + let new_wit = new_accumulator + .update_non_mem_wit_on_addition( + &non_membership_witnesses[j], + &non_members[j], + &members[i], + ) + .unwrap(); + update_nm_post_add_duration += start.elapsed(); + update_nm_post_add_counter += 1; + + assert!(new_accumulator.verify_non_membership( + &non_members[j], + &new_wit, + &keypair.public_key, + ¶ms + )); + non_membership_witnesses[j] = new_wit; + } + + for k in 0..i { + let start = Instant::now(); + let new_wit = accumulator.update_mem_wit_on_addition( + &membership_witnesses[k], + &members[k], + &elem, + ); + update_m_post_add_duration += start.elapsed(); + update_m_post_add_counter += 1; + + assert!(new_accumulator.verify_membership( + &members[k], + &new_wit, + &keypair.public_key, + ¶ms + )); + membership_witnesses[k] = new_wit; + } + + accumulator = new_accumulator; + } + + // Remove an existing element, update witness of a non-member and check that the new witness is valid + for i in 0..n { + let new_accumulator = accumulator + .remove( + members[i].clone(), + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + for j in 0..n { + assert!(!new_accumulator.verify_non_membership( + &non_members[j], + &non_membership_witnesses[j], + &keypair.public_key, + ¶ms + )); + + let start = Instant::now(); + let new_wit = accumulator.update_non_mem_wit_on_removal( + &non_membership_witnesses[j], + &non_members[j], + &members[i], + ); + update_nm_post_remove_duration += start.elapsed(); + update_nm_post_remove_counter += 1; + + assert!(new_accumulator.verify_non_membership( + &non_members[j], + &new_wit, + &keypair.public_key, + ¶ms + )); + non_membership_witnesses[j] = new_wit; + } + for k in i + 1..n { + let start = Instant::now(); + let new_wit = new_accumulator + .update_mem_wit_on_removal(&membership_witnesses[k], &members[k], &members[i]) + .unwrap(); + update_m_post_remove_duration += start.elapsed(); + update_m_post_remove_counter += 1; + membership_witnesses[k] = new_wit; + } + accumulator = new_accumulator; + } + + println!( + "Universal Accumulator non-membership: Single update witness time after {} additions {:?}", + update_nm_post_add_counter, update_nm_post_add_duration + ); + println!( + "Universal Accumulator non-membership: Single update witness time after {} removals {:?}", + update_nm_post_remove_counter, update_nm_post_remove_duration + ); + println!( + "Universal Accumulator membership: Single update witness time after {} additions {:?}", + update_m_post_add_counter, update_m_post_add_duration + ); + println!( + "Universal Accumulator membership: Single update witness time after {} removals {:?}", + update_m_post_remove_counter, update_m_post_remove_duration + ); + } + + #[test] + fn batch_updates_witnesses_kb_universal_accumulator() { + // Accumulator manager who knows the secret key batch updates witnesses + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, accumulator, domain, mut mem_state, mut non_mem_state) = + setup_kb_universal_accum(&mut rng, max); + + let additions_1: Vec = (0..10).map(|i| domain[i]).collect(); + let additions_2: Vec = (20..30).map(|i| domain[i]).collect(); + let additions_3: Vec = (30..40).map(|i| domain[i]).collect(); + let removals: Vec = vec![0, 1, 6, 9] + .into_iter() + .map(|i| additions_2[i]) + .collect(); + + let mut non_members = vec![]; + let mut non_membership_witnesses = vec![]; + + let n = 10; + + // Add elements in `additions_1` + let accumulator_1 = accumulator + .add_batch( + additions_1.clone(), + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + let membership_witnesses_add_1 = accumulator_1 + .get_membership_witnesses_for_batch(&additions_1, &keypair.secret_key, &mem_state) + .unwrap(); + + for i in 50..50 + n { + let elem = domain[i]; + let wit = accumulator_1 + .get_non_membership_witness(&elem, &keypair.secret_key, &non_mem_state) + .unwrap(); + non_members.push(elem); + non_membership_witnesses.push(wit); + } + + // Add elements in `additions_2`, batch update witnesses + let accumulator_2 = accumulator_1 + .add_batch( + additions_2.clone(), + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + for i in 0..n { + assert!(!accumulator_2.verify_non_membership( + &non_members[i], + &non_membership_witnesses[i], + &keypair.public_key, + ¶ms + )); + } + + let membership_witnesses_1 = accumulator_1 + .update_mem_wit_using_secret_key_on_batch_additions( + &additions_2, + &additions_1, + &membership_witnesses_add_1, + &keypair.secret_key, + ) + .unwrap(); + assert_eq!( + membership_witnesses_add_1.len(), + membership_witnesses_1.len() + ); + for i in 0..additions_1.len() { + assert!(accumulator_2.verify_membership( + &additions_1[i], + &membership_witnesses_1[i], + &keypair.public_key, + ¶ms + )); + } + + let non_membership_witnesses_1 = accumulator_1 + .update_non_mem_wit_using_secret_key_on_batch_additions( + &additions_2, + &non_members, + &non_membership_witnesses, + &keypair.secret_key, + ) + .unwrap(); + assert_eq!( + non_membership_witnesses.len(), + non_membership_witnesses_1.len() + ); + for i in 0..n { + assert!(accumulator_2.verify_non_membership( + &non_members[i], + &non_membership_witnesses_1[i], + &keypair.public_key, + ¶ms + )); + } + + // Remove elements from `removals`, batch update witnesses + let accumulator_3 = accumulator_2 + .remove_batch( + removals.clone(), + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + for i in 0..n { + assert!(!accumulator_3.verify_non_membership( + &non_members[i], + &non_membership_witnesses_1[i], + &keypair.public_key, + ¶ms + )); + } + + let membership_witnesses_2 = accumulator_2 + .update_mem_wit_using_secret_key_on_batch_removals( + &removals, + &additions_1, + &membership_witnesses_1, + &keypair.secret_key, + ) + .unwrap(); + assert_eq!(membership_witnesses_2.len(), membership_witnesses_1.len()); + for i in 0..additions_1.len() { + assert!(accumulator_3.verify_membership( + &additions_1[i], + &membership_witnesses_2[i], + &keypair.public_key, + ¶ms + )); + } + + let non_membership_witnesses_2 = accumulator_2 + .update_non_mem_wit_using_secret_key_on_batch_removals( + &removals, + &non_members, + &non_membership_witnesses_1, + &keypair.secret_key, + ) + .unwrap(); + assert_eq!( + non_membership_witnesses_2.len(), + non_membership_witnesses_1.len() + ); + for i in 0..n { + assert!(accumulator_3.verify_non_membership( + &non_members[i], + &non_membership_witnesses_2[i], + &keypair.public_key, + ¶ms + )); + } + + // Remove elements remaining from `additions_2`, add elements in `additions_3` + // and update witnesses for the absent elements + let mut remaining = additions_2.clone(); + for e in removals { + remaining.retain(|&x| x != e); + } + + let accumulator_4 = accumulator_3 + .batch_updates( + additions_3.clone(), + remaining.clone(), + &keypair.secret_key, + &mut mem_state, + &mut non_mem_state, + ) + .unwrap(); + for i in 0..n { + assert!(!accumulator_4.verify_non_membership( + &non_members[i], + &non_membership_witnesses_2[i], + &keypair.public_key, + ¶ms + )); + } + + let start = Instant::now(); + let membership_witnesses_3 = accumulator_3 + .update_mem_wit_using_secret_key_on_batch_updates( + &additions_3, + &remaining, + &additions_1, + &membership_witnesses_2, + &keypair.secret_key, + ) + .unwrap(); + let non_membership_witnesses_3 = accumulator_3 + .update_non_mem_wit_using_secret_key_on_batch_updates( + &additions_3, + &remaining, + &non_members, + &non_membership_witnesses_2, + &keypair.secret_key, + ) + .unwrap(); + let wit_update_time = start.elapsed(); + + assert_eq!(membership_witnesses_2.len(), membership_witnesses_3.len()); + for i in 0..additions_1.len() { + assert!(accumulator_4.verify_membership( + &additions_1[i], + &membership_witnesses_3[i], + &keypair.public_key, + ¶ms + )); + } + + assert_eq!( + non_membership_witnesses_2.len(), + non_membership_witnesses_3.len() + ); + for i in 0..n { + assert!(accumulator_4.verify_non_membership( + &non_members[i], + &non_membership_witnesses_3[i], + &keypair.public_key, + ¶ms + )); + } + + let start = Instant::now(); + let (membership_witnesses_4, non_membership_witnesses_4) = accumulator_3 + .update_both_wit_using_secret_key_on_batch_updates( + &additions_3, + &remaining, + &additions_1, + &membership_witnesses_2, + &non_members, + &non_membership_witnesses_2, + &keypair.secret_key, + ) + .unwrap(); + let wit_update_time_1 = start.elapsed(); + + assert_eq!(membership_witnesses_3, membership_witnesses_4); + assert_eq!(non_membership_witnesses_3, non_membership_witnesses_4); + + println!( + "Time to update witnesses in separate calls {:?}", + wit_update_time + ); + println!( + "Time to generate witnesses in single call {:?}", + wit_update_time_1 + ); + + let start = Instant::now(); + let omega_mem = accumulator_3.generate_omega_for_membership_witnesses( + &additions_3, + &remaining, + &keypair.secret_key, + ); + let omega_non_mem = accumulator_3.generate_omega_for_non_membership_witnesses( + &additions_3, + &remaining, + &keypair.secret_key, + ); + let omega_time = start.elapsed(); + + for i in 0..additions_1.len() { + let new_wit = membership_witnesses_2[i] + .update_using_public_info_after_batch_updates( + &additions_3, + &remaining, + &omega_mem, + &additions_1[i], + ) + .unwrap(); + assert!(accumulator_4.verify_membership( + &additions_1[i], + &new_wit, + &keypair.public_key, + ¶ms + )); + } + + for i in 0..non_members.len() { + let new_wit = non_membership_witnesses_2[i] + .update_using_public_info_after_batch_updates( + &additions_3, + &remaining, + &omega_non_mem, + &non_members[i], + ) + .unwrap(); + assert!(accumulator_4.verify_non_membership( + &non_members[i], + &new_wit, + &keypair.public_key, + ¶ms + )); + } + + let start = Instant::now(); + let (omega_mem_1, omega_non_mem_1) = accumulator_3.generate_omega_for_both_witnesses( + &additions_3, + &remaining, + &keypair.secret_key, + ); + let omega_time_1 = start.elapsed(); + + assert_eq!(omega_mem, omega_mem_1); + assert_eq!(omega_non_mem, omega_non_mem_1); + + println!( + "Time to generate Omega for witnesses in separate calls {:?}", + omega_time + ); + println!( + "Time to generate Omega for witnesses in single calls {:?}", + omega_time_1 + ); + } +} diff --git a/vb_accumulator/src/lib.rs b/vb_accumulator/src/lib.rs index 569eeda1..893e82c2 100644 --- a/vb_accumulator/src/lib.rs +++ b/vb_accumulator/src/lib.rs @@ -1,11 +1,18 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(non_snake_case)] +//! # Accumulators based on bilinear map (pairings) +//! +//! ## vb_accumulator //! Dynamic Positive and Universal accumulators according to the paper: [Dynamic Universal Accumulator with Batch Update over Bilinear Groups](https://eprint.iacr.org/2020/777) -//! Provides +//! Implements //! - a dynamic positive accumulator [`PositiveAccumulator`], that supports membership proofs. //! - a dynamic universal accumulator [`UniversalAccumulator`], that supports membership and non-membership proofs. -//! - a zero knowledge proof of membership and non-membership in the accumulators with [`ProofProtocol`]. +//! - a zero knowledge proof of membership and non-membership in the accumulators with [`ProofProtocol`] as described in the paper. +//! These are essentially proofs of knowledge of a weak-BB signature +//! - an alternate and more efficient protocol of zero knowledge proof of membership and non-membership based on a more +//! efficient protocol for proving knowledge of a weak-BB signature. This isn't described in the paper. +//! - keyed verification proofs of membership and non-membership where the verifier knows the secret key //! //! Allows //! - single and batch updates (additions, removals or both) to the accumulators. @@ -18,6 +25,20 @@ //! and [`NonMembershipWitness`]. //! The implementation tries to use the same variable names as the paper and thus violate Rust's naming conventions at places. //! +//! ## kb_accumulator +//! Dynamic Positive and Universal accumulators according to the paper: [Efficient Constructions of Pairing Based Accumulators](https://eprint.iacr.org/2021/638) +//! Implements +//! - a dynamic positive accumulator [`KBPositiveAccumulator`], that supports membership proofs. Based on construction 2 in the paper. +//! - a dynamic universal accumulator [`KBUniversalAccumulator`], that supports membership and non-membership proofs. Based on construction 3 in the paper +//! - zero knowledge proofs of membership and non-membership in the accumulators. These are essentially proofs of knowledge of a +//! BB signature and weak-BB signature. +//! - an alternate and more efficient protocol for membership and non-membership proofs +//! - keyed verification proofs of membership and non-membership where the verifier knows the secret key +//! +//! Allows batch updates to the accumulator and the witness using the techniques from `vb_accumulator` +//! +//! The implementation uses type-3 pairings compared to type-1 in the paper. +//! //! [`Accumulator`]: crate::positive::Accumulator //! [`PositiveAccumulator`]: crate::positive::PositiveAccumulator //! [`UniversalAccumulator`]: crate::universal::UniversalAccumulator @@ -26,15 +47,22 @@ //! [`Witness`]: crate::witness::Witness //! [`Omega`]: crate::batch_utils::Omega //! [`ProofProtocol`]: crate::proofs::ProofProtocol +//! [`KBPositiveAccumulator`]: crate::kb_positive_accumulator::adaptive_accumulator::KBPositiveAccumulator +//! [`KBUniversalAccumulator`]: crate::kb_universal_accumulator::accumulator::KBUniversalAccumulator #[macro_use] pub mod utils; pub mod batch_utils; pub mod error; +pub mod kb_positive_accumulator; +pub mod kb_universal_accumulator; pub mod persistence; pub mod positive; pub mod proofs; +pub mod proofs_alt; +pub mod proofs_keyed_verification; pub mod setup; +pub mod setup_keyed_verification; pub mod universal; pub mod universal_init_constants; pub mod witness; diff --git a/vb_accumulator/src/positive.rs b/vb_accumulator/src/positive.rs index 41b9ea1c..5a36f02b 100644 --- a/vb_accumulator/src/positive.rs +++ b/vb_accumulator/src/positive.rs @@ -432,6 +432,10 @@ pub trait Accumulator { /// Create an `Accumulator` using the accumulated value. This is used for membership verification /// purposes only fn from_accumulated(accumulated: E::G1Affine) -> Self; + + fn randomized_value(&self, randomizer: &E::ScalarField) -> E::G1Affine { + (*self.value() * randomizer).into_affine() + } } impl Accumulator for PositiveAccumulator @@ -449,13 +453,19 @@ where } } +impl AsRef for PositiveAccumulator { + fn as_ref(&self) -> &E::G1Affine { + self.value() + } +} + impl PositiveAccumulator where E: Pairing, { /// Create a new positive accumulator - pub fn initialize(setup_params: &SetupParams) -> Self { - Self(setup_params.P) + pub fn initialize(params_gen: impl AsRef) -> Self { + Self(*params_gen.as_ref()) } /// Compute new accumulated value after addition @@ -655,6 +665,19 @@ pub mod tests { ¶ms )); total_mem_check_time += start.elapsed(); + + // Randomizing the witness and accumulator + let random = Fr::rand(&mut rng); + let randomized_accum = accumulator.randomized_value(&random); + let randomized_wit = m_wit.randomize(&random); + let verification_accumulator = PositiveAccumulator::from_accumulated(randomized_accum); + assert!(verification_accumulator.verify_membership( + &elem, + &randomized_wit, + &keypair.public_key, + ¶ms + )); + elems.push(elem); } @@ -685,11 +708,14 @@ pub mod tests { let (params, keypair, mut accumulator_1, mut state_1) = setup_positive_accum(&mut rng); // Create more accumulators to compare. Same elements will be added and removed from them as accumulator_1 - let mut accumulator_2 = PositiveAccumulator::initialize(¶ms); + let mut accumulator_2: PositiveAccumulator = + PositiveAccumulator::initialize(¶ms); let mut state_2 = InMemoryState::::new(); - let mut accumulator_3 = PositiveAccumulator::initialize(¶ms); + let mut accumulator_3: PositiveAccumulator = + PositiveAccumulator::initialize(¶ms); let mut state_3 = InMemoryState::::new(); - let mut accumulator_4 = PositiveAccumulator::initialize(¶ms); + let mut accumulator_4: PositiveAccumulator = + PositiveAccumulator::initialize(¶ms); let mut state_4 = InMemoryState::::new(); let additions: Vec = (0..10).map(|_| Fr::rand(&mut rng)).collect(); @@ -764,11 +790,8 @@ pub mod tests { assert_ne!(*accumulator_2.value(), *accumulator_3.value()); // Add and remove in call as a batch - let computed_new = accumulator_3.compute_new_post_batch_updates( - &new_additions, - &removals, - &keypair.secret_key, - ); + let computed_new: ::G1Affine = accumulator_3 + .compute_new_post_batch_updates(&new_additions, &removals, &keypair.secret_key); accumulator_3 = accumulator_3 .batch_updates( new_additions.clone(), diff --git a/vb_accumulator/src/proofs.rs b/vb_accumulator/src/proofs.rs index b040f2ae..42be9149 100644 --- a/vb_accumulator/src/proofs.rs +++ b/vb_accumulator/src/proofs.rs @@ -93,7 +93,9 @@ use crate::{ error::VBAccumulatorError, - setup::{PreparedPublicKey, PreparedSetupParams, PublicKey, SetupParams}, + setup::{ + NonMembershipProvingKey, PreparedPublicKey, PreparedSetupParams, PublicKey, SetupParams, + }, witness::{MembershipWitness, NonMembershipWitness}, }; use ark_ec::{ @@ -104,130 +106,14 @@ use ark_ec::{ use ark_ff::{Field, PrimeField}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{fmt::Debug, io::Write, rand::RngCore, vec::Vec, UniformRand}; -use digest::Digest; -use dock_crypto_utils::{ - affine_group_element_from_byte_slices, hashing_utils::projective_group_elem_from_try_and_incr, - serde_utils::*, -}; +use dock_crypto_utils::serde_utils::*; use schnorr_pok::{error::SchnorrError, SchnorrChallengeContributor}; use zeroize::{Zeroize, ZeroizeOnDrop}; -use dock_crypto_utils::{ - concat_slices, msm::WindowTable, randomized_pairing_check::RandomizedPairingChecker, -}; +use dock_crypto_utils::{msm::WindowTable, randomized_pairing_check::RandomizedPairingChecker}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; - -/// The public parameters (in addition to public key, accumulator setup params) used during the proof -/// of membership and non-membership are called `ProvingKey`. These are mutually agreed upon by the -/// prover and verifier and can be different between different provers and verifiers but using the -/// same accumulator parameters and keys. The parameters are named as `X`, `Y` `Z` and `K` in the paper -/// -/// Common elements of the membership and non-membership proving key, i.e., `X`, `Y` and `Z` -#[serde_as] -#[derive( - Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, -)] -pub struct ProvingKey { - #[serde_as(as = "ArkObjectBytes")] - pub X: G, - #[serde_as(as = "ArkObjectBytes")] - pub Y: G, - #[serde_as(as = "ArkObjectBytes")] - pub Z: G, -} - -/// Used between prover and verifier only to prove knowledge of member and corresponding witness. -/// `X`, `Y` and `Z` from the paper -#[serde_as] -#[derive( - Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, -)] -pub struct MembershipProvingKey( - #[serde(bound = "ProvingKey: Serialize, for<'a> ProvingKey: Deserialize<'a>")] - pub ProvingKey, -); - -/// Used between prover and verifier only to prove knowledge of non-member and corresponding witness -/// `X`, `Y`, `Z` and `K` from the paper -#[serde_as] -#[derive( - Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, -)] -pub struct NonMembershipProvingKey { - #[serde(bound = "ProvingKey: Serialize, for<'a> ProvingKey: Deserialize<'a>")] - pub XYZ: ProvingKey, - #[serde_as(as = "ArkObjectBytes")] - pub K: G, -} - -impl ProvingKey -where - G: AffineRepr, -{ - /// Generate using a random number generator - fn generate_proving_key_using_rng(rng: &mut R) -> ProvingKey { - ProvingKey { - X: G::Group::rand(rng).into(), - Y: G::Group::rand(rng).into(), - Z: G::Group::rand(rng).into(), - } - } - - /// Generate by hashing known strings - fn generate_proving_key_using_hash(label: &[u8]) -> ProvingKey { - // 3 G1 elements - ProvingKey { - X: affine_group_element_from_byte_slices![label, b" : X"], - Y: affine_group_element_from_byte_slices![label, b" : Y"], - Z: affine_group_element_from_byte_slices![label, b" : Z"], - } - } -} - -impl MembershipProvingKey -where - G: AffineRepr, -{ - /// Generate using a random number generator - pub fn generate_using_rng(rng: &mut R) -> Self { - Self(ProvingKey::generate_proving_key_using_rng(rng)) - } - - /// Generate by hashing known strings - pub fn new(label: &[u8]) -> Self { - Self(ProvingKey::generate_proving_key_using_hash::(label)) - } -} - -impl NonMembershipProvingKey -where - G: AffineRepr, -{ - /// Generate using a random number generator - pub fn generate_using_rng(rng: &mut R) -> Self { - let XYZ = ProvingKey::generate_proving_key_using_rng(rng); - Self { - XYZ, - K: G::Group::rand(rng).into(), - } - } - - /// Generate by hashing known strings - pub fn new(label: &[u8]) -> Self { - let XYZ = ProvingKey::generate_proving_key_using_hash::(label); - Self { - XYZ, - K: projective_group_elem_from_try_and_incr::(&concat_slices![label, b" : K"]) - .into(), - } - } - - /// Derive the membership proving key when doing a membership proof with a universal accumulator. - pub fn derive_membership_proving_key(&self) -> MembershipProvingKey { - MembershipProvingKey(self.XYZ.clone()) - } -} +use short_group_sig::common::ProvingKey; /// Common elements of the randomized witness between membership and non-membership witness #[serde_as] @@ -645,31 +531,6 @@ where } } -impl SchnorrChallengeContributor for ProvingKey { - fn challenge_contribution(&self, mut writer: W) -> Result<(), SchnorrError> { - self.X.serialize_compressed(&mut writer)?; - self.Y.serialize_compressed(&mut writer)?; - self.Z - .serialize_compressed(&mut writer) - .map_err(|e| e.into()) - } -} - -impl SchnorrChallengeContributor for MembershipProvingKey { - fn challenge_contribution(&self, writer: W) -> Result<(), SchnorrError> { - self.0.challenge_contribution(writer) - } -} - -impl SchnorrChallengeContributor for NonMembershipProvingKey { - fn challenge_contribution(&self, mut writer: W) -> Result<(), SchnorrError> { - self.XYZ.challenge_contribution(&mut writer)?; - self.K - .serialize_compressed(&mut writer) - .map_err(|e| e.into()) - } -} - impl SchnorrResponse { pub fn get_response_for_element(&self) -> &F { &self.s_y @@ -1123,7 +984,7 @@ where witness: &MembershipWitness, pk: &PublicKey, params: &SetupParams, - prk: &MembershipProvingKey, + prk: impl AsRef>, ) -> Self { let (rw, sc, bl) = Self::randomize_witness_and_compute_commitments( rng, @@ -1133,7 +994,7 @@ where None, pk, params, - &prk.0, + prk.as_ref(), ); Self { element: *element, @@ -1152,7 +1013,7 @@ where accumulator_value: &E::G1Affine, pk: &PublicKey, params: &SetupParams, - prk: &MembershipProvingKey, + prk: impl AsRef>, writer: W, ) -> Result<(), VBAccumulatorError> { Self::compute_challenge_contribution( @@ -1161,7 +1022,7 @@ where accumulator_value, pk, params, - &prk.0, + prk.as_ref(), writer, ) } @@ -1329,7 +1190,7 @@ where accumulator_value: &E::G1Affine, pk: &PublicKey, params: &SetupParams, - prk: &MembershipProvingKey, + prk: impl AsRef>, writer: W, ) -> Result<(), VBAccumulatorError> { MembershipProofProtocol::compute_challenge_contribution( @@ -1338,7 +1199,7 @@ where accumulator_value, pk, params, - &prk.0, + prk.as_ref(), writer, ) } @@ -1352,7 +1213,7 @@ where challenge: &E::ScalarField, pk: impl Into>, params: impl Into>, - prk: &MembershipProvingKey, + prk: impl AsRef>, ) -> Result<(), VBAccumulatorError> { as ProofProtocol>::verify_proof( &self.randomized_witness.0, @@ -1363,7 +1224,7 @@ where challenge, pk, params, - &prk.0, + prk.as_ref(), ) } @@ -1373,7 +1234,7 @@ where challenge: &E::ScalarField, pk: impl Into>, params: impl Into>, - prk: &MembershipProvingKey, + prk: impl AsRef>, pairing_checker: &mut RandomizedPairingChecker, ) -> Result<(), VBAccumulatorError> { as ProofProtocol>::verify_proof_with_randomized_pairing_checker( @@ -1385,7 +1246,7 @@ where challenge, pk, params, - &prk.0, + prk.as_ref(), pairing_checker ) } @@ -1570,6 +1431,7 @@ mod tests { universal::tests::setup_universal_accum, }; + use crate::setup::MembershipProvingKey; use ark_bls12_381::Bls12_381; use ark_std::{ rand::{rngs::StdRng, SeedableRng}, @@ -1617,7 +1479,7 @@ mod tests { let mut proof_verif_duration = Duration::default(); let mut proof_verif_with_prepared_duration = Duration::default(); let mut proof_verif_with_rand_pair_check_duration = Duration::default(); - let mut proof_verif__with_prepared_and_rand_pair_check_duration = Duration::default(); + let mut proof_verif_with_prepared_and_rand_pair_check_duration = Duration::default(); let mut pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); @@ -1719,7 +1581,32 @@ mod tests { &mut pairing_checker, ) .unwrap(); - proof_verif__with_prepared_and_rand_pair_check_duration += start.elapsed(); + proof_verif_with_prepared_and_rand_pair_check_duration += start.elapsed(); + + // Randomizing accumulator and witness + let random = Fr::rand(&mut rng); + let randomized_accum = (*accumulator.value() * random).into_affine(); + let randomized_wit = MembershipWitness((witnesses[i].0 * random).into_affine()); + let protocol = MembershipProofProtocol::init( + &mut rng, + &elems[i], + None, + &randomized_wit, + &keypair.public_key, + ¶ms, + &prk, + ); + let challenge = Fr::rand(&mut rng); + let proof = protocol.gen_proof(&challenge); + proof + .verify( + &randomized_accum, + &challenge, + keypair.public_key.clone(), + params.clone(), + &prk, + ) + .unwrap(); } let start = Instant::now(); @@ -1744,7 +1631,7 @@ mod tests { ); println!( "Time to verify {} membership proofs using prepared params and randomized pairing checker is {:?}", - count, proof_verif__with_prepared_and_rand_pair_check_duration + count, proof_verif_with_prepared_and_rand_pair_check_duration ); } @@ -1795,7 +1682,7 @@ mod tests { let mut proof_verif_duration = Duration::default(); let mut proof_verif_with_prepared_duration = Duration::default(); let mut proof_verif_with_rand_pair_check_duration = Duration::default(); - let mut proof_verif__with_prepared_and_rand_pair_check_duration = Duration::default(); + let mut proof_verif_with_prepared_and_rand_pair_check_duration = Duration::default(); let mut pairing_checker = RandomizedPairingChecker::new_using_rng(&mut rng, true); @@ -1896,7 +1783,35 @@ mod tests { &mut pairing_checker, ) .unwrap(); - proof_verif__with_prepared_and_rand_pair_check_duration += start.elapsed(); + proof_verif_with_prepared_and_rand_pair_check_duration += start.elapsed(); + + // Randomizing accumulator and witness + let random = Fr::rand(&mut rng); + let randomized_accum = (*accumulator.value() * random).into_affine(); + let randomized_wit = NonMembershipWitness { + d: witnesses[i].d * random, + C: (witnesses[i].C * random).into_affine(), + }; + let protocol = NonMembershipProofProtocol::init( + &mut rng, + &elems[i], + None, + &randomized_wit, + &keypair.public_key, + ¶ms, + &prk, + ); + let challenge = Fr::rand(&mut rng); + let proof = protocol.gen_proof(&challenge); + proof + .verify( + &randomized_accum, + &challenge, + keypair.public_key.clone(), + params.clone(), + &prk, + ) + .unwrap(); } let start = Instant::now(); @@ -1921,7 +1836,7 @@ mod tests { ); println!( "Time to verify {} non-membership proofs using prepared params and randomized pairing checker is {:?}", - count, proof_verif__with_prepared_and_rand_pair_check_duration + count, proof_verif_with_prepared_and_rand_pair_check_duration ); } } diff --git a/vb_accumulator/src/proofs_alt.rs b/vb_accumulator/src/proofs_alt.rs new file mode 100644 index 00000000..11b49e5e --- /dev/null +++ b/vb_accumulator/src/proofs_alt.rs @@ -0,0 +1,556 @@ +//! Alternate implementation of zero knowledge proof protocols for membership and non-membership witnesses. The protocol for proving +//! membership is the protocol for proof of knowledge of weak-BB signature proposed by Camenisch et. al where the prover does not do pairings. +//! The protocol for proving non-membership is an extension of this protocol as: +//! Non membership witness is `(C, d)`, accumulator value is `V`, non-member is `y`, secret key is `alpha` and public generators `P` +//! and `P_tilde` satisfying the relation `C*(y + alpha) + P*d = V`.Both prover and verifier have access to a public generator `Q` such that +//! discrete log of `Q` wrt `P` is not known. +//! 1. Prover picks a random `r` from Z_p. +//! 2. Prover randomizes the witness as `(C' = C * r, d' = d * r)` +//! 3. Prover creates `C_bar = V * r - C * y * r - P * d * r = V * r - C' * y - P * d'` and `J = Q * d * r = Q * d'` and +//! sends both to the verifier +//! 4. Prover creates proof for knowledge of `r`, `y`, `d'` in the relations `C_bar = V * r - C' * y - P * d'` and `J = Q * d'`. +//! 5. Verifier checks proofs from point 4 and that `C'` and `J` are not 0 (ensuring D is not 0). +//! 6. Verifier checks `e(C_bar, P_tilde) = e(C', pk)` + +use crate::{ + error::VBAccumulatorError, + prelude::{MembershipWitness, NonMembershipWitness, PreparedPublicKey, PreparedSetupParams}, +}; +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 dock_crypto_utils::{ + randomized_pairing_check::RandomizedPairingChecker, serde_utils::ArkObjectBytes, +}; +use schnorr_pok::{ + error::SchnorrError, impl_proof_of_knowledge_of_discrete_log, SchnorrCommitment, + SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use short_group_sig::weak_bb_sig_pok_alt::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +impl_proof_of_knowledge_of_discrete_log!(DKnowledgeProtocol, DKnowledgeProof); + +/// A wrapper over the protocol for proof of knowledge of weak-BB signature. The accumulator witness is the weak-BB signature and the +/// accumulator value becomes g1 in that protocol +#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] +pub struct MembershipProofProtocol(pub PoKOfSignatureG1Protocol); + +/// A wrapper over the proof of knowledge of weak-BB signature +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct MembershipProof(pub PoKOfSignatureG1Proof); + +/// An extension over the protocol for proof of knowledge of weak-BB signature. +#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] +pub struct NonMembershipProofProtocol { + /// The randomized witness `C'` + #[zeroize(skip)] + pub C_prime: E::G1Affine, + /// `V * r - C' * y - P * d'` + #[zeroize(skip)] + pub C_bar: E::G1Affine, + /// The commitment to the randomized witness `Q * d'` + #[zeroize(skip)] + pub J: E::G1Affine, + /// For relation `C_bar = V * r - C' * y - P * d'` + pub sc_comm_1: SchnorrCommitment, + /// (r, y, d') + sc_wits_1: (E::ScalarField, E::ScalarField, E::ScalarField), + /// For relation `J = Q * d'` + pub sc_comm_2: DKnowledgeProtocol, +} + +#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct NonMembershipProof { + /// The randomized witness `C'` + pub C_prime: E::G1Affine, + /// `V * r - C' * y - P * d'` + pub C_bar: E::G1Affine, + /// The commitment to the randomized witness `Q * d'` + pub J: E::G1Affine, + /// For relation `C_bar = V * r - C' * y - P * d'` + pub t_1: E::G1Affine, + pub sc_resp_1: SchnorrResponse, + /// For relation `J = Q * d'` + pub sc_2: DKnowledgeProof, +} + +impl MembershipProofProtocol { + pub fn init( + rng: &mut R, + element: E::ScalarField, + element_blinding: Option, + accumulator_value: E::G1Affine, + witness: &MembershipWitness, + ) -> Result { + let p = PoKOfSignatureG1Protocol::init( + rng, + witness, + element, + element_blinding, + accumulator_value, + )?; + Ok(Self(p)) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer)?; + Ok(()) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(MembershipProof(self.0.clone().gen_proof(challenge)?)) + } +} + +impl MembershipProof { + pub fn verify( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + ) -> Result<(), VBAccumulatorError> { + let params = params.into(); + self.0 + .verify(challenge, pk.into().0, accumulator_value, params.P_tilde)?; + Ok(()) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), VBAccumulatorError> { + let params = params.into(); + self.0.verify_with_randomized_pairing_checker( + challenge, + pk.into().0, + accumulator_value, + params.P_tilde, + pairing_checker, + )?; + Ok(()) + } + + pub fn challenge_contribution( + &self, + accumulator_value: E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + self.0.challenge_contribution(accumulator_value, writer)?; + Ok(()) + } + + pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { + self.0.get_resp_for_message().unwrap() + } +} + +impl NonMembershipProofProtocol { + pub fn init( + rng: &mut R, + element: E::ScalarField, + element_blinding: Option, + accumulator_value: E::G1Affine, + witness: &NonMembershipWitness, + g1: impl Into, + Q: impl Into, + ) -> Self { + let r = E::ScalarField::rand(rng); + let element_blinding = element_blinding.unwrap_or_else(|| E::ScalarField::rand(rng)); + let g1 = g1.into(); + let Q = Q.into(); + let d_prime = witness.d * r; + let C_prime = witness.C * r; + let C_prime_neg = C_prime.neg(); + let g1_neg = g1.into_group().neg(); + // C_bar = accumulator_value * r - C' * element - g1 * d * r + let C_bar = + (accumulator_value * r + C_prime_neg * element + g1_neg * d_prime).into_affine(); + let d_prime_blinding = E::ScalarField::rand(rng); + // J = Q * d * r + let J = (Q * d_prime).into_affine(); + let sc_comm_1 = SchnorrCommitment::new( + &[accumulator_value, C_prime_neg.into(), g1_neg.into()], + vec![ + E::ScalarField::rand(rng), + element_blinding, + d_prime_blinding, + ], + ); + let sc_wits_1 = (r, element, d_prime); + let sc_comm_2 = DKnowledgeProtocol::init(d_prime, d_prime_blinding, &Q); + + Self { + C_prime: C_prime.into(), + C_bar, + J, + sc_comm_1, + sc_comm_2, + sc_wits_1, + } + } + + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + g1: &E::G1Affine, + Q: &E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + Self::compute_challenge_contribution( + &self.C_prime, + &self.C_bar, + &self.J, + accumulator_value, + g1, + Q, + &self.sc_comm_1.t, + &self.sc_comm_2.t, + writer, + ) + } + + pub fn gen_proof( + self, + challenge: &E::ScalarField, + ) -> Result, VBAccumulatorError> { + Ok(NonMembershipProof { + C_prime: self.C_prime, + C_bar: self.C_bar, + J: self.J, + t_1: self.sc_comm_1.t, + sc_resp_1: self.sc_comm_1.response( + &[self.sc_wits_1.0, self.sc_wits_1.1, self.sc_wits_1.2], + challenge, + )?, + sc_2: self.sc_comm_2.clone().gen_proof(challenge), + }) + } + + pub fn compute_challenge_contribution( + C_prime: &E::G1Affine, + C_bar: &E::G1Affine, + J: &E::G1Affine, + accumulator_value: &E::G1Affine, + g1: &E::G1Affine, + Q: &E::G1Affine, + t_1: &E::G1Affine, + t_2: &E::G1Affine, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + C_bar.serialize_compressed(&mut writer)?; + C_prime.serialize_compressed(&mut writer)?; + J.serialize_compressed(&mut writer)?; + accumulator_value.serialize_compressed(&mut writer)?; + g1.serialize_compressed(&mut writer)?; + Q.serialize_compressed(&mut writer)?; + t_1.serialize_compressed(&mut writer)?; + t_2.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl NonMembershipProof { + pub fn verify( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + Q: impl Into, + ) -> Result<(), VBAccumulatorError> { + let params = params.into(); + self.verify_except_pairing(accumulator_value, challenge, ¶ms, Q)?; + if !E::multi_pairing( + [ + E::G1Prepared::from(self.C_bar), + E::G1Prepared::from(-(self.C_prime.into_group())), + ], + [params.P_tilde, pk.into().0], + ) + .is_zero() + { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + Ok(()) + } + + pub fn verify_with_randomized_pairing_checker( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + pk: impl Into>, + params: impl Into>, + Q: impl Into, + pairing_checker: &mut RandomizedPairingChecker, + ) -> Result<(), VBAccumulatorError> { + let params = params.into(); + self.verify_except_pairing(accumulator_value, challenge, ¶ms, Q)?; + pairing_checker.add_sources(&self.C_prime, pk.into().0, &self.C_bar, params.P_tilde); + Ok(()) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &E::G1Affine, + g1: &E::G1Affine, + Q: &E::G1Affine, + writer: W, + ) -> Result<(), VBAccumulatorError> { + NonMembershipProofProtocol::::compute_challenge_contribution( + &self.C_prime, + &self.C_bar, + &self.J, + accumulator_value, + g1, + Q, + &self.t_1, + &self.sc_2.t, + writer, + ) + } + + pub fn get_schnorr_response_for_element(&self) -> &E::ScalarField { + self.sc_resp_1.get_response(1).unwrap() + } + + fn verify_except_pairing( + &self, + accumulator_value: E::G1Affine, + challenge: &E::ScalarField, + params: &PreparedSetupParams, + Q: impl Into, + ) -> Result<(), VBAccumulatorError> { + if self.C_prime.is_zero() { + return Err(VBAccumulatorError::CannotBeZero); + } + if self.J.is_zero() { + return Err(VBAccumulatorError::CannotBeZero); + } + self.sc_resp_1.is_valid( + &[ + accumulator_value, + self.C_prime.into_group().neg().into(), + params.P.into_group().neg().into(), + ], + &self.C_bar, + &self.t_1, + challenge, + )?; + if !self.sc_2.verify(&self.J, &Q.into(), challenge) { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + // d'(=d*r) is same in both relations + if *self.sc_resp_1.get_response(2)? != self.sc_2.response { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::positive::{tests::setup_positive_accum, Accumulator}; + use std::time::{Duration, Instant}; + + use crate::universal::tests::setup_universal_accum; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn membership_proof_positive_accumulator() { + // Proof of knowledge of membership witness + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator, mut state) = setup_positive_accum(&mut rng); + let prepared_params = PreparedSetupParams::from(params.clone()); + let prepared_pk = PreparedPublicKey::from(keypair.public_key.clone()); + + let mut elems = vec![]; + let mut witnesses = vec![]; + let count = 10; + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + accumulator = accumulator + .add(elem, &keypair.secret_key, &mut state) + .unwrap(); + elems.push(elem); + } + + for i in 0..count { + let w = accumulator + .get_membership_witness(&elems[i], &keypair.secret_key, &state) + .unwrap(); + assert!(accumulator.verify_membership(&elems[i], &w, &keypair.public_key, ¶ms)); + witnesses.push(w); + } + + let mut proof_create_duration = Duration::default(); + let mut proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = MembershipProofProtocol::init( + &mut rng, + elems[i], + None, + *accumulator.value(), + &witnesses[i], + ) + .unwrap(); + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(*accumulator.value(), &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let start = Instant::now(); + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(*accumulator.value(), &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + proof + .verify( + *accumulator.value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + ) + .unwrap(); + proof_verif_duration += start.elapsed(); + } + + println!( + "Time to create {} membership proofs is {:?}", + count, proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, proof_verif_duration + ); + } + + #[test] + fn non_membership_proof_universal_accumulator() { + // Proof of knowledge of non-membership witness + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, keypair, mut accumulator, initial_elems, mut state) = + setup_universal_accum(&mut rng, max); + + let prepared_params = PreparedSetupParams::from(params.clone()); + let prepared_pk = PreparedPublicKey::from(keypair.public_key.clone()); + + let Q = G1Affine::rand(&mut rng); + + let mut elems = vec![]; + let mut witnesses = vec![]; + let count = 10; + + for _ in 0..50 { + accumulator = accumulator + .add( + Fr::rand(&mut rng), + &keypair.secret_key, + &initial_elems, + &mut state, + ) + .unwrap(); + } + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + let w = accumulator + .get_non_membership_witness(&elem, &keypair.secret_key, &mut state, ¶ms) + .unwrap(); + assert!(accumulator.verify_non_membership(&elem, &w, &keypair.public_key, ¶ms)); + elems.push(elem); + witnesses.push(w); + } + + let mut proof_create_duration = Duration::default(); + let mut proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = NonMembershipProofProtocol::::init( + &mut rng, + elems[i], + None, + *accumulator.value(), + &witnesses[i], + params.P.clone(), + Q.clone(), + ); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(accumulator.value(), ¶ms.P, &Q, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let start = Instant::now(); + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution( + accumulator.value(), + ¶ms.P, + &Q, + &mut chal_bytes_verifier, + ) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + proof + .verify( + *accumulator.value(), + &challenge_verifier, + prepared_pk.clone(), + prepared_params.clone(), + Q.clone(), + ) + .unwrap(); + proof_verif_duration += start.elapsed(); + } + + println!( + "Time to create {} non-membership proofs is {:?}", + count, proof_create_duration + ); + println!( + "Time to verify {} non-membership proofs is {:?}", + count, proof_verif_duration + ); + } +} diff --git a/vb_accumulator/src/proofs_keyed_verification.rs b/vb_accumulator/src/proofs_keyed_verification.rs new file mode 100644 index 00000000..581dddcb --- /dev/null +++ b/vb_accumulator/src/proofs_keyed_verification.rs @@ -0,0 +1,865 @@ +//! Proofs of membership and non-membership with keyed-verification, i.e. the verifier needs to know the secret key to verify the proofs. +//! These are essentially keyed-verification proofs of knowledge of weak-BB signature. The protocols are as follows +//! Accumulator = `V`, secret key = `alpha`, `P` and `Q` are generators of group G1 +//! Membership protocol +//! witness = `C`, member = `y`, `C * (y + alpha) = V` +//! 1. User chooses random element `l` from Z_p. +//! 2. User creates `C' = C * l` and `C_bar = V * l - C' * y`. Note that `C_bar = C' * alpha` +//! 3. User creates proof of knowledge `pi`, of `l` and `y` in `C_bar` and sends `pi, C', C_bar` to the verifier. +//! 4. Verifier checks if `C_bar = C' * alpha` and then verifies proof `pi` +//! Non-membership protocol +//! witness = `(C, d)`, member = `y`, `C * (y + alpha) + P * d = V` +//! 1. User chooses random element `l` from Z_p. +//! 2. User creates `C' = C * l, d' = d * l, C_hat = Q * d'` and `C_bar = V * l - C' * y - P * d'`. Note that `C_bar = C' * alpha` +//! 3. User creates proof of knowledge `pi_1`, of `l`, `y` and `d'` in `C_bar`, and proof of knowledge `pi_2`, of `d'` in `C_hat` and sends `pi_1, pi_2, C', C_hat, C_bar` to the verifier. +//! 4. Verifier checks if `C_bar = C' * alpha` and then verifies proof `pi_1` and `pi_2` and checks that `d'` is same in both + +use crate::{ + error::VBAccumulatorError, + setup::SecretKey, + setup_keyed_verification::{PublicKey, SetupParams}, + witness::{MembershipWitness, NonMembershipWitness}, +}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{fmt::Debug, io::Write, ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand}; +use digest::Digest; +use dock_crypto_utils::serde_utils::ArkObjectBytes; +use schnorr_pok::{ + compute_random_oracle_challenge, error::SchnorrError, impl_proof_of_knowledge_of_discrete_log, + SchnorrCommitment, SchnorrResponse, +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +impl_proof_of_knowledge_of_discrete_log!(DKnowledgeProtocol, DKnowledgeProof); + +#[derive( + Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop, CanonicalSerialize, CanonicalDeserialize, +)] +pub struct MembershipProofProtocol { + #[zeroize(skip)] + pub C_prime: G, + #[zeroize(skip)] + pub C_bar: G, + pub sc_comm: SchnorrCommitment, + sc_wits: (G::ScalarField, G::ScalarField), +} + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct MembershipProof { + #[serde_as(as = "ArkObjectBytes")] + pub C_prime: G, + #[serde_as(as = "ArkObjectBytes")] + pub C_bar: G, + #[serde_as(as = "ArkObjectBytes")] + pub t: G, + pub sc_resp: SchnorrResponse, +} + +/// The part of membership proof whose verification requires knowledge of secret key. +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct DelegatedMembershipProof { + #[serde_as(as = "ArkObjectBytes")] + pub C_prime: G, + #[serde_as(as = "ArkObjectBytes")] + pub C_bar: G, +} + +#[derive( + Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop, CanonicalSerialize, CanonicalDeserialize, +)] +pub struct NonMembershipProofProtocol { + #[zeroize(skip)] + pub C_prime: G, + #[zeroize(skip)] + pub C_hat: G, + #[zeroize(skip)] + pub C_bar: G, + pub sc_comm: SchnorrCommitment, + sc_wits: (G::ScalarField, G::ScalarField, G::ScalarField), + pub sc_comm_2: DKnowledgeProtocol, +} + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct NonMembershipProof { + #[serde_as(as = "ArkObjectBytes")] + pub C_prime: G, + #[serde_as(as = "ArkObjectBytes")] + pub C_hat: G, + #[serde_as(as = "ArkObjectBytes")] + pub C_bar: G, + #[serde_as(as = "ArkObjectBytes")] + pub t: G, + pub sc_resp: SchnorrResponse, + pub sc_resp_2: DKnowledgeProof, +} + +/// The part of non-membership proof whose verification requires knowledge of secret key. +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct DelegatedNonMembershipProof { + #[serde_as(as = "ArkObjectBytes")] + pub C_prime: G, + #[serde_as(as = "ArkObjectBytes")] + pub C_bar: G, +} + +impl_proof_of_knowledge_of_discrete_log!(SecretKeyKnowledgeProtocol, SecretKeyKnowledgeProof); +impl_proof_of_knowledge_of_discrete_log!(MemWitCorrectnessProtocol, MemWitCorrectnessProof); +impl_proof_of_knowledge_of_discrete_log!(NonMemWitCorrectnessProtocol, NonMemWitCorrectnessProof); + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct MembershipWitnessCorrectnessProof { + pub wit_proof: MemWitCorrectnessProof, + pub sk_proof: SecretKeyKnowledgeProof, +} + +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct NonMembershipWitnessCorrectnessProof { + pub wit_proof: NonMemWitCorrectnessProof, + pub sk_proof: SecretKeyKnowledgeProof, +} + +impl MembershipWitnessCorrectnessProof { + pub fn new( + rng: &mut R, + accumulator: &G, + witness: &MembershipWitness, + member: &G::ScalarField, + secret_key: SecretKey, + public_key: &PublicKey, + params: &SetupParams, + ) -> Self { + let mut challenge_bytes = vec![]; + let sk_blinding = G::ScalarField::rand(rng); + let wit_protocol = + MemWitCorrectnessProtocol::init(secret_key.0, sk_blinding.clone(), &witness.0); + let y = Self::compute_y(accumulator, witness, member); + wit_protocol + .challenge_contribution(&witness.0, &y, &mut challenge_bytes) + .unwrap(); + let sk_protocol = SecretKeyKnowledgeProtocol::init(secret_key.0, sk_blinding, ¶ms.0); + sk_protocol + .challenge_contribution(¶ms.0, &public_key.0, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + let wit_proof = wit_protocol.gen_proof(&challenge); + let sk_proof = sk_protocol.gen_proof(&challenge); + Self { + wit_proof, + sk_proof, + } + } + + pub fn verify( + &self, + accumulator: &G, + witness: &MembershipWitness, + member: &G::ScalarField, + public_key: &PublicKey, + params: &SetupParams, + ) -> Result<(), VBAccumulatorError> { + if self.wit_proof.response != self.sk_proof.response { + return Err(VBAccumulatorError::InvalidMembershipCorrectnessProof); + } + let mut challenge_bytes = vec![]; + let y = Self::compute_y(accumulator, witness, member); + self.wit_proof + .challenge_contribution(&witness.0, &y, &mut challenge_bytes) + .unwrap(); + self.sk_proof + .challenge_contribution(¶ms.0, &public_key.0, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + if !self.wit_proof.verify(&y, &witness.0, &challenge) { + return Err(VBAccumulatorError::InvalidMembershipCorrectnessProof); + } + if !self.sk_proof.verify(&public_key.0, ¶ms.0, &challenge) { + return Err(VBAccumulatorError::InvalidMembershipCorrectnessProof); + } + Ok(()) + } + + fn compute_y(accumulator: &G, witness: &MembershipWitness, member: &G::ScalarField) -> G { + (accumulator.into_group() - (witness.0 * member)).into_affine() + } +} + +impl NonMembershipWitnessCorrectnessProof { + pub fn new( + rng: &mut R, + accumulator: &G, + witness: &NonMembershipWitness, + non_member: &G::ScalarField, + secret_key: SecretKey, + public_key: &PublicKey, + params: &SetupParams, + ) -> Self { + let mut challenge_bytes = vec![]; + let sk_blinding = G::ScalarField::rand(rng); + let wit_protocol = + NonMemWitCorrectnessProtocol::init(secret_key.0, sk_blinding.clone(), &witness.C); + let y = Self::compute_y(accumulator, witness, non_member, params); + wit_protocol + .challenge_contribution(&witness.C, &y, &mut challenge_bytes) + .unwrap(); + let sk_protocol = SecretKeyKnowledgeProtocol::init(secret_key.0, sk_blinding, ¶ms.0); + sk_protocol + .challenge_contribution(¶ms.0, &public_key.0, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + let wit_proof = wit_protocol.gen_proof(&challenge); + let sk_proof = sk_protocol.gen_proof(&challenge); + Self { + wit_proof, + sk_proof, + } + } + + pub fn verify( + &self, + accumulator: &G, + witness: &NonMembershipWitness, + non_member: &G::ScalarField, + public_key: &PublicKey, + params: &SetupParams, + ) -> Result<(), VBAccumulatorError> { + if self.wit_proof.response != self.sk_proof.response { + return Err(VBAccumulatorError::InvalidMembershipCorrectnessProof); + } + let mut challenge_bytes = vec![]; + let y = Self::compute_y(accumulator, witness, non_member, params); + self.wit_proof + .challenge_contribution(&witness.C, &y, &mut challenge_bytes) + .unwrap(); + self.sk_proof + .challenge_contribution(¶ms.0, &public_key.0, &mut challenge_bytes) + .unwrap(); + let challenge = compute_random_oracle_challenge::(&challenge_bytes); + if !self.wit_proof.verify(&y, &witness.C, &challenge) { + return Err(VBAccumulatorError::InvalidMembershipCorrectnessProof); + } + if !self.sk_proof.verify(&public_key.0, ¶ms.0, &challenge) { + return Err(VBAccumulatorError::InvalidMembershipCorrectnessProof); + } + Ok(()) + } + + fn compute_y( + accumulator: &G, + witness: &NonMembershipWitness, + non_member: &G::ScalarField, + params: &SetupParams, + ) -> G { + (accumulator.into_group() - (witness.C * non_member) - (params.0 * witness.d)).into_affine() + } +} + +impl MembershipProofProtocol { + /// Initialize a membership proof protocol. + pub fn init( + rng: &mut R, + element: G::ScalarField, + element_blinding: Option, + witness: &MembershipWitness, + accumulator: G, + ) -> Self { + let l = G::ScalarField::rand(rng); + let C_prime = witness.0 * l; + 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); + Self { + C_prime: C_prime.into(), + C_bar, + sc_comm, + sc_wits, + } + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + Self::compute_challenge_contribution( + accumulator_value, + &self.C_prime, + &self.C_bar, + &self.sc_comm.t, + &mut writer, + ) + } + + pub fn gen_proof( + self, + challenge: &G::ScalarField, + ) -> Result, VBAccumulatorError> { + let sc_resp = self + .sc_comm + .response(&[self.sc_wits.0, self.sc_wits.1], challenge)?; + Ok(MembershipProof { + C_prime: self.C_prime, + C_bar: self.C_bar, + t: self.sc_comm.t, + sc_resp, + }) + } + + fn compute_challenge_contribution( + accumulator_value: &G, + C_prime: &G, + C_bar: &G, + t: &G, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + accumulator_value.serialize_compressed(&mut writer)?; + C_prime.serialize_compressed(&mut writer)?; + C_bar.serialize_compressed(&mut writer)?; + t.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl MembershipProof { + pub fn verify( + &self, + accumulator: G, + secret_key: &SecretKey, + challenge: &G::ScalarField, + ) -> Result<(), VBAccumulatorError> { + if self.C_bar != (self.C_prime * secret_key.0).into() { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + self.verify_schnorr_proof(accumulator, challenge) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + MembershipProofProtocol::compute_challenge_contribution( + accumulator_value, + &self.C_prime, + &self.C_bar, + &self.t, + &mut writer, + ) + } + + pub fn verify_schnorr_proof( + &self, + 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)?; + Ok(()) + } + + pub fn to_delegated_proof(&self) -> DelegatedMembershipProof { + DelegatedMembershipProof { + C_prime: self.C_prime, + C_bar: self.C_bar, + } + } + + pub fn get_schnorr_response_for_element(&self) -> &G::ScalarField { + self.sc_resp.get_response(1).unwrap() + } +} + +impl DelegatedMembershipProof { + pub fn verify(&self, secret_key: &SecretKey) -> Result<(), VBAccumulatorError> { + if self.C_bar != (self.C_prime * secret_key.0).into() { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + Ok(()) + } +} + +impl NonMembershipProofProtocol { + /// Initialize a non-membership proof protocol. + pub fn init( + rng: &mut R, + element: G::ScalarField, + element_blinding: Option, + witness: &NonMembershipWitness, + accumulator: G, + params: &SetupParams, + Q: &G, + ) -> Self { + let l = G::ScalarField::rand(rng); + let C_prime = witness.C * l; + let d_prime = witness.d * l; + let C_hat = *Q * d_prime; + let C_prime_neg = C_prime.neg(); + let params_neg = params.0.into_group().neg(); + let C_bar = (accumulator * l + C_prime_neg * element + params_neg * d_prime).into_affine(); + let element_blinding = element_blinding.unwrap_or_else(|| G::ScalarField::rand(rng)); + let d_prime_blinding = G::ScalarField::rand(rng); + let bases = [accumulator, C_prime_neg.into(), params_neg.into()]; + let randomness = vec![ + G::ScalarField::rand(rng), + element_blinding, + d_prime_blinding, + ]; + let sc_wits = (l, element, d_prime.clone()); + let sc_comm = SchnorrCommitment::new(&bases, randomness); + let sc_comm_2 = DKnowledgeProtocol::init(d_prime, d_prime_blinding, Q); + Self { + C_prime: C_prime.into(), + C_hat: C_hat.into(), + C_bar, + sc_comm, + sc_wits, + sc_comm_2, + } + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + params: &SetupParams, + Q: &G, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + Self::compute_challenge_contribution( + accumulator_value, + &self.C_prime, + &self.C_hat, + &self.C_bar, + ¶ms.0, + &self.sc_comm.t, + Q, + &self.sc_comm_2.t, + &mut writer, + ) + } + + pub fn gen_proof( + self, + challenge: &G::ScalarField, + ) -> Result, VBAccumulatorError> { + let sc_resp = self + .sc_comm + .response(&[self.sc_wits.0, self.sc_wits.1, self.sc_wits.2], challenge)?; + let sc_resp_2 = self.sc_comm_2.clone().gen_proof(challenge); + Ok(NonMembershipProof { + C_prime: self.C_prime, + C_hat: self.C_hat, + C_bar: self.C_bar, + t: self.sc_comm.t, + sc_resp, + sc_resp_2, + }) + } + + fn compute_challenge_contribution( + accumulator_value: &G, + C_prime: &G, + C_hat: &G, + C_bar: &G, + g: &G, + t: &G, + Q: &G, + t_2: &G, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + accumulator_value.serialize_compressed(&mut writer)?; + C_prime.serialize_compressed(&mut writer)?; + C_hat.serialize_compressed(&mut writer)?; + C_bar.serialize_compressed(&mut writer)?; + g.serialize_compressed(&mut writer)?; + t.serialize_compressed(&mut writer)?; + Q.serialize_compressed(&mut writer)?; + t_2.serialize_compressed(&mut writer)?; + Ok(()) + } +} + +impl NonMembershipProof { + pub fn verify( + &self, + accumulator: G, + secret_key: &SecretKey, + challenge: &G::ScalarField, + params: &SetupParams, + Q: &G, + ) -> Result<(), VBAccumulatorError> { + if self.C_bar != (self.C_prime * secret_key.0).into() { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + if self.C_hat.is_zero() { + return Err(VBAccumulatorError::CannotBeZero); + } + self.verify_schnorr_proof(accumulator, challenge, params, Q) + } + + pub fn challenge_contribution( + &self, + accumulator_value: &G, + params: &SetupParams, + Q: &G, + mut writer: W, + ) -> Result<(), VBAccumulatorError> { + NonMembershipProofProtocol::compute_challenge_contribution( + accumulator_value, + &self.C_prime, + &self.C_hat, + &self.C_bar, + ¶ms.0, + &self.t, + Q, + &self.sc_resp_2.t, + &mut writer, + ) + } + + pub fn verify_schnorr_proof( + &self, + accumulator: G, + challenge: &G::ScalarField, + params: &SetupParams, + Q: &G, + ) -> Result<(), VBAccumulatorError> { + let bases = [ + accumulator, + self.C_prime.into_group().neg().into(), + params.0.into_group().neg().into(), + ]; + self.sc_resp + .is_valid(&bases, &self.C_bar, &self.t, challenge)?; + if !self.sc_resp_2.verify(&self.C_hat, Q, challenge) { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + if *self.sc_resp.get_response(2)? != self.sc_resp_2.response { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + Ok(()) + } + + pub fn to_delegated_proof(&self) -> DelegatedNonMembershipProof { + DelegatedNonMembershipProof { + C_prime: self.C_prime, + C_bar: self.C_bar, + } + } + + pub fn get_schnorr_response_for_element(&self) -> &G::ScalarField { + self.sc_resp.get_response(1).unwrap() + } +} + +impl DelegatedNonMembershipProof { + pub fn verify(&self, secret_key: &SecretKey) -> Result<(), VBAccumulatorError> { + if self.C_bar != (self.C_prime * secret_key.0).into() { + return Err(VBAccumulatorError::IncorrectRandomizedWitness); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::positive::Accumulator; + + use crate::{ + persistence::test::{InMemoryInitialElements, InMemoryState}, + positive::PositiveAccumulator, + universal::UniversalAccumulator, + }; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use std::time::{Duration, Instant}; + + pub fn setup_positive_accum() -> ( + SetupParams, + SecretKey, + PublicKey, + PositiveAccumulator, + InMemoryState, + ) { + let params = SetupParams::::new::(b"test"); + let seed = [0, 1, 2, 10, 11]; + let sk = SecretKey::generate_using_seed::(&seed); + let pk = PublicKey::new_from_secret_key(&sk, ¶ms); + + let accumulator = PositiveAccumulator::initialize(¶ms); + let state = InMemoryState::new(); + (params, sk, pk, accumulator, state) + } + + pub fn setup_universal_accum( + rng: &mut StdRng, + max: u64, + ) -> ( + SetupParams, + SecretKey, + PublicKey, + UniversalAccumulator, + InMemoryInitialElements, + InMemoryState, + ) { + let params = SetupParams::::new::(b"test"); + let seed = [0, 1, 2, 10, 11]; + let sk = SecretKey::generate_using_seed::(&seed); + let pk = PublicKey::new_from_secret_key(&sk, ¶ms); + + let mut initial_elements = InMemoryInitialElements::new(); + let accumulator = UniversalAccumulator::initialize_with_all_random( + rng, + ¶ms, + max, + &sk, + &mut initial_elements, + ); + let state = InMemoryState::new(); + (params, sk, pk, accumulator, initial_elements, state) + } + + #[test] + fn membership_proof_positive_accumulator() { + // Proof of knowledge of membership witness + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, secret_key, public_key, mut accumulator, mut state) = setup_positive_accum(); + + let mut elems = vec![]; + let mut witnesses = vec![]; + let count = 10; + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + accumulator = accumulator.add(elem, &secret_key, &mut state).unwrap(); + elems.push(elem); + } + + for i in 0..count { + let w = accumulator + .get_membership_witness(&elems[i], &secret_key, &state) + .unwrap(); + let correctness_proof = MembershipWitnessCorrectnessProof::new::( + &mut rng, + accumulator.value(), + &w, + &elems[i], + secret_key.clone(), + &public_key, + ¶ms, + ); + correctness_proof + .verify::(accumulator.value(), &w, &elems[i], &public_key, ¶ms) + .unwrap(); + witnesses.push(w); + } + + let mut proof_create_duration = Duration::default(); + let mut proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = MembershipProofProtocol::init( + &mut rng, + elems[i].clone(), + None, + &witnesses[i], + accumulator.value().clone(), + ); + proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(accumulator.value(), &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + // TODO Uncomment + // Proof can be serialized + // test_serialization!(MembershipProof, proof); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(accumulator.value(), &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + + assert_eq!(challenge_prover, challenge_verifier); + + let start = Instant::now(); + proof + .verify( + accumulator.value().clone(), + &secret_key, + &challenge_verifier, + ) + .unwrap(); + proof_verif_duration += start.elapsed(); + + proof + .verify_schnorr_proof(accumulator.value().clone(), &challenge_verifier) + .unwrap(); + let delegated_proof = proof.to_delegated_proof(); + delegated_proof.verify(&secret_key).unwrap(); + } + + println!( + "Time to create {} membership proofs is {:?}", + count, proof_create_duration + ); + println!( + "Time to verify {} membership proofs is {:?}", + count, proof_verif_duration + ); + } + + #[test] + fn non_membership_proof_universal_accumulator() { + // Proof of knowledge of non-membership witness + let max = 100; + let mut rng = StdRng::seed_from_u64(0u64); + + let (params, secret_key, public_key, mut accumulator, initial_elems, mut state) = + setup_universal_accum(&mut rng, max); + + let Q = G1Affine::rand(&mut rng); + + let mut elems = vec![]; + let mut witnesses = vec![]; + let count = 10; + + for _ in 0..50 { + accumulator = accumulator + .add(Fr::rand(&mut rng), &secret_key, &initial_elems, &mut state) + .unwrap(); + } + + for _ in 0..count { + let elem = Fr::rand(&mut rng); + let w = accumulator + .get_non_membership_witness(&elem, &secret_key, &mut state, ¶ms) + .unwrap(); + let correctness_proof = NonMembershipWitnessCorrectnessProof::new::( + &mut rng, + accumulator.value(), + &w, + &elem, + secret_key.clone(), + &public_key, + ¶ms, + ); + correctness_proof + .verify::(accumulator.value(), &w, &elem, &public_key, ¶ms) + .unwrap(); + elems.push(elem); + witnesses.push(w); + } + + let mut proof_create_duration = Duration::default(); + let mut proof_verif_duration = Duration::default(); + + for i in 0..count { + let start = Instant::now(); + let protocol = NonMembershipProofProtocol::init( + &mut rng, + elems[i].clone(), + None, + &witnesses[i], + accumulator.value().clone(), + ¶ms, + &Q, + ); + proof_create_duration += start.elapsed(); + + let mut chal_bytes_prover = vec![]; + protocol + .challenge_contribution(accumulator.value(), ¶ms, &Q, &mut chal_bytes_prover) + .unwrap(); + let challenge_prover = + compute_random_oracle_challenge::(&chal_bytes_prover); + + let start = Instant::now(); + let proof = protocol.gen_proof(&challenge_prover).unwrap(); + proof_create_duration += start.elapsed(); + + let mut chal_bytes_verifier = vec![]; + proof + .challenge_contribution(accumulator.value(), ¶ms, &Q, &mut chal_bytes_verifier) + .unwrap(); + let challenge_verifier = + compute_random_oracle_challenge::(&chal_bytes_verifier); + assert_eq!(challenge_prover, challenge_verifier); + + // TODO: uncomment + // test_serialization!(NonMembershipProof, proof); + + let start = Instant::now(); + proof + .verify( + accumulator.value().clone(), + &secret_key, + &challenge_verifier, + ¶ms, + &Q, + ) + .unwrap(); + proof_verif_duration += start.elapsed(); + + proof + .verify_schnorr_proof( + accumulator.value().clone(), + &challenge_verifier, + ¶ms, + &Q, + ) + .unwrap(); + let delegated_proof = proof.to_delegated_proof(); + delegated_proof.verify(&secret_key).unwrap(); + } + + println!( + "Time to create {} non-membership proofs is {:?}", + count, proof_create_duration + ); + println!( + "Time to verify {} non-membership proofs is {:?}", + count, proof_verif_duration + ); + } +} diff --git a/vb_accumulator/src/setup.rs b/vb_accumulator/src/setup.rs index 28b5814c..21882a1f 100644 --- a/vb_accumulator/src/setup.rs +++ b/vb_accumulator/src/setup.rs @@ -38,12 +38,18 @@ use ark_std::{fmt::Debug, io::Write, rand::RngCore, vec::Vec, UniformRand}; use zeroize::{Zeroize, ZeroizeOnDrop}; use digest::{Digest, DynDigest}; -use schnorr_pok::{error::SchnorrError, impl_proof_of_knowledge_of_discrete_log}; +use schnorr_pok::{ + error::SchnorrError, impl_proof_of_knowledge_of_discrete_log, SchnorrChallengeContributor, +}; -use dock_crypto_utils::{affine_group_element_from_byte_slices, join, serde_utils::*}; +use dock_crypto_utils::{ + affine_group_element_from_byte_slices, concat_slices, join, serde_utils::*, +}; +use dock_crypto_utils::hashing_utils::projective_group_elem_from_try_and_incr; use serde::{Deserialize, Serialize}; use serde_with::serde_as; +use short_group_sig::common::ProvingKey; /// Secret key for accumulator manager #[serde_as] @@ -140,6 +146,12 @@ where } } +impl AsRef for SetupParams { + fn as_ref(&self) -> &E::G1Affine { + &self.P + } +} + impl Keypair where E: Pairing, @@ -236,6 +248,95 @@ impl From> for PreparedPublicKey { // Implement proof of knowledge of secret key in public key impl_proof_of_knowledge_of_discrete_log!(PoKSecretKeyInPublicKey, PoKSecretKeyInPublicKeyProof); +/// Used between prover and verifier only to prove knowledge of member and corresponding witness. +/// `X`, `Y` and `Z` from the paper +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct MembershipProvingKey( + #[serde(bound = "ProvingKey: Serialize, for<'a> ProvingKey: Deserialize<'a>")] + pub ProvingKey, +); + +/// Used between prover and verifier only to prove knowledge of non-member and corresponding witness +/// `X`, `Y`, `Z` and `K` from the paper +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct NonMembershipProvingKey { + #[serde(bound = "ProvingKey: Serialize, for<'a> ProvingKey: Deserialize<'a>")] + pub XYZ: ProvingKey, + #[serde_as(as = "ArkObjectBytes")] + pub K: G, +} + +impl MembershipProvingKey +where + G: AffineRepr, +{ + /// Generate using a random number generator + pub fn generate_using_rng(rng: &mut R) -> Self { + Self(ProvingKey::generate_using_rng(rng)) + } + + /// Generate by hashing known strings + pub fn new(label: &[u8]) -> Self { + Self(ProvingKey::generate_using_hash::(label)) + } +} + +impl NonMembershipProvingKey +where + G: AffineRepr, +{ + /// Generate using a random number generator + pub fn generate_using_rng(rng: &mut R) -> Self { + let XYZ = ProvingKey::generate_using_rng(rng); + Self { + XYZ, + K: G::Group::rand(rng).into(), + } + } + + /// Generate by hashing known strings + pub fn new(label: &[u8]) -> Self { + let XYZ = ProvingKey::generate_using_hash::(label); + Self { + XYZ, + K: projective_group_elem_from_try_and_incr::(&concat_slices![label, b" : K"]) + .into(), + } + } + + /// Derive the membership proving key when doing a membership proof with a universal accumulator. + pub fn derive_membership_proving_key(&self) -> MembershipProvingKey { + MembershipProvingKey(self.XYZ.clone()) + } +} + +impl AsRef> for MembershipProvingKey { + fn as_ref(&self) -> &ProvingKey { + &self.0 + } +} + +impl SchnorrChallengeContributor for MembershipProvingKey { + fn challenge_contribution(&self, writer: W) -> Result<(), SchnorrError> { + self.0.challenge_contribution(writer) + } +} + +impl SchnorrChallengeContributor for NonMembershipProvingKey { + fn challenge_contribution(&self, mut writer: W) -> Result<(), SchnorrError> { + self.XYZ.challenge_contribution(&mut writer)?; + self.K + .serialize_compressed(&mut writer) + .map_err(|e| e.into()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/vb_accumulator/src/setup_keyed_verification.rs b/vb_accumulator/src/setup_keyed_verification.rs new file mode 100644 index 00000000..ffc5130d --- /dev/null +++ b/vb_accumulator/src/setup_keyed_verification.rs @@ -0,0 +1,123 @@ +use crate::setup::SecretKey; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{io::Write, vec::Vec}; +use digest::Digest; +use dock_crypto_utils::{affine_group_element_from_byte_slices, serde_utils::ArkObjectBytes}; +use schnorr_pok::{error::SchnorrError, impl_proof_of_knowledge_of_discrete_log}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Public key for accumulator manager +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct PublicKey(#[serde_as(as = "ArkObjectBytes")] pub G); + +/// Setup parameters for accumulators +#[serde_as] +#[derive( + Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, +)] +pub struct SetupParams(#[serde_as(as = "ArkObjectBytes")] pub G); + +impl SetupParams { + /// Generate params by hashing a known string. The hash function is vulnerable to timing + /// attack but since all this is public knowledge, it is fine. + /// This is useful if people need to be convinced that the discrete log of group elements wrt each other is not known. + pub fn new(label: &[u8]) -> Self { + Self(affine_group_element_from_byte_slices!(label, b" : P")) + } + + /// Params shouldn't be 0 + pub fn is_valid(&self) -> bool { + !self.0.is_zero() + } +} + +impl AsRef for SetupParams { + fn as_ref(&self) -> &G { + &self.0 + } +} + +impl PublicKey { + /// Generate public key from given secret key and signature parameters + pub fn new_from_secret_key( + secret_key: &SecretKey, + setup_params: &SetupParams, + ) -> Self { + Self( + setup_params + .0 + .mul_bigint(secret_key.0.into_bigint()) + .into_affine(), + ) + } + + /// Public key shouldn't be 0 + pub fn is_valid(&self) -> bool { + !self.0.is_zero() + } +} + +// Implement proof of knowledge of secret key in public key +impl_proof_of_knowledge_of_discrete_log!(PoKSecretKeyInPublicKey, PoKSecretKeyInPublicKeyProof); + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_serialization; + use ark_bls12_381::{Fr, G1Affine}; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use schnorr_pok::compute_random_oracle_challenge; + + #[test] + fn proof_of_knowledge_of_public_key() { + let mut rng = StdRng::seed_from_u64(0u64); + let params = SetupParams::::new::(b"test"); + assert!(params.is_valid()); + + let seed = [0, 1, 2, 10, 11]; + let sk = SecretKey::generate_using_seed::(&seed); + let pk = PublicKey::new_from_secret_key(&sk, ¶ms); + assert!(pk.is_valid()); + + let base = ¶ms.0; + let witness = sk.0; + let blinding = Fr::rand(&mut rng); + + let protocol = PoKSecretKeyInPublicKey::::init(witness, blinding, base); + + let mut chal_contrib_prover = vec![]; + protocol + .challenge_contribution(base, &pk.0, &mut chal_contrib_prover) + .unwrap(); + + test_serialization!(PoKSecretKeyInPublicKey::, 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, &pk.0, &mut chal_contrib_verifier) + .unwrap(); + + let challenge_verifier = + compute_random_oracle_challenge::(&chal_contrib_verifier); + assert!(proof.verify(&pk.0, base, &challenge_verifier)); + assert_eq!(chal_contrib_prover, chal_contrib_verifier); + assert_eq!(challenge_prover, challenge_verifier); + + test_serialization!(PoKSecretKeyInPublicKeyProof, proof); + } +} diff --git a/vb_accumulator/src/universal.rs b/vb_accumulator/src/universal.rs index cff0757a..e6ea39ba 100644 --- a/vb_accumulator/src/universal.rs +++ b/vb_accumulator/src/universal.rs @@ -107,6 +107,12 @@ pub struct UniversalAccumulator { pub max_size: u64, } +impl AsRef for UniversalAccumulator { + fn as_ref(&self) -> &E::G1Affine { + self.value() + } +} + impl Accumulator for UniversalAccumulator where E: Pairing, @@ -138,33 +144,18 @@ where /// the `max_size + 1` are generated randomly. pub fn initialize( rng: &mut R, - setup_params: &SetupParams, + params_gen: impl AsRef, max_size: u64, sk: &SecretKey, xs: Vec, initial_elements_store: &mut dyn InitialElementsStore, ) -> Self { - let mut f_V = E::ScalarField::one(); - for x in xs { - f_V *= x + sk.0; - initial_elements_store.add(x); - } - - // We need more secret elements than known elements (in case all witness holders collude). As there can - // be at most `max_size` witnesses, there must be at least `max_size + 1` initial elements secret. - // It's assumed that elements in `xs` are public constants and thus `max_size + 1` more random elements are generated. - // Thus there are `max_size + xs.len() + 1` initial elements in total. However, if `xs` could be assumed - // secret, then only `max_size - xs.len() + 1` random elements need to be generated. - // Accepting an argument indicating whether `xs` is public could be another way to solve it - // but as `xs.len <<< max_size` in practice, didn't feel right to make the function accept - // one more argument and make caller decide one more thing. - for _ in 0..(max_size + 1) { - let elem = E::ScalarField::rand(rng); - f_V *= elem + sk.0; - initial_elements_store.add(elem); - } + let f_V = Self::compute_initial(rng, max_size, sk, xs, initial_elements_store); - let V = setup_params.P.mul_bigint(f_V.into_bigint()).into_affine(); + let V = params_gen + .as_ref() + .mul_bigint(f_V.into_bigint()) + .into_affine(); Self { V, f_V, max_size } } @@ -175,21 +166,16 @@ where /// is present for legacy purposes. This will likely be removed. pub fn initialize_with_all_random( rng: &mut R, - setup_params: &SetupParams, + params_gen: impl AsRef, max_size: u64, sk: &SecretKey, initial_elements_store: &mut dyn InitialElementsStore, ) -> Self { - let mut f_V = E::ScalarField::one(); - for _ in 0..max_size + 1 { - // Each of the random values should be preserved by the manager and should not be removed (check before removing) - // from the accumulator - let elem = E::ScalarField::rand(rng); - f_V *= elem + sk.0; - initial_elements_store.add(elem); - } - - let V = setup_params.P.mul_bigint(f_V.into_bigint()).into_affine(); + let f_V = Self::compute_random_initial(rng, max_size, sk, initial_elements_store); + let V = params_gen + .as_ref() + .mul_bigint(f_V.into_bigint()) + .into_affine(); Self { V, f_V, max_size } } @@ -199,10 +185,13 @@ where /// where `y_i` are the initial elements and `alpha` is the secret key pub fn initialize_given_f_V( f_V: E::ScalarField, - setup_params: &SetupParams, + params_gen: impl AsRef, max_size: u64, ) -> Self { - let V = setup_params.P.mul_bigint(f_V.into_bigint()).into_affine(); + let V = params_gen + .as_ref() + .mul_bigint(f_V.into_bigint()) + .into_affine(); Self { V, f_V, max_size } } @@ -442,13 +431,13 @@ where d: E::ScalarField, non_member: &E::ScalarField, sk: &SecretKey, - params: &SetupParams, + params_gen: impl AsRef, ) -> Result, VBAccumulatorError> { if d.is_zero() { return Err(VBAccumulatorError::CannotBeZero); } let mut y_plus_alpha_inv = (*non_member + sk.0).inverse().unwrap(); - let mut C = params.P.into_group(); + let mut C = params_gen.as_ref().into_group(); C *= (self.f_V - d) * y_plus_alpha_inv; y_plus_alpha_inv.zeroize(); Ok(NonMembershipWitness { @@ -467,7 +456,7 @@ where E::ScalarField, ElementIterator = impl Iterator, >, - params: &SetupParams, + params_gen: impl AsRef, ) -> Result, VBAccumulatorError> { if state.has(non_member) { return Err(VBAccumulatorError::ElementPresent); @@ -482,7 +471,7 @@ where d *= *member - non_member; } - self.compute_non_membership_witness_given_d(d, non_member, sk, params) + self.compute_non_membership_witness_given_d(d, non_member, sk, params_gen) } /// Compute a vector `d` for a batch where each `non_member_i` in batch has `d` as `d_i` and @@ -516,7 +505,7 @@ where d: Vec, non_members: &[E::ScalarField], sk: &SecretKey, - params: &SetupParams, + params_gen: impl AsRef, ) -> Result>, VBAccumulatorError> { if cfg_iter!(d).any(|&x| x.is_zero()) { return Err(VBAccumulatorError::CannotBeZero); @@ -534,8 +523,10 @@ where .collect::>(); // The same group element (self.V) has to be multiplied by each element in P_multiple, so we create a window table - let mut wits = - multiply_field_elems_with_same_group_elem(params.P.into_group(), P_multiple.as_slice()); + let mut wits = multiply_field_elems_with_same_group_elem( + params_gen.as_ref().into_group(), + P_multiple.as_slice(), + ); let wits_affine = E::G1::normalize_batch(&wits); cfg_iter_mut!(y_plus_alpha_inv).for_each(|y| y.zeroize()); wits.iter_mut().for_each(|w| w.zeroize()); @@ -559,7 +550,7 @@ where E::ScalarField, ElementIterator = impl Iterator, >, - params: &SetupParams, + params_gen: impl AsRef, ) -> Result>, VBAccumulatorError> { for element in non_members { if state.has(element) { @@ -591,7 +582,7 @@ where d_for_witnesses, non_members, sk, - params, + params_gen, ) } @@ -646,6 +637,52 @@ where pub fn from_value(f_V: E::ScalarField, V: E::G1Affine, max_size: u64) -> Self { Self { f_V, V, max_size } } + + fn compute_initial( + rng: &mut R, + max_size: u64, + sk: &SecretKey, + xs: Vec, + initial_elements_store: &mut dyn InitialElementsStore, + ) -> E::ScalarField { + let mut f_V = E::ScalarField::one(); + for x in xs { + f_V *= x + sk.0; + initial_elements_store.add(x); + } + + // We need more secret elements than known elements (in case all witness holders collude). As there can + // be at most `max_size` witnesses, there must be at least `max_size + 1` initial elements secret. + // It's assumed that elements in `xs` are public constants and thus `max_size + 1` more random elements are generated. + // Thus there are `max_size + xs.len() + 1` initial elements in total. However, if `xs` could be assumed + // secret, then only `max_size - xs.len() + 1` random elements need to be generated. + // Accepting an argument indicating whether `xs` is public could be another way to solve it + // but as `xs.len <<< max_size` in practice, didn't feel right to make the function accept + // one more argument and make caller decide one more thing. + for _ in 0..(max_size + 1) { + let elem = E::ScalarField::rand(rng); + f_V *= elem + sk.0; + initial_elements_store.add(elem); + } + f_V + } + + fn compute_random_initial( + rng: &mut R, + max_size: u64, + sk: &SecretKey, + initial_elements_store: &mut dyn InitialElementsStore, + ) -> E::ScalarField { + let mut f_V = E::ScalarField::one(); + for _ in 0..max_size + 1 { + // Each of the random values should be preserved by the manager and should not be removed (check before removing) + // from the accumulator + let elem = E::ScalarField::rand(rng); + f_V *= elem + sk.0; + initial_elements_store.add(elem); + } + f_V + } } #[cfg(test)] @@ -694,7 +731,7 @@ pub mod tests { let (params, keypair, accumulator, initial_elements, _) = setup_universal_accum(&mut rng, max); - let accumulator_1 = + let accumulator_1: UniversalAccumulator = UniversalAccumulator::initialize_given_f_V(accumulator.f_V, ¶ms, max); assert_eq!(accumulator, accumulator_1); @@ -721,7 +758,7 @@ pub mod tests { let initial: Vec = initial_elements_for_bls12_381!(Fr); assert_eq!(initial.len(), 12); let mut initial_elements_1 = InMemoryInitialElements::new(); - let accumulator_1 = UniversalAccumulator::initialize( + let accumulator_1: UniversalAccumulator = UniversalAccumulator::initialize( &mut rng, ¶ms, max, @@ -741,7 +778,7 @@ pub mod tests { #[test] fn membership_non_membership() { - // Test to check (non)membership in accumulator + // Test to check membership and non-membership in accumulator let max = 100; let mut rng = StdRng::seed_from_u64(0u64); @@ -790,6 +827,18 @@ pub mod tests { nm_wit ); + // Randomizing the witness and accumulator + let random = Fr::rand(&mut rng); + let randomized_accum = accumulator.randomized_value(&random); + let randomized_wit = nm_wit.randomize(&random); + let verification_accum = UniversalAccumulator::from_accumulated(randomized_accum); + assert!(verification_accum.verify_non_membership( + &elem, + &randomized_wit, + &keypair.public_key, + ¶ms + )); + assert!(accumulator .remove(&elem, &keypair.secret_key, &initial_elements, &mut state) .is_err()); diff --git a/vb_accumulator/src/witness.rs b/vb_accumulator/src/witness.rs index 1ba00189..ef8f4146 100644 --- a/vb_accumulator/src/witness.rs +++ b/vb_accumulator/src/witness.rs @@ -171,15 +171,10 @@ pub trait Witness { } // `d_A` = Evaluation of polynomial `d_A(y)` for each y in `elements` // `v_A` = Evaluation of polynomial `v_A(y)` for each y in `elements` - let (d_A, v_A): (Vec<_>, Vec<_>) = cfg_iter!(elements) - .map(|element| { - ( - Poly_d::eval_direct(additions, element), - Poly_v_A::eval_direct(additions, &sk.0, element), - ) - }) - .unzip(); - + let d_A: Vec<_> = cfg_iter!(elements) + .map(|element| Poly_d::eval_direct(additions, element)) + .collect(); + let v_A = Poly_v_A::eval_direct_on_batch(additions, &sk.0, elements); // The same group element (self.V) has to multiplied by each inverse so creating a window table let table = WindowTable::new(elements.len(), old_accumulator.into_group()); @@ -210,15 +205,11 @@ pub trait Witness { } // `d_D` = Evaluation of polynomial `d_D(y)` for each y in `elements` // `v_D` = Evaluation of polynomial `v_D(y)` for each y in `elements` - let (mut d_D, v_D): (Vec<_>, Vec<_>) = cfg_iter!(elements) - .map(|element| { - ( - Poly_d::eval_direct(removals, element), - Poly_v_D::eval_direct(removals, &sk.0, element), - ) - }) - .unzip(); + let mut d_D: Vec<_> = cfg_iter!(elements) + .map(|element| Poly_d::eval_direct(removals, element)) + .collect(); + let v_D = Poly_v_D::eval_direct_on_batch(removals, &sk.0, elements); // The same group element (self.V) has to multiplied by each inverse so creating a window table let table = WindowTable::new(elements.len(), old_accumulator.into_group()); @@ -263,10 +254,7 @@ pub trait Witness { ) }) .unzip(); - let v_AD = cfg_iter!(elements) - .map(|element| Poly_v_AD::eval_direct(additions, removals, &sk.0, element)) - .collect::>(); - + let v_AD = Poly_v_AD::eval_direct_on_batch(additions, removals, &sk.0, elements); // The same group element (self.V) has to multiplied by each inverse so creating a window table let table = WindowTable::new(elements.len(), old_accumulator.into_group()); @@ -329,7 +317,7 @@ pub trait Witness { element: &G::ScalarField, old_witness: &G, ) -> Result<(G::ScalarField, G), VBAccumulatorError> { - if updates_and_omegas.len() < 2 { + if updates_and_omegas.len() == 1 { return Self::compute_update_using_public_info_after_batch_updates( updates_and_omegas[0].0, updates_and_omegas[0].1, @@ -348,6 +336,16 @@ pub trait Witness { omegas.push(omega); } + Self::compute_update_for_multiple_batches(additions, removals, omegas, element, old_witness) + } + + fn compute_update_for_multiple_batches( + additions: Vec<&[G::ScalarField]>, + removals: Vec<&[G::ScalarField]>, + omegas: Vec<&Omega>, + element: &G::ScalarField, + old_witness: &G, + ) -> Result<(G::ScalarField, G), VBAccumulatorError> { // d_{A_{i->j}} - product of all evaluations of polynomial d_A let mut d_A_ij = G::ScalarField::one(); // d_{D_{i->j}} - product of all evaluations of polynomial d_D @@ -385,8 +383,12 @@ pub trait Witness { max_omega_size = omegas[t].len(); } - d_A_ij *= Poly_d::eval_direct(additions[t], element); - d_D_ij *= Poly_d::eval_direct(removals[t], element); + if additions.len() > t { + d_A_ij *= Poly_d::eval_direct(additions[t], element); + } + if removals.len() > t { + d_D_ij *= Poly_d::eval_direct(removals[t], element); + } } let d_D_ij_inv = d_D_ij.inverse().ok_or(VBAccumulatorError::CannotBeZero)?; @@ -473,6 +475,24 @@ pub struct NonMembershipWitness { pub C: G, } +impl AsRef for MembershipWitness { + fn as_ref(&self) -> &G { + &self.0 + } +} + +impl AsRef for NonMembershipWitness { + fn as_ref(&self) -> &G { + &self.C + } +} + +impl From for MembershipWitness { + fn from(value: G) -> Self { + Self(value) + } +} + impl Witness for MembershipWitness where G: AffineRepr {} impl MembershipWitness @@ -612,6 +632,10 @@ where pub fn affine_points_to_membership_witnesses(wits: Vec) -> Vec> { cfg_into_iter!(wits).map(MembershipWitness).collect() } + + pub fn randomize(&self, randomizer: &G::ScalarField) -> Self { + Self((self.0 * randomizer).into_affine()) + } } impl Witness for NonMembershipWitness where G: AffineRepr {} @@ -767,6 +791,13 @@ where }) } + pub fn randomize(&self, randomizer: &G::ScalarField) -> Self { + Self { + d: self.d * randomizer, + C: (self.C * randomizer).into_affine(), + } + } + fn prepare_non_membership_witnesses( d_factor: Vec, new_wits: Vec, @@ -785,9 +816,10 @@ where #[cfg(test)] mod tests { + use super::*; use std::time::{Duration, Instant}; - use ark_bls12_381::Bls12_381; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; use ark_ec::pairing::Pairing; use ark_std::{ rand::{rngs::StdRng, SeedableRng}, @@ -802,11 +834,6 @@ mod tests { universal::{tests::setup_universal_accum, UniversalAccumulator}, }; - use super::*; - - type Fr = ::ScalarField; - type G1 = ::G1Affine; - #[test] fn single_membership_witness_update_positive_accumulator() { // Test to update membership witness after single addition or removal @@ -1100,6 +1127,7 @@ mod tests { )); } + let start = Instant::now(); let new_wits = MembershipWitness::update_using_secret_key_after_batch_additions( &additions_2, &additions_1, @@ -1108,6 +1136,13 @@ mod tests { &keypair.secret_key, ) .unwrap(); + println!( + "Updating {} membership witnesses after {} additions takes {:?}", + additions_1.len(), + additions_2.len(), + start.elapsed() + ); + assert_eq!(new_wits.len(), witnesses_1.len()); for i in 0..new_wits.len() { assert!(verification_accumulator.verify_membership( @@ -1145,6 +1180,7 @@ mod tests { )); } + let start = Instant::now(); let new_wits = MembershipWitness::update_using_secret_key_after_batch_removals( &removals, &additions_2, @@ -1153,6 +1189,13 @@ mod tests { &keypair.secret_key, ) .unwrap(); + println!( + "Updating {} membership witnesses after {} removals takes {:?}", + additions_2.len(), + removals.len(), + start.elapsed() + ); + assert_eq!(new_wits.len(), witnesses_3.len()); for i in 0..new_wits.len() { assert!(verification_accumulator.verify_membership( @@ -1191,11 +1234,14 @@ mod tests { additions: Vec, removals: &[Fr], elements: &[Fr], - old_witnesses: &[MembershipWitness], + old_witnesses: &[MembershipWitness], keypair: &Keypair, params: &SetupParams, state: &mut dyn State, - ) -> (PositiveAccumulator, Vec>) { + ) -> ( + PositiveAccumulator, + Vec>, + ) { let accumulator_new = current_accm .batch_updates(additions.clone(), removals, &keypair.secret_key, state) .unwrap(); @@ -1210,6 +1256,7 @@ mod tests { )); } + let start = Instant::now(); let new_witnesses = MembershipWitness::update_using_secret_key_after_batch_updates( &additions, removals, @@ -1219,6 +1266,14 @@ mod tests { &keypair.secret_key, ) .unwrap(); + println!( + "Updating {} membership witnesses after {} additions and {} removals takes {:?}", + elements.len(), + additions.len(), + removals.len(), + start.elapsed() + ); + assert_eq!(new_witnesses.len(), old_witnesses.len()); for i in 0..new_witnesses.len() { assert!(verification_accumulator.verify_membership(