tnt_bls/
verifiers.rs

1//! ## Algorithms for optimized verification of aggregate and batched BLS signatures.
2//!
3//!
4//!
5
6use core::borrow::Borrow;
7#[cfg(feature = "std")]
8use std::collections::HashMap;
9
10#[cfg(feature = "std")]
11use ark_ec::AffineRepr;
12#[cfg(feature = "std")]
13use ark_ff::field_hashers::{DefaultFieldHasher, HashToField};
14#[cfg(feature = "std")]
15use ark_serialize::CanonicalSerialize;
16#[cfg(feature = "std")]
17use digest::DynDigest;
18#[cfg(feature = "std")]
19use digest::FixedOutputReset;
20
21use ark_ec::CurveGroup;
22
23use alloc::vec::Vec;
24
25use super::*;
26
27// We define these convenience type alias here instead of engine.rs
28// because seemingly only verifier implementations really employ them.
29// And we `pub use engine::*` in lib.rs.
30
31/// Convenience type alias for projective form of `PublicKeyGroup`
32pub type PublicKeyProjective<E> = <E as EngineBLS>::PublicKeyGroup;
33
34/// Convenience type alias for affine form of `PublicKeyGroup`
35pub type PublicKeyAffine<E> = <<E as EngineBLS>::PublicKeyGroup as CurveGroup>::Affine;
36
37/// Convenience type alias for projective form of `SignatureGroup`
38pub type SignatureProjective<E> = <E as EngineBLS>::SignatureGroup;
39
40/// Convenience type alias for affine form of `SignatureGroup`
41pub type SignatureAffine<E> = <<E as EngineBLS>::SignatureGroup as CurveGroup>::Affine;
42
43/// Simple unoptimized BLS signature verification.  Useful for testing.
44pub fn verify_unoptimized<S: Signed>(s: S) -> bool {
45    let signature = S::E::prepare_signature(s.signature().0);
46    let prepared = s
47        .messages_and_publickeys()
48        .map(|(message, public_key)| {
49            (
50                S::E::prepare_public_key(public_key.borrow().0),
51                S::E::prepare_signature(message.borrow().hash_to_signature_curve::<S::E>()),
52            )
53        })
54        .collect::<Vec<(_, _)>>();
55    S::E::verify_prepared(signature, prepared.iter())
56}
57
58/// Simple universal BLS signature verification
59///
60/// We support an unstable `Signed::messages_and_publickeys()`
61/// securely by calling it only once and batch normalizing all
62/// points, as do most other verification routines here.
63/// We do no optimizations that reduce the number of pairings
64/// by combining repeated messages or signers.
65pub fn verify_simple<S: Signed>(s: S) -> bool {
66    let signature = s.signature().0;
67    // We could write this more idiomatically using iterator adaptors,
68    // and avoiding an unecessary allocation for publickeys, but only
69    // by calling self.messages_and_publickeys() repeatedly.
70    let itr = s.messages_and_publickeys();
71    let l = {
72        let (lower, upper) = itr.size_hint();
73        upper.unwrap_or(lower)
74    };
75    let mut gpk = Vec::with_capacity(l);
76    let mut gms = Vec::with_capacity(l + 1);
77    for (message, publickey) in itr {
78        gpk.push(publickey.borrow().0.clone());
79        gms.push(message.borrow().hash_to_signature_curve::<S::E>());
80    }
81    let gpk = <<S as Signed>::E as EngineBLS>::PublicKeyGroup::normalize_batch(gpk.as_mut_slice());
82    gms.push(signature);
83    let mut gms =
84        <<S as Signed>::E as EngineBLS>::SignatureGroup::normalize_batch(gms.as_mut_slice());
85    let signature = <<S as Signed>::E as EngineBLS>::prepare_signature(gms.pop().unwrap());
86    let prepared = gpk
87        .iter()
88        .zip(gms)
89        .map(|(pk, m)| {
90            (
91                <<S as Signed>::E as EngineBLS>::prepare_public_key(*pk),
92                <<S as Signed>::E as EngineBLS>::prepare_signature(m),
93            )
94        })
95        .collect::<Vec<(_, _)>>();
96    S::E::verify_prepared(signature, prepared.iter())
97}
98
99/// BLS signature verification optimized for all unique messages
100///
101/// Assuming all messages are distinct, the minimum number of pairings
102/// is the number of unique signers, which we achieve here.
103/// We do not verify message uniqueness here, but leave this to the
104/// aggregate signature type, like `DistinctMessages`.
105///
106/// We merge any messages with identical signers and batch normalize
107/// message points and the signature itself.
108/// We optionally batch normalize the public keys in the event that
109/// they are provided by algerbaic operaations, but this sounds
110/// unlikely given our requirement that messages be distinct.
111#[cfg(feature = "std")]
112pub fn verify_with_distinct_messages<S: Signed>(signed: S, normalize_public_keys: bool) -> bool {
113    let signature = signed.signature().0;
114    // We first hash the messages to the signature curve and
115    // normalize the public keys to operate on them as bytes.
116    // TODO: Assess if we should mutate in place using interior
117    // mutability, maybe using `BorrowMut` support in
118    // `batch_normalization`.
119    let itr = signed.messages_and_publickeys();
120    let l = {
121        let (lower, upper) = itr.size_hint();
122        upper.unwrap_or(lower)
123    };
124    let mut publickeys = Vec::with_capacity(l);
125    let mut messages = Vec::with_capacity(l + 1);
126    for (m, pk) in itr {
127        publickeys.push(pk.borrow().0.clone());
128        messages.push(m.borrow().hash_to_signature_curve::<S::E>());
129    }
130    let mut affine_publickeys = if normalize_public_keys {
131        <<S as Signed>::E as EngineBLS>::PublicKeyGroup::normalize_batch(&publickeys)
132    } else {
133        publickeys
134            .iter()
135            .map(|pk| pk.into_affine())
136            .collect::<Vec<_>>()
137    };
138
139    // We next accumulate message points with the same signer.
140    // We could avoid the allocation here if we sorted both
141    // arrays in parallel.  This might mean (a) some sort function
142    // using `ops::IndexMut` instead of slices, and (b) wrapper types
143    // types to make tuples of slices satisfy `ops::IndexMut`.
144    // TODO:  Impl PartialEq, Eq, Hash for pairing::EncodedPoint
145    // to avoid  struct H(E::PublicKeyGroup::Affine::Uncompressed);
146    type AA<E> = (PublicKeyAffine<E>, SignatureProjective<E>);
147    let mut pks_n_ms = HashMap::with_capacity(l);
148    for (pk, m) in affine_publickeys.drain(..).zip(messages.drain(..)) {
149        let mut pk_uncompressed = vec![0; pk.uncompressed_size()];
150        pk.serialize_uncompressed(&mut pk_uncompressed[..]).unwrap();
151        pks_n_ms
152            .entry(pk_uncompressed)
153            .and_modify(|(_pk0, m0): &mut AA<S::E>| {
154                *m0 += m;
155            })
156            .or_insert((pk, m));
157    }
158
159    let mut publickeys = Vec::with_capacity(l);
160    for (_, (pk, m)) in pks_n_ms.drain() {
161        messages.push(m);
162        publickeys.push(pk.clone());
163    }
164
165    // We finally normalize the messages and signature
166
167    messages.push(signature);
168    let mut affine_messages =
169        <<S as Signed>::E as EngineBLS>::SignatureGroup::normalize_batch(messages.as_mut_slice());
170    let signature =
171        <<S as Signed>::E as EngineBLS>::prepare_signature(affine_messages.pop().unwrap());
172    // TODO: Assess if we could cache normalized message hashes anyplace
173    // using interior mutability, but probably this does not work well
174    // with ur optimization of collecting messages with thesame signer.
175
176    // And verify the aggregate signature.
177    let prepared = publickeys
178        .iter()
179        .zip(messages)
180        .map(|(pk, m)| {
181            (
182                <<S as Signed>::E as EngineBLS>::prepare_public_key(*pk),
183                <<S as Signed>::E as EngineBLS>::prepare_signature(m),
184            )
185        })
186        .collect::<Vec<(_, _)>>();
187
188    S::E::verify_prepared(signature, prepared.iter())
189
190    //let prepared = publickeys.iter().zip(&messages);
191    //S::E::verify_prepared( &signature, prepared )
192}
193
194/// BLS signature verification optimized for all unique messages
195///
196/// Assuming all messages are distinct, the minimum number of pairings
197/// is the number of unique signers, which we achieve here.
198/// We do not verify message uniqueness here, but leave this to the
199/// aggregate signature type, like `DistinctMessages`.
200///
201/// We merge any messages with identical signers and batch normalize
202/// message points and the signature itself.
203/// We optionally batch normalize the public keys in the event that
204/// they are provided by algerbaic operaations, but this sounds
205/// unlikely given our requirement that messages be distinct.
206#[cfg(feature = "std")]
207pub fn verify_using_aggregated_auxiliary_public_keys<
208    E: EngineBLS,
209    H: DynDigest + FixedOutputReset + Default + Clone,
210>(
211    signed: &single_pop_aggregator::SignatureAggregatorAssumingPoP<E>,
212    normalize_public_keys: bool,
213    aggregated_aux_pub_key: <E as EngineBLS>::SignatureGroup,
214) -> bool {
215    let signature = Signed::signature(&signed).0;
216
217    let mut signature_as_bytes = vec![0; signature.compressed_size()];
218    signature
219        .serialize_compressed(&mut signature_as_bytes[..])
220        .expect("compressed size has been alocated");
221
222    let itr = signed.messages_and_publickeys();
223    let l = {
224        let (lower, upper) = itr.size_hint();
225        upper.unwrap_or(lower)
226    };
227    let (first_message, first_public_key) = match signed.messages_and_publickeys().next() {
228        Some((first_message, first_public_key)) => (first_message, first_public_key),
229        None => return false,
230    };
231
232    let mut first_public_key_as_bytes = vec![0; first_public_key.compressed_size()];
233    first_public_key
234        .serialize_compressed(&mut first_public_key_as_bytes[..])
235        .expect("compressed size has been alocated");
236
237    let first_message_point = first_message.hash_to_signature_curve::<E>();
238    let first_message_point_as_bytes = E::signature_point_to_byte(&first_message_point);
239
240    let mut aggregated_aux_pub_key_as_bytes = vec![0; aggregated_aux_pub_key.compressed_size()];
241    aggregated_aux_pub_key
242        .serialize_compressed(&mut aggregated_aux_pub_key_as_bytes[..])
243        .expect("compressed size has been alocated");
244
245    // We first hash the messages to the signature curve and
246    // normalize the public keys to operate on them as bytes.
247    // TODO: Assess if we should mutate in place using interior
248    // mutability, maybe using `BorrowMut` support in
249    // `batch_normalization`.
250
251    // deterministic randomness for adding aggregated auxiliary pub keys
252    //TODO you can't just assume that there is one pubickey you need to stop if they were more or aggregate them
253
254    let pseudo_random_scalar_seed = [
255        first_message_point_as_bytes,
256        first_public_key_as_bytes,
257        aggregated_aux_pub_key_as_bytes,
258        signature_as_bytes,
259    ]
260    .concat();
261
262    let hasher = <DefaultFieldHasher<H> as HashToField<E::Scalar>>::new(&[]);
263    let pseudo_random_scalar: E::Scalar =
264        hasher.hash_to_field::<1>(&pseudo_random_scalar_seed[..])[0];
265
266    let signature = signature + aggregated_aux_pub_key * pseudo_random_scalar;
267
268    let mut publickeys = Vec::with_capacity(l);
269    let mut messages = Vec::with_capacity(l + 1);
270
271    //Simplify from here on.
272    for (m, pk) in itr {
273        publickeys.push(pk.0.clone());
274        messages.push(
275            m.hash_to_signature_curve::<E>()
276                + E::SignatureGroupAffine::generator() * pseudo_random_scalar,
277        );
278    }
279
280    let mut affine_publickeys = if normalize_public_keys {
281        <E as EngineBLS>::PublicKeyGroup::normalize_batch(&publickeys)
282    } else {
283        publickeys
284            .iter()
285            .map(|pk| pk.into_affine())
286            .collect::<Vec<_>>()
287    };
288
289    // We next accumulate message points with the same signer.
290    // We could avoid the allocation here if we sorted both
291    // arrays in parallel.  This might mean (a) some sort function
292    // using `ops::IndexMut` instead of slices, and (b) wrapper types
293    // types to make tuples of slices satisfy `ops::IndexMut`.
294    // TODO:  Impl PartialEq, Eq, Hash for pairing::EncodedPoint
295    // to avoid  struct H(E::PublicKeyGroup::Affine::Uncompressed);
296    type AA<E> = (PublicKeyAffine<E>, SignatureProjective<E>);
297    let mut pks_n_ms = HashMap::with_capacity(l);
298    for (pk, m) in affine_publickeys.drain(..).zip(messages.drain(..)) {
299        let mut pk_uncompressed = vec![0; pk.uncompressed_size()];
300        pk.serialize_uncompressed(&mut pk_uncompressed[..]).unwrap();
301        pks_n_ms
302            .entry(pk_uncompressed)
303            .and_modify(|(_pk0, m0): &mut AA<E>| {
304                *m0 += m;
305            })
306            .or_insert((pk, m));
307    }
308
309    let mut publickeys = Vec::with_capacity(l);
310    for (_, (pk, m)) in pks_n_ms.drain() {
311        messages.push(m);
312        publickeys.push(pk.clone());
313    }
314
315    // We finally normalize the messages and signature
316
317    messages.push(signature);
318    let mut affine_messages =
319        <E as EngineBLS>::SignatureGroup::normalize_batch(messages.as_mut_slice());
320    let signature = <E as EngineBLS>::prepare_signature(affine_messages.pop().unwrap());
321    // TODO: Assess if we could cache normalized message hashes anyplace
322    // using interior mutability, but probably this does not work well
323    // with ur optimization of collecting messages with thesame signer.
324
325    // And verify the aggregate signature.
326    let prepared = publickeys
327        .iter()
328        .zip(messages)
329        .map(|(pk, m)| {
330            (
331                <E as EngineBLS>::prepare_public_key(*pk),
332                <E as EngineBLS>::prepare_signature(m),
333            )
334        })
335        .collect::<Vec<(_, _)>>();
336
337    E::verify_prepared(signature, prepared.iter())
338
339    //let prepared = publickeys.iter().zip(&messages);
340    //S::E::verify_prepared( &signature, prepared )
341}
342
343/*
344/// Excessively optimized BLS signature verification
345///
346/// We minimize the number of pairing operations by doing two
347/// basis change operation using Gaussian elimination, first in the
348/// message space and then in the signer space.  As a result, we
349/// do only `1 + min(msg_d,pk_d)` pairings where `msg_d` and `pk_d`
350/// are the numbers of distinct messages and signers, respectively.
351///
352/// We expect this to improve performance dramatically when both
353/// signers and messages are repeated enough, simpler strategies
354/// work as well or better when say messages are distinct.
355///
356/// Explination:
357///
358/// We consider the bipartite graph with vertex sets given by points
359/// on the two curves and edges given by desired pairings between them.
360/// We let $M$ denote the bipartite adjacency matrix for this graph,
361/// so that multiplying $M$ on the the right and left by the vectors
362/// of messages and signers respectively reproduces our original sum
363/// of pairings.
364///
365/// We first use elementary "row" operations to make $M$ upper
366/// triangular, as in Gaussian elimination, but at the cost of also
367/// performing one-sided "change of basis" operations that collect
368/// our original "basis vectors" into sums of curve points.
369/// We next use elementary "column" operations to make $M$ diagonal,
370/// again adjusting the basis with curve point operations.
371///
372/// In this, we regard $M$ as a matrix over the scalar field $F_p$
373/// so we may do row or column swaps and row or column addition
374/// operations with small scalars, but not lone row or column scalar
375/// multiplication because these always involve divisions, which
376/// produces large curve points that slow us down thereafter.
377/// We do not require such divisions because we do not solve any
378/// system of equations and do not need ones on the diagonal.
379///
380/// TODO:
381/// We leave implementing this optimization to near future work
382/// because it benifits from public keys being affine or having
383/// another hashable representation.
384///
385///
386/// As a curiosity, we note one interesting but suboptimal algorithm
387/// that avoids small scalar multiplications when doing this:
388///
389/// If we ignore subtraction, then the minimal number of pairing
390/// operations required to verify aggregated BLS signatures is the
391/// minimal bipartite edge cover, aka bipartite dimension, of the
392/// bipartite graph with vertices given by points on the two curves
393/// and edges given by desired pairings.
394/// In general, this problem is NP-hard even to approximate.
395/// See:  https://en.wikipedia.org/wiki/Bipartite_dimension
396///
397/// There are polynomial time algorithms for bipartite edge cover in
398/// special cases, with domino-free graphs being among the widest
399/// known classes.  See:
400/// Amilhastre, Jérôme; Janssen, Philippe; Vilarem, Marie-Catherine,
401/// "Computing a minimum biclique cover is polynomial for bipartite domino-free graphs" (1997)
402/// https://core.ac.uk/download/pdf/82546650.pdf
403///
404/// If we now exploit subtraction, then these dominos can be
405/// completed into $K_{3,3}$s, like
406///  $(a,x)+(a,y)+(b,x)+(b,y)+(b,z)+(c,y)+(c,z) = (a+b+c,x+y+z) - (a,z) - (c,z)$
407/// which looks optimal for itself, and likely permits the further
408/// aggregation, and maybe the subtracted terms can be aggregated later.
409///
410/// We could not however find the optimal numbers of pairings by
411/// completing dominos like this because (a+b+c,x+y+z) - (b,y),
412/// which looks optimal for itself, but only has one subtraction.
413fn verify_with_gaussian_elimination<S: Signed>(s: S) -> bool {
414    unimplemented!()
415}
416
417*/