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