diff --git a/bbs_plus/README.md b/bbs_plus/README.md index e5ef24d3..6e92c7fc 100644 --- a/bbs_plus/README.md +++ b/bbs_plus/README.md @@ -22,15 +22,19 @@ Threshold BBS and BBS+ signatures based on the paper [Threshold BBS+ Signatures The threshold signing protocol has 3 phases (not communication rounds) 1. This is the randomness generation phase 2. This is the phase where multiplications happen - 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. + 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. This phase + is non-interactive from signers' point of view as they don't just interact among themselves Note that only 3rd phase requires the messages to be known so the first 2 phases can be treated as pre-computation -and can be done proactively. Secondly since the communication time among signers is most likely to be the bottleneck +and can be done proactively and thus only phase 1 and 2 are online phases of the MPC protocol and phase 3 is the offline +phase. +Secondly since the communication time among signers is most likely to be the bottleneck in threshold signing, phase 1 and 2 support batching meaning that to generate `n` signatures only a single execution of phase 1 and 2 needs to done, although with larger inputs. Then `n` executions of phase 3 are done to generate -the signature. -Also its assumed that parties have done the DKG as well as the base OT and stored their results. -Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly different. +the signature. +Also, its assumed that parties have done the DKG as well as the base OT and stored their results before starting phase 1. +Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly +less expensive as BBS+ needs 2 random fields elements but BBS needs only 1. ### Modules @@ -45,12 +49,13 @@ different from BBS+ but public key is same. The implementation tries to use the same variable names as the paper and thus violate Rust's naming conventions at places. + [`setup`]: https://docs.rs/bbs_plus/latest/bbs_plus/setup/ [`signature`]: https://docs.rs/bbs_plus/latest/bbs_plus/signature/ [`proof`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof/ [`signature_23`]: https://docs.rs/bbs_plus/latest/bbs_plus/signature_23/ [`proof_23`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof_23/ -[`proof_23_cdl`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof_23_alternate/ +[`proof_23_cdl`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof_23_cdl/ [`threshold`]: https://docs.rs/bbs_plus/latest/bbs_plus/threshold/ diff --git a/bbs_plus/src/lib.rs b/bbs_plus/src/lib.rs index 82fd635d..da6e4189 100644 --- a/bbs_plus/src/lib.rs +++ b/bbs_plus/src/lib.rs @@ -21,15 +21,19 @@ //! The threshold signing protocol has 3 phases (not communication rounds) //! 1. This is the randomness generation phase //! 2. This is the phase where multiplications happen -//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. +//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. This phase +//! is non-interactive from signers' point of view as they don't just interact among themselves //! //! Note that only 3rd phase requires the messages to be known so the first 2 phases can be treated as pre-computation -//! and can be done proactively. Secondly since the communication time among signers is most likely to be the bottleneck +//! and can be done proactively and thus only phase 1 and 2 are online phases of the MPC protocol and phase 3 is the offline +//! phase. +//! Secondly since the communication time among signers is most likely to be the bottleneck //! in threshold signing, phase 1 and 2 support batching meaning that to generate `n` signatures only a single execution //! of phase 1 and 2 needs to done, although with larger inputs. Then `n` executions of phase 3 are done to generate //! the signature. -//! Also, its assumed that parties have done the DKG as well as the base OT and stored their results. -//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly different. +//! Also, its assumed that parties have done the DKG as well as the base OT and stored their results before starting phase 1. +//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly +//! less expensive as BBS+ needs 2 random fields elements but BBS needs only 1. //! //! ## Modules //! diff --git a/bbs_plus/src/threshold/base_ot_phase.rs b/bbs_plus/src/threshold/base_ot_phase.rs index d9baa9f6..c604ec8d 100644 --- a/bbs_plus/src/threshold/base_ot_phase.rs +++ b/bbs_plus/src/threshold/base_ot_phase.rs @@ -247,6 +247,7 @@ pub mod tests { UniformRand, }; use blake2::Blake2b512; + use std::time::Instant; pub fn check_base_ot_keys( choices: &[Bit], @@ -343,11 +344,12 @@ pub mod tests { #[test] fn base_ot_for_threshold_sig() { let mut rng = StdRng::seed_from_u64(0u64); - let num_base_ot = 256; - let num_parties = 5; - let all_party_set = (1..=num_parties).into_iter().collect::>(); + let num_base_ot = 256; + for num_parties in vec![5, 10, 15, 20] { + let all_party_set = (1..=num_parties).into_iter().collect::>(); - do_base_ot_for_threshold_sig::<16>(&mut rng, num_base_ot, num_parties, all_party_set); + do_base_ot_for_threshold_sig::<16>(&mut rng, num_base_ot, num_parties, all_party_set); + } } } diff --git a/bbs_plus/src/threshold/mod.rs b/bbs_plus/src/threshold/mod.rs index cf24dae3..464232cb 100644 --- a/bbs_plus/src/threshold/mod.rs +++ b/bbs_plus/src/threshold/mod.rs @@ -2,15 +2,19 @@ //! The threshold signing protocol has 3 phases (not communication rounds) //! 1. This is the randomness generation phase //! 2. This is the phase where multiplications happen -//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. +//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. This phase +//! is non-interactive from signers' point of view as they don't just interact among themselves //! //! Note that only 3rd phase requires the messages to be known so the first 2 phases can be treated as pre-computation -//! and can be done proactively. Secondly since the communication time among signers is most likely to be the bottleneck +//! and can be done proactively and thus only phase 1 and 2 are online phases of the MPC protocol and phase 3 is the offline +//! phase. +//! Secondly since the communication time among signers is most likely to be the bottleneck //! in threshold signing, phase 1 and 2 support batching meaning that to generate `n` signatures only a single execution //! of phase 1 and 2 needs to done, although with larger inputs. Then `n` executions of phase 3 are done to generate //! the signature. -//! Also its assumed that parties have done the DKG as well as the base OT and stored their results. -//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly different. +//! Also, its assumed that parties have done the DKG as well as the base OT and stored their results before starting phase 1. +//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly +//! less expensive as BBS+ needs 2 random fields elements but BBS needs only 1. pub mod base_ot_phase; pub mod cointoss; diff --git a/bbs_plus/src/threshold/multiplication_phase.rs b/bbs_plus/src/threshold/multiplication_phase.rs index 2316da55..12cd0c99 100644 --- a/bbs_plus/src/threshold/multiplication_phase.rs +++ b/bbs_plus/src/threshold/multiplication_phase.rs @@ -207,3 +207,151 @@ impl } } } + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bls12_381::Fr; + use std::time::Instant; + + use crate::threshold::base_ot_phase::tests::do_base_ot_for_threshold_sig; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use oblivious_transfer_protocols::ot_based_multiplication::{ + dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector, + }; + + #[test] + fn multiplication_phase() { + let mut rng = StdRng::seed_from_u64(0u64); + const BASE_OT_KEY_SIZE: u16 = 128; + const KAPPA: u16 = 256; + const STATISTICAL_SECURITY_PARAMETER: u16 = 80; + let ote_params = MultiplicationOTEParams:: {}; + let gadget_vector = GadgetVector::::new::< + Blake2b512, + >(ote_params, b"test-gadget-vector"); + + fn check( + rng: &mut StdRng, + ote_params: MultiplicationOTEParams, + threshold: u16, + total: u16, + batch_size: u32, + gadget_vector: &GadgetVector, + ) { + let total_party_set = (1..=total).into_iter().collect::>(); + let threshold_party_set = (1..=threshold).into_iter().collect::>(); + + // Run OT protocol instances. This is also a one time setup. + let base_ot_outputs = do_base_ot_for_threshold_sig::( + rng, + ote_params.num_base_ot(), + total, + total_party_set.clone(), + ); + + let mut mult_phase = vec![]; + let mut all_msg_1s = vec![]; + let total_time; + let mut times = BTreeMap::new(); + let mut products = vec![]; + + // Initiate multiplication phase and each party sends messages to others + let start = Instant::now(); + for i in 1..=threshold { + let start = Instant::now(); + let mut others = threshold_party_set.clone(); + others.remove(&i); + let a = (0..batch_size).map(|_| Fr::rand(rng)).collect::>(); + let b = (0..batch_size).map(|_| Fr::rand(rng)).collect::>(); + let (phase, U) = Phase2::init( + rng, + i, + a.clone(), + b.clone(), + base_ot_outputs[i as usize - 1].clone(), + others, + ote_params, + &gadget_vector, + ) + .unwrap(); + times.insert(i, start.elapsed()); + products.push((a, b)); + mult_phase.push(phase); + all_msg_1s.push((i, U)); + } + + // Each party process messages received from others + let mut all_msg_2s = vec![]; + for (sender_id, msg_1s) in all_msg_1s { + for (receiver_id, m) in msg_1s { + let start = Instant::now(); + let m2 = mult_phase[receiver_id as usize - 1] + .receive_message1::(sender_id, m, &gadget_vector) + .unwrap(); + times.insert( + receiver_id, + *times.get(&receiver_id).unwrap() + start.elapsed(), + ); + all_msg_2s.push((receiver_id, sender_id, m2)); + } + } + + for (sender_id, receiver_id, m2) in all_msg_2s { + let start = Instant::now(); + mult_phase[receiver_id as usize - 1] + .receive_message2::(sender_id, m2, &gadget_vector) + .unwrap(); + times.insert( + receiver_id, + *times.get(&receiver_id).unwrap() + start.elapsed(), + ); + } + + let mult_phase_outputs = mult_phase + .into_iter() + .map(|p| { + let start = Instant::now(); + let i = p.id; + let o = p.finish(); + times.insert(i, *times.get(&i).unwrap() + start.elapsed()); + o + }) + .collect::>(); + total_time = start.elapsed(); + println!( + "Multiplication of batch size {} among parties with threshold {} took {:?}", + batch_size, threshold, total_time + ); + + // Check that multiplication works, i.e. each party has an additive share of + // a multiplication with every other party + for i in 1..=threshold { + for (j, z_A) in &mult_phase_outputs[i as usize - 1].z_A { + let z_B = mult_phase_outputs[*j as usize - 1].z_B.get(&i).unwrap(); + for k in 0..batch_size as usize { + assert_eq!( + z_A.0[k] + z_B.0[k], + products[i as usize - 1].0[k] * products[*j as usize - 1].1[k] + ); + assert_eq!( + z_A.1[k] + z_B.1[k], + products[i as usize - 1].1[k] * products[*j as usize - 1].0[k] + ); + } + } + } + } + + check(&mut rng, ote_params, 5, 8, 1, &gadget_vector); + check(&mut rng, ote_params, 5, 8, 10, &gadget_vector); + check(&mut rng, ote_params, 5, 8, 20, &gadget_vector); + check(&mut rng, ote_params, 5, 8, 30, &gadget_vector); + check(&mut rng, ote_params, 10, 20, 10, &gadget_vector); + check(&mut rng, ote_params, 20, 30, 10, &gadget_vector); + } +} diff --git a/bbs_plus/src/threshold/threshold_bbs.rs b/bbs_plus/src/threshold/threshold_bbs.rs index 4052d084..a4179702 100644 --- a/bbs_plus/src/threshold/threshold_bbs.rs +++ b/bbs_plus/src/threshold/threshold_bbs.rs @@ -185,8 +185,7 @@ impl BBSSignatureShare { #[cfg(test)] pub mod tests { use super::*; - use ark_bls12_381::Bls12_381; - use ark_ec::pairing::Pairing; + use ark_bls12_381::{Bls12_381, Fr}; use ark_ff::Zero; use std::time::{Duration, Instant}; @@ -206,8 +205,6 @@ pub mod tests { dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector, }; - type Fr = ::ScalarField; - #[test] fn signing() { let mut rng = StdRng::seed_from_u64(0u64); @@ -226,9 +223,12 @@ pub mod tests { let total_signers = 8; let all_party_set = (1..=total_signers).into_iter().collect::>(); let threshold_party_set = (1..=threshold_signers).into_iter().collect::>(); + + // The signers do a keygen. This is a one time setup. let (sk, sk_shares) = trusted_party_keygen::<_, Fr>(&mut rng, threshold_signers, total_signers); + // The signers run OT protocol instances. This is also a one time setup. let base_ot_outputs = do_base_ot_for_threshold_sig::( &mut rng, ote_params.num_base_ot(), @@ -246,11 +246,14 @@ pub mod tests { sig_batch_size, threshold_signers ); + // Following have to happen for each new batch of signatures. Batch size can be 1 when creating one signature at a time + let mut round1s = vec![]; let mut commitments = vec![]; let mut commitments_zero_share = vec![]; let mut round1outs = vec![]; + // Signers initiate round-1 and each signer sends commitments to others let start = Instant::now(); for i in 1..=threshold_signers { let mut others = threshold_party_set.clone(); @@ -268,6 +271,7 @@ pub mod tests { commitments_zero_share.push(comm_zero); } + // Signers process round-1 commitments received from others for i in 1..=threshold_signers { for j in 1..=threshold_signers { if i != j { @@ -285,6 +289,7 @@ pub mod tests { } } + // Signers create round-1 shares once they have the required commitments from others for i in 1..=threshold_signers { for j in 1..=threshold_signers { if i != j { @@ -298,6 +303,7 @@ pub mod tests { } } + // Signers finish round-1 to generate the output let mut expected_sk = Fr::zero(); for (i, round1) in round1s.into_iter().enumerate() { let out = round1.finish_for_bbs::(&sk_shares[i]).unwrap(); @@ -314,6 +320,7 @@ pub mod tests { let mut round2s = vec![]; let mut all_msg_1s = vec![]; + // Signers initiate round-2 and each signer sends messages to others let start = Instant::now(); for i in 1..=threshold_signers { let mut others = threshold_party_set.clone(); @@ -333,6 +340,7 @@ pub mod tests { all_msg_1s.push((i, U)); } + // Signers process round-2 messages received from others let mut all_msg_2s = vec![]; for (sender_id, msg_1s) in all_msg_1s { for (receiver_id, m) in msg_1s { @@ -352,6 +360,8 @@ pub mod tests { let round2_outputs = round2s.into_iter().map(|p| p.finish()).collect::>(); println!("Phase 2 took {:?}", start.elapsed()); + // Check that multiplication phase ran successfully, i.e. each signer has an additive share of + // a multiplication with every other signer for i in 1..=threshold_signers { for (j, z_A) in &round2_outputs[i as usize - 1].z_A { let z_B = round2_outputs[*j as usize - 1].z_B.get(&i).unwrap(); @@ -370,6 +380,8 @@ pub mod tests { } } + // This is the final step where each signer generates his share of the signature without interaction + // with any other signer and sends this share to the client let mut sig_shares_time = Duration::default(); let mut sig_aggr_time = Duration::default(); for k in 0..sig_batch_size as usize { @@ -378,6 +390,7 @@ pub mod tests { .map(|_| Fr::rand(&mut rng)) .collect::>(); + // Get shares from a threshold number of signers let mut shares = vec![]; let start = Instant::now(); for i in 0..threshold_signers as usize { @@ -393,6 +406,7 @@ pub mod tests { } sig_shares_time += start.elapsed(); + // Client aggregate the shares to get the final signature let start = Instant::now(); let sig = BBSSignatureShare::aggregate(shares).unwrap(); sig_aggr_time += start.elapsed(); diff --git a/bbs_plus/src/threshold/threshold_bbs_plus.rs b/bbs_plus/src/threshold/threshold_bbs_plus.rs index e00cdbc2..1e4f36e2 100644 --- a/bbs_plus/src/threshold/threshold_bbs_plus.rs +++ b/bbs_plus/src/threshold/threshold_bbs_plus.rs @@ -196,8 +196,7 @@ impl BBSPlusSignatureShare { #[cfg(test)] pub mod tests { use super::*; - use ark_bls12_381::Bls12_381; - use ark_ec::pairing::Pairing; + use ark_bls12_381::{Bls12_381, Fr}; use ark_ff::Zero; use std::time::{Duration, Instant}; @@ -219,8 +218,6 @@ pub mod tests { use secret_sharing_and_dkg::shamir_ss::deal_random_secret; - type Fr = ::ScalarField; - pub fn trusted_party_keygen( rng: &mut R, threshold: ParticipantId, @@ -254,8 +251,10 @@ pub mod tests { let total_party_set = (1..=total_signers).into_iter().collect::>(); let threshold_party_set = (1..=threshold_signers).into_iter().collect::>(); + // The signers do a keygen. This is a one time setup. let (sk, sk_shares) = trusted_party_keygen::<_, Fr>(rng, threshold_signers, total_signers); + let params = SignatureParamsG1::::generate_using_rng(rng, message_count); let public_key = PublicKeyG2::generate_using_secret_key(&SecretKey(sk), ¶ms); @@ -264,6 +263,7 @@ pub mod tests { sig_batch_size, message_count, threshold_signers ); + // The signers run OT protocol instances. This is also a one time setup. let start = Instant::now(); let base_ot_outputs = do_base_ot_for_threshold_sig::( rng, @@ -273,6 +273,7 @@ pub mod tests { ); println!("Base OT phase took {:?}", start.elapsed()); + // Following have to happen for each new batch of signatures. Batch size can be 1 when creating one signature at a time let mut round1s = vec![]; let mut commitments = vec![]; let mut commitments_zero_share = vec![]; @@ -280,6 +281,7 @@ pub mod tests { let total_phase1_time; let mut phase1_times = BTreeMap::new(); + // Signers initiate round-1 and each signer sends commitments to others let start = Instant::now(); for i in 1..=threshold_signers { let start = Instant::now(); @@ -299,6 +301,7 @@ pub mod tests { commitments_zero_share.push(comm_zero); } + // Signers process round-1 commitments received from others for i in 1..=threshold_signers { for j in 1..=threshold_signers { if i != j { @@ -318,6 +321,7 @@ pub mod tests { } } + // Signers create round-1 shares once they have the required commitments from others for i in 1..=threshold_signers { for j in 1..=threshold_signers { if i != j { @@ -335,6 +339,7 @@ pub mod tests { } } + // Signers finish round-1 to generate the output let mut expected_sk = Fr::zero(); for (i, round1) in round1s.into_iter().enumerate() { let id = round1.id; @@ -360,6 +365,7 @@ pub mod tests { let total_phase2_time; let mut phase2_times = BTreeMap::new(); + // Signers initiate round-2 and each signer sends messages to others let start = Instant::now(); for i in 1..=threshold_signers { let start = Instant::now(); @@ -381,6 +387,7 @@ pub mod tests { all_msg_1s.push((i, U)); } + // Signers process round-2 messages received from others let mut all_msg_2s = vec![]; for (sender_id, msg_1s) in all_msg_1s { for (receiver_id, m) in msg_1s { @@ -420,6 +427,8 @@ pub mod tests { total_phase2_time = start.elapsed(); println!("Phase 2 took {:?}", total_phase2_time); + // Check that multiplication phase ran successfully, i.e. each signer has an additive share of + // a multiplication with every other signer for i in 1..=threshold_signers { for (j, z_A) in &round2_outputs[i as usize - 1].z_A { let z_B = round2_outputs[*j as usize - 1].z_B.get(&i).unwrap(); @@ -438,10 +447,12 @@ pub mod tests { } } + // This is the final step where each signer generates his share of the signature without interaction + // with any other signer and sends this share to the client + let mut total_sig_shares_time = Duration::default(); let mut total_sig_aggr_time = Duration::default(); let mut share_gen_times = BTreeMap::new(); - for k in 0..sig_batch_size as usize { let messages = (0..message_count) .into_iter() @@ -466,6 +477,7 @@ pub mod tests { } total_sig_shares_time += start.elapsed(); + // Client aggregate the shares to get the final signature let start = Instant::now(); let sig = BBSPlusSignatureShare::aggregate(shares).unwrap(); total_sig_aggr_time += start.elapsed(); @@ -502,6 +514,7 @@ pub mod tests { } } + check(&mut rng, ote_params, 5, 8, 1, 3, &gadget_vector); check(&mut rng, ote_params, 5, 8, 10, 3, &gadget_vector); check(&mut rng, ote_params, 5, 8, 20, 3, &gadget_vector); check(&mut rng, ote_params, 5, 8, 30, 3, &gadget_vector); diff --git a/coconut/src/setup/keygen/common.rs b/coconut/src/setup/keygen/common.rs index c24ee98c..c061e2fe 100644 --- a/coconut/src/setup/keygen/common.rs +++ b/coconut/src/setup/keygen/common.rs @@ -129,7 +129,7 @@ impl SecretKeyModel { } } - /// Applies given `f` to the each contained entity producing a new `SecretKeyModel`. + /// Applies given `f` to each contained entity producing a new `SecretKeyModel`. pub(crate) fn map(self, mut f: F) -> SecretKeyModel where F: FnMut(X) -> R, diff --git a/coconut/src/setup/keygen/shamir_ss.rs b/coconut/src/setup/keygen/shamir_ss.rs index 0cbdf515..3952fe06 100644 --- a/coconut/src/setup/keygen/shamir_ss.rs +++ b/coconut/src/setup/keygen/shamir_ss.rs @@ -104,7 +104,7 @@ mod shamir_ss_tests { BlindSignature::new(comm_and_blindings.clone(), &sk, &h).unwrap(); let sig = blind_signature - .unblind(blind_indices.clone().zip(&blindings), &pk) + .unblind(blind_indices.clone().zip(&blindings), &pk, &h) .unwrap(); (sk, sig) diff --git a/coconut/src/signature/aggregated_signature.rs b/coconut/src/signature/aggregated_signature.rs index 0ae9944d..f2a745a3 100644 --- a/coconut/src/signature/aggregated_signature.rs +++ b/coconut/src/signature/aggregated_signature.rs @@ -120,7 +120,7 @@ mod aggregated_signature_tests { BlindSignature::new(comms.clone(), &sk, &h).unwrap(); let sig = blind_signature - .unblind(blind_indices.clone().zip(blindings.iter()), &pk) + .unblind(blind_indices.clone().zip(blindings.iter()), &pk, &h) .unwrap(); sig.verify(&msgs, &pk, ¶ms).unwrap(); diff --git a/coconut/src/signature/blind_signature.rs b/coconut/src/signature/blind_signature.rs index 7286ed5a..185cbb4f 100644 --- a/coconut/src/signature/blind_signature.rs +++ b/coconut/src/signature/blind_signature.rs @@ -116,10 +116,15 @@ impl BlindSignature { self, indexed_blindings_sorted_by_index: IB, PublicKey { beta, .. }: &PublicKey, + &h: &E::G1Affine, ) -> Result> where IB: IntoIterator, { + if self.0.sigma_1 != h { + return Err(BlindPSError::InvalidH); + } + let blindings_with_beta: OwnedPairs<_, _> = pair_valid_items_with_slice( indexed_blindings_sorted_by_index, CheckLeft(seq_pairs_satisfy(|a, b| a < b)), @@ -131,10 +136,10 @@ impl BlindSignature { // \sum_{j}(beta_{j} * (-o_{j})) let beta_mul_neg_o = blindings_with_beta.msm(); - Ok(Signature::combine( - self.0.sigma_1, - beta_mul_neg_o + self.0.sigma_2, - )) + // \sum_{j}(beta_{j} * (-o_{j})) + c + let s_i = beta_mul_neg_o + self.0.sigma_2; + + Ok(Signature::combine(self.0.sigma_1, s_i)) } } @@ -193,7 +198,7 @@ mod tests { ) .unwrap(); let sig_unblinded = sig_blinded - .unblind(blindings.iter().enumerate(), &pk) + .unblind(blindings.iter().enumerate(), &pk, &h) .unwrap(); sig_unblinded.verify(&msgs, &pk, ¶ms).unwrap(); @@ -211,6 +216,7 @@ mod tests { // In practice, this should be generated by hashing the Pedersen commitment to all the hidden messages let h = G1::rand(&mut rng).into_affine(); + let blindings: Vec<_> = n_rand(&mut rng, count_msgs).collect(); let blinding_msg_pairs = Pairs::new(&blindings, &msgs).unwrap(); @@ -227,7 +233,7 @@ mod tests { ) .unwrap(); let sig_unblinded = sig_blinded - .unblind(blindings.iter().enumerate(), &pk) + .unblind(blindings.iter().enumerate(), &pk, &h) .unwrap(); sig_unblinded.verify(&msgs, &pk, ¶ms).unwrap(); } @@ -263,7 +269,7 @@ mod tests { &h, ) .unwrap(); - let sig_unblinded = sig_blinded.unblind(None, &pk).unwrap(); + let sig_unblinded = sig_blinded.unblind(None, &pk, &h).unwrap(); sig_unblinded.verify(&msgs, &pk, ¶ms).unwrap(); } @@ -313,6 +319,7 @@ mod tests { .iter() .map(|(blinding, (idx, _))| (*idx, blinding)), &pk, + &h, ) .unwrap(); @@ -348,7 +355,7 @@ mod tests { ) .unwrap(); let sig_unblinded = sig_blinded - .unblind(blindings.iter().enumerate(), &pk) + .unblind(blindings.iter().enumerate(), &pk, &h) .unwrap(); sig_unblinded.verify(&msgs, &pk, ¶ms).unwrap(); } diff --git a/coconut/src/signature/error.rs b/coconut/src/signature/error.rs index 4202aa83..bc95493b 100644 --- a/coconut/src/signature/error.rs +++ b/coconut/src/signature/error.rs @@ -36,6 +36,9 @@ pub enum BlindPSError { }, BlindingIndicesMustBeUniqueAndSorted(InvalidPair), IncompatibleVerificationKey, + // The h given by signer is not the same as generated by the user. It was generated by hashing the + // commitment to all messages, i.e. result of query `(ro.query.ini,sid,com)` from the paper + InvalidH, } impl From for BlindPSError { diff --git a/coconut/src/tests.rs b/coconut/src/tests.rs index 45c51313..1f053395 100644 --- a/coconut/src/tests.rs +++ b/coconut/src/tests.rs @@ -79,7 +79,7 @@ fn construction_pac_workflow() { let blind_signature = BlindSignature::new(m_comms, &sk, &h).unwrap(); let sig = blind_signature - .unblind(blind_indices.zip(blindings), &pk) + .unblind(blind_indices.zip(blindings), &pk, &h) .unwrap(); sig.verify(&msgs, &pk, ¶ms).unwrap(); diff --git a/kvac/src/bddt_2016/mod.rs b/kvac/src/bddt_2016/mod.rs index 26d56e6b..6969913b 100644 --- a/kvac/src/bddt_2016/mod.rs +++ b/kvac/src/bddt_2016/mod.rs @@ -1,6 +1,8 @@ //! Implements KVAC from [Improved Algebraic MACs and Practical Keyed-Verification Anonymous Credentials](https://link.springer.com/chapter/10.1007/978-3-319-69453-5_20) //! An alternate implementation of proof of knowledge of MAC is added which is adapted from the protocol to prove knowledge of //! BBS+ signatures described in section 4.5 of the paper [Anonymous Attestation Using the Strong Diffie Hellman Assumption Revisited](https://eprint.iacr.org/2016/663) +//! In addition it supports generating proof of validity or invalidity of keyed-proofs, i.e. the proof verifying which requires the knowledge of +//! secret key. pub mod keyed_proof; pub mod mac; diff --git a/kvac/src/lib.rs b/kvac/src/lib.rs index 5d7b1084..2a61019c 100644 --- a/kvac/src/lib.rs +++ b/kvac/src/lib.rs @@ -10,7 +10,8 @@ //! Both implementations support additional verification methods that allow joint verification of proof of possession of credentials where one //! of the verifier is the issuer who knows the secret key and another verifier does not know secret key but learns the revealed attributes which //! are not shared with the issuer. This lets us build for a use-case where issuer wants to allow anytime its issued credential is used -//! (eg. to get paid by the verifier) while still not harming the user's privacy as it doesn't learn any revealed attributes. +//! (eg. to get paid by the verifier) while still not harming the user's privacy as it doesn't learn any revealed attributes. The first +//! verifier, i.e. the issuer can also provide a proof of validity or invalidity to the second verifier. pub mod bddt_2016; pub mod cddh_2019; diff --git a/legogroth16/Cargo.toml b/legogroth16/Cargo.toml index bd25e97d..03466abb 100644 --- a/legogroth16/Cargo.toml +++ b/legogroth16/Cargo.toml @@ -23,7 +23,7 @@ ark-r1cs-std = { workspace = true, optional = true } tracing = { version = "0.1", default-features = false, features = [ "attributes" ], optional = true } derivative = { version = "2.0", features = ["use_core"], optional = true } rayon = { workspace = true, optional = true } -wasmer = { version = "3.3.0", optional = true, default-features = false } +wasmer = { version = "4.3.0", optional = true, default-features = false } fnv = { version = "1.0.3", default-features = false, optional = true } num-bigint = { version = "0.4", default-features = false, optional = true } log = "0.4" diff --git a/legogroth16/src/link/mod.rs b/legogroth16/src/link/mod.rs index d9419dfc..90836a60 100644 --- a/legogroth16/src/link/mod.rs +++ b/legogroth16/src/link/mod.rs @@ -11,8 +11,12 @@ mod test { use ark_bls12_381::{Bls12_381, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ff::{One, PrimeField, UniformRand, Zero}; - use ark_std::rand::{rngs::StdRng, SeedableRng}; - use std::ops::Add; + use ark_std::{ + ops::Add, + rand::{rngs::StdRng, SeedableRng}, + vec, + vec::Vec, + }; #[test] fn test_basic() { diff --git a/legogroth16/src/tests.rs b/legogroth16/src/tests.rs index 718d2c2f..3ddbb656 100644 --- a/legogroth16/src/tests.rs +++ b/legogroth16/src/tests.rs @@ -8,6 +8,7 @@ use ark_ec::{pairing::Pairing, CurveGroup}; use ark_ff::Field; use ark_std::{ rand::{rngs::StdRng, RngCore, SeedableRng}, + vec::Vec, UniformRand, };