w3f_bls/
lib.rs

1//! # Aggregate BLS signature library with extensive tuning options.
2//!
3//! In short, anyone using BLS signatures should normally choose both
4//! an orientation as well as some aggregation and batching strategies
5//! These two decissions impact performance dramaticaly, but making
6//! the optimal choises requires some attentiom.  This crate employs
7//! convenient abstraction boundaries between curver arithmatic,
8//! verifier routines, and aggregated and/or batched BLS signatures.
9//!
10//! ### Pairings
11//!
12//! If we have two elliptic curve with a pairing `e`, then
13//! a BLS signature `sigma = s*H(msg)` by a public key `S = s g1`
14//! can be verified with the one equation `e(g1,sigma) = e(S,H(msg))`.
15//! These simple BLS signatures are very slow to verify however
16//! because the pairing map `e` is far slower than many cryptographic
17//! primitives.
18//!
19//! Our pairing `e` maps from a small curve over `F(q)` and a larger
20//! curve over `F(q^2)` into some multipliccative group if a field,
21//! normally over `F(q^12)`.  In principle, this map `e` into `F(q^12)`
22//! makes pairing based cryptography like BLS less secure than
23//! other elliptic curve based cryptography, which further slows down
24//! BLS signatures by requiring larger `q`.
25//!
26//! ### Arithmatic
27//!
28//! An almost universally applicable otimization is to seperate the
29//! "Miller loop" that computes in `F(q)` and `F(q^2)` from the slow
30//! final exponentiation that happens in `F(q^12)`.  So our actual
31//! verification equation more resembles `e(-g1,sigma) e(S,H(msg)) = 1`.
32//!
33//! As one curve is smaller and hence faster, the user should choose
34//! which orientation of curves they prefer, meaning to which curve
35//! they hash, and which curves hold the signatues and public keys.
36//! In other words, your desired aggregation techniques and usage
37//! characteristics should determine if youp refer the verification
38//! equation `e(g1,sigma) = e(S,H(msg))` or the fliped form
39//! `e(sigma,g2) = e(H(msg),S)`.  See `UsualBLS` and `TinyBLS`.
40//!
41//! ### Aggregation
42//!
43//! We consder BLS signatures interesting because they support
44//! dramatic optimizations when handling multiple signatures together.
45//! In fact, BLS signatures support aggregation by a third party
46//! that makes signatures smaller, not merely batch verification.  
47//! All this stems from the bilinearity of `e`, meaning we reduce
48//! the number of pairings, or size of the miller loop, by appling
49//! rules like `e(x,z)e(y,z) = e(x+y,z)`, `e(x,y)e(x,z) = e(x,y+z)`,
50//! etc.
51//!
52//! In essence, our aggregation tricks fall into two categories,
53//! linear aggregation, in which only addition is used, and
54//! delinearized optimiztions, in which we multiply curve points
55//! by values unforseeable to the signers.
56//! In general, linear techniques provide much better performance,
57//! but require stronger invariants be maintained by the caller,
58//! like messages being distinct, or limited signer sets with
59//! proofs-of-possession.  Also, the delinearized techniques remain
60//! secure without tricky assumptions, but require more computation.
61//!
62//! ### Verification
63//!
64//! We can often further reduce the pairings required in the
65//! verification equation, beyond the naieve information tracked
66//! by the aggregated signature itself.  Aggregated signature must
67//! state all the individual messages and/or public keys, but
68//! verifiers may collapse anything permitted.
69//! We thus encounter aggregation-like decissions that impact
70//! verifier performance.
71//!
72//! We therefore provide an abstract interface that permits
73//! doing further aggregation and/or passing any aggregate signature
74//! to any verification routine.
75//!
76//! As a rule, we also attempt to batch normalize different arithmatic
77//! outputs, but concievably small signer set sizes might make this
78//! a pessimization.
79//!
80//!
81//!
82
83//#![feature(test)] needed for cargo bench
84#![cfg_attr(not(feature = "std"), no_std)]
85#[cfg_attr(feature = "std", doc = include_str!("../README.md"))]
86#[cfg(doctest)]
87pub struct ReadmeDoctests;
88
89extern crate ark_serialize;
90extern crate ark_serialize_derive;
91
92extern crate ark_ec;
93extern crate ark_ff;
94extern crate digest;
95extern crate rand;
96extern crate rand_chacha;
97extern crate rand_core;
98extern crate sha3;
99
100extern crate alloc;
101
102use core::borrow::Borrow;
103use digest::DynDigest;
104
105pub mod chaum_pedersen_signature;
106pub mod double;
107pub mod double_pop;
108pub mod engine;
109pub mod schnorr_pop;
110pub mod serialize;
111pub mod single;
112pub mod verifiers;
113
114#[cfg(feature = "std")]
115pub mod multi_pop_aggregator;
116#[cfg(feature = "std")]
117pub mod single_pop_aggregator;
118
119#[cfg(feature = "experimental")]
120pub mod bit;
121#[cfg(feature = "experimental")]
122pub mod delinear;
123#[cfg(feature = "experimental")]
124pub mod distinct;
125
126pub use engine::*;
127
128pub use double::{
129    DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, PublicKeyInSignatureGroup,
130};
131pub use double_pop::{NuggetBLSPoP, NuggetBLSnCPPoP};
132pub use schnorr_pop::SchnorrProof;
133pub use serialize::SerializableToBytes;
134pub use single::{Keypair, KeypairVT, PublicKey, SecretKey, SecretKeyVT, Signature, SignedMessage};
135
136use alloc::vec::Vec;
137
138/// Internal message hash size.  
139///
140/// We choose 256 bits here so that birthday bound attacks cannot
141/// find messages with the same hash.
142const MESSAGE_SIZE: usize = 32;
143
144/// Ciphersuite standards from BLS signature draft IETF proposal
145const PROOF_OF_POSSESSION_ID: &'static [u8] = b"BLS_POP_";
146const NORMAL_MESSAGE_SIGNATURE_ID: &'static [u8] = b"BLS_SIG_";
147
148const NORMAL_MESSAGE_SIGNATURE_ASSUMING_POP: &'static [u8] = b"POP_";
149const NORMAL_MESSAGE_SIGNATURE_BASIC: &'static [u8] = b"NUL_";
150const POP_MESSAGE: &'static [u8] = b"POP_";
151
152type MessageDigest = [u8; MESSAGE_SIZE];
153/// Internal message hash type.  Short for frequent rehashing
154/// by `HashMap`, etc.
155#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
156pub struct Message(pub MessageDigest, pub alloc::vec::Vec<u8>, MessageType);
157
158#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
159enum MessageType {
160    ProofOfPossession,
161    NormalAssumingPoP,
162    NormalBasic,
163}
164
165impl Message {
166    pub fn new(context: &[u8], message: &[u8]) -> Message {
167        let msg_hash = Self::compute_internal_hash(context, message);
168        Message(
169            msg_hash,
170            [context, message].concat(),
171            MessageType::NormalBasic,
172        )
173    }
174
175    pub fn new_assuming_pop(context: &[u8], message: &[u8]) -> Message {
176        let msg_hash = Self::compute_internal_hash(context, message);
177        Message(
178            msg_hash,
179            [context, message].concat(),
180            MessageType::NormalAssumingPoP,
181        )
182    }
183
184    pub fn new_pop_message(context: &[u8], message: &[u8]) -> Message {
185        let msg_hash = Self::compute_internal_hash(context, message);
186        Message(
187            msg_hash,
188            [context, message].concat(),
189            MessageType::ProofOfPossession,
190        )
191    }
192
193    fn compute_internal_hash(context: &[u8], message: &[u8]) -> [u8; MESSAGE_SIZE] {
194        use sha3::{
195            digest::{ExtendableOutput, Update, XofReader},
196            Shake128,
197        };
198        let mut h = Shake128::default();
199        h.update(context);
200        let l = message.len() as u64;
201        h.update(&l.to_le_bytes());
202        h.update(message);
203
204        let mut msg_hash = [0u8; MESSAGE_SIZE];
205        h.finalize_xof().read(&mut msg_hash[..]);
206
207        msg_hash
208    }
209
210    /// generate ciphersuite string added to the context according to
211    /// BLS Signature draft proposal to IETF
212    fn cipher_suite<E: EngineBLS>(&self) -> Vec<u8> {
213        let id = match self.2 {
214            MessageType::ProofOfPossession => PROOF_OF_POSSESSION_ID,
215            _ => NORMAL_MESSAGE_SIGNATURE_ID,
216        };
217
218        let h2c_suite_id = [
219            E::CURVE_NAME,
220            E::SIG_GROUP_NAME,
221            E::CIPHER_SUIT_DOMAIN_SEPARATION,
222        ]
223        .concat();
224
225        let sc_tag = match self.2 {
226            MessageType::ProofOfPossession => POP_MESSAGE,
227            MessageType::NormalAssumingPoP => NORMAL_MESSAGE_SIGNATURE_ASSUMING_POP,
228            _ => NORMAL_MESSAGE_SIGNATURE_BASIC,
229        };
230
231        [id, &h2c_suite_id[..], sc_tag].concat()
232    }
233
234    pub fn hash_to_signature_curve<E: EngineBLS>(&self) -> E::SignatureGroup {
235        E::hash_to_signature_curve(&[&self.cipher_suite::<E>()[..], &self.1[..]].concat()[..])
236    }
237}
238
239impl<'a> From<&'a [u8]> for Message {
240    fn from(x: &[u8]) -> Message {
241        Message::new(b"", x)
242    }
243}
244
245/// Representation of an aggregated BLS signature.
246///
247/// We implement this trait only for borrows of appropriate structs
248/// because otherwise we'd need extensive lifetime plumbing here,
249/// due to the absence of assocaited type constructers (ATCs).
250/// We shall make `messages_and_publickeys` take `&sefl` and
251/// remove these limitations in the future once ATCs stabalize,
252/// thus removing `PKG`.  See [Rust RFC 1598](https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md)
253/// We shall eventually remove MnPK entirely whenever `-> impl Trait`
254/// in traits gets stabalized.  See [Rust RFCs 1522, 1951, and 2071](https://github.com/rust-lang/rust/issues/34511
255pub trait Signed: Sized {
256    type E: EngineBLS;
257
258    /// Return the aggregated signature
259    fn signature(&self) -> Signature<Self::E>;
260
261    type M: Borrow<Message>; // = Message;
262    type PKG: Borrow<PublicKey<Self::E>>; // = PublicKey<Self::E>;
263
264    /// Iterator over, messages and public key reference pairs.
265    type PKnM: Iterator<Item = (Self::M, Self::PKG)> + ExactSizeIterator;
266    // type PKnM<'a>: Iterator<Item = (
267    //    &'a <<Self as Signed<'a>>::E as EngineBLS>::PublicKeyGroup,
268    //    &'a Self::M,
269    // )> + DoubleEndedIterator + ExactSizeIterator + 'a;
270
271    /// Returns an iterator over messages and public key reference for
272    /// pairings, often only partially aggregated.
273    fn messages_and_publickeys(self) -> Self::PKnM;
274    // fn messages_and_publickeys<'a>(&'s self) -> PKnM<'a>
275    // -> impl Iterator<Item = (&'a Self::M, &'a Self::E::PublicKeyGroup)> + 'a;
276
277    /// Appropriate BLS signature verification for the `Self` type.
278    ///
279    /// We use `verify_simple` as a default implementation because
280    /// it supports unstable `self.messages_and_publickeys()` securely
281    /// by calling it only once, and does not expect pulic key points
282    /// to be normalized, but this should usually be replaced by more
283    /// optimized variants.
284    fn verify(self) -> bool {
285        verifiers::verify_simple(self)
286    }
287}
288
289pub trait ProofOfPossession<E, H, PV>
290where
291    E: EngineBLS,
292    H: DynDigest + Default + Clone,
293{
294    fn verify(&self, public_key_of_prover: &PV) -> bool;
295}
296
297/// ProofOfPossion trait which should be implemented by secret
298pub trait ProofOfPossessionGenerator<
299    E: EngineBLS,
300    H: DynDigest + Default + Clone,
301    PV,
302    P: ProofOfPossession<E, H, PV>,
303>
304{
305    /// The proof of possession generator is supposed to
306    /// to produce a schnorr signature or a bls signature using
307    /// the secret key which it claims to possess. This proves that
308    /// that the secret key is known.
309    fn generate_pok(&mut self) -> P;
310}