Skip to content

Commit

Permalink
Update wasmer version, update Coconut blind signature with additional…
Browse files Browse the repository at this point in the history
… check and add docs

Signed-off-by: lovesh <[email protected]>
  • Loading branch information
lovesh committed May 15, 2024
1 parent 289a29f commit e77a7b8
Show file tree
Hide file tree
Showing 18 changed files with 251 additions and 43 deletions.
17 changes: 11 additions & 6 deletions bbs_plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ Threshold BBS and BBS+ signatures based on the paper [Threshold BBS+ Signatures
The threshold signing protocol has 3 phases (not communication rounds)
1. This is the randomness generation phase
2. This is the phase where multiplications happen
3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature.
3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. This phase
is non-interactive from signers' point of view as they don't just interact among themselves

Note that only 3rd phase requires the messages to be known so the first 2 phases can be treated as pre-computation
and can be done proactively. Secondly since the communication time among signers is most likely to be the bottleneck
and can be done proactively and thus only phase 1 and 2 are online phases of the MPC protocol and phase 3 is the offline
phase.
Secondly since the communication time among signers is most likely to be the bottleneck
in threshold signing, phase 1 and 2 support batching meaning that to generate `n` signatures only a single execution
of phase 1 and 2 needs to done, although with larger inputs. Then `n` executions of phase 3 are done to generate
the signature.
Also its assumed that parties have done the DKG as well as the base OT and stored their results.
Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly different.
the signature.
Also, its assumed that parties have done the DKG as well as the base OT and stored their results before starting phase 1.
Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly
less expensive as BBS+ needs 2 random fields elements but BBS needs only 1.

### Modules

Expand All @@ -45,12 +49,13 @@ different from BBS+ but public key is same.

The implementation tries to use the same variable names as the paper and thus violate Rust's naming conventions at places.


[`setup`]: https://docs.rs/bbs_plus/latest/bbs_plus/setup/
[`signature`]: https://docs.rs/bbs_plus/latest/bbs_plus/signature/
[`proof`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof/
[`signature_23`]: https://docs.rs/bbs_plus/latest/bbs_plus/signature_23/
[`proof_23`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof_23/
[`proof_23_cdl`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof_23_alternate/
[`proof_23_cdl`]: https://docs.rs/bbs_plus/latest/bbs_plus/proof_23_cdl/
[`threshold`]: https://docs.rs/bbs_plus/latest/bbs_plus/threshold/

<!-- cargo-rdme end -->
Expand Down
12 changes: 8 additions & 4 deletions bbs_plus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@
//! The threshold signing protocol has 3 phases (not communication rounds)
//! 1. This is the randomness generation phase
//! 2. This is the phase where multiplications happen
//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature.
//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. This phase
//! is non-interactive from signers' point of view as they don't just interact among themselves
//!
//! Note that only 3rd phase requires the messages to be known so the first 2 phases can be treated as pre-computation
//! and can be done proactively. Secondly since the communication time among signers is most likely to be the bottleneck
//! and can be done proactively and thus only phase 1 and 2 are online phases of the MPC protocol and phase 3 is the offline
//! phase.
//! Secondly since the communication time among signers is most likely to be the bottleneck
//! in threshold signing, phase 1 and 2 support batching meaning that to generate `n` signatures only a single execution
//! of phase 1 and 2 needs to done, although with larger inputs. Then `n` executions of phase 3 are done to generate
//! the signature.
//! Also, its assumed that parties have done the DKG as well as the base OT and stored their results.
//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly different.
//! Also, its assumed that parties have done the DKG as well as the base OT and stored their results before starting phase 1.
//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly
//! less expensive as BBS+ needs 2 random fields elements but BBS needs only 1.
//!
//! ## Modules
//!
Expand Down
10 changes: 6 additions & 4 deletions bbs_plus/src/threshold/base_ot_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ pub mod tests {
UniformRand,
};
use blake2::Blake2b512;
use std::time::Instant;

pub fn check_base_ot_keys(
choices: &[Bit],
Expand Down Expand Up @@ -343,11 +344,12 @@ pub mod tests {
#[test]
fn base_ot_for_threshold_sig() {
let mut rng = StdRng::seed_from_u64(0u64);
let num_base_ot = 256;

let num_parties = 5;
let all_party_set = (1..=num_parties).into_iter().collect::<BTreeSet<_>>();
let num_base_ot = 256;
for num_parties in vec![5, 10, 15, 20] {
let all_party_set = (1..=num_parties).into_iter().collect::<BTreeSet<_>>();

do_base_ot_for_threshold_sig::<16>(&mut rng, num_base_ot, num_parties, all_party_set);
do_base_ot_for_threshold_sig::<16>(&mut rng, num_base_ot, num_parties, all_party_set);
}
}
}
12 changes: 8 additions & 4 deletions bbs_plus/src/threshold/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
//! The threshold signing protocol has 3 phases (not communication rounds)
//! 1. This is the randomness generation phase
//! 2. This is the phase where multiplications happen
//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature.
//! 3. Here the outputs of phases 1 and 2 and the messages to be signed are used to generate the signature. This phase
//! is non-interactive from signers' point of view as they don't just interact among themselves
//!
//! Note that only 3rd phase requires the messages to be known so the first 2 phases can be treated as pre-computation
//! and can be done proactively. Secondly since the communication time among signers is most likely to be the bottleneck
//! and can be done proactively and thus only phase 1 and 2 are online phases of the MPC protocol and phase 3 is the offline
//! phase.
//! Secondly since the communication time among signers is most likely to be the bottleneck
//! in threshold signing, phase 1 and 2 support batching meaning that to generate `n` signatures only a single execution
//! of phase 1 and 2 needs to done, although with larger inputs. Then `n` executions of phase 3 are done to generate
//! the signature.
//! Also its assumed that parties have done the DKG as well as the base OT and stored their results.
//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly different.
//! Also, its assumed that parties have done the DKG as well as the base OT and stored their results before starting phase 1.
//! Both BBS and BBS+ implementations share the same multiplication phase and the base OT phase but their phase 1 is slightly
//! less expensive as BBS+ needs 2 random fields elements but BBS needs only 1.
pub mod base_ot_phase;
pub mod cointoss;
Expand Down
148 changes: 148 additions & 0 deletions bbs_plus/src/threshold/multiplication_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,151 @@ impl<F: PrimeField, const KAPPA: u16, const STATISTICAL_SECURITY_PARAMETER: u16>
}
}
}

#[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::<KAPPA, STATISTICAL_SECURITY_PARAMETER> {};
let gadget_vector = GadgetVector::<Fr, KAPPA, STATISTICAL_SECURITY_PARAMETER>::new::<
Blake2b512,
>(ote_params, b"test-gadget-vector");

fn check(
rng: &mut StdRng,
ote_params: MultiplicationOTEParams<KAPPA, STATISTICAL_SECURITY_PARAMETER>,
threshold: u16,
total: u16,
batch_size: u32,
gadget_vector: &GadgetVector<Fr, KAPPA, STATISTICAL_SECURITY_PARAMETER>,
) {
let total_party_set = (1..=total).into_iter().collect::<BTreeSet<_>>();
let threshold_party_set = (1..=threshold).into_iter().collect::<BTreeSet<_>>();

// Run OT protocol instances. This is also a one time setup.
let base_ot_outputs = do_base_ot_for_threshold_sig::<BASE_OT_KEY_SIZE>(
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::<Vec<_>>();
let b = (0..batch_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
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::<Blake2b512>(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::<Blake2b512>(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::<Vec<_>>();
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);
}
}
22 changes: 18 additions & 4 deletions bbs_plus/src/threshold/threshold_bbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,7 @@ impl<E: Pairing> BBSSignatureShare<E> {
#[cfg(test)]
pub mod tests {
use super::*;
use ark_bls12_381::Bls12_381;
use ark_ec::pairing::Pairing;
use ark_bls12_381::{Bls12_381, Fr};
use ark_ff::Zero;
use std::time::{Duration, Instant};

Expand All @@ -206,8 +205,6 @@ pub mod tests {
dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector,
};

type Fr = <Bls12_381 as Pairing>::ScalarField;

#[test]
fn signing() {
let mut rng = StdRng::seed_from_u64(0u64);
Expand All @@ -226,9 +223,12 @@ pub mod tests {
let total_signers = 8;
let all_party_set = (1..=total_signers).into_iter().collect::<BTreeSet<_>>();
let threshold_party_set = (1..=threshold_signers).into_iter().collect::<BTreeSet<_>>();

// The signers do a keygen. This is a one time setup.
let (sk, sk_shares) =
trusted_party_keygen::<_, Fr>(&mut rng, threshold_signers, total_signers);

// The signers run OT protocol instances. This is also a one time setup.
let base_ot_outputs = do_base_ot_for_threshold_sig::<BASE_OT_KEY_SIZE>(
&mut rng,
ote_params.num_base_ot(),
Expand All @@ -246,11 +246,14 @@ pub mod tests {
sig_batch_size, threshold_signers
);

// Following have to happen for each new batch of signatures. Batch size can be 1 when creating one signature at a time

let mut round1s = vec![];
let mut commitments = vec![];
let mut commitments_zero_share = vec![];
let mut round1outs = vec![];

// Signers initiate round-1 and each signer sends commitments to others
let start = Instant::now();
for i in 1..=threshold_signers {
let mut others = threshold_party_set.clone();
Expand All @@ -268,6 +271,7 @@ pub mod tests {
commitments_zero_share.push(comm_zero);
}

// Signers process round-1 commitments received from others
for i in 1..=threshold_signers {
for j in 1..=threshold_signers {
if i != j {
Expand All @@ -285,6 +289,7 @@ pub mod tests {
}
}

// Signers create round-1 shares once they have the required commitments from others
for i in 1..=threshold_signers {
for j in 1..=threshold_signers {
if i != j {
Expand All @@ -298,6 +303,7 @@ pub mod tests {
}
}

// Signers finish round-1 to generate the output
let mut expected_sk = Fr::zero();
for (i, round1) in round1s.into_iter().enumerate() {
let out = round1.finish_for_bbs::<Blake2b512>(&sk_shares[i]).unwrap();
Expand All @@ -314,6 +320,7 @@ pub mod tests {
let mut round2s = vec![];
let mut all_msg_1s = vec![];

// Signers initiate round-2 and each signer sends messages to others
let start = Instant::now();
for i in 1..=threshold_signers {
let mut others = threshold_party_set.clone();
Expand All @@ -333,6 +340,7 @@ pub mod tests {
all_msg_1s.push((i, U));
}

// Signers process round-2 messages received from others
let mut all_msg_2s = vec![];
for (sender_id, msg_1s) in all_msg_1s {
for (receiver_id, m) in msg_1s {
Expand All @@ -352,6 +360,8 @@ pub mod tests {
let round2_outputs = round2s.into_iter().map(|p| p.finish()).collect::<Vec<_>>();
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();
Expand All @@ -370,6 +380,8 @@ pub mod tests {
}
}

// This is the final step where each signer generates his share of the signature without interaction
// with any other signer and sends this share to the client
let mut sig_shares_time = Duration::default();
let mut sig_aggr_time = Duration::default();
for k in 0..sig_batch_size as usize {
Expand All @@ -378,6 +390,7 @@ pub mod tests {
.map(|_| Fr::rand(&mut rng))
.collect::<Vec<_>>();

// Get shares from a threshold number of signers
let mut shares = vec![];
let start = Instant::now();
for i in 0..threshold_signers as usize {
Expand All @@ -393,6 +406,7 @@ pub mod tests {
}
sig_shares_time += start.elapsed();

// Client aggregate the shares to get the final signature
let start = Instant::now();
let sig = BBSSignatureShare::aggregate(shares).unwrap();
sig_aggr_time += start.elapsed();
Expand Down
Loading

0 comments on commit e77a7b8

Please sign in to comment.