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
102#[cfg(feature = "serde")]
103extern crate serde;
104
105use core::borrow::Borrow;
106use digest::DynDigest;
107
108pub mod chaum_pedersen_signature;
109pub mod double;
110pub mod double_pop;
111pub mod engine;
112pub mod schnorr_pop;
113pub mod serialize;
114pub mod single;
115pub mod verifiers;
116
117#[cfg(feature = "std")]
118pub mod multi_pop_aggregator;
119#[cfg(feature = "std")]
120pub mod single_pop_aggregator;
121
122#[cfg(feature = "experimental")]
123pub mod bit;
124#[cfg(feature = "experimental")]
125pub mod delinear;
126#[cfg(feature = "experimental")]
127pub mod distinct;
128
129pub use engine::*;
130
131pub use double::{
132    DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, PublicKeyInSignatureGroup,
133};
134pub use double_pop::{NuggetBLSPoP, NuggetBLSnCPPoP};
135pub use schnorr_pop::SchnorrProof;
136pub use serialize::SerializableToBytes;
137pub use single::{Keypair, KeypairVT, PublicKey, SecretKey, SecretKeyVT, Signature, SignedMessage};
138
139use alloc::vec::Vec;
140
141/// Internal message hash size.  
142///
143/// We choose 256 bits here so that birthday bound attacks cannot
144/// find messages with the same hash.
145const MESSAGE_SIZE: usize = 32;
146
147/// Ciphersuite standards from BLS signature draft IETF proposal
148const PROOF_OF_POSSESSION_ID: &'static [u8] = b"BLS_POP_";
149const NORMAL_MESSAGE_SIGNATURE_ID: &'static [u8] = b"BLS_SIG_";
150
151const NORMAL_MESSAGE_SIGNATURE_ASSUMING_POP: &'static [u8] = b"POP_";
152const NORMAL_MESSAGE_SIGNATURE_BASIC: &'static [u8] = b"NUL_";
153const POP_MESSAGE: &'static [u8] = b"POP_";
154
155type MessageDigest = [u8; MESSAGE_SIZE];
156/// Internal message hash type.  Short for frequent rehashing
157/// by `HashMap`, etc.
158#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
159pub struct Message(pub MessageDigest, pub alloc::vec::Vec<u8>, MessageType);
160
161#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
162enum MessageType {
163    ProofOfPossession,
164    NormalAssumingPoP,
165    NormalBasic,
166}
167
168impl Message {
169    pub fn new(context: &[u8], message: &[u8]) -> Message {
170        let msg_hash = Self::compute_internal_hash(context, message);
171        Message(
172            msg_hash,
173            [context, message].concat(),
174            MessageType::NormalBasic,
175        )
176    }
177
178    pub fn new_assuming_pop(context: &[u8], message: &[u8]) -> Message {
179        let msg_hash = Self::compute_internal_hash(context, message);
180        Message(
181            msg_hash,
182            [context, message].concat(),
183            MessageType::NormalAssumingPoP,
184        )
185    }
186
187    pub fn new_pop_message(context: &[u8], message: &[u8]) -> Message {
188        let msg_hash = Self::compute_internal_hash(context, message);
189        Message(
190            msg_hash,
191            [context, message].concat(),
192            MessageType::ProofOfPossession,
193        )
194    }
195
196    fn compute_internal_hash(context: &[u8], message: &[u8]) -> [u8; MESSAGE_SIZE] {
197        use sha3::{
198            digest::{ExtendableOutput, Update, XofReader},
199            Shake128,
200        };
201        let mut h = Shake128::default();
202        h.update(context);
203        let l = message.len() as u64;
204        h.update(&l.to_le_bytes());
205        h.update(message);
206
207        let mut msg_hash = [0u8; MESSAGE_SIZE];
208        h.finalize_xof().read(&mut msg_hash[..]);
209
210        msg_hash
211    }
212
213    /// generate ciphersuite string added to the context according to
214    /// BLS Signature draft proposal to IETF
215    fn cipher_suite<E: EngineBLS>(&self) -> Vec<u8> {
216        let id = match self.2 {
217            MessageType::ProofOfPossession => PROOF_OF_POSSESSION_ID,
218            _ => NORMAL_MESSAGE_SIGNATURE_ID,
219        };
220
221        let h2c_suite_id = [
222            E::CURVE_NAME,
223            E::SIG_GROUP_NAME,
224            E::CIPHER_SUIT_DOMAIN_SEPARATION,
225        ]
226        .concat();
227
228        let sc_tag = match self.2 {
229            MessageType::ProofOfPossession => POP_MESSAGE,
230            MessageType::NormalAssumingPoP => NORMAL_MESSAGE_SIGNATURE_ASSUMING_POP,
231            _ => NORMAL_MESSAGE_SIGNATURE_BASIC,
232        };
233
234        [id, &h2c_suite_id[..], sc_tag].concat()
235    }
236
237    pub fn hash_to_signature_curve<E: EngineBLS>(&self) -> E::SignatureGroup {
238        E::hash_to_signature_curve(&[&self.cipher_suite::<E>()[..], &self.1[..]].concat()[..])
239    }
240}
241
242impl<'a> From<&'a [u8]> for Message {
243    fn from(x: &[u8]) -> Message {
244        Message::new(b"", x)
245    }
246}
247
248/// Representation of an aggregated BLS signature.
249///
250/// We implement this trait only for borrows of appropriate structs
251/// because otherwise we'd need extensive lifetime plumbing here,
252/// due to the absence of assocaited type constructers (ATCs).
253/// We shall make `messages_and_publickeys` take `&sefl` and
254/// remove these limitations in the future once ATCs stabalize,
255/// thus removing `PKG`.  See [Rust RFC 1598](https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md)
256/// We shall eventually remove MnPK entirely whenever `-> impl Trait`
257/// in traits gets stabalized.  See [Rust RFCs 1522, 1951, and 2071](https://github.com/rust-lang/rust/issues/34511
258pub trait Signed: Sized {
259    type E: EngineBLS;
260
261    /// Return the aggregated signature
262    fn signature(&self) -> Signature<Self::E>;
263
264    type M: Borrow<Message>; // = Message;
265    type PKG: Borrow<PublicKey<Self::E>>; // = PublicKey<Self::E>;
266
267    /// Iterator over, messages and public key reference pairs.
268    type PKnM: Iterator<Item = (Self::M, Self::PKG)> + ExactSizeIterator;
269    // type PKnM<'a>: Iterator<Item = (
270    //    &'a <<Self as Signed<'a>>::E as EngineBLS>::PublicKeyGroup,
271    //    &'a Self::M,
272    // )> + DoubleEndedIterator + ExactSizeIterator + 'a;
273
274    /// Returns an iterator over messages and public key reference for
275    /// pairings, often only partially aggregated.
276    fn messages_and_publickeys(self) -> Self::PKnM;
277    // fn messages_and_publickeys<'a>(&'s self) -> PKnM<'a>
278    // -> impl Iterator<Item = (&'a Self::M, &'a Self::E::PublicKeyGroup)> + 'a;
279
280    /// Appropriate BLS signature verification for the `Self` type.
281    ///
282    /// We use `verify_simple` as a default implementation because
283    /// it supports unstable `self.messages_and_publickeys()` securely
284    /// by calling it only once, and does not expect pulic key points
285    /// to be normalized, but this should usually be replaced by more
286    /// optimized variants.
287    fn verify(self) -> bool {
288        verifiers::verify_simple(self)
289    }
290}
291
292pub trait ProofOfPossession<E, H, PV>
293where
294    E: EngineBLS,
295    H: DynDigest + Default + Clone,
296{
297    fn verify(&self, public_key_of_prover: &PV) -> bool;
298}
299
300/// ProofOfPossion trait which should be implemented by secret
301pub trait ProofOfPossessionGenerator<
302    E: EngineBLS,
303    H: DynDigest + Default + Clone,
304    PV,
305    P: ProofOfPossession<E, H, PV>,
306>
307{
308    /// The proof of possession generator is supposed to
309    /// to produce a schnorr signature or a bls signature using
310    /// the secret key which it claims to possess. This proves that
311    /// that the secret key is known.
312    fn generate_pok(&mut self) -> P;
313}