From d932a3e12b2ff91fc948224df9d87c5f9608b46d Mon Sep 17 00:00:00 2001 From: lovesh Date: Thu, 23 May 2024 23:27:28 +0530 Subject: [PATCH] Move core mpc and multiplication code out of BBS+ crate and to OT crate and protyping threshold accumulator update and witness creation Signed-off-by: lovesh --- bbs_plus/Cargo.toml | 1 + bbs_plus/src/threshold/mod.rs | 5 +- .../src/threshold/multiplication_phase.rs | 337 +------- .../threshold/randomness_generation_phase.rs | 19 +- bbs_plus/src/threshold/threshold_bbs.rs | 32 +- bbs_plus/src/threshold/threshold_bbs_plus.rs | 30 +- bbs_plus/src/threshold/utils.rs | 8 +- oblivious_transfer/README.md | 10 +- .../src}/cointoss.rs | 29 +- oblivious_transfer/src/dkls_mul_2p.rs | 580 -------------- oblivious_transfer/src/error.rs | 23 + oblivious_transfer/src/lib.rs | 23 +- .../base_ot_multi_party_pairwise.rs | 89 +-- .../batch_mul_multi_party.rs | 368 +++++++++ .../dkls19_batch_mul_2p.rs | 14 +- .../src/ot_based_multiplication/mod.rs | 4 + .../src}/zero_sharing.rs | 30 +- proof_system/src/verifier.rs | 1 + .../src/distributed_dlog_check/semi_honest.rs | 3 - .../src/feldman_dvss_dkg.rs | 7 +- secret_sharing_and_dkg/src/shamir_ss.rs | 2 +- test_utils/Cargo.toml | 3 +- test_utils/src/lib.rs | 1 + test_utils/src/ot.rs | 104 +++ utils/src/randomized_pairing_check.rs | 2 +- vb_accumulator/Cargo.toml | 7 +- vb_accumulator/src/error.rs | 16 + vb_accumulator/src/lib.rs | 1 + vb_accumulator/src/threshold/mod.rs | 731 ++++++++++++++++++ 29 files changed, 1462 insertions(+), 1018 deletions(-) rename {bbs_plus/src/threshold => oblivious_transfer/src}/cointoss.rs (92%) delete mode 100644 oblivious_transfer/src/dkls_mul_2p.rs rename bbs_plus/src/threshold/base_ot_phase.rs => oblivious_transfer/src/ot_based_multiplication/base_ot_multi_party_pairwise.rs (82%) create mode 100644 oblivious_transfer/src/ot_based_multiplication/batch_mul_multi_party.rs rename {bbs_plus/src/threshold => oblivious_transfer/src}/zero_sharing.rs (92%) create mode 100644 test_utils/src/ot.rs create mode 100644 vb_accumulator/src/threshold/mod.rs diff --git a/bbs_plus/Cargo.toml b/bbs_plus/Cargo.toml index 6f4f9b09..1840618c 100644 --- a/bbs_plus/Cargo.toml +++ b/bbs_plus/Cargo.toml @@ -34,6 +34,7 @@ ark-bls12-381.workspace = true serde_json = "1.0" rmp-serde = "1.0" ark-poly.workspace = true +test_utils = { path = "../test_utils" } [features] default = [ "parallel" ] diff --git a/bbs_plus/src/threshold/mod.rs b/bbs_plus/src/threshold/mod.rs index 464232cb..c7dcfceb 100644 --- a/bbs_plus/src/threshold/mod.rs +++ b/bbs_plus/src/threshold/mod.rs @@ -16,11 +16,10 @@ //! 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; pub mod multiplication_phase; pub mod randomness_generation_phase; pub mod threshold_bbs; pub mod threshold_bbs_plus; pub mod utils; -pub mod zero_sharing; + +pub type ParticipantId = oblivious_transfer_protocols::ParticipantId; diff --git a/bbs_plus/src/threshold/multiplication_phase.rs b/bbs_plus/src/threshold/multiplication_phase.rs index 12cd0c99..f108fbbf 100644 --- a/bbs_plus/src/threshold/multiplication_phase.rs +++ b/bbs_plus/src/threshold/multiplication_phase.rs @@ -1,6 +1,7 @@ //! The multiplication phase of the threshold signing protocol of BBS and BBS+. -use crate::{error::BBSPlusError, threshold::base_ot_phase::BaseOTPhaseOutput}; +use super::ParticipantId; +use crate::error::BBSPlusError; use ark_ff::PrimeField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ @@ -9,51 +10,23 @@ use ark_std::{ vec::Vec, }; use digest::DynDigest; -use dock_crypto_utils::transcript::MerlinTranscript; -use itertools::interleave; -use oblivious_transfer_protocols::{ - ot_based_multiplication::{ - dkls18_mul_2p::MultiplicationOTEParams, - dkls19_batch_mul_2p::{GadgetVector, MaskedInputs, Party1, Party2, RLC}, +use oblivious_transfer_protocols::ot_based_multiplication::{ + base_ot_multi_party_pairwise::BaseOTOutput, + batch_mul_multi_party::{ + Message1, Message2, Participant as MultiplicationParty, + ParticipantOutput as MultiplicationPartyOutput, }, - ot_extensions::kos_ote::{CorrelationTag, RLC as KOSRLC}, - BitMatrix, ParticipantId, + dkls18_mul_2p::MultiplicationOTEParams, + dkls19_batch_mul_2p::GadgetVector, }; -/// The participant will acts as -/// - a receiver in OT extension, also called Party2 in multiplication protocol, and its id is less than other participant -/// - a sender in OT extension, also called Party1 in multiplication protocol, and its id is greater than other participant #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] -pub struct Phase2 { - pub id: ParticipantId, - /// Number of threshold signatures being generated in a single batch. - pub batch_size: u32, - /// Transcripts to record protocol interactions with each participant and later used to generate random challenges - pub transcripts: BTreeMap, - pub ote_params: MultiplicationOTEParams, - /// Map where this participant plays the role of sender, i.e Party1 - pub multiplication_party1: - BTreeMap>, - /// Map where this participant plays the role of receiver, i.e Party2 - pub multiplication_party2: - BTreeMap>, - pub z_A: BTreeMap, Vec)>, - pub z_B: BTreeMap, Vec)>, -} - -/// Message sent from Party2 to Party1 of multiplication protocol -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Message1(BitMatrix, KOSRLC, MaskedInputs); +pub struct Phase2( + pub MultiplicationParty, +); -/// Message sent from Party1 to Party2 of multiplication protocol. This message is created after Part1 processes `Message1` #[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Message2(CorrelationTag, RLC, MaskedInputs); - -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Phase2Output { - pub z_A: BTreeMap, Vec)>, - pub z_B: BTreeMap, Vec)>, -} +pub struct Phase2Output(pub MultiplicationPartyOutput); impl Phase2 @@ -63,77 +36,23 @@ impl id: ParticipantId, masked_signing_key_share: Vec, masked_r: Vec, - mut base_ot_output: BaseOTPhaseOutput, + base_ot_output: BaseOTOutput, others: BTreeSet, ote_params: MultiplicationOTEParams, gadget_vector: &GadgetVector, ) -> Result<(Self, BTreeMap>), BBSPlusError> { - 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 multiplication_party1 = - BTreeMap::>::new(); - let mut multiplication_party2 = - BTreeMap::>::new(); - let mut Us = BTreeMap::new(); - - // When an OT extension receiver, generate input to multiplication as `[masked_signing_key_share[0], masked_r[0], masked_signing_key_share[1], masked_r[1]], masked_signing_key_share[2], masked_r[2], ...` - let mult_when_ot_recv = - interleave(masked_signing_key_share.clone(), masked_r.clone()).collect::>(); - // When an OT extension sender, generate input to multiplication as `[masked_r[0], masked_signing_key_share[0], masked_r[1], masked_signing_key_share[1], masked_r[2], masked_signing_key_share[2], ...` - let mult_when_ot_sendr = - interleave(masked_r.clone(), masked_signing_key_share.clone()).collect::>(); - - for other in others { - 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) - { - let party1 = Party1::new( - rng, - mult_when_ot_recv.clone(), - base_ot_choices, - base_ot_keys, - ote_params, - )?; - multiplication_party1.insert(other, party1); - } else { - return Err(BBSPlusError::MissingOTReceiverFor(other)); - } - } else { - if let Some(base_ot_keys) = base_ot_output.sender_keys.remove(&other) { - let (party2, U, rlc, gamma) = Party2::new( - rng, - mult_when_ot_sendr.clone(), - base_ot_keys, - &mut trans, - ote_params, - &gadget_vector, - )?; - multiplication_party2.insert(other, party2); - Us.insert(other, Message1(U, rlc, gamma)); - } else { - return Err(BBSPlusError::MissingOTSenderFor(other)); - } - } - transcripts.insert(other, trans); - } - Ok(( - Self { - id, - batch_size, - transcripts, - ote_params, - multiplication_party1, - multiplication_party2, - z_A: Default::default(), - z_B: Default::default(), - }, - Us, - )) + let (p, m) = MultiplicationParty::init( + rng, + id, + masked_signing_key_share, + masked_r, + base_ot_output, + others, + ote_params, + gadget_vector, + b"Multiplication phase for threshold BBS and BBS+", + )?; + Ok((Self(p), m)) } /// Process received message from Party2 of multiplication protocol @@ -143,30 +62,10 @@ impl message: Message1, gadget_vector: &GadgetVector, ) -> Result, BBSPlusError> { - if self.multiplication_party2.contains_key(&sender_id) { - return Err(BBSPlusError::NotAMultiplicationParty2(sender_id)); - } - if !self.multiplication_party1.contains_key(&sender_id) { - return Err(BBSPlusError::NotAMultiplicationParty1(sender_id)); - } - let Message1(U, rlc, gamma) = message; - let party1 = self.multiplication_party1.remove(&sender_id).unwrap(); - let trans = self.transcripts.get_mut(&sender_id).unwrap(); - - let (shares, tau, r, gamma_a) = - party1.receive::(U, rlc, gamma, trans, &gadget_vector)?; - debug_assert_eq!(shares.len() as u32, 2 * self.batch_size); - let mut z_A_0 = Vec::with_capacity(self.batch_size as usize); - let mut z_A_1 = Vec::with_capacity(self.batch_size as usize); - for (i, share) in shares.0.into_iter().enumerate() { - if (i & 1) == 0 { - z_A_0.push(share); - } else { - z_A_1.push(share); - } - } - self.z_A.insert(sender_id, (z_A_0, z_A_1)); - Ok(Message2(tau, r, gamma_a)) + let m = self + .0 + .receive_message1::(sender_id, message, gadget_vector)?; + Ok(m) } /// Process received message from Party1 of multiplication protocol @@ -176,182 +75,12 @@ impl message: Message2, gadget_vector: &GadgetVector, ) -> Result<(), BBSPlusError> { - if self.multiplication_party1.contains_key(&sender_id) { - return Err(BBSPlusError::NotAMultiplicationParty1(sender_id)); - } - if !self.multiplication_party2.contains_key(&sender_id) { - return Err(BBSPlusError::NotAMultiplicationParty2(sender_id)); - } - let Message2(tau, rlc, gamma) = message; - let party2 = self.multiplication_party2.remove(&sender_id).unwrap(); - let trans = self.transcripts.get_mut(&sender_id).unwrap(); - let shares = party2.receive::(tau, rlc, gamma, trans, &gadget_vector)?; - debug_assert_eq!(shares.len() as u32, 2 * self.batch_size); - let mut z_B_0 = Vec::with_capacity(self.batch_size as usize); - let mut z_B_1 = Vec::with_capacity(self.batch_size as usize); - for (i, share) in shares.0.into_iter().enumerate() { - if (i & 1) == 0 { - z_B_0.push(share); - } else { - z_B_1.push(share); - } - } - self.z_B.insert(sender_id, (z_B_0, z_B_1)); + self.0 + .receive_message2::(sender_id, message, gadget_vector)?; Ok(()) } pub fn finish(self) -> Phase2Output { - Phase2Output { - z_A: self.z_A, - z_B: self.z_B, - } - } -} - -#[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); + Phase2Output(self.0.finish()) } } diff --git a/bbs_plus/src/threshold/randomness_generation_phase.rs b/bbs_plus/src/threshold/randomness_generation_phase.rs index 83540905..8ecec525 100644 --- a/bbs_plus/src/threshold/randomness_generation_phase.rs +++ b/bbs_plus/src/threshold/randomness_generation_phase.rs @@ -1,12 +1,10 @@ -use crate::{ - error::BBSPlusError, - threshold::{cointoss::Commitments, utils::compute_masked_arguments_to_multiply}, -}; +use super::ParticipantId; +use crate::{error::BBSPlusError, threshold::utils::compute_masked_arguments_to_multiply}; use ark_ff::PrimeField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::vec::Vec; use digest::DynDigest; -use oblivious_transfer_protocols::ParticipantId; +use oblivious_transfer_protocols::{cointoss, zero_sharing}; /// This is the first phase of the signing protocol where parties generate random values, jointly and /// individually including additive shares of 0. @@ -15,11 +13,12 @@ pub struct Phase1 { pub id: ParticipantId, /// Number of threshold signatures being generated in a single batch. pub batch_size: u32, + /// Shares of the random `r`, one share for each item in the batch pub r: Vec, - /// Protocols to generate shares of random values used in signature like `e` - pub commitment_protocol: super::cointoss::Party, + /// Protocols to generate shares of random values used in signature like `e` for BBS and (`e`, `s`) for BBS+ + pub commitment_protocol: cointoss::Party, /// Protocols to generate shares of 0s. - pub zero_sharing_protocol: super::zero_sharing::Party, + pub zero_sharing_protocol: zero_sharing::Party, } impl Phase1 { @@ -44,8 +43,8 @@ impl Phase1 { pub fn receive_commitment( &mut self, sender_id: ParticipantId, - comm: Commitments, - comm_zero_share: Commitments, + comm: cointoss::Commitments, + comm_zero_share: cointoss::Commitments, ) -> Result<(), BBSPlusError> { self.commitment_protocol .receive_commitment(sender_id, comm)?; diff --git a/bbs_plus/src/threshold/threshold_bbs.rs b/bbs_plus/src/threshold/threshold_bbs.rs index a4179702..6e12bb8a 100644 --- a/bbs_plus/src/threshold/threshold_bbs.rs +++ b/bbs_plus/src/threshold/threshold_bbs.rs @@ -1,6 +1,6 @@ use ark_ec::{AffineRepr, CurveGroup}; -use super::{cointoss::Commitments, multiplication_phase::Phase2Output, utils::compute_R_and_u}; +use super::{multiplication_phase::Phase2Output, utils::compute_R_and_u}; use ark_ec::pairing::Pairing; use ark_ff::{Field, PrimeField, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; @@ -17,14 +17,15 @@ use crate::{ threshold::randomness_generation_phase::Phase1, }; use dock_crypto_utils::signature::MultiMessageSignatureParams; -use oblivious_transfer_protocols::ParticipantId; +use oblivious_transfer_protocols::{cointoss, zero_sharing, ParticipantId}; /// The length of vectors `r`, `e`, `masked_signing_key_shares`, `masked_rs` should -/// be `batch_size` each item of the vector corresponds to 1 signature +/// be `batch_size` as each item of the vector corresponds to 1 signature #[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct Phase1Output { pub id: ParticipantId, pub batch_size: u32, + /// Shares of the random `r`, one share for each item in the batch pub r: Vec, pub e: Vec, /// Additive shares of the signing key masked by a random `alpha` @@ -51,17 +52,24 @@ impl Phase1 { id: ParticipantId, others: BTreeSet, protocol_id: Vec, - ) -> Result<(Self, Commitments, BTreeMap), BBSPlusError> { + ) -> Result< + ( + Self, + cointoss::Commitments, + BTreeMap, + ), + BBSPlusError, + > { if others.contains(&id) { return Err(BBSPlusError::ParticipantCannotBePresentInOthers(id)); } let r = (0..batch_size).map(|_| F::rand(rng)).collect(); - // 1 random values `e` need to be generated per signature + // 1 random value `e` need to be generated per signature let (commitment_protocol, comm) = - super::cointoss::Party::commit(rng, id, batch_size, protocol_id.clone()); + cointoss::Party::commit(rng, id, batch_size, protocol_id.clone()); // Each signature will have its own zero-sharing of `alpha` and `beta` let (zero_sharing_protocol, comm_zero_share) = - super::zero_sharing::Party::init(rng, id, 2 * batch_size, others, protocol_id); + zero_sharing::Party::init(rng, id, 2 * batch_size, others, protocol_id); Ok(( Self { id, @@ -192,8 +200,7 @@ pub mod tests { use crate::{ setup::{PublicKeyG2, SecretKey}, threshold::{ - base_ot_phase::tests::do_base_ot_for_threshold_sig, multiplication_phase::Phase2, - threshold_bbs_plus::tests::trusted_party_keygen, + multiplication_phase::Phase2, threshold_bbs_plus::tests::trusted_party_keygen, }, }; use ark_std::{ @@ -204,6 +211,7 @@ pub mod tests { use oblivious_transfer_protocols::ot_based_multiplication::{ dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector, }; + use test_utils::ot::do_pairwise_base_ot; #[test] fn signing() { @@ -229,7 +237,7 @@ pub mod tests { 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::( + let base_ot_outputs = do_pairwise_base_ot::( &mut rng, ote_params.num_base_ot(), total_signers, @@ -363,8 +371,8 @@ pub mod tests { // 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(); + for (j, z_A) in &round2_outputs[i as usize - 1].0.z_A { + let z_B = round2_outputs[*j as usize - 1].0.z_B.get(&i).unwrap(); for k in 0..sig_batch_size as usize { assert_eq!( z_A.0[k] + z_B.0[k], diff --git a/bbs_plus/src/threshold/threshold_bbs_plus.rs b/bbs_plus/src/threshold/threshold_bbs_plus.rs index 1e4f36e2..fbadfac8 100644 --- a/bbs_plus/src/threshold/threshold_bbs_plus.rs +++ b/bbs_plus/src/threshold/threshold_bbs_plus.rs @@ -1,4 +1,3 @@ -use crate::threshold::cointoss::Commitments; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; use ark_ff::{Field, PrimeField, Zero}; @@ -10,7 +9,7 @@ use ark_std::{ }; use digest::DynDigest; use dock_crypto_utils::expect_equality; -use oblivious_transfer_protocols::ParticipantId; +use oblivious_transfer_protocols::{cointoss, zero_sharing, ParticipantId}; use super::{multiplication_phase::Phase2Output, utils::compute_R_and_u}; use crate::{ @@ -25,6 +24,7 @@ use dock_crypto_utils::signature::MultiMessageSignatureParams; pub struct Phase1Output { pub id: ParticipantId, pub batch_size: u32, + /// Shares of the random `r`, one share for each item in the batch pub r: Vec, pub e: Vec, pub s: Vec, @@ -53,17 +53,24 @@ impl Phase1 { id: ParticipantId, others: BTreeSet, protocol_id: Vec, - ) -> Result<(Self, Commitments, BTreeMap), BBSPlusError> { + ) -> Result< + ( + Self, + cointoss::Commitments, + BTreeMap, + ), + BBSPlusError, + > { if others.contains(&id) { return Err(BBSPlusError::ParticipantCannotBePresentInOthers(id)); } let r = (0..batch_size).map(|_| F::rand(rng)).collect(); // 2 because 2 random values `e` and `s` need to be generated per signature let (commitment_protocol, comm) = - super::cointoss::Party::commit(rng, id, 2 * batch_size, protocol_id.clone()); + cointoss::Party::commit(rng, id, 2 * batch_size, protocol_id.clone()); // Each signature will have its own zero-sharing of `alpha` and `beta` let (zero_sharing_protocol, comm_zero_share) = - super::zero_sharing::Party::init(rng, id, 2 * batch_size, others, protocol_id); + zero_sharing::Party::init(rng, id, 2 * batch_size, others, protocol_id); Ok(( Self { id, @@ -202,9 +209,7 @@ pub mod tests { use crate::{ setup::{PublicKeyG2, SecretKey}, - threshold::{ - base_ot_phase::tests::do_base_ot_for_threshold_sig, multiplication_phase::Phase2, - }, + threshold::multiplication_phase::Phase2, }; use oblivious_transfer_protocols::ot_based_multiplication::{ dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector, @@ -217,6 +222,7 @@ pub mod tests { use blake2::Blake2b512; use secret_sharing_and_dkg::shamir_ss::deal_random_secret; + use test_utils::ot::do_pairwise_base_ot; pub fn trusted_party_keygen( rng: &mut R, @@ -265,7 +271,7 @@ pub mod tests { // 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::( + let base_ot_outputs = do_pairwise_base_ot::( rng, ote_params.num_base_ot(), total_signers, @@ -418,7 +424,7 @@ pub mod tests { .into_iter() .map(|p| { let start = Instant::now(); - let i = p.id; + let i = p.0.id; let o = p.finish(); phase2_times.insert(i, *phase2_times.get(&i).unwrap() + start.elapsed()); o @@ -430,8 +436,8 @@ pub mod tests { // 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(); + for (j, z_A) in &round2_outputs[i as usize - 1].0.z_A { + let z_B = round2_outputs[*j as usize - 1].0.z_B.get(&i).unwrap(); for k in 0..sig_batch_size as usize { assert_eq!( z_A.0[k] + z_B.0[k], diff --git a/bbs_plus/src/threshold/utils.rs b/bbs_plus/src/threshold/utils.rs index 3eddef35..58eaf0c6 100644 --- a/bbs_plus/src/threshold/utils.rs +++ b/bbs_plus/src/threshold/utils.rs @@ -4,10 +4,10 @@ use ark_ff::PrimeField; use ark_std::{cfg_into_iter, ops::Mul, vec::Vec}; use itertools::Itertools; use oblivious_transfer_protocols::ParticipantId; +use secret_sharing_and_dkg::error::SSError; #[cfg(feature = "parallel")] use rayon::prelude::*; -use secret_sharing_and_dkg::error::SSError; pub fn compute_masked_arguments_to_multiply( signing_key: &F, @@ -21,6 +21,8 @@ pub fn compute_masked_arguments_to_multiply( let alphas = zero_shares.drain(0..batch_size).collect::>(); let betas = zero_shares; let lambda = secret_sharing_and_dkg::common::lagrange_basis_at_0::(&others, self_id)?; + // masked_signing_key_shares[i] = alphas[i] + (lambda * signing_key) + // masked_r[i] = betas[i] * r[i] let (masked_signing_key_shares, masked_rs) = cfg_into_iter!(r) .zip(cfg_into_iter!(alphas).zip(cfg_into_iter!(betas))) .map(|(r, (alpha, beta))| { @@ -45,11 +47,11 @@ pub fn compute_R_and_u( ) -> (G, G::ScalarField) { let R = base.mul(r).into_affine(); let mut u = *masked_r * (*e + masked_signing_key_share); - for (_, (a, b)) in &phase2.z_A { + for (_, (a, b)) in &phase2.0.z_A { u += a[index_in_output as usize]; u += b[index_in_output as usize]; } - for (_, (a, b)) in &phase2.z_B { + for (_, (a, b)) in &phase2.0.z_B { u += a[index_in_output as usize]; u += b[index_in_output as usize]; } diff --git a/oblivious_transfer/README.md b/oblivious_transfer/README.md index 9fd8ab4c..5ca87ff1 100644 --- a/oblivious_transfer/README.md +++ b/oblivious_transfer/README.md @@ -1,4 +1,6 @@ -# Oblivious Transfer (OT) and Oblivious Transfer Extensions (OTE) + + +# Oblivious Transfer (OT), Oblivious Transfer Extensions (OTE) and multi-party protocols based on that. ## Oblivious Transfer protocols @@ -6,10 +8,12 @@ 2. [Naor Pinkas OT](./src/base_ot/naor_pinkas_ot.rs) 3. [Endemic OT](./src/base_ot/endemic_ot.rs) -## Oblivious Transfer Extensions +## Oblivious Transfer Extensions 1. [ALSZ](./src/ot_extensions/alsz_ote.rs) 2. [KOS](./src/ot_extensions/kos_ote.rs) ## Oblivious Transfer based multiplication 1. [DKLS18](./src/ot_based_multiplication/dkls18_mul_2p.rs) - 2 party multiplication of where each party has a single input -2. [DKLS19](./src/ot_based_multiplication/dkls19_batch_mul_2p.rs) - 2 party batch-multiplication of where each party has multiple inputs, say `n` inputs and those inputs will be multiplied, i.e. a total of `2*n` multiplications will be done with each being between 2 inputs \ No newline at end of file +2. [DKLS19](./src/ot_based_multiplication/dkls19_batch_mul_2p.rs) - 2 party batch-multiplication of where each party has multiple inputs, say `n` inputs and those inputs will be multiplied, i.e. a total of `2*n` multiplications will be done with each being between 2 inputs + + diff --git a/bbs_plus/src/threshold/cointoss.rs b/oblivious_transfer/src/cointoss.rs similarity index 92% rename from bbs_plus/src/threshold/cointoss.rs rename to oblivious_transfer/src/cointoss.rs index 0f97cf39..88376e87 100644 --- a/bbs_plus/src/threshold/cointoss.rs +++ b/oblivious_transfer/src/cointoss.rs @@ -1,5 +1,5 @@ //! Generate 1 or more random numbers using commit-and-release coin tossing. -//! Called F_com in the paper +//! Called F_com in the paper [Threshold BBS+ Signatures for Distributed Anonymous Credential Issuance](https://eprint.iacr.org/2023/602) use ark_ff::PrimeField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; @@ -7,11 +7,12 @@ use ark_std::{cfg_into_iter, collections::BTreeMap, rand::RngCore, vec, vec::Vec use digest::Digest; use sha3::Sha3_256; -use oblivious_transfer_protocols::ParticipantId; +use super::ParticipantId; -use crate::error::BBSPlusError; +use crate::error::OTError; use dock_crypto_utils::expect_equality; + #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -69,19 +70,17 @@ impl Party { &mut self, sender_id: ParticipantId, commitments: Commitments, - ) -> Result<(), BBSPlusError> { + ) -> Result<(), OTError> { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if self.other_commitments.contains_key(&sender_id) { - return Err(BBSPlusError::AlreadyHaveCommitmentFromParticipant( - sender_id, - )); + return Err(OTError::AlreadyHaveCommitmentFromParticipant(sender_id)); } expect_equality!( self.own_shares_and_salts.len(), commitments.0.len(), - BBSPlusError::IncorrectNoOfCommitments + OTError::IncorrectNoOfCommitments ); self.other_commitments.insert(sender_id, commitments); Ok(()) @@ -93,24 +92,24 @@ impl Party { &mut self, sender_id: ParticipantId, shares_and_salts: Vec<(F, [u8; SALT_SIZE])>, - ) -> Result<(), BBSPlusError> { + ) -> Result<(), OTError> { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if !self.other_commitments.contains_key(&sender_id) { - return Err(BBSPlusError::MissingCommitmentFromParticipant(sender_id)); + return Err(OTError::MissingCommitmentFromParticipant(sender_id)); } if self.other_shares.contains_key(&sender_id) { - return Err(BBSPlusError::AlreadyHaveSharesFromParticipant(sender_id)); + return Err(OTError::AlreadyHaveSharesFromParticipant(sender_id)); } expect_equality!( self.own_shares_and_salts.len(), shares_and_salts.len(), - BBSPlusError::IncorrectNoOfShares + OTError::IncorrectNoOfShares ); let expected_commitments = Self::compute_commitments(&shares_and_salts, &self.protocol_id); if expected_commitments != self.other_commitments.get(&sender_id).unwrap().0 { - return Err(BBSPlusError::IncorrectCommitment); + return Err(OTError::IncorrectCommitment); } self.other_shares.insert( sender_id, diff --git a/oblivious_transfer/src/dkls_mul_2p.rs b/oblivious_transfer/src/dkls_mul_2p.rs deleted file mode 100644 index b0b9b069..00000000 --- a/oblivious_transfer/src/dkls_mul_2p.rs +++ /dev/null @@ -1,580 +0,0 @@ -//! Two party multiplication. Implements 2 variants, single where each party has 1 input and -//! batch multiplication where each party has multiple inputs. -//! The single multiplication is based on the protocol 5 of the paper [Secure Two-party Threshold ECDSA from ECDSA Assumptions](https://eprint.iacr.org/2018/499) -//! The batch multiplication is based on protocol 1 of the paper [Threshold ECDSA from ECDSA Assumptions: The Multiparty Case](https://eprint.iacr.org/2019/523) -//! Multiplication participants are called Party1 and Party2 where Party1 acts as the OT sender and Party2 as the -//! receiver - -use ark_ff::{BigInteger, One, PrimeField, Zero}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{cfg_into_iter, cfg_iter, rand::RngCore, vec::Vec, UniformRand}; - -use digest::Digest; -use itertools::Itertools; - -use crate::Bit; -use dock_crypto_utils::concat_slices; -use dock_crypto_utils::hashing_utils::field_elem_from_try_and_incr; - -#[cfg(feature = "parallel")] -use rayon::prelude::*; -use crate::configs::OTEConfig; -use crate::kos_ote::{OTExtensionReceiverSetup, OTExtensionSenderSetup}; -use crate::simplest_ot::OneOfTwoROTSenderKeys; - -#[derive(Clone, Debug, PartialEq, Copy, CanonicalSerialize, CanonicalDeserialize)] -pub struct MultiplicationOTEParams {} - -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct GadgetVector( - pub MultiplicationOTEParams, - pub Vec, -); - -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct GadgetVectorForBatchMultiplication( - pub MultiplicationOTEParams, - pub Vec, -); - -impl MultiplicationOTEParams { - pub const fn num_base_ot(&self) -> u16 { - kappa - } - - pub const fn num_extensions(&self) -> usize { - 2 * (kappa as usize + s as usize) - } - - pub const fn overhead(&self) -> usize { - kappa as usize + 2 * s as usize - } -} - -impl GadgetVector { - pub fn new(ote_params: MultiplicationOTEParams, label: &[u8]) -> Self { - let mut g = Vec::with_capacity(ote_params.num_extensions()); - g.push(F::one()); - for i in 1..ote_params.num_base_ot() { - g.push(g[i as usize - 1].double()) - } - let prefix = concat_slices!(label, b"-"); - for i in 0..ote_params.overhead() { - g.push(field_elem_from_try_and_incr::(&concat_slices!( - prefix, - &i.to_be_bytes() - ))) - } - Self(ote_params, g) - } -} - -impl - GadgetVectorForBatchMultiplication -{ - pub fn new(ote_params: MultiplicationOTEParams, label: &[u8]) -> Self { - let overhead = ote_params.overhead(); - let mut g = Vec::with_capacity(overhead as usize); - let prefix = concat_slices!(label, b"-"); - for i in 0..overhead { - g.push(field_elem_from_try_and_incr::(&concat_slices!( - prefix, - &i.to_be_bytes() - ))) - } - Self(ote_params, g) - } -} - -/// Acts as sender in OT extension -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Party1 { - pub ote_params: MultiplicationOTEParams, - pub alpha: F, - pub alpha_hat: F, - pub ot_ext_sender_setup: OTExtensionSenderSetup -} - -/// Acts as receiver in OT extension -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Party2 { - pub ote_params: MultiplicationOTEParams, - pub beta: F, - pub encoded_beta: Vec, - pub ot_ext_receiver_setup: OTExtensionReceiverSetup -} - -/// Acts as sender in OT extension -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Party1ForBatchMultiplication { - pub batch_size: usize, - pub ote_params: MultiplicationOTEParams, - pub a: Vec, - pub a_hat: Vec, - pub a_tilde: Vec, -} - -/// Acts as receiver in OT extension -#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] -pub struct Party2ForBatchMultiplication { - pub batch_size: usize, - pub ote_params: MultiplicationOTEParams, - pub b: Vec, - pub b_tilde: Vec, - pub beta: Vec, -} - -impl Party1 { - pub fn new( - rng: &mut R, - alpha: F, - ote_params: MultiplicationOTEParams, - ) -> Self { - let alpha_hat = F::rand(rng); - Self { - alpha, - alpha_hat, - ote_params, - } - } - - pub fn get_ote_correlation(&self) -> Vec<(F, F)> { - cfg_into_iter!(0..self.ote_params.num_extensions()) - .map(|_| (self.alpha.clone(), self.alpha_hat.clone())) - .collect() - } -} - -impl Party2 { - pub fn new( - rng: &mut R, - beta: F, - base_ot_keys: OneOfTwoROTSenderKeys, - ote_params: MultiplicationOTEParams, - gadget_vector: &GadgetVector, - ) -> Self { - assert_eq!(ote_params, gadget_vector.0); - let extended_ot_count = ote_params.num_extensions(); - let ote_config = OTEConfig::new(ote_params.num_base_ot(), extended_ot_count).unwrap(); - let encoded_beta = Self::encode(rng, beta, gadget_vector); - // TODO: Remove unwrap - let (ext_receiver_setup, U, rlc) = OTExtensionReceiverSetup::new::( - rng, - ote_config, - encoded_beta.clone(), - base_ot_keys, - ) - .unwrap(); - Self { - ote_params, - beta, - encoded_beta, - } - } - - /// Assumes gadget vector has correct OTE params. - pub fn encode( - rng: &mut R, - element: F, - gadget: &GadgetVector, - ) -> Vec { - let mut gamma = (0..gadget.0.overhead()) - .map(|_| bool::rand(rng)) - .collect::>(); - let inner_product = cfg_iter!(gamma) - .enumerate() - .map(|(i, gm)| { - gadget.1[gadget.0.num_base_ot() as usize + i] * { - if *gm { - F::one() - } else { - F::zero() - } - } - }) - .sum::(); - let mut encoded = (element - inner_product).into_bigint().to_bits_le(); - encoded.append(&mut gamma); - encoded - } -} - -impl Party1ForBatchMultiplication { - pub fn new( - rng: &mut R, - a: Vec, - ote_params: MultiplicationOTEParams, - ) -> Self { - let batch_size = a.len(); - let a_hat = (0..batch_size).map(|_| F::rand(rng)).collect::>(); - let a_tilde = (0..batch_size).map(|_| F::rand(rng)).collect::>(); - Self { - batch_size, - a, - a_hat, - a_tilde, - ote_params, - } - } - - pub fn get_ote_correlation(&self) -> Vec<(F, F)> { - let overhead = self.ote_params.overhead(); - cfg_into_iter!(0..self.batch_size) - .flat_map(|i| { - cfg_into_iter!(0..overhead) - .map(|_| (self.a_tilde[i].clone(), self.a_hat[i].clone())) - .collect::>() - }) - .collect() - } -} - -impl Party2ForBatchMultiplication { - pub fn new( - rng: &mut R, - b: Vec, - ote_params: MultiplicationOTEParams, - gadget_vector: &GadgetVectorForBatchMultiplication, - ) -> Self { - let batch_size = b.len(); - let overhead = ote_params.overhead(); - let beta = (0..batch_size * overhead as usize) - .map(|_| bool::rand(rng)) - .collect::>(); - Self::new_with_given_ote_choices(b, beta, ote_params, gadget_vector) - } - - /// Same as `Self::new` except the choices used in OT extension are provided by the caller and - /// not generated internally - pub fn new_with_given_ote_choices( - b: Vec, - beta: Vec, - ote_params: MultiplicationOTEParams, - gadget_vector: &GadgetVectorForBatchMultiplication, - ) -> Self { - assert_eq!(ote_params, gadget_vector.0); - let batch_size = b.len(); - let overhead = ote_params.overhead() as usize; - let b_tilde = cfg_into_iter!(0..batch_size) - .map(|i| { - cfg_iter!(beta[i * overhead..((i + 1) * overhead)]) - .enumerate() - .map(|(j, gm)| { - gadget_vector.1[j] * { - if *gm { - F::one() - } else { - F::zero() - } - } - }) - .sum::() - }) - .collect(); - Self { - batch_size, - ote_params, - b, - b_tilde, - beta, - } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::alsz_ote::tests::do_1_of_2_base_ot; - use crate::kos_ote::{OTExtensionReceiverSetup, OTExtensionSenderSetup}; - - use crate::configs::OTEConfig; - use ark_bls12_381::Bls12_381; - use ark_ec::pairing::Pairing; - use ark_ff::Field; - use ark_std::rand::{rngs::StdRng, SeedableRng}; - use ark_std::UniformRand; - use blake2::Blake2b512; - - type Fr = ::ScalarField; - - #[test] - fn two_party_multiplication() { - let mut rng = StdRng::seed_from_u64(0u64); - let B = ::G1Affine::rand(&mut rng); - - fn check( - rng: &mut StdRng, - alpha: Fr, - beta: Fr, - ote_params: MultiplicationOTEParams, - gadget_vector: &GadgetVector, - B: &::G1Affine, - ) { - // Perform base OT with roles reversed - // TODO: Do VSOT - let (base_ot_choices, base_ot_sender_keys, base_ot_receiver_keys) = - do_1_of_2_base_ot::(rng, ote_params.num_base_ot(), B); - - // party1 will act as sender and party2 as receiver in the OT extension - let party1 = Party1::new::(rng, alpha, ote_params); - let party2 = Party2::new(rng, beta, base_ot_sender_keys, ote_params, &gadget_vector); - - // Perform OT extension - let extended_ot_count = ote_params.num_extensions(); - let ote_config = OTEConfig::new(ote_params.num_base_ot(), extended_ot_count).unwrap(); - let (ext_receiver_setup, U, rlc) = OTExtensionReceiverSetup::new::<_, SSP>( - rng, - ote_config, - party2.encoded_beta.clone(), - base_ot_sender_keys, - ) - .unwrap(); - - let base_ot_choices = base_ot_choices - .into_iter() - .map(|b| b % 2 != 0) - .collect::>(); - let ext_sender_setup = OTExtensionSenderSetup::new::( - ote_config, - U, - rlc, - base_ot_choices, - base_ot_receiver_keys, - ) - .unwrap(); - - // TODO: Gen by hashing - let chi = Fr::rand(rng); - let chi_hat = Fr::rand(rng); - - // Do actions by party1 - let correlations = party1.get_ote_correlation(); - let (t_A, tau) = ext_sender_setup.transfer::(correlations.clone()); - let r = cfg_iter!(t_A) - .map(|(t_A_i, t_A_hat_i)| chi * t_A_i + chi_hat * t_A_hat_i) - .collect::>(); - let u = chi * party1.alpha + chi_hat * party1.alpha_hat; - let share_A = cfg_iter!(t_A) - .enumerate() - .map(|(i, (t_A_i, _))| t_A_i * &gadget_vector.1[i]) - .sum::(); - - // Do actions by party2 - let t_B = ext_receiver_setup.receive::(tau); - let res = cfg_iter!(t_B) - .zip(cfg_into_iter!(r)) - .enumerate() - .try_for_each(|(i, ((t_B_i, t_B_hat_i), r_i))| { - let u_j = if party2.encoded_beta[i] { - Fr::one() * u - } else { - Fr::zero() * u - }; - let rhs = u_j - r_i; - if ((chi * t_B_i) + (chi_hat * t_B_hat_i)) == rhs { - Ok(()) - } else { - Err(()) - } - }); - assert!(res.is_ok()); - - for (i, b) in party2.encoded_beta.iter().enumerate() { - if *b { - assert_eq!(correlations[i].0 - t_A[i].0, t_B[i].0); - assert_eq!(correlations[i].1 - t_A[i].1, t_B[i].1); - } else { - assert_eq!(t_A[i].0, -t_B[i].0); - assert_eq!(t_A[i].1, -t_B[i].1); - } - } - - let share_B = cfg_iter!(t_B) - .enumerate() - .map(|(i, (t_B_i, _))| t_B_i * &gadget_vector.1[i]) - .sum::(); - assert_eq!(share_A + share_B, alpha * beta); - } - - const kappa: u16 = 256; - const s: u16 = 80; - let ote_params = MultiplicationOTEParams:: {}; - let gadget_vector = GadgetVector::::new::(ote_params, b"test"); - assert_eq!(gadget_vector.1.len(), ote_params.num_extensions()); - for i in 0..ote_params.num_base_ot() as usize { - assert_eq!(gadget_vector.1[i], Fr::from(2u64).pow(&[i as u64])); - } - let alpha = Fr::rand(&mut rng); - let beta = Fr::rand(&mut rng); - check::<16, kappa, s>( - &mut rng, - alpha, - beta, - ote_params, - &gadget_vector, - &B, - ); - } - - #[test] - fn two_party_batch_multiplication() { - let mut rng = StdRng::seed_from_u64(0u64); - let B = ::G1Affine::rand(&mut rng); - - fn check( - rng: &mut StdRng, - a: Vec, - b: Vec, - ote_params: MultiplicationOTEParams, - gadget_vector: &GadgetVectorForBatchMultiplication, - B: &::G1Affine, - ) { - // Perform base OT with roles reversed - // TODO: Do VSOT - let (base_ot_choices, base_ot_sender_keys, base_ot_receiver_keys) = - do_1_of_2_base_ot::(rng, ote_params.num_base_ot(), B); - - let batch_size = a.len(); - - // party1 will act as sender and party2 as receiver in the OT extension - let party1 = Party1ForBatchMultiplication::new(rng, a, ote_params); - let party2 = Party2ForBatchMultiplication::new(rng, b, ote_params, &gadget_vector); - - // Perform OT extension - let extended_ot_count = batch_size * ote_params.overhead(); - let ote_config = OTEConfig::new(ote_params.num_base_ot(), extended_ot_count).unwrap(); - - let (ext_receiver_setup, U, rlc) = OTExtensionReceiverSetup::new::<_, SSP>( - rng, - ote_config, - party2.beta.clone(), - base_ot_sender_keys, - ) - .unwrap(); - - let base_ot_choices = base_ot_choices - .into_iter() - .map(|b| b % 2 != 0) - .collect::>(); - let ext_sender_setup = OTExtensionSenderSetup::new::( - ote_config, - U, - rlc, - base_ot_choices, - base_ot_receiver_keys, - ) - .unwrap(); - - // TODO: Gen by hashing - let chi = vec![Fr::rand(rng); batch_size]; - let chi_hat = vec![Fr::rand(rng); batch_size]; - - // Do actions by party1 - let correlations = party1.get_ote_correlation(); - let (t_A, tau) = ext_sender_setup.transfer::(correlations.clone()); - let overhead = ote_params.overhead(); - assert_eq!(correlations.len(), overhead * batch_size); - assert_eq!(t_A.len(), tau.len()); - assert_eq!(t_A.len(), overhead * batch_size); - - let r = cfg_into_iter!(0..overhead) - .map(|i| { - cfg_into_iter!(0..batch_size) - .map(|j| { - chi[j] * t_A[j * overhead + i].0 + chi_hat[j] * t_A[j * overhead + i].1 - }) - .sum::() - }) - .collect::>(); - let (u, gamma_a) = cfg_into_iter!(0..batch_size) - .map(|i| { - let u_i = chi[i] * party1.a_tilde[i] + chi_hat[i] * party1.a_hat[i]; - let gamma_a_i = party1.a[i] - party1.a_tilde[i]; - (u_i, gamma_a_i) - }) - .collect::>() - .into_iter() - .multiunzip::<(Vec<_>, Vec<_>)>(); - - // Do actions by party2 - let t_B = ext_receiver_setup.receive::(tau); - assert_eq!(t_B.len(), overhead * batch_size); - let res = cfg_into_iter!(0..overhead).try_for_each(|i| { - let mut lhs = cfg_into_iter!(0..batch_size) - .map(|j| { - chi[j] * t_B[j * overhead + i].0 + chi_hat[j] * t_B[j * overhead + i].1 - }) - .sum::(); - lhs += r[i]; - let rhs = cfg_into_iter!(0..batch_size) - .map(|j| { - if party2.beta[j * overhead + i] { - Fr::one() * u[j] - } else { - Fr::zero() * u[j] - } - }) - .sum::(); - if lhs == rhs { - Ok(()) - } else { - Err(()) - } - }); - assert!(res.is_ok()); - - for (i, b) in party2.beta.iter().enumerate() { - if *b { - assert_eq!(correlations[i].0 - t_A[i].0, t_B[i].0); - assert_eq!(correlations[i].1 - t_A[i].1, t_B[i].1); - } else { - assert_eq!(t_A[i].0, -t_B[i].0); - assert_eq!(t_A[i].1, -t_B[i].1); - } - } - // Party 2 generates their multiplication share - let shares_B = cfg_into_iter!(0..batch_size) - .map(|i| { - (party2.b_tilde[i] * gamma_a[i]) - + cfg_into_iter!(0..overhead) - .map(|j| gadget_vector.1[j] * t_B[i * overhead + j].0) - .sum::() - }) - .collect::>(); - let gamma_b = cfg_into_iter!(0..batch_size) - .map(|i| party2.b[i] - party2.b_tilde[i]) - .collect::>(); - - // Party 1 generates their multiplication share - let shares_A = cfg_into_iter!(0..batch_size) - .map(|i| { - (party1.a[i] * gamma_b[i]) - + cfg_into_iter!(0..overhead) - .map(|j| gadget_vector.1[j] * t_A[i * overhead + j].0) - .sum::() - }) - .collect::>(); - - // Check if shares are correct - for i in 0..batch_size { - assert_eq!(shares_A[i] + shares_B[i], party1.a[i] * party2.b[i]); - } - } - - let batch_size = 8; - const kappa: u16 = 256; - const s: u16 = 80; - let ote_params = MultiplicationOTEParams:: {}; - let gadget_vector = GadgetVectorForBatchMultiplication::::new::( - ote_params, b"test", - ); - let a = (0..batch_size) - .map(|_| Fr::rand(&mut rng)) - .collect::>(); - let b = (0..batch_size) - .map(|_| Fr::rand(&mut rng)) - .collect::>(); - - check::<16, kappa, s>(&mut rng, a, b, ote_params, &gadget_vector, &B); - } -} diff --git a/oblivious_transfer/src/error.rs b/oblivious_transfer/src/error.rs index 23640304..c4f777ea 100644 --- a/oblivious_transfer/src/error.rs +++ b/oblivious_transfer/src/error.rs @@ -1,3 +1,4 @@ +use crate::ParticipantId; use schnorr_pok::error::SchnorrError; use serde::Serialize; @@ -39,6 +40,28 @@ pub enum OTError { IncorrectCorrelationTagSize(usize, usize), InvalidSchnorrProof, SchnorrError(SchnorrError), + NotABaseOTSender(ParticipantId), + NotABaseOTReceiver(ParticipantId), + AlreadyHaveSenderPubkeyFrom(ParticipantId), + SenderIdCannotBeSameAsSelf(ParticipantId, ParticipantId), + AlreadyHaveReceiverPubkeyFrom(ParticipantId), + ReceiverNotReadyForChallengeFrom(ParticipantId), + AlreadyHaveChallengesFrom(ParticipantId), + SenderEitherNotReadyForResponseOrAlreadySentIt(ParticipantId), + ReceiverEitherNotReadyForHashedKeysOrAlreadyVerifiedIt(ParticipantId), + MissingOTReceiverFor(ParticipantId), + MissingOTSenderFor(ParticipantId), + NotAMultiplicationParty2(ParticipantId), + NotAMultiplicationParty1(ParticipantId), + AlreadyHaveCommitmentFromParticipant(ParticipantId), + IncorrectNoOfCommitments(usize, usize), + IncorrectNoOfShares(usize, usize), + MissingCommitmentFromParticipant(ParticipantId), + AlreadyHaveSharesFromParticipant(ParticipantId), + IncorrectCommitment, + UnexpectedParticipant(ParticipantId), + MissingSharesFromParticipant(ParticipantId), + ParticipantCannotBePresentInOthers(ParticipantId), } impl From for OTError { diff --git a/oblivious_transfer/src/lib.rs b/oblivious_transfer/src/lib.rs index ef4ecaf1..2ed9c77e 100644 --- a/oblivious_transfer/src/lib.rs +++ b/oblivious_transfer/src/lib.rs @@ -1,11 +1,31 @@ +//! # Oblivious Transfer (OT), Oblivious Transfer Extensions (OTE) and multi-party protocols based on that. +//! +//! ## Oblivious Transfer protocols +//! +//! 1. [Simplest OT protocol](./src/base_ot/simplest_ot.rs) +//! 2. [Naor Pinkas OT](./src/base_ot/naor_pinkas_ot.rs) +//! 3. [Endemic OT](./src/base_ot/endemic_ot.rs) +//! +//! ## Oblivious Transfer Extensions +//! 1. [ALSZ](./src/ot_extensions/alsz_ote.rs) +//! 2. [KOS](./src/ot_extensions/kos_ote.rs) +//! +//! ## Oblivious Transfer based multiplication +//! 1. [DKLS18](./src/ot_based_multiplication/dkls18_mul_2p.rs) - 2 party multiplication of where each party has a single input +//! 2. [DKLS19](./src/ot_based_multiplication/dkls19_batch_mul_2p.rs) - 2 party batch-multiplication of where each party has multiple inputs, say `n` inputs and those inputs will be multiplied, i.e. a total of `2*n` multiplications will be done with each being between 2 inputs +//! + #![cfg_attr(not(feature = "std"), no_std)] #![allow(non_snake_case)] pub mod error; pub mod base_ot; +pub mod cointoss; +/// 2-party and multi-party multiplication protocols built on Oblivious Transfer (OT) pub mod ot_based_multiplication; pub mod ot_extensions; +pub mod zero_sharing; pub mod configs; @@ -20,7 +40,8 @@ pub type Key = Vec; pub type Bit = bool; pub type Message = Vec; -/// A bit matrix stored in row-major order +/// A bit matrix stored in row-major order, i.e. the first byte has the first 8 bits, second byte has +/// next 8 bits, and so on. #[derive( Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, )] diff --git a/bbs_plus/src/threshold/base_ot_phase.rs b/oblivious_transfer/src/ot_based_multiplication/base_ot_multi_party_pairwise.rs similarity index 82% rename from bbs_plus/src/threshold/base_ot_phase.rs rename to oblivious_transfer/src/ot_based_multiplication/base_ot_multi_party_pairwise.rs index eaea8f45..500f7c5d 100644 --- a/bbs_plus/src/threshold/base_ot_phase.rs +++ b/oblivious_transfer/src/ot_based_multiplication/base_ot_multi_party_pairwise.rs @@ -1,7 +1,14 @@ //! Each pair of participants must run a base OT among themselves and stores the OT receiver choices and //! the output, i.e sender and receiver keys. This needs to be done only once unless they are lost or compromised. -use crate::error::BBSPlusError; +use crate::{ + base_ot::simplest_ot::{ + Challenges, HashedKey, OneOfTwoROTSenderKeys, ROTReceiverKeys, ROTSenderSetup, + ReceiverPubKeys, Responses, SenderPubKey, VSROTChallenger, VSROTResponder, + }, + error::OTError, + Bit, ParticipantId, +}; use ark_ec::AffineRepr; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ @@ -11,13 +18,6 @@ use ark_std::{ UniformRand, }; use digest::Digest; -use oblivious_transfer_protocols::{ - base_ot::simplest_ot::{ - Challenges, HashedKey, OneOfTwoROTSenderKeys, ROTReceiverKeys, ROTSenderSetup, - ReceiverPubKeys, Responses, SenderPubKey, VSROTChallenger, VSROTResponder, - }, - Bit, ParticipantId, -}; use schnorr_pok::discrete_log::PokDiscreteLog; use serde::{Deserialize, Serialize}; @@ -27,7 +27,7 @@ use serde::{Deserialize, Serialize}; Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, )] #[serde(bound = "")] -pub struct BaseOTPhase { +pub struct Participant { pub id: ParticipantId, /// Number of base OTs to perform pub count: u16, @@ -41,10 +41,11 @@ pub struct BaseOTPhase { pub receiver_responder: BTreeMap, } +/// Output of base OT run between each pair of participants of the multi-party multiplication protocol #[derive( Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, )] -pub struct BaseOTPhaseOutput { +pub struct BaseOTOutput { pub id: ParticipantId, pub sender_keys: BTreeMap, pub receiver: BTreeMap, ROTReceiverKeys)>, @@ -56,14 +57,14 @@ pub struct BaseOTPhaseOutput { #[serde(bound = "")] pub struct SenderPubKeyAndProof(SenderPubKey, PokDiscreteLog); -impl BaseOTPhase { +impl Participant { pub fn init( rng: &mut R, id: ParticipantId, others: BTreeSet, num_base_ot: u16, B: &G, - ) -> Result<(Self, BTreeMap>), BBSPlusError> { + ) -> Result<(Self, BTreeMap>), OTError> { let mut base_ot_sender_setup = BTreeMap::new(); let mut base_ot_receiver_choices = BTreeMap::new(); let mut base_ot_s = BTreeMap::new(); @@ -101,18 +102,18 @@ impl BaseOTPhase { sender_id: ParticipantId, sender_pk_and_proof: SenderPubKeyAndProof, B: &G, - ) -> Result, BBSPlusError> { + ) -> Result, OTError> { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if self.id < sender_id { - return Err(BBSPlusError::NotABaseOTSender(sender_id)); + return Err(OTError::NotABaseOTSender(sender_id)); } if !self.receiver_choices.contains_key(&sender_id) { - return Err(BBSPlusError::NotABaseOTSender(sender_id)); + return Err(OTError::NotABaseOTSender(sender_id)); } if self.receiver_keys.contains_key(&sender_id) { - return Err(BBSPlusError::AlreadyHaveSenderPubkeyFrom(sender_id)); + return Err(OTError::AlreadyHaveSenderPubkeyFrom(sender_id)); } let SenderPubKeyAndProof(S, proof) = sender_pk_and_proof; let (receiver_keys, pub_key) = ROTReceiverKeys::new_verifiable::<_, _, D, KEY_SIZE>( @@ -131,18 +132,18 @@ impl BaseOTPhase { &mut self, sender_id: ParticipantId, R: ReceiverPubKeys, - ) -> Result { + ) -> Result { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if self.id > sender_id { - return Err(BBSPlusError::NotABaseOTReceiver(sender_id)); + return Err(OTError::NotABaseOTReceiver(sender_id)); } if self.sender_keys.contains_key(&sender_id) { - return Err(BBSPlusError::AlreadyHaveReceiverPubkeyFrom(sender_id)); + return Err(OTError::AlreadyHaveReceiverPubkeyFrom(sender_id)); } if !self.sender_setup.contains_key(&sender_id) { - return Err(BBSPlusError::NotABaseOTReceiver(sender_id)); + return Err(OTError::NotABaseOTReceiver(sender_id)); } if let Some(sender_setup) = self.sender_setup.get(&sender_id) { let sender_keys = @@ -152,7 +153,7 @@ impl BaseOTPhase { self.sender_keys.insert(sender_id, sender_keys); Ok(challenges) } else { - Err(BBSPlusError::NotABaseOTReceiver(sender_id)) + Err(OTError::NotABaseOTReceiver(sender_id)) } } @@ -160,15 +161,15 @@ impl BaseOTPhase { &mut self, sender_id: ParticipantId, challenges: Challenges, - ) -> Result { + ) -> Result { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if self.id < sender_id { - return Err(BBSPlusError::NotABaseOTSender(sender_id)); + return Err(OTError::NotABaseOTSender(sender_id)); } if self.receiver_responder.contains_key(&sender_id) { - return Err(BBSPlusError::AlreadyHaveChallengesFrom(sender_id)); + return Err(OTError::AlreadyHaveChallengesFrom(sender_id)); } if let Some(receiver_keys) = self.receiver_keys.get(&sender_id) { let (receiver_responder, responses) = VSROTResponder::new( @@ -180,7 +181,7 @@ impl BaseOTPhase { .insert(sender_id, receiver_responder); Ok(responses) } else { - Err(BBSPlusError::ReceiverNotReadyForChallengeFrom(sender_id)) + Err(OTError::ReceiverNotReadyForChallengeFrom(sender_id)) } } @@ -188,18 +189,20 @@ impl BaseOTPhase { &mut self, sender_id: ParticipantId, responses: Responses, - ) -> Result, BBSPlusError> { + ) -> Result, OTError> { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if self.id > sender_id { - return Err(BBSPlusError::NotABaseOTReceiver(sender_id)); + return Err(OTError::NotABaseOTReceiver(sender_id)); } if let Some(sender_challenger) = self.sender_challenger.remove(&sender_id) { let hashed_keys = sender_challenger.verify_responses(responses)?; Ok(hashed_keys) } else { - Err(BBSPlusError::SenderEitherNotReadyForResponseOrAlreadySentIt(sender_id)) + Err(OTError::SenderEitherNotReadyForResponseOrAlreadySentIt( + sender_id, + )) } } @@ -207,29 +210,29 @@ impl BaseOTPhase { &mut self, sender_id: ParticipantId, hashed_keys: Vec<(HashedKey, HashedKey)>, - ) -> Result<(), BBSPlusError> { + ) -> Result<(), OTError> { if self.id == sender_id { - return Err(BBSPlusError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); + return Err(OTError::SenderIdCannotBeSameAsSelf(sender_id, self.id)); } if self.id < sender_id { - return Err(BBSPlusError::NotABaseOTSender(sender_id)); + return Err(OTError::NotABaseOTSender(sender_id)); } if let Some(receiver_responder) = self.receiver_responder.remove(&sender_id) { receiver_responder.verify_sender_hashed_keys(hashed_keys)?; Ok(()) } else { - Err(BBSPlusError::ReceiverEitherNotReadyForHashedKeysOrAlreadyVerifiedIt(sender_id)) + Err(OTError::ReceiverEitherNotReadyForHashedKeysOrAlreadyVerifiedIt(sender_id)) } } - pub fn finish(mut self) -> BaseOTPhaseOutput { + pub fn finish(mut self) -> BaseOTOutput { // TODO: Ensure keys from everyone let mut base_ot_receiver = BTreeMap::new(); for (id, choice) in self.receiver_choices { let keys = self.receiver_keys.remove(&id).unwrap(); base_ot_receiver.insert(id, (choice, keys)); } - BaseOTPhaseOutput { + BaseOTOutput { id: self.id, sender_keys: self.sender_keys, receiver: base_ot_receiver, @@ -262,12 +265,12 @@ pub mod tests { } } - pub fn do_base_ot_for_threshold_sig( + pub fn do_pairwise_base_ot( rng: &mut StdRng, num_base_ot: u16, num_parties: u16, all_party_set: BTreeSet, - ) -> Vec { + ) -> Vec { let B = ::G1Affine::rand(rng); let mut base_ots = vec![]; let mut sender_pks = BTreeMap::new(); @@ -277,7 +280,7 @@ pub mod tests { let mut others = all_party_set.clone(); others.remove(&i); let (base_ot, sender_pk_and_proof) = - BaseOTPhase::init::<_, Blake2b512>(rng, i, others, num_base_ot, &B).unwrap(); + Participant::init::<_, Blake2b512>(rng, i, others, num_base_ot, &B).unwrap(); base_ots.push(base_ot); sender_pks.insert(i, sender_pk_and_proof); } @@ -341,14 +344,14 @@ pub mod tests { } #[test] - fn base_ot_for_threshold_sig() { + fn base_ot_pairwise() { let mut rng = StdRng::seed_from_u64(0u64); 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_pairwise_base_ot::<16>(&mut rng, num_base_ot, num_parties, all_party_set); } } } diff --git a/oblivious_transfer/src/ot_based_multiplication/batch_mul_multi_party.rs b/oblivious_transfer/src/ot_based_multiplication/batch_mul_multi_party.rs new file mode 100644 index 00000000..a91bac0e --- /dev/null +++ b/oblivious_transfer/src/ot_based_multiplication/batch_mul_multi_party.rs @@ -0,0 +1,368 @@ +//! A multi-party multiplication protocol based on 2-party multiplication from DKLS19. +//! This assumes that each party has already run an OT protocol with other. +//! This is described as part of protocol 4.1 of the paper [Threshold BBS+ Signatures for Distributed Anonymous Credential Issuance](https://eprint.iacr.org/2023/602) + +use crate::{ + error::OTError, + ot_based_multiplication::{ + base_ot_multi_party_pairwise::BaseOTOutput, + dkls18_mul_2p::MultiplicationOTEParams, + dkls19_batch_mul_2p::{GadgetVector, MaskedInputs, Party1, Party2, RLC}, + }, + ot_extensions::kos_ote::CorrelationTag, + BitMatrix, ParticipantId, +}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ + collections::{BTreeMap, BTreeSet}, + rand::RngCore, + vec::Vec, +}; +use digest::DynDigest; +use dock_crypto_utils::transcript::MerlinTranscript; +use itertools::interleave; + +/// The participant will acts as +/// - a receiver in OT extension, also called Party2 in multiplication protocol, and its id is less than other participant +/// - a sender in OT extension, also called Party1 in multiplication protocol, and its id is greater than other participant +#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] +pub struct Participant { + pub id: ParticipantId, + /// Number of multiplications done in a single batch. + pub batch_size: u32, + /// Transcripts to record protocol interactions with each participant and later used to generate random challenges + pub transcripts: BTreeMap, + pub ote_params: MultiplicationOTEParams, + /// Map where this participant plays the role of sender, i.e. Party1 + pub multiplication_party1: + BTreeMap>, + /// Map where this participant plays the role of receiver, i.e. Party2 + pub multiplication_party2: + BTreeMap>, + pub z_A: BTreeMap, Vec)>, + pub z_B: BTreeMap, Vec)>, +} + +/// Message sent from Party2 to Party1 of multiplication protocol +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Message1( + BitMatrix, + crate::ot_extensions::kos_ote::RLC, + MaskedInputs, +); + +/// Message sent from Party1 to Party2 of multiplication protocol. This message is created after Party1 processes `Message1` +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Message2(CorrelationTag, RLC, MaskedInputs); + +/// A participant's output on completion of the multiplication protocol +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct ParticipantOutput { + pub z_A: BTreeMap, Vec)>, + pub z_B: BTreeMap, Vec)>, +} + +impl + Participant +{ + pub fn init( + rng: &mut R, + id: ParticipantId, + x: Vec, + y: Vec, + mut base_ot_output: BaseOTOutput, + others: BTreeSet, + ote_params: MultiplicationOTEParams, + gadget_vector: &GadgetVector, + label: &'static [u8], + ) -> Result<(Self, BTreeMap>), OTError> { + assert_eq!(x.len(), y.len()); + let batch_size = x.len() as u32; + + let mut transcripts = BTreeMap::::new(); + let mut multiplication_party1 = + BTreeMap::>::new(); + let mut multiplication_party2 = + BTreeMap::>::new(); + let mut Us = BTreeMap::new(); + + // When an OT extension receiver, generate input to multiplication as `[x[0], y[0], x[1], y[1], x[2], y[2], ...` + let mult_when_ot_recv = interleave(x.clone(), y.clone()).collect::>(); + // When an OT extension sender, generate input to multiplication as `[y[0], x[0], y[1], x[1], y[2], x[2], ...` + let mult_when_ot_sendr = interleave(y.clone(), x.clone()).collect::>(); + + for other in others { + let mut trans = MerlinTranscript::new(label); + if id > other { + if let Some((base_ot_choices, base_ot_keys)) = + base_ot_output.receiver.remove(&other) + { + let party1 = Party1::new( + rng, + mult_when_ot_recv.clone(), + base_ot_choices, + base_ot_keys, + ote_params, + )?; + multiplication_party1.insert(other, party1); + } else { + return Err(OTError::MissingOTReceiverFor(other)); + } + } else { + if let Some(base_ot_keys) = base_ot_output.sender_keys.remove(&other) { + let (party2, U, rlc, gamma) = Party2::new( + rng, + mult_when_ot_sendr.clone(), + base_ot_keys, + &mut trans, + ote_params, + &gadget_vector, + )?; + multiplication_party2.insert(other, party2); + Us.insert(other, Message1(U, rlc, gamma)); + } else { + return Err(OTError::MissingOTSenderFor(other)); + } + } + transcripts.insert(other, trans); + } + Ok(( + Self { + id, + batch_size, + transcripts, + ote_params, + multiplication_party1, + multiplication_party2, + z_A: Default::default(), + z_B: Default::default(), + }, + Us, + )) + } + + /// Process received message from Party2 of multiplication protocol + pub fn receive_message1( + &mut self, + sender_id: ParticipantId, + message: Message1, + gadget_vector: &GadgetVector, + ) -> Result, OTError> { + if self.multiplication_party2.contains_key(&sender_id) { + return Err(OTError::NotAMultiplicationParty2(sender_id)); + } + if !self.multiplication_party1.contains_key(&sender_id) { + return Err(OTError::NotAMultiplicationParty1(sender_id)); + } + let Message1(U, rlc, gamma) = message; + let party1 = self.multiplication_party1.remove(&sender_id).unwrap(); + let trans = self.transcripts.get_mut(&sender_id).unwrap(); + + let (shares, tau, r, gamma_a) = + party1.receive::(U, rlc, gamma, trans, &gadget_vector)?; + debug_assert_eq!(shares.len() as u32, 2 * self.batch_size); + let mut z_A_0 = Vec::with_capacity(self.batch_size as usize); + let mut z_A_1 = Vec::with_capacity(self.batch_size as usize); + for (i, share) in shares.0.into_iter().enumerate() { + if (i & 1) == 0 { + z_A_0.push(share); + } else { + z_A_1.push(share); + } + } + self.z_A.insert(sender_id, (z_A_0, z_A_1)); + Ok(Message2(tau, r, gamma_a)) + } + + /// Process received message from Party1 of multiplication protocol + pub fn receive_message2( + &mut self, + sender_id: ParticipantId, + message: Message2, + gadget_vector: &GadgetVector, + ) -> Result<(), OTError> { + if self.multiplication_party1.contains_key(&sender_id) { + return Err(OTError::NotAMultiplicationParty1(sender_id)); + } + if !self.multiplication_party2.contains_key(&sender_id) { + return Err(OTError::NotAMultiplicationParty2(sender_id)); + } + let Message2(tau, rlc, gamma) = message; + let party2 = self.multiplication_party2.remove(&sender_id).unwrap(); + let trans = self.transcripts.get_mut(&sender_id).unwrap(); + let shares = party2.receive::(tau, rlc, gamma, trans, &gadget_vector)?; + debug_assert_eq!(shares.len() as u32, 2 * self.batch_size); + let mut z_B_0 = Vec::with_capacity(self.batch_size as usize); + let mut z_B_1 = Vec::with_capacity(self.batch_size as usize); + for (i, share) in shares.0.into_iter().enumerate() { + if (i & 1) == 0 { + z_B_0.push(share); + } else { + z_B_1.push(share); + } + } + self.z_B.insert(sender_id, (z_B_0, z_B_1)); + Ok(()) + } + + pub fn finish(self) -> ParticipantOutput { + ParticipantOutput { + z_A: self.z_A, + z_B: self.z_B, + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bls12_381::Fr; + use std::time::Instant; + + use crate::ot_based_multiplication::{ + base_ot_multi_party_pairwise::tests::do_pairwise_base_ot, + dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector, + }; + use ark_std::{ + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + + #[test] + fn multi_party_multiplication() { + 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_pairwise_base_ot::( + 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![]; + + let label = b"mutliparty multiplication"; + // Initiate multiplication phase and each party sends messages to others + let start = Instant::now(); + for i in 1..=threshold { + let mut others = threshold_party_set.clone(); + others.remove(&i); + // Create 2 random vectors whose elements will be multiplied pairwise + let a = (0..batch_size).map(|_| Fr::rand(rng)).collect::>(); + let b = (0..batch_size).map(|_| Fr::rand(rng)).collect::>(); + let start = Instant::now(); + let (phase, U) = Participant::init( + rng, + i, + a.clone(), + b.clone(), + base_ot_outputs[i as usize - 1].clone(), + others, + ote_params, + &gadget_vector, + label, + ) + .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(); + assert_eq!(z_A.0.len() as u32, batch_size); + assert_eq!(z_B.0.len() as u32, batch_size); + 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/oblivious_transfer/src/ot_based_multiplication/dkls19_batch_mul_2p.rs b/oblivious_transfer/src/ot_based_multiplication/dkls19_batch_mul_2p.rs index ff485f7c..cd5885cb 100644 --- a/oblivious_transfer/src/ot_based_multiplication/dkls19_batch_mul_2p.rs +++ b/oblivious_transfer/src/ot_based_multiplication/dkls19_batch_mul_2p.rs @@ -564,7 +564,7 @@ pub mod tests { GadgetVector::::new::(ote_params, b"test-gadget-vector"); test_serialization!(GadgetVector, gadget_vector); - let mut checked = false; + let mut checked_serialization = false; for batch_size in [2, 4, 8, 20, 40, 80] { let a = (0..batch_size) .map(|_| Fr::rand(&mut rng)) @@ -572,8 +572,16 @@ pub mod tests { let b = (0..batch_size) .map(|_| Fr::rand(&mut rng)) .collect::>(); - check::<128, KAPPA, SSP>(&mut rng, a, b, ote_params, &gadget_vector, &B, !checked); - checked = true; + check::<128, KAPPA, SSP>( + &mut rng, + a, + b, + ote_params, + &gadget_vector, + &B, + !checked_serialization, + ); + checked_serialization = true; } } } diff --git a/oblivious_transfer/src/ot_based_multiplication/mod.rs b/oblivious_transfer/src/ot_based_multiplication/mod.rs index 003c0017..f6667019 100644 --- a/oblivious_transfer/src/ot_based_multiplication/mod.rs +++ b/oblivious_transfer/src/ot_based_multiplication/mod.rs @@ -1,2 +1,6 @@ +//! 2-party and multi-party multiplication protocols built on Oblivious Transfer (OT) + +pub mod base_ot_multi_party_pairwise; +pub mod batch_mul_multi_party; pub mod dkls18_mul_2p; pub mod dkls19_batch_mul_2p; diff --git a/bbs_plus/src/threshold/zero_sharing.rs b/oblivious_transfer/src/zero_sharing.rs similarity index 92% rename from bbs_plus/src/threshold/zero_sharing.rs rename to oblivious_transfer/src/zero_sharing.rs index e6444c1a..82ab0c8c 100644 --- a/bbs_plus/src/threshold/zero_sharing.rs +++ b/oblivious_transfer/src/zero_sharing.rs @@ -1,9 +1,10 @@ //! Generate a secret sharing of 0. Does not use a trusted party or Shamir secret sharing. -//! Called F_zero and described in section 3.1 in the paper +//! Called F_zero and described in section 3.1 in the paper [Threshold BBS+ Signatures for Distributed Anonymous Credential Issuance](https://eprint.iacr.org/2023/602) +use super::ParticipantId; use crate::{ - error::BBSPlusError, - threshold::cointoss::{Commitments, Party as CommitmentParty}, + cointoss::{Commitments, Party as CommitmentParty}, + error::OTError, }; use ark_ff::{ field_hashers::{DefaultFieldHasher, HashToField}, @@ -17,7 +18,6 @@ use ark_std::{ vec::Vec, }; use digest::DynDigest; -use oblivious_transfer_protocols::ParticipantId; #[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct Party { @@ -64,9 +64,9 @@ impl Party { &mut self, sender_id: ParticipantId, commitments: Commitments, - ) -> Result<(), BBSPlusError> { + ) -> Result<(), OTError> { if !self.cointoss_protocols.contains_key(&sender_id) { - return Err(BBSPlusError::UnexpectedParticipant(sender_id)); + return Err(OTError::UnexpectedParticipant(sender_id)); } let protocol = self.cointoss_protocols.get_mut(&sender_id).unwrap(); protocol.receive_commitment(sender_id, commitments)?; @@ -79,9 +79,9 @@ impl Party { &mut self, sender_id: ParticipantId, shares: Vec<(F, [u8; SALT_SIZE])>, - ) -> Result<(), BBSPlusError> { + ) -> Result<(), OTError> { if !self.cointoss_protocols.contains_key(&sender_id) { - return Err(BBSPlusError::UnexpectedParticipant(sender_id)); + return Err(OTError::UnexpectedParticipant(sender_id)); } let protocol = self.cointoss_protocols.get_mut(&sender_id).unwrap(); protocol.receive_shares(sender_id, shares)?; @@ -89,14 +89,12 @@ impl Party { } /// Use the shares received from all parties to create `batch_size` sets of shares of 0 - pub fn compute_zero_shares( - self, - ) -> Result, BBSPlusError> { + pub fn compute_zero_shares(self) -> Result, OTError> { let mut randoness = BTreeMap::>::new(); let mut shares = vec![F::zero(); self.batch_size as usize]; for (id, protocol) in self.cointoss_protocols { if !protocol.has_shares_from(&id) { - return Err(BBSPlusError::MissingSharesFromParticipant(id)); + return Err(OTError::MissingSharesFromParticipant(id)); } randoness.insert(id, protocol.compute_joint_randomness()); } @@ -118,17 +116,17 @@ impl Party { Ok(shares) } - pub fn has_commitment_from(&self, id: &ParticipantId) -> Result { + pub fn has_commitment_from(&self, id: &ParticipantId) -> Result { if !self.cointoss_protocols.contains_key(id) { - return Err(BBSPlusError::UnexpectedParticipant(*id)); + return Err(OTError::UnexpectedParticipant(*id)); } let protocol = self.cointoss_protocols.get(id).unwrap(); Ok(protocol.other_commitments.contains_key(id)) } - pub fn has_shares_from(&self, id: &ParticipantId) -> Result { + pub fn has_shares_from(&self, id: &ParticipantId) -> Result { if !self.cointoss_protocols.contains_key(id) { - return Err(BBSPlusError::UnexpectedParticipant(*id)); + return Err(OTError::UnexpectedParticipant(*id)); } let protocol = self.cointoss_protocols.get(id).unwrap(); Ok(protocol.other_shares.contains_key(id)) diff --git a/proof_system/src/verifier.rs b/proof_system/src/verifier.rs index 49806719..f9bb59ca 100644 --- a/proof_system/src/verifier.rs +++ b/proof_system/src/verifier.rs @@ -1547,6 +1547,7 @@ impl Proof { } } + // If randomized pairing checker was used, verify all its pairing checks if let Some(c) = pairing_checker { if !c.verify() { return Err(ProofSystemError::RandomizedPairingCheckFailed); diff --git a/secret_sharing_and_dkg/src/distributed_dlog_check/semi_honest.rs b/secret_sharing_and_dkg/src/distributed_dlog_check/semi_honest.rs index 52e08542..7ae0baec 100644 --- a/secret_sharing_and_dkg/src/distributed_dlog_check/semi_honest.rs +++ b/secret_sharing_and_dkg/src/distributed_dlog_check/semi_honest.rs @@ -18,9 +18,6 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use zeroize::{Zeroize, ZeroizeOnDrop}; -#[cfg(feature = "parallel")] -use rayon::prelude::*; - /// Commitment to the share of the secret #[serde_as] #[derive( diff --git a/secret_sharing_and_dkg/src/feldman_dvss_dkg.rs b/secret_sharing_and_dkg/src/feldman_dvss_dkg.rs index 5b25bd31..e87a92cf 100644 --- a/secret_sharing_and_dkg/src/feldman_dvss_dkg.rs +++ b/secret_sharing_and_dkg/src/feldman_dvss_dkg.rs @@ -2,19 +2,16 @@ use crate::{ common, - common::{lagrange_basis_at_0, CommitmentToCoefficients, ParticipantId, Share, ShareId}, + common::{CommitmentToCoefficients, ParticipantId, Share, ShareId}, error::SSError, }; use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; use ark_ff::{PrimeField, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{cfg_iter, collections::BTreeMap, vec, vec::Vec}; +use ark_std::{collections::BTreeMap, vec, vec::Vec}; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; -#[cfg(feature = "parallel")] -use rayon::prelude::*; - /// Used by a participant to store received shares and commitment coefficients. #[derive( Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize, diff --git a/secret_sharing_and_dkg/src/shamir_ss.rs b/secret_sharing_and_dkg/src/shamir_ss.rs index a5caf3cb..0ac626af 100644 --- a/secret_sharing_and_dkg/src/shamir_ss.rs +++ b/secret_sharing_and_dkg/src/shamir_ss.rs @@ -2,7 +2,7 @@ use ark_ff::PrimeField; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; -use ark_std::{cfg_into_iter, cfg_iter, rand::RngCore, vec::Vec}; +use ark_std::{cfg_into_iter, rand::RngCore, vec::Vec}; use crate::{ common, diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index f5710aaf..5591775e 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -17,9 +17,10 @@ ark-serialize.workspace = true blake2.workspace = true proof_system = { default-features = false, path = "../proof_system"} kvac = { default-features = false, path = "../kvac"} +oblivious_transfer_protocols = { default-features = false, path = "../oblivious_transfer"} [features] default = ["parallel"] -parallel = ["proof_system/default"] +parallel = ["proof_system/parallel", "kvac/parallel", "oblivious_transfer_protocols/parallel"] wasmer-js = ["proof_system/wasmer-js"] wasmer-sys = ["proof_system/wasmer-sys"] \ No newline at end of file diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 20a49809..5704970a 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -10,3 +10,4 @@ pub mod bbs; #[macro_use] pub mod serialization; pub mod kvac; +pub mod ot; diff --git a/test_utils/src/ot.rs b/test_utils/src/ot.rs new file mode 100644 index 00000000..dd90cf18 --- /dev/null +++ b/test_utils/src/ot.rs @@ -0,0 +1,104 @@ +use ark_bls12_381::Bls12_381; +use ark_ec::pairing::Pairing; +use ark_std::{rand::prelude::StdRng, UniformRand}; +use blake2::Blake2b512; +use oblivious_transfer_protocols::{ + base_ot::simplest_ot::{OneOfTwoROTSenderKeys, ROTReceiverKeys}, + ot_based_multiplication::base_ot_multi_party_pairwise::{ + BaseOTOutput, Participant as BaseOTParty, + }, + Bit, ParticipantId, +}; +use std::collections::{BTreeMap, BTreeSet}; + +pub fn check_base_ot_keys( + choices: &[Bit], + receiver_keys: &ROTReceiverKeys, + sender_keys: &OneOfTwoROTSenderKeys, +) { + for i in 0..sender_keys.len() { + if choices[i] { + assert_eq!(sender_keys.0[i].1, receiver_keys.0[i]); + } else { + assert_eq!(sender_keys.0[i].0, receiver_keys.0[i]); + } + } +} + +pub fn do_pairwise_base_ot( + rng: &mut StdRng, + num_base_ot: u16, + num_parties: u16, + all_party_set: BTreeSet, +) -> Vec { + let B = ::G1Affine::rand(rng); + let mut base_ots = vec![]; + let mut sender_pks = BTreeMap::new(); + let mut receiver_pks = BTreeMap::new(); + + for i in 1..=num_parties { + let mut others = all_party_set.clone(); + others.remove(&i); + let (base_ot, sender_pk_and_proof) = + BaseOTParty::init::<_, Blake2b512>(rng, i, others, num_base_ot, &B).unwrap(); + base_ots.push(base_ot); + sender_pks.insert(i, sender_pk_and_proof); + } + + for (sender_id, pks) in sender_pks { + for (id, pk) in pks { + let recv_pk = base_ots[id as usize - 1] + .receive_sender_pubkey::<_, Blake2b512, KEY_SIZE>(rng, sender_id, pk, &B) + .unwrap(); + receiver_pks.insert((id, sender_id), recv_pk); + } + } + + let mut challenges = BTreeMap::new(); + let mut responses = BTreeMap::new(); + let mut hashed_keys = BTreeMap::new(); + + for ((sender, receiver), pk) in receiver_pks { + let chal = base_ots[receiver as usize - 1] + .receive_receiver_pubkey::(sender, pk) + .unwrap(); + challenges.insert((receiver, sender), chal); + } + + for ((sender, receiver), chal) in challenges { + let resp = base_ots[receiver as usize - 1] + .receive_challenges(sender, chal) + .unwrap(); + responses.insert((receiver, sender), resp); + } + + for ((sender, receiver), resp) in responses { + let hk = base_ots[receiver as usize - 1] + .receive_responses(sender, resp) + .unwrap(); + hashed_keys.insert((receiver, sender), hk); + } + + for ((sender, receiver), hk) in hashed_keys { + base_ots[receiver as usize - 1] + .receive_hashed_keys(sender, hk) + .unwrap() + } + + let mut base_ot_outputs = vec![]; + for b in base_ots { + base_ot_outputs.push(b.finish()); + } + + for base_ot in &base_ot_outputs { + for (other, sender_keys) in &base_ot.sender_keys { + let (choices, rec_keys) = base_ot_outputs[*other as usize - 1] + .receiver + .get(&base_ot.id) + .unwrap(); + assert_eq!(rec_keys.len(), sender_keys.len()); + check_base_ot_keys(&choices, &rec_keys, &sender_keys); + } + } + base_ot_outputs +} diff --git a/utils/src/randomized_pairing_check.rs b/utils/src/randomized_pairing_check.rs index a732a0a1..1f4bd7ae 100644 --- a/utils/src/randomized_pairing_check.rs +++ b/utils/src/randomized_pairing_check.rs @@ -202,7 +202,7 @@ impl RandomizedPairingChecker { /// Verify that all added pairing equations are satisfied. pub fn verify(&self) -> bool { - assert_eq!(self.pending.0.len(), self.pending.1.len()); + debug_assert_eq!(self.pending.0.len(), self.pending.1.len()); let left = if !self.pending.0.is_empty() { let mut p = E::multi_miller_loop(self.pending.0.clone(), self.pending.1.clone()); p.0.mul_assign(self.left.0); diff --git a/vb_accumulator/Cargo.toml b/vb_accumulator/Cargo.toml index a3debe16..beacc55a 100644 --- a/vb_accumulator/Cargo.toml +++ b/vb_accumulator/Cargo.toml @@ -26,15 +26,18 @@ schnorr_pok = { version = "0.18.0", default-features = false, path = "../schnorr dock_crypto_utils = { version = "0.18.0", default-features = false, path = "../utils" } short_group_sig = { version = "0.2.0", default-features = false, path = "../short_group_sig" } kvac = { version = "0.3.0", default-features = false, path = "../kvac" } +oblivious_transfer_protocols = { version = "0.7.0", default-features = false, path = "../oblivious_transfer" } +secret_sharing_and_dkg = { version = "0.11.0", default-features = false, path = "../secret_sharing_and_dkg" } [dev-dependencies] blake2.workspace = true ark-bls12-381.workspace = true serde_json = "1.0" rmp-serde = "1.0" +test_utils = { path = "../test_utils" } [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", "short_group_sig/std", "kvac/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", "kvac/std", "oblivious_transfer_protocols/std", "secret_sharing_and_dkg/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", "short_group_sig/parallel", "kvac/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", "kvac/parallel", "oblivious_transfer_protocols/parallel", "secret_sharing_and_dkg/parallel"] \ No newline at end of file diff --git a/vb_accumulator/src/error.rs b/vb_accumulator/src/error.rs index 0b90c8a2..2f0ffa5b 100644 --- a/vb_accumulator/src/error.rs +++ b/vb_accumulator/src/error.rs @@ -6,7 +6,9 @@ use ark_serialize::SerializationError; use ark_std::fmt::Debug; use dock_crypto_utils::serde_utils::ArkSerializationError; use kvac::error::KVACError; +use oblivious_transfer_protocols::error::OTError; use schnorr_pok::error::SchnorrError; +use secret_sharing_and_dkg::error::SSError; use serde::Serialize; use short_group_sig::error::ShortGroupSigError; @@ -42,6 +44,8 @@ pub enum VBAccumulatorError { ShortGroupSigError(ShortGroupSigError), MismatchBetweenSignatureAndAccumulatorValue, KVACError(KVACError), + SSError(SSError), + OTError(OTError), } impl From for VBAccumulatorError { @@ -67,3 +71,15 @@ impl From for VBAccumulatorError { Self::KVACError(e) } } + +impl From for VBAccumulatorError { + fn from(e: SSError) -> Self { + Self::SSError(e) + } +} + +impl From for VBAccumulatorError { + fn from(e: OTError) -> Self { + Self::OTError(e) + } +} diff --git a/vb_accumulator/src/lib.rs b/vb_accumulator/src/lib.rs index f334a355..76c2a60c 100644 --- a/vb_accumulator/src/lib.rs +++ b/vb_accumulator/src/lib.rs @@ -65,6 +65,7 @@ pub mod proofs_cdh; pub mod proofs_keyed_verification; pub mod setup; pub mod setup_keyed_verification; +pub mod threshold; pub mod universal; pub mod universal_init_constants; pub mod witness; diff --git a/vb_accumulator/src/threshold/mod.rs b/vb_accumulator/src/threshold/mod.rs new file mode 100644 index 00000000..dc49678b --- /dev/null +++ b/vb_accumulator/src/threshold/mod.rs @@ -0,0 +1,731 @@ +//! Accumulator update, witness generation and updated witness generation in a threshold setting, i.e. where the +//! accumulator secret key `alpha` is split among many accumulator managers using Shamir secret sharing. The general idea is: +//! 1. Accumulator value post deletion: Say the current accumulator value is `V` and the deleted element is `y`, +//! then each manager creates shares `R_i = r_i * V` and `u_i = < share of r_i * (y + l_i * alpha_i)>` and sends to the user who +//! then computes `\sum_i{V_i} * 1 / \sum_i{u_i}` to get `V * 1/(y + alpha)`. This also gives the membership witness of `y`. +//! 2. Witness generation: Say the current accumulator value is `V` and the user wants witness of `y` but does not want to +//! reveal `y` to any manager. It gives shares of `y` to the managers such that each manager has `y_i` and `\sum_i{l_i * y_i} = y`. +//! Now each manager shares `R_i = r_i * V` and `u_i = < share of r_i * l_i * (y_i + alpha_i)>` and sends to the user who +//! then computes `\sum_i{V_i} * 1 / \sum_i{u_i}` to get `V * 1/(y + alpha)`. But here the user also needs to prove to each +//! manager that share `y_i` is a valid share of `y` and this `y` is a member of the accumulator `V`. + +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{Field, PrimeField, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{ + collections::{BTreeMap, BTreeSet}, + rand::RngCore, + vec::Vec, +}; +use digest::DynDigest; +use oblivious_transfer_protocols::{ + cointoss::Commitments, error::OTError, + ot_based_multiplication::batch_mul_multi_party::ParticipantOutput as MultOut, zero_sharing, + ParticipantId, +}; +use secret_sharing_and_dkg::error::SSError; + +use crate::error::VBAccumulatorError; + +/// Share created by a manager when `V * 1/ (y + alpha)` needs to computed and each manager knows `y` but +/// only a share of `alpha` +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct ShareOfKnownMember { + pub id: ParticipantId, + pub u: G::ScalarField, + pub R: G, +} + +/// Share created by a manager when `V * 1/ (y + alpha)` needs to computed and no manager knows `y` but +/// only a share of `y` and `alpha` +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct ShareOfSharedMember { + pub id: ParticipantId, + pub u: G::ScalarField, + pub R: G, +} + +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Phase1 { + pub id: ParticipantId, + pub r: F, + /// Protocols to generate shares of 0s. + pub zero_sharing_protocol: zero_sharing::Party, +} + +#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct Phase1Output { + pub id: ParticipantId, + pub r: F, + pub masked_signing_key_shares: F, + pub masked_rs: F, + pub others: Vec, +} + +impl Phase1 { + pub fn get_comm_shares_and_salts_for_zero_sharing_protocol_with_other( + &self, + other_id: &ParticipantId, + ) -> Vec<(F, [u8; SALT_SIZE])> { + // TODO: Remove unwrap + self.zero_sharing_protocol + .cointoss_protocols + .get(other_id) + .unwrap() + .own_shares_and_salts + .clone() + } + + pub fn receive_commitment( + &mut self, + sender_id: ParticipantId, + comm_zero_share: Commitments, + ) -> Result<(), VBAccumulatorError> { + self.zero_sharing_protocol + .receive_commitment(sender_id, comm_zero_share)?; + Ok(()) + } + + pub fn receive_shares( + &mut self, + sender_id: ParticipantId, + zero_shares: Vec<(F, [u8; SALT_SIZE])>, + ) -> Result<(), VBAccumulatorError> { + self.zero_sharing_protocol + .receive_shares(sender_id, zero_shares)?; + Ok(()) + } + + pub fn compute_randomness_and_arguments_for_multiplication( + self, + signing_key: &F, + ) -> Result<(Vec, F, F), VBAccumulatorError> { + let others = self + .zero_sharing_protocol + .cointoss_protocols + .keys() + .map(|p| *p) + .collect::>(); + let zero_shares = self.zero_sharing_protocol.compute_zero_shares::()?; + let (masked_signing_key_share, masked_r) = compute_masked_arguments_to_multiply( + signing_key, + self.r, + zero_shares, + self.id, + &others, + )?; + Ok((others, masked_signing_key_share, masked_r)) + } + + pub fn ready_to_compute_randomness_and_arguments_for_multiplication(&self) -> bool { + self.zero_sharing_protocol + .has_shares_from_all_who_committed() + } +} + +impl Phase1 { + pub fn init( + rng: &mut R, + id: ParticipantId, + others: BTreeSet, + protocol_id: Vec, + ) -> Result<(Self, BTreeMap), VBAccumulatorError> { + if others.contains(&id) { + let e = OTError::ParticipantCannotBePresentInOthers(id); + return Err(VBAccumulatorError::OTError(e)); + } + let r = F::rand(rng); + let (zero_sharing_protocol, comm_zero_share) = + zero_sharing::Party::init(rng, id, 2, others, protocol_id); + Ok(( + Self { + id, + r, + zero_sharing_protocol, + }, + comm_zero_share, + )) + } + + pub fn finish( + self, + signing_key: &F, + ) -> Result, VBAccumulatorError> { + // TODO: Ensure every one has participated in both protocols + let id = self.id; + let r = self.r.clone(); + let (others, masked_signing_key_share, masked_r) = + self.compute_randomness_and_arguments_for_multiplication::(signing_key)?; + Ok(Phase1Output { + id, + r, + masked_signing_key_shares: masked_signing_key_share, + masked_rs: masked_r, + others, + }) + } +} + +impl ShareOfKnownMember { + pub fn new( + y: &G::ScalarField, + accum: &G, + phase1: &Phase1Output, + phase2: &MultOut, + ) -> Result { + let (R, u) = Self::compute_R_and_u( + accum, + y, + &phase1.r, + &phase1.masked_rs, + &phase1.masked_signing_key_shares, + phase2, + ); + Ok(Self { + id: phase1.id, + u, + R, + }) + } + + pub fn aggregate(shares: Vec) -> G { + let mut sum_R = G::Group::zero(); + let mut sum_u = G::ScalarField::zero(); + for share in shares.into_iter() { + sum_u += share.u; + sum_R += share.R; + } + return (sum_R * sum_u.inverse().unwrap()).into_affine(); + } + + fn compute_R_and_u( + base: &G, + y: &G::ScalarField, + r: &G::ScalarField, + masked_r: &G::ScalarField, + masked_signing_key_share: &G::ScalarField, + phase2: &MultOut, + ) -> (G, G::ScalarField) { + let R = base.mul(r).into_affine(); + let mut u = *masked_r * (*y + masked_signing_key_share); + for (_, (a, b)) in &phase2.z_A { + u += a[0]; + u += b[0]; + } + for (_, (a, b)) in &phase2.z_B { + u += a[0]; + u += b[0]; + } + (R, u) + } +} + +impl ShareOfSharedMember { + pub fn new( + accum: &G, + phase1: &Phase1Output, + phase2: &MultOut, + ) -> Result { + let (R, u) = Self::compute_R_and_u( + accum, + &phase1.r, + &phase1.masked_rs, + &phase1.masked_signing_key_shares, + phase2, + ); + Ok(Self { + id: phase1.id, + u, + R, + }) + } + + pub fn aggregate(shares: Vec) -> G { + let mut sum_R = G::Group::zero(); + let mut sum_u = G::ScalarField::zero(); + for share in shares.into_iter() { + sum_u += share.u; + sum_R += share.R; + } + return (sum_R * sum_u.inverse().unwrap()).into_affine(); + } + + fn compute_R_and_u( + base: &G, + r: &G::ScalarField, + masked_r: &G::ScalarField, + masked_signing_key_share: &G::ScalarField, + phase2: &MultOut, + ) -> (G, G::ScalarField) { + let R = base.mul(r).into_affine(); + let mut u = *masked_r * masked_signing_key_share; + for (_, (a, b)) in &phase2.z_A { + u += a[0]; + u += b[0]; + } + for (_, (a, b)) in &phase2.z_B { + u += a[0]; + u += b[0]; + } + (R, u) + } +} + +pub fn compute_masked_arguments_to_multiply( + signing_key: &F, + r: F, + mut zero_shares: Vec, + self_id: ParticipantId, + others: &[ParticipantId], +) -> Result<(F, F), SSError> { + let beta = zero_shares.pop().unwrap(); + let alpha = zero_shares.pop().unwrap(); + let lambda = secret_sharing_and_dkg::common::lagrange_basis_at_0::(&others, self_id)?; + Ok((alpha + (lambda * signing_key), beta + r)) +} + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_ff::Zero; + use std::time::Instant; + + use crate::{ + persistence::test::InMemoryState, + positive::{Accumulator, PositiveAccumulator}, + prelude::SetupParams, + setup::{PublicKey, SecretKey}, + }; + use ark_std::{ + cfg_iter, + rand::{rngs::StdRng, SeedableRng}, + UniformRand, + }; + use blake2::Blake2b512; + use oblivious_transfer_protocols::ot_based_multiplication::{ + batch_mul_multi_party::Participant as MultParty, dkls18_mul_2p::MultiplicationOTEParams, + dkls19_batch_mul_2p::GadgetVector, + }; + use secret_sharing_and_dkg::shamir_ss::{deal_random_secret, deal_secret}; + use test_utils::ot::do_pairwise_base_ot; + + #[cfg(feature = "parallel")] + use rayon::prelude::*; + + pub fn trusted_party_keygen( + rng: &mut R, + threshold: ParticipantId, + total: ParticipantId, + ) -> (F, Vec) { + let (secret, shares, _) = deal_random_secret(rng, threshold, total).unwrap(); + (secret, shares.0.into_iter().map(|s| s.share).collect()) + } + + #[test] + fn accumulator_on_deletion() { + 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"); + + let protocol_id = b"test".to_vec(); + + let threshold_signers = 5; + 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); + + let params = SetupParams::::generate_using_rng(&mut rng); + let mut accumulator = PositiveAccumulator::::initialize(¶ms); + let mut state = InMemoryState::new(); + let secret_key = SecretKey(sk); + let secret_key_shares = cfg_iter!(sk_shares) + .map(|s| SecretKey(*s)) + .collect::>(); + + // The signers run OT protocol instances. This is also a one time setup. + let base_ot_outputs = do_pairwise_base_ot::( + &mut rng, + ote_params.num_base_ot(), + total_signers, + all_party_set.clone(), + ); + + let count = 10; + let mut elems = vec![]; + for _ in 0..count { + let elem = Fr::rand(&mut rng); + accumulator = accumulator.add(elem, &secret_key, &mut state).unwrap(); + elems.push(elem); + } + + let remove_element = &elems[5]; + let expected_new = accumulator.compute_new_post_remove(remove_element, &secret_key); + + let mut round1s = 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(); + others.remove(&i); + let (round1, comm_zero) = + Phase1::::init(&mut rng, i, others, protocol_id.clone()).unwrap(); + round1s.push(round1); + 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 { + round1s[i as usize - 1] + .receive_commitment( + j, + commitments_zero_share[j as usize - 1] + .get(&i) + .unwrap() + .clone(), + ) + .unwrap(); + } + } + } + + // 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 { + let zero_share = round1s[j as usize - 1] + .get_comm_shares_and_salts_for_zero_sharing_protocol_with_other(&i); + round1s[i as usize - 1] + .receive_shares(j, zero_share) + .unwrap(); + } + } + } + + // 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::(&secret_key_shares[i].0) + .unwrap(); + expected_sk += out.masked_signing_key_shares; + round1outs.push(out); + } + println!("Phase 1 took {:?}", start.elapsed()); + + assert_eq!(expected_sk, sk); + + let mut round2s = vec![]; + let mut all_msg_1s = vec![]; + + let label = b"test"; + + // 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(); + others.remove(&i); + let (phase, U) = MultParty::init( + &mut rng, + i, + vec![round1outs[i as usize - 1].masked_signing_key_shares], + vec![round1outs[i as usize - 1].masked_rs], + base_ot_outputs[i as usize - 1].clone(), + others, + ote_params, + &gadget_vector, + label, + ) + .unwrap(); + round2s.push(phase); + 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 { + let m2 = round2s[receiver_id as usize - 1] + .receive_message1::(sender_id, m, &gadget_vector) + .unwrap(); + all_msg_2s.push((receiver_id, sender_id, m2)); + } + } + + for (sender_id, receiver_id, m2) in all_msg_2s { + round2s[receiver_id as usize - 1] + .receive_message2::(sender_id, m2, &gadget_vector) + .unwrap(); + } + + 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(); + assert_eq!( + z_A.0[0] + z_B.0[0], + round1outs[i as usize - 1].masked_signing_key_shares + * round1outs[*j as usize - 1].masked_rs + ); + assert_eq!( + z_A.1[0] + z_B.1[0], + round1outs[i as usize - 1].masked_rs + * round1outs[*j as usize - 1].masked_signing_key_shares + ); + } + } + + let mut shares = vec![]; + let start = Instant::now(); + for i in 0..threshold_signers as usize { + let share = ShareOfKnownMember::new( + remove_element, + accumulator.value(), + &round1outs[i], + &round2_outputs[i], + ) + .unwrap(); + shares.push(share); + } + println!( + "Creating {} new shares took {:?}", + threshold_signers, + start.elapsed() + ); + + let start = Instant::now(); + let updated_accum = ShareOfKnownMember::aggregate(shares); + println!( + "Aggregating {} shares took {:?}", + threshold_signers, + start.elapsed() + ); + assert_eq!(updated_accum, expected_new); + + accumulator = accumulator + .remove(remove_element, &secret_key, &mut state) + .unwrap(); + assert_eq!(expected_new, *accumulator.value()); + } + + #[test] + fn witness_generation() { + 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"); + + let protocol_id = b"test".to_vec(); + + let threshold_signers = 5; + 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); + + let params = SetupParams::::generate_using_rng(&mut rng); + let mut accumulator = PositiveAccumulator::::initialize(¶ms); + let mut state = InMemoryState::new(); + let secret_key = SecretKey(sk); + let secret_key_shares = cfg_iter!(sk_shares) + .map(|s| SecretKey(*s)) + .collect::>(); + let public_key = PublicKey::new_from_secret_key(&secret_key, ¶ms); + + // The signers run OT protocol instances. This is also a one time setup. + let base_ot_outputs = do_pairwise_base_ot::( + &mut rng, + ote_params.num_base_ot(), + total_signers, + all_party_set.clone(), + ); + + let count = 10; + let mut elems = vec![]; + for _ in 0..count { + let elem = Fr::rand(&mut rng); + accumulator = accumulator.add(elem, &secret_key, &mut state).unwrap(); + elems.push(elem); + } + + let member = &elems[1]; + let expected_wit = accumulator + .get_membership_witness(&member, &secret_key, &mut state) + .unwrap(); + assert!(accumulator.verify_membership(member, &expected_wit, &public_key, ¶ms)); + + let (member_shares, _) = + deal_secret::(&mut rng, *member, threshold_signers, total_signers).unwrap(); + + let mut round1s = 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(); + others.remove(&i); + let (round1, comm_zero) = + Phase1::::init(&mut rng, i, others, protocol_id.clone()).unwrap(); + round1s.push(round1); + 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 { + round1s[i as usize - 1] + .receive_commitment( + j, + commitments_zero_share[j as usize - 1] + .get(&i) + .unwrap() + .clone(), + ) + .unwrap(); + } + } + } + + // 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 { + let zero_share = round1s[j as usize - 1] + .get_comm_shares_and_salts_for_zero_sharing_protocol_with_other(&i); + round1s[i as usize - 1] + .receive_shares(j, zero_share) + .unwrap(); + } + } + } + + // Signers finish round-1 to generate the output + let mut expected_sum = Fr::zero(); + for (i, round1) in round1s.into_iter().enumerate() { + let out = round1 + .finish::(&(secret_key_shares[i].0 + member_shares.0[i].share)) + .unwrap(); + expected_sum += out.masked_signing_key_shares; + round1outs.push(out); + } + println!("Phase 1 took {:?}", start.elapsed()); + + assert_eq!(expected_sum, sk + member); + + let label = b"test"; + + 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(); + others.remove(&i); + let (phase, U) = MultParty::init( + &mut rng, + i, + vec![round1outs[i as usize - 1].masked_signing_key_shares], + vec![round1outs[i as usize - 1].masked_rs], + base_ot_outputs[i as usize - 1].clone(), + others, + ote_params, + &gadget_vector, + label, + ) + .unwrap(); + round2s.push(phase); + 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 { + let m2 = round2s[receiver_id as usize - 1] + .receive_message1::(sender_id, m, &gadget_vector) + .unwrap(); + all_msg_2s.push((receiver_id, sender_id, m2)); + } + } + + for (sender_id, receiver_id, m2) in all_msg_2s { + round2s[receiver_id as usize - 1] + .receive_message2::(sender_id, m2, &gadget_vector) + .unwrap(); + } + + 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(); + assert_eq!( + z_A.0[0] + z_B.0[0], + round1outs[i as usize - 1].masked_signing_key_shares + * round1outs[*j as usize - 1].masked_rs + ); + assert_eq!( + z_A.1[0] + z_B.1[0], + round1outs[i as usize - 1].masked_rs + * round1outs[*j as usize - 1].masked_signing_key_shares + ); + } + } + + let mut shares = vec![]; + let start = Instant::now(); + for i in 0..threshold_signers as usize { + let share = + ShareOfSharedMember::new(accumulator.value(), &round1outs[i], &round2_outputs[i]) + .unwrap(); + shares.push(share); + } + println!( + "Creating {} new shares took {:?}", + threshold_signers, + start.elapsed() + ); + + let start = Instant::now(); + let witness = ShareOfSharedMember::aggregate(shares); + println!( + "Aggregating {} shares took {:?}", + threshold_signers, + start.elapsed() + ); + + assert_eq!(witness, expected_wit.0); + } +}