w3f_bls/
single_pop_aggregator.rs

1//! ## Aggregation of BLS signatures using proofs-of-possession
2//!
3//! In this module, we provide the linear flavor of aggregate
4//! BLS signature in which the verifiers has previously checked
5//! proofs-of-possession for all public keys.  In other words,
6//! we simply add up the signatures because the previously checked
7//! proofs-of-possession for all signers prevent rogue key attacks.
8//! See the security arguments in The Power of Proofs-of-Possession:
9//! Securing Multiparty Signatures against Rogue-Key Attacks
10//! by Thomas Ristenpart and Scott Yilek at https://eprint.iacr.org/2007/264.pdf
11//!
12//! These proof-of-possession are simply self-signed certificates,
13//! so a BLS signature by each secret key on its own public key.
14//! Importantly, the message for this self-signed certificates
15//! must uniquely distinguish the public key for which the signature
16//! establishes a proof-of-possession.
17//! It follows that each proof-of-possession has a unique message,
18//! so distinct message aggregation is optimal for verifying them.
19//!
20//! In this vein, we note that aggregation under proofs-of-possession
21//! cannot improve performance when signers sign distinct messages,
22//! so proofs-of-possession help with aggregating votes in a concensus
23//! protocol, but should never be used for accounts on a block chain.
24//!
25//! We assume here that users provide their own data structure for
26//! proofs-of-poossession.  We provide more structure for users who
27//! one bit per vote in a concensus protocol:  
28//! You first verify the proofs-of-possession when building a data
29//! structure that holds the voters' keys.  You implement the
30//! `ProofsOfPossession` trait for this data strtcuture as well,
31//! so that the `BitPoPSignedMessage` type provides a signature
32//! data type with reasonable sanity checks.
33
34// Aside about proof-of-possession in the DLOG setting
35// https://twitter.com/btcVeg/status/1085490561082183681
36
37use ark_ff::Zero;
38
39use super::verifiers::{
40    verify_using_aggregated_auxiliary_public_keys, verify_with_distinct_messages,
41};
42use super::*;
43
44use digest::DynDigest;
45
46/// Batch or aggregate BLS signatures with attached messages and
47/// signers, for whom we previously checked proofs-of-possession.
48///
49/// In this type, we provide a high-risk low-level batching and
50/// aggregation mechanism that merely adds up signatures under the
51/// assumption that all required proofs-of-possession were previously
52/// checked.
53///
54/// We say a signing key has provided a proof-of-possession if the
55/// verifier remembers having checked some self-signed certificate
56/// by that key.  It's insecure to use this aggregation strategy
57/// without first cehcking proofs-of-possession.  In particular
58/// it is insecure to use this aggregation strategy when checking
59/// proofs-of-possession, and could not improve performance anyways.  
60/// Distinct message aggregation is always optimal for checking
61/// proofs-of-possession.  Please see the module level doumentation
62/// for additional discussion and notes on security.
63///
64/// We foresee this type primarily being used to batch several
65/// `BitPoPSignedMessage`s into one verification.  We do not track
66/// aggreggated public keys here, instead merging multiples signers
67/// public keys anytime they sign the same message, so this type
68/// essentially provides only fast batch verificartion.  
69/// In principle, our `add_*` methods suffice for building an actual
70/// aggregate signature type.  Yet, normally direct approaches like
71/// `BitPoPSignedMessage` work better for aggregation because
72/// the `ProofsOfPossession` trait tooling permits both enforce the
73/// proofs-of-possession and provide a compact serialization.
74/// We see no reason to support serialization for this type as present.
75/// message assumptions, or other aggre
76///
77/// In principle, one might combine proof-of-possession with distinct
78/// message assumptions, or other aggregation strategies, when
79/// verifiers have only observed a subset of the proofs-of-possession,
80/// but this sounds complex or worse fragile.
81///
82/// TODO: Implement gaussian elimination verification scheme.
83use core::iter::once;
84
85use double::PublicKeyInSignatureGroup;
86use single::PublicKey;
87
88#[derive(Clone)]
89pub struct SignatureAggregatorAssumingPoP<E: EngineBLS> {
90    message: Message,
91    aggregated_publickey: PublicKey<E>,
92    signature: Signature<E>,
93    aggregated_auxiliary_public_key: PublicKeyInSignatureGroup<E>,
94}
95
96impl<E: EngineBLS> SignatureAggregatorAssumingPoP<E> {
97    pub fn new(message: Message) -> SignatureAggregatorAssumingPoP<E> {
98        SignatureAggregatorAssumingPoP {
99            message: message,
100            aggregated_publickey: PublicKey(E::PublicKeyGroup::zero()),
101            signature: Signature(E::SignatureGroup::zero()),
102            aggregated_auxiliary_public_key: PublicKeyInSignatureGroup(E::SignatureGroup::zero()),
103        }
104    }
105
106    /// Add only a `Signature<E>` to our internal signature.
107    ///
108    /// Useful for constructing an aggregate signature, but we
109    pub fn add_signature(&mut self, signature: &Signature<E>) {
110        self.signature.0 += &signature.0;
111    }
112
113    /// Add only a `PublicKey<E>` to our internal data.
114    ///
115    /// Useful for constructing an aggregate signature, but we
116    /// recommend instead using a custom types like `BitPoPSignedMessage`.
117    pub fn add_publickey(&mut self, publickey: &PublicKey<E>) {
118        self.aggregated_publickey.0 += publickey.0;
119    }
120
121    /// Aggregate the auxiliary public keys in the signature group to be used verification using aux key
122    pub fn add_auxiliary_public_key(
123        &mut self,
124        publickey_in_signature_group: &PublicKeyInSignatureGroup<E>,
125    ) {
126        self.aggregated_auxiliary_public_key.0 += publickey_in_signature_group.0;
127    }
128
129    /// Returns the aggergated public key.
130    ///
131    pub fn aggregated_publickey(&self) -> PublicKey<E> {
132        self.aggregated_publickey
133    }
134
135    // /// Aggregage BLS signatures assuming they have proofs-of-possession
136    // /// TODO this function should return Result refusing to aggregate messages
137    // /// different than the message the aggregator is initiated at
138    // pub fn aggregate<'a,S>(&mut self, signed: &'a S)
139    // where
140    //     &'a S: Signed<E=E>,
141    //     <&'a S as Signed>::PKG: Borrow<PublicKey<E>>,
142    // {
143    //     let signature = signed.signature();
144    //     for (message,pubickey) in signed.messages_and_publickeys() {
145    //         self.add_message_n_publickey(message.borrow(),pubickey.borrow());
146    //     }
147    //     self.add_signature(&signature);
148    // }
149
150    pub fn verify_using_aggregated_auxiliary_public_keys<
151        RandomOracle: DynDigest + Default + Clone,
152    >(
153        &self,
154    ) -> bool {
155        verify_using_aggregated_auxiliary_public_keys::<E, RandomOracle>(
156            self,
157            true,
158            self.aggregated_auxiliary_public_key.0,
159        )
160    }
161}
162
163impl<'a, E: EngineBLS> Signed for &'a SignatureAggregatorAssumingPoP<E> {
164    type E = E;
165
166    type M = Message;
167    type PKG = PublicKey<Self::E>;
168    type PKnM = ::core::iter::Once<(Message, PublicKey<E>)>;
169
170    fn messages_and_publickeys(self) -> Self::PKnM {
171        once((self.message.clone(), self.aggregated_publickey)) // TODO:  Avoid clone
172    }
173
174    fn signature(&self) -> Signature<E> {
175        self.signature
176    }
177
178    fn verify(self) -> bool {
179        // We have already aggregated distinct messages, so our distinct
180        // message verification code provides reasonable optimizations,
181        // except the public keys might not be normalized here.
182        // We foresee verification via gaussian elimination being faster,
183        // but requires affine keys or normalization.
184        verify_with_distinct_messages(self, true)
185        // TODO: verify_with_gaussian_elimination(self)
186    }
187}
188
189#[cfg(all(test, feature = "std"))]
190mod tests {
191
192    use crate::EngineBLS;
193    use crate::Keypair;
194    use crate::Message;
195    use crate::TinyBLS;
196    use crate::UsualBLS;
197    use rand::thread_rng;
198    use sha2::Sha256;
199
200    use ark_bls12_377::Bls12_377;
201    use ark_bls12_381::Bls12_381;
202
203    use super::*;
204
205    #[test]
206    fn verify_aggregate_single_message_single_signer() {
207        let good = Message::new(b"ctx", b"test message");
208
209        let mut keypair =
210            Keypair::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::generate(thread_rng());
211        let good_sig0 = keypair.sign(&good);
212        assert!(good_sig0.verify(&good, &keypair.public));
213    }
214
215    #[test]
216    fn verify_aggregate_single_message_multi_signers() {
217        let good = Message::new(b"ctx", b"test message");
218
219        let mut keypair0 =
220            Keypair::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::generate(thread_rng());
221        let good_sig0 = keypair0.sign(&good);
222
223        let mut keypair1 =
224            Keypair::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::generate(thread_rng());
225        let good_sig1 = keypair1.sign(&good);
226
227        let mut aggregated_sigs =
228            SignatureAggregatorAssumingPoP::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::new(good);
229        aggregated_sigs.add_signature(&good_sig0);
230        aggregated_sigs.add_signature(&good_sig1);
231
232        aggregated_sigs.add_publickey(&keypair0.public);
233        aggregated_sigs.add_publickey(&keypair1.public);
234
235        assert!(
236            aggregated_sigs.verify() == true,
237            "good aggregated signature of a single message with multiple key does not verify"
238        );
239    }
240
241    #[test]
242    fn verify_aggregate_single_message_repetative_signers() {
243        let good = Message::new(b"ctx", b"test message");
244
245        let mut keypair =
246            Keypair::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::generate(thread_rng());
247        let good_sig = keypair.sign(&good);
248
249        let mut aggregated_sigs =
250            SignatureAggregatorAssumingPoP::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::new(good);
251        aggregated_sigs.add_signature(&good_sig);
252        aggregated_sigs.add_signature(&good_sig);
253
254        aggregated_sigs.add_publickey(&keypair.public);
255        aggregated_sigs.add_publickey(&keypair.public);
256
257        assert!(
258            aggregated_sigs.verify() == true,
259            "good aggregate of a repetitive signature does not verify"
260        );
261    }
262
263    #[test]
264    fn aggregate_of_signature_of_a_wrong_message_should_not_verify() {
265        let good0 = Message::new(b"ctx", b"Space over Tab");
266        let bad1 = Message::new(b"ctx", b"Tab over Space");
267
268        let mut keypair0 =
269            Keypair::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::generate(thread_rng());
270        let good_sig0 = keypair0.sign(&good0);
271
272        let mut keypair1 =
273            Keypair::<UsualBLS<Bls12_381, ark_bls12_381::Config>>::generate(thread_rng());
274        let bad_sig1 = keypair1.sign(&bad1);
275
276        let mut aggregated_sigs = SignatureAggregatorAssumingPoP::<
277            UsualBLS<Bls12_381, ark_bls12_381::Config>,
278        >::new(good0);
279        aggregated_sigs.add_signature(&good_sig0);
280        aggregated_sigs.add_signature(&bad_sig1);
281
282        aggregated_sigs.add_publickey(&keypair0.public);
283        aggregated_sigs.add_publickey(&keypair1.public);
284
285        assert!(
286            aggregated_sigs.verify() == false,
287            "aggregated signature of a wrong message should not verify"
288        );
289    }
290
291    #[test]
292    fn test_aggregate_tiny_sigs_and_verify_in_g1() {
293        let message = Message::new(b"ctx", b"test message");
294        let mut keypairs: Vec<_> = (0..3)
295            .into_iter()
296            .map(|_| Keypair::<TinyBLS<Bls12_377, ark_bls12_377::Config>>::generate(thread_rng()))
297            .collect();
298        let pub_keys_in_sig_grp: Vec<PublicKeyInSignatureGroup<TinyBLS377>> = keypairs
299            .iter()
300            .map(|k| k.into_public_key_in_signature_group())
301            .collect();
302
303        let mut aggregator = SignatureAggregatorAssumingPoP::<TinyBLS377>::new(message.clone());
304        let mut aggregated_public_key =
305            PublicKey::<TinyBLS377>(<TinyBLS377 as EngineBLS>::PublicKeyGroup::zero());
306
307        for k in &mut keypairs {
308            aggregator.add_signature(&k.sign(&message));
309            aggregated_public_key.0 += k.public.0;
310        }
311
312        let mut verifier_aggregator = SignatureAggregatorAssumingPoP::<TinyBLS377>::new(message);
313
314        verifier_aggregator.add_signature(&aggregator.signature);
315        verifier_aggregator.add_publickey(&aggregated_public_key);
316
317        for k in &pub_keys_in_sig_grp {
318            verifier_aggregator.add_auxiliary_public_key(k);
319        }
320
321        assert!(
322            verifier_aggregator.verify_using_aggregated_auxiliary_public_keys::<Sha256>(),
323            "verifying with honest auxilary public key should pass"
324        );
325
326        //false aggregation in signature group should fails verification.
327        verifier_aggregator
328            .add_auxiliary_public_key(&keypairs[0].into_public_key_in_signature_group());
329        assert!(
330            !verifier_aggregator.verify_using_aggregated_auxiliary_public_keys::<Sha256>(),
331            "verification using non-matching auxilary public key should fail"
332        );
333    }
334}