Skip to main content

bbs_plus/threshold/
threshold_bbs.rs

1use ark_ec::{AffineRepr, CurveGroup};
2
3use super::{multiplication_phase::Phase2Output, utils::compute_R_and_u};
4use ark_ec::pairing::Pairing;
5use ark_ff::{Field, PrimeField, Zero};
6use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
7use ark_std::{
8    collections::{BTreeMap, BTreeSet},
9    rand::RngCore,
10    vec::Vec,
11};
12use digest::{Digest, DynDigest};
13use dock_crypto_utils::expect_equality;
14
15use crate::{
16    error::BBSPlusError, setup::SignatureParams23G1, signature_23::Signature23G1,
17    threshold::randomness_generation_phase::Phase1,
18};
19use dock_crypto_utils::signature::MultiMessageSignatureParams;
20use oblivious_transfer_protocols::{cointoss, zero_sharing, ParticipantId};
21
22/// The length of vectors `r`, `e`, `masked_signing_key_shares`, `masked_rs` should
23/// be `batch_size` as each item of the vector corresponds to 1 signature
24#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
25pub struct Phase1Output<F: PrimeField> {
26    pub id: ParticipantId,
27    pub batch_size: u32,
28    /// Shares of the random `r`, one share for each item in the batch
29    pub r: Vec<F>,
30    pub e: Vec<F>,
31    /// Additive shares of the signing key masked by a random `alpha`
32    pub masked_signing_key_shares: Vec<F>,
33    /// Additive shares of `r` masked by a random `beta`
34    pub masked_rs: Vec<F>,
35    pub others: Vec<ParticipantId>,
36}
37
38/// A share of the BBS signature created by one signer. A client will aggregate many such shares to
39/// create the final signature. Note that this is done by the signer where it uses outputs of
40/// phase 1 and 2 and these outputs should not be sent to the user. Only this share needs to be sent.
41#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
42pub struct BBSSignatureShare<E: Pairing> {
43    pub id: ParticipantId,
44    pub e: E::ScalarField,
45    pub u: E::ScalarField,
46    pub R: E::G1Affine,
47}
48
49impl<F: PrimeField, const SALT_SIZE: usize> Phase1<F, SALT_SIZE> {
50    pub fn init_for_bbs<R: RngCore, D: Digest>(
51        rng: &mut R,
52        batch_size: u32,
53        id: ParticipantId,
54        others: BTreeSet<ParticipantId>,
55        protocol_id: Vec<u8>,
56    ) -> Result<
57        (
58            Self,
59            cointoss::Commitments,
60            BTreeMap<ParticipantId, cointoss::Commitments>,
61        ),
62        BBSPlusError,
63    > {
64        if others.contains(&id) {
65            return Err(BBSPlusError::ParticipantCannotBePresentInOthers(id));
66        }
67        let r = (0..batch_size).map(|_| F::rand(rng)).collect();
68        // 1 random value `e` need to be generated per signature
69        let (commitment_protocol, comm) =
70            cointoss::Party::commit::<R, D>(rng, id, batch_size, protocol_id.clone());
71        // Each signature will have its own zero-sharing of `alpha` and `beta`
72        let (zero_sharing_protocol, comm_zero_share) =
73            zero_sharing::Party::init::<R, D>(rng, id, 2 * batch_size, others, protocol_id);
74        Ok((
75            Self {
76                id,
77                batch_size,
78                r,
79                commitment_protocol,
80                zero_sharing_protocol,
81            },
82            comm,
83            comm_zero_share,
84        ))
85    }
86
87    /// End phase 1 and return the output of this phase
88    pub fn finish_for_bbs<D: Default + DynDigest + Clone>(
89        self,
90        signing_key: &F,
91    ) -> Result<Phase1Output<F>, BBSPlusError> {
92        // TODO: Ensure every one has participated in both protocols
93        let id = self.id;
94        let batch_size = self.batch_size;
95        let r = self.r.clone();
96        let (others, randomness, masked_signing_key_share, masked_r) =
97            self.compute_randomness_and_arguments_for_multiplication::<D>(signing_key)?;
98        debug_assert_eq!(randomness.len() as u32, batch_size);
99        let e = randomness;
100        Ok(Phase1Output {
101            id,
102            batch_size,
103            r,
104            e,
105            masked_signing_key_shares: masked_signing_key_share,
106            masked_rs: masked_r,
107            others,
108        })
109    }
110}
111
112impl<E: Pairing> BBSSignatureShare<E> {
113    /// `sig_index_in_batch` is the index of this signature in batch and also in the Phase1 and Phase2 outputs
114    pub fn new(
115        messages: &[E::ScalarField],
116        sig_index_in_batch: usize,
117        phase1: &Phase1Output<E::ScalarField>,
118        phase2: &Phase2Output<E::ScalarField>,
119        sig_params: &SignatureParams23G1<E>,
120    ) -> Result<Self, BBSPlusError> {
121        if messages.is_empty() {
122            return Err(BBSPlusError::NoMessageToSign);
123        }
124        expect_equality!(
125            messages.len(),
126            sig_params.supported_message_count(),
127            BBSPlusError::MessageCountIncompatibleWithSigParams
128        );
129        // Create map of msg index (0-based) -> message
130        let msg_map: BTreeMap<usize, &E::ScalarField> =
131            messages.iter().enumerate().map(|(i, e)| (i, e)).collect();
132        Self::new_with_committed_messages(
133            &E::G1Affine::zero(),
134            msg_map,
135            sig_index_in_batch,
136            phase1,
137            phase2,
138            sig_params,
139        )
140    }
141
142    /// `sig_index_in_batch` is the index of this signature in batch and also in the Phase1 and Phase2 outputs
143    pub fn new_with_committed_messages(
144        commitment: &E::G1Affine,
145        uncommitted_messages: BTreeMap<usize, &E::ScalarField>,
146        sig_index_in_batch: usize,
147        phase1: &Phase1Output<E::ScalarField>,
148        phase2: &Phase2Output<E::ScalarField>,
149        sig_params: &SignatureParams23G1<E>,
150    ) -> Result<Self, BBSPlusError> {
151        let b = sig_params.b(uncommitted_messages)?;
152        let commitment_plus_b = b + commitment;
153        let (R, u) = compute_R_and_u(
154            commitment_plus_b,
155            &phase1.r[sig_index_in_batch],
156            &phase1.e[sig_index_in_batch],
157            &phase1.masked_rs[sig_index_in_batch],
158            &phase1.masked_signing_key_shares[sig_index_in_batch],
159            sig_index_in_batch as u32,
160            phase2,
161        );
162        Ok(Self {
163            id: phase1.id,
164            e: phase1.e[sig_index_in_batch],
165            u,
166            R,
167        })
168    }
169
170    pub fn aggregate(sig_shares: Vec<Self>) -> Result<Signature23G1<E>, BBSPlusError> {
171        // TODO: Ensure correct threshold. Share should contain threshold and share id
172        let mut sum_R = E::G1::zero();
173        let mut sum_u = E::ScalarField::zero();
174        let mut expected_e = E::ScalarField::zero();
175        for (i, share) in sig_shares.into_iter().enumerate() {
176            if i == 0 {
177                expected_e = share.e;
178            } else {
179                if expected_e != share.e {
180                    return Err(BBSPlusError::IncorrectEByParticipant(share.id));
181                }
182            }
183            sum_u += share.u;
184            sum_R += share.R;
185        }
186        let A = sum_R * sum_u.inverse().unwrap();
187        Ok(Signature23G1 {
188            A: A.into_affine(),
189            e: expected_e,
190        })
191    }
192}
193
194#[cfg(test)]
195pub mod tests {
196    use super::*;
197    use crate::{
198        setup::{PublicKeyG2, SecretKey},
199        threshold::{
200            multiplication_phase::Phase2, threshold_bbs_plus::tests::trusted_party_keygen,
201        },
202    };
203    use ark_bls12_381::{Bls12_381, Fr};
204    use ark_ff::Zero;
205    use ark_std::{
206        rand::{rngs::StdRng, SeedableRng},
207        UniformRand,
208    };
209    use blake2::Blake2b512;
210    use oblivious_transfer_protocols::ot_based_multiplication::{
211        dkls18_mul_2p::MultiplicationOTEParams, dkls19_batch_mul_2p::GadgetVector,
212    };
213    use sha3::Shake256;
214    use std::time::{Duration, Instant};
215    use test_utils::ot::do_pairwise_base_ot;
216
217    #[test]
218    fn signing() {
219        let mut rng = StdRng::seed_from_u64(0u64);
220        const BASE_OT_KEY_SIZE: u16 = 128;
221        const KAPPA: u16 = 256;
222        const STATISTICAL_SECURITY_PARAMETER: u16 = 80;
223        let ote_params = MultiplicationOTEParams::<KAPPA, STATISTICAL_SECURITY_PARAMETER> {};
224        let gadget_vector = GadgetVector::<Fr, KAPPA, STATISTICAL_SECURITY_PARAMETER>::new::<
225            Blake2b512,
226        >(ote_params, b"test-gadget-vector");
227
228        let protocol_id = b"test".to_vec();
229
230        let sig_batch_size = 3;
231        let threshold_signers = 5;
232        let total_signers = 8;
233        let all_party_set = (1..=total_signers).into_iter().collect::<BTreeSet<_>>();
234        let threshold_party_set = (1..=threshold_signers).into_iter().collect::<BTreeSet<_>>();
235
236        // The signers do a keygen. This is a one time setup.
237        let (sk, sk_shares) =
238            trusted_party_keygen::<_, Fr>(&mut rng, threshold_signers, total_signers);
239
240        // The signers run OT protocol instances. This is also a one time setup.
241        let base_ot_outputs = do_pairwise_base_ot::<BASE_OT_KEY_SIZE>(
242            &mut rng,
243            ote_params.num_base_ot(),
244            total_signers,
245            all_party_set.clone(),
246        );
247
248        let message_count = 5;
249        let params = SignatureParams23G1::<Bls12_381>::generate_using_rng(&mut rng, message_count);
250        let public_key =
251            PublicKeyG2::generate_using_secret_key_and_bbs23_params(&SecretKey(sk), &params);
252
253        println!(
254            "For a batch size of {} BBS signatures and {} signers",
255            sig_batch_size, threshold_signers
256        );
257
258        // Following have to happen for each new batch of signatures. Batch size can be 1 when creating one signature at a time
259
260        let mut round1s = vec![];
261        let mut commitments = vec![];
262        let mut commitments_zero_share = vec![];
263        let mut round1outs = vec![];
264
265        // Signers initiate round-1 and each signer sends commitments to others
266        let start = Instant::now();
267        for i in 1..=threshold_signers {
268            let mut others = threshold_party_set.clone();
269            others.remove(&i);
270            let (round1, comm, comm_zero) = Phase1::<Fr, 256>::init_for_bbs::<_, Blake2b512>(
271                &mut rng,
272                sig_batch_size,
273                i,
274                others,
275                protocol_id.clone(),
276            )
277            .unwrap();
278            round1s.push(round1);
279            commitments.push(comm);
280            commitments_zero_share.push(comm_zero);
281        }
282
283        // Signers process round-1 commitments received from others
284        for i in 1..=threshold_signers {
285            for j in 1..=threshold_signers {
286                if i != j {
287                    round1s[i as usize - 1]
288                        .receive_commitment(
289                            j,
290                            commitments[j as usize - 1].clone(),
291                            commitments_zero_share[j as usize - 1]
292                                .get(&i)
293                                .unwrap()
294                                .clone(),
295                        )
296                        .unwrap();
297                }
298            }
299        }
300
301        // Signers create round-1 shares once they have the required commitments from others
302        for i in 1..=threshold_signers {
303            for j in 1..=threshold_signers {
304                if i != j {
305                    let share = round1s[j as usize - 1].get_comm_shares_and_salts();
306                    let zero_share = round1s[j as usize - 1]
307                        .get_comm_shares_and_salts_for_zero_sharing_protocol_with_other(&i);
308                    round1s[i as usize - 1]
309                        .receive_shares::<Blake2b512>(j, share, zero_share)
310                        .unwrap();
311                }
312            }
313        }
314
315        // Signers finish round-1 to generate the output
316        let mut expected_sk = Fr::zero();
317        for (i, round1) in round1s.into_iter().enumerate() {
318            let out = round1.finish_for_bbs::<Blake2b512>(&sk_shares[i]).unwrap();
319            expected_sk += out.masked_signing_key_shares.iter().sum::<Fr>();
320            round1outs.push(out);
321        }
322        println!("Phase 1 took {:?}", start.elapsed());
323
324        assert_eq!(expected_sk, sk * Fr::from(sig_batch_size));
325        for i in 1..threshold_signers {
326            assert_eq!(round1outs[0].e, round1outs[i as usize].e);
327        }
328
329        let mut round2s = vec![];
330        let mut all_msg_1s = vec![];
331
332        // Signers initiate round-2 and each signer sends messages to others
333        let start = Instant::now();
334        for i in 1..=threshold_signers {
335            let mut others = threshold_party_set.clone();
336            others.remove(&i);
337            let (phase, U) = Phase2::init::<_, Shake256>(
338                &mut rng,
339                i,
340                round1outs[i as usize - 1].masked_signing_key_shares.clone(),
341                round1outs[i as usize - 1].masked_rs.clone(),
342                base_ot_outputs[i as usize - 1].clone(),
343                others,
344                ote_params,
345                &gadget_vector,
346            )
347            .unwrap();
348            round2s.push(phase);
349            all_msg_1s.push((i, U));
350        }
351
352        // Signers process round-2 messages received from others
353        let mut all_msg_2s = vec![];
354        for (sender_id, msg_1s) in all_msg_1s {
355            for (receiver_id, m) in msg_1s {
356                let m2 = round2s[receiver_id as usize - 1]
357                    .receive_message1::<Blake2b512, Shake256>(sender_id, m, &gadget_vector)
358                    .unwrap();
359                all_msg_2s.push((receiver_id, sender_id, m2));
360            }
361        }
362
363        for (sender_id, receiver_id, m2) in all_msg_2s {
364            round2s[receiver_id as usize - 1]
365                .receive_message2::<Blake2b512>(sender_id, m2, &gadget_vector)
366                .unwrap();
367        }
368
369        let round2_outputs = round2s.into_iter().map(|p| p.finish()).collect::<Vec<_>>();
370        println!("Phase 2 took {:?}", start.elapsed());
371
372        // Check that multiplication phase ran successfully, i.e. each signer has an additive share of
373        // a multiplication with every other signer
374        for i in 1..=threshold_signers {
375            for (j, z_A) in &round2_outputs[i as usize - 1].0.z_A {
376                let z_B = round2_outputs[*j as usize - 1].0.z_B.get(&i).unwrap();
377                for k in 0..sig_batch_size as usize {
378                    assert_eq!(
379                        z_A.0[k] + z_B.0[k],
380                        round1outs[i as usize - 1].masked_signing_key_shares[k]
381                            * round1outs[*j as usize - 1].masked_rs[k]
382                    );
383                    assert_eq!(
384                        z_A.1[k] + z_B.1[k],
385                        round1outs[i as usize - 1].masked_rs[k]
386                            * round1outs[*j as usize - 1].masked_signing_key_shares[k]
387                    );
388                }
389            }
390        }
391
392        // This is the final step where each signer generates his share of the signature without interaction
393        // with any other signer and sends this share to the client
394        let mut sig_shares_time = Duration::default();
395        let mut sig_aggr_time = Duration::default();
396        for k in 0..sig_batch_size as usize {
397            let messages = (0..message_count)
398                .into_iter()
399                .map(|_| Fr::rand(&mut rng))
400                .collect::<Vec<_>>();
401
402            // Get shares from a threshold number of signers
403            let mut shares = vec![];
404            let start = Instant::now();
405            for i in 0..threshold_signers as usize {
406                let share = BBSSignatureShare::new(
407                    &messages,
408                    k,
409                    &round1outs[i],
410                    &round2_outputs[i],
411                    &params,
412                )
413                .unwrap();
414                shares.push(share);
415            }
416            sig_shares_time += start.elapsed();
417
418            // Client aggregate the shares to get the final signature
419            let start = Instant::now();
420            let sig = BBSSignatureShare::aggregate(shares).unwrap();
421            sig_aggr_time += start.elapsed();
422            sig.verify(&messages, public_key.clone(), params.clone())
423                .unwrap();
424        }
425
426        println!("Generating signature shares took {:?}", sig_shares_time);
427        println!("Aggregating signature shares took {:?}", sig_aggr_time);
428    }
429}