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}