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}