From 103178cd17562659390dd3e3881649637265c0b2 Mon Sep 17 00:00:00 2001 From: mertwole Date: Tue, 26 May 2026 14:13:59 +0200 Subject: [PATCH 1/6] Fix TODO in Signed trait --- src/experimental/bit.rs | 15 +++++----- src/experimental/delinear.rs | 5 ++-- src/experimental/distinct.rs | 5 ++-- src/lib.rs | 14 ++-------- src/multi_pop_aggregator.rs | 54 +++++++++++++++++++++--------------- src/nugget.rs | 12 ++++---- src/single.rs | 6 ++-- src/single_pop_aggregator.rs | 43 +++++++++++++++++----------- 8 files changed, 83 insertions(+), 71 deletions(-) diff --git a/src/experimental/bit.rs b/src/experimental/bit.rs index 27c91b5..9665014 100644 --- a/src/experimental/bit.rs +++ b/src/experimental/bit.rs @@ -205,9 +205,9 @@ where type M = Message; type PKG = PublicKey; - type PKnM = ::core::iter::Once<(Message, PublicKey)>; - - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { let mut publickey = E::PublicKeyGroup::zero(); for i in 0..8 * self.signers.borrow().len() { if self.signers.borrow()[i / 8] & (1 << (i % 8)) != 0 { @@ -380,9 +380,9 @@ where type M = Message; type PKG = PublicKey; - type PKnM = ::core::iter::Once<(Message, PublicKey)>; - - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { let mut publickey = E::PublicKeyGroup::zero(); for signers in self.signers.iter().rev().map(|signers| signers.borrow()) { publickey.double_in_place(); @@ -660,8 +660,7 @@ mod tests { assert!(bitsig1.merge(&bitsig2).is_err()); let mut multimsg = - crate::multi_pop_aggregator::MultiMessageSignatureAggregatorAssumingPoP::::new( - ); + crate::multi_pop_aggregator::MultiMessageSignatureAggregatorAssumingPoP::::new(); multimsg.aggregate(&bitsig1); multimsg.aggregate(&bitsig2); assert!(multimsg.verify()); // verifiers::verify_with_distinct_messages(&dms,true) diff --git a/src/experimental/delinear.rs b/src/experimental/delinear.rs index 67f1367..cf74dbd 100644 --- a/src/experimental/delinear.rs +++ b/src/experimental/delinear.rs @@ -63,9 +63,10 @@ impl<'a, E: EngineBLS> Signed for &'a Delinearized { type M = &'a Message; type PKG = &'a PublicKey; - type PKnM = ::std::collections::hash_map::Iter<'a, Message, PublicKey>; - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { self.messages_n_publickeys.iter() } diff --git a/src/experimental/distinct.rs b/src/experimental/distinct.rs index 3e7c653..4a6cc52 100644 --- a/src/experimental/distinct.rs +++ b/src/experimental/distinct.rs @@ -81,9 +81,10 @@ impl<'a, E: EngineBLS> Signed for &'a DistinctMessages { type M = &'a Message; type PKG = &'a PublicKey; - type PKnM = ::std::collections::hash_map::Iter<'a, Message, PublicKey>; - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { self.messages_n_publickeys.iter() } diff --git a/src/lib.rs b/src/lib.rs index c97a2e6..36d390b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,6 @@ #[cfg(doctest)] pub struct ReadmeDoctests; - extern crate ark_serialize; extern crate ark_serialize_derive; @@ -253,8 +252,6 @@ impl<'a> From<&'a [u8]> for Message { /// 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; @@ -264,16 +261,11 @@ pub trait Signed: Sized { type M: Borrow; // = Message; type PKG: Borrow>; // = PublicKey; - /// Iterator over, messages and public key reference pairs. - type PKnM: Iterator + ExactSizeIterator; - // type PKnM<'a>: Iterator>::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( + self, + ) -> impl Iterator + ExactSizeIterator; // fn messages_and_publickeys<'a>(&'s self) -> PKnM<'a> // -> impl Iterator + 'a; diff --git a/src/multi_pop_aggregator.rs b/src/multi_pop_aggregator.rs index 81a07b2..8e3bef7 100644 --- a/src/multi_pop_aggregator.rs +++ b/src/multi_pop_aggregator.rs @@ -35,7 +35,7 @@ // https://twitter.com/btcVeg/status/1085490561082183681 use core::borrow::Borrow; // BorrowMut -// We use BTreeMap instead of BTreeMap for no_std compatibility. + // We use BTreeMap instead of BTreeMap for no_std compatibility. use alloc::collections::BTreeMap; use ark_ff::Zero; @@ -134,9 +134,10 @@ impl<'a, E: EngineBLS> Signed for &'a MultiMessageSignatureAggregatorAssumingPoP type M = &'a Message; type PKG = &'a PublicKey; - type PKnM = alloc::collections::btree_map::Iter<'a, Message, PublicKey>; - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { self.messages_n_publickeys.iter() } @@ -161,8 +162,8 @@ mod tests { use crate::Keypair; use crate::Message; use crate::UsualBLS; - use rand::SeedableRng; use rand::rngs::StdRng; + use rand::SeedableRng; use ark_bls12_381::Bls12_381; @@ -172,8 +173,9 @@ mod tests { fn verify_aggregate_single_message_single_signer() { let good = Message::new(b"ctx", b"test message"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair.sign(&good); assert!(good_sig0.verify(&good, &keypair.public)); } @@ -182,12 +184,14 @@ mod tests { fn verify_aggregate_single_message_multi_signers() { let good = Message::new(b"ctx", b"test message"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let good_sig1 = keypair1.sign(&good); let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< @@ -210,8 +214,9 @@ mod tests { let good0 = Message::new(b"ctx", b"Tab over Space"); let good1 = Message::new(b"ctx", b"Space over Tab"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair.sign(&good0); let good_sig1 = keypair.sign(&good1); @@ -236,12 +241,14 @@ mod tests { let good0 = Message::new(b"ctx", b"in the beginning"); let good1 = Message::new(b"ctx", b"there was a flying spaghetti monster"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good0); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let good_sig1 = keypair1.sign(&good1); let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< @@ -263,8 +270,9 @@ mod tests { fn verify_aggregate_single_message_repetative_signers() { let good = Message::new(b"ctx", b"test message"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig = keypair.sign(&good); let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< @@ -287,12 +295,14 @@ mod tests { let good0 = Message::new(b"ctx", b"Space over Tab"); let bad1 = Message::new(b"ctx", b"Tab over Space"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good0); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let bad_sig1 = keypair1.sign(&bad1); let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< diff --git a/src/nugget.rs b/src/nugget.rs index 0c7df81..7d19cfd 100644 --- a/src/nugget.rs +++ b/src/nugget.rs @@ -21,9 +21,9 @@ use digest::FixedOutputReset; use sha2::Sha256; use crate::broken_derives; +use crate::chaum_pedersen_signature::DLEQProof; use crate::chaum_pedersen_signature::{ChaumPedersenSigner, ChaumPedersenVerifier}; use crate::dual_scalar_mul::DualScalarMultiplication; -use crate::chaum_pedersen_signature::DLEQProof; use crate::serialize::SerializableToBytes; use crate::single::{Keypair, KeypairVT, PublicKey, SecretKeyVT, Signature}; use crate::{EngineBLS, Message, Signed}; @@ -34,8 +34,6 @@ use crate::{EngineBLS, Message, Signed}; pub struct PublicKeyInSignatureGroup(pub E::SignatureGroup); broken_derives!(PublicKeyInSignatureGroup); // Actually the derive works for this one, not sure why. -//TODO: Make a type for a sister group. This makes sense because SisterGroup it doesn't mean on itself -// SisterGroup = CurveGroup + PrimeGroup + SerializableToBytes /// Wrapper for a point in the third curve sister group which is supposed to /// have the same logarithm as the public key in the public key group #[derive(Debug, Clone, Copy, PartialEq, Eq, CanonicalDeserialize)] @@ -218,10 +216,10 @@ where type M = Message; type PKG = PublicKey; - type PKnM = ::core::iter::Once<(Message, PublicKey)>; - - fn messages_and_publickeys(self) -> Self::PKnM { - once((self.message.clone(), self.publickey.into_bls_public_key())) // TODO: Avoid clone + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { + once((self.message.clone(), self.publickey.into_bls_public_key())) } fn signature(&self) -> Signature { diff --git a/src/single.rs b/src/single.rs index 7b826b3..d6109ae 100644 --- a/src/single.rs +++ b/src/single.rs @@ -667,9 +667,9 @@ impl<'a, E: EngineBLS> Signed for &'a SignedMessage { type M = Message; type PKG = PublicKey; - type PKnM = ::core::iter::Once<(Message, PublicKey)>; - - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { once((self.message.clone(), self.publickey)) // TODO: Avoid clone } diff --git a/src/single_pop_aggregator.rs b/src/single_pop_aggregator.rs index 344e51c..bfdbdc1 100644 --- a/src/single_pop_aggregator.rs +++ b/src/single_pop_aggregator.rs @@ -165,9 +165,10 @@ impl<'a, E: EngineBLS> Signed for &'a SignatureAggregatorAssumingPoP { type M = Message; type PKG = PublicKey; - type PKnM = ::core::iter::Once<(Message, PublicKey)>; - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator)> + ExactSizeIterator { once((self.message.clone(), self.aggregated_publickey)) // TODO: Avoid clone } @@ -194,8 +195,8 @@ mod tests { use crate::Message; use crate::TinyBLS; use crate::UsualBLS; - use rand::SeedableRng; use rand::rngs::StdRng; + use rand::SeedableRng; use sha2::Sha256; use ark_bls12_377::Bls12_377; @@ -207,8 +208,9 @@ mod tests { fn verify_aggregate_single_message_single_signer() { let good = Message::new(b"ctx", b"test message"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair.sign(&good); assert!(good_sig0.verify(&good, &keypair.public)); } @@ -217,12 +219,14 @@ mod tests { fn verify_aggregate_single_message_multi_signers() { let good = Message::new(b"ctx", b"test message"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let good_sig1 = keypair1.sign(&good); let mut aggregated_sigs = @@ -243,8 +247,9 @@ mod tests { fn verify_aggregate_single_message_repetative_signers() { let good = Message::new(b"ctx", b"test message"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig = keypair.sign(&good); let mut aggregated_sigs = @@ -266,12 +271,14 @@ mod tests { let good0 = Message::new(b"ctx", b"Space over Tab"); let bad1 = Message::new(b"ctx", b"Tab over Space"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good0); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let bad_sig1 = keypair1.sign(&bad1); let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< @@ -294,7 +301,11 @@ mod tests { let message = Message::new(b"ctx", b"test message"); let mut keypairs: Vec<_> = (0..3) .into_iter() - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) + }) .collect(); let pub_keys_in_sig_grp: Vec> = keypairs .iter() From 5dd813983b976f427387fc4a6c06b6fbcb19a9fb Mon Sep 17 00:00:00 2001 From: mertwole Date: Tue, 26 May 2026 14:21:01 +0200 Subject: [PATCH 2/6] Remove commented out code --- src/single.rs | 13 ------------- src/single_pop_aggregator.rs | 15 --------------- 2 files changed, 28 deletions(-) diff --git a/src/single.rs b/src/single.rs index d6109ae..0b6f901 100644 --- a/src/single.rs +++ b/src/single.rs @@ -105,11 +105,7 @@ impl SecretKeyVT { /// Derive our public key from our secret key pub fn into_public(&self) -> PublicKey { - // TODO str4d never decided on projective vs affine here, so benchmark both versions. PublicKey(::Affine::generator().into_group() * self.0) - // let mut g = ::one(); - // g *= self.0; - // PublicKey(p) } } @@ -278,15 +274,6 @@ impl SecretKey { let mut publickey = generator * self.key[0]; publickey += generator.into_group() * self.key[1]; PublicKey(publickey) - // TODO str4d never decided on projective vs affine here, so benchmark this. - /* - let mut x = ::one(); - x *= self.0; - let y = ::one(); - y *= self.1; - x += &y; - PublicKey(x) - */ } } diff --git a/src/single_pop_aggregator.rs b/src/single_pop_aggregator.rs index bfdbdc1..2ad44ea 100644 --- a/src/single_pop_aggregator.rs +++ b/src/single_pop_aggregator.rs @@ -132,21 +132,6 @@ impl SignatureAggregatorAssumingPoP { self.aggregated_publickey } - // /// Aggregage BLS signatures assuming they have proofs-of-possession - // /// TODO this function should return Result refusing to aggregate messages - // /// different than the message the aggregator is initiated at - // pub fn aggregate<'a,S>(&mut self, signed: &'a S) - // where - // &'a S: Signed, - // <&'a S as Signed>::PKG: Borrow>, - // { - // let signature = signed.signature(); - // for (message,pubickey) in signed.messages_and_publickeys() { - // self.add_message_n_publickey(message.borrow(),pubickey.borrow()); - // } - // self.add_signature(&signature); - // } - pub fn verify_using_aggregated_auxiliary_public_keys< RandomOracle: FixedOutputReset + Default + Clone, >( From adeb630f52c2a6b7632bf467da479157c751d6d6 Mon Sep 17 00:00:00 2001 From: mertwole Date: Mon, 8 Jun 2026 15:03:55 +0200 Subject: [PATCH 3/6] Cleanup stale comments and resolve TODOs --- src/lib.rs | 6 ++---- src/single.rs | 20 +++----------------- src/single_pop_aggregator.rs | 6 +++--- src/verifiers.rs | 9 ++------- 4 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 36d390b..664bbcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,16 +258,14 @@ pub trait Signed: Sized { /// Return the aggregated signature fn signature(&self) -> Signature; - type M: Borrow; // = Message; - type PKG: Borrow>; // = PublicKey; + type M: Borrow; + type PKG: Borrow>; /// Returns an iterator over messages and public key reference for /// pairings, often only partially aggregated. fn messages_and_publickeys( self, ) -> impl Iterator + ExactSizeIterator; - // fn messages_and_publickeys<'a>(&'s self) -> PKnM<'a> - // -> impl Iterator + 'a; /// Appropriate BLS signature verification for the `Self` type. /// diff --git a/src/single.rs b/src/single.rs index 0b6f901..f5ef8d5 100644 --- a/src/single.rs +++ b/src/single.rs @@ -18,10 +18,6 @@ //! if we had seperate types for affine points, but if doing so //! improved performance enough then we instead suggest tweaking //! `CurveGroup::add_mixed` to test for normalized points. -//! -//! TODO: Add serde support for serialization throughout. See -//! https://github.com/ebfull/pairing/pull/87#issuecomment-402397091 -//! https://github.com/poanetwork/hbbft/blob/38178af1244ddeca27f9d23750ca755af6e886ee/src/crypto/serde_impl.rs#L95 use alloc::{vec, vec::Vec}; @@ -151,9 +147,6 @@ impl SecretKeyVT { /// methods of `SecretKeyVT`, so roughly /// `SecretKeyVT::from_repr(SecretKeyVT::read(reader) ?) ?.into_split(thread_rng())`. /// -/// TODO: Provide sensible `to_bytes` and `from_bytes` methods -/// for `ZBLS` and `TinyBLS<..>`. -/// /// TODO: Is Pippenger’s algorithm, or another fast MSM algorithm, /// secure when used with key splitting? @@ -367,7 +360,6 @@ where E: EngineBLS, { fn check(&self) -> Result<(), SerializationError> { - //TODO probabaly turn into vartime and check that because vartime impl valid match (self.key[0].check(), self.key[1].check()) { (Ok(()), Ok(())) => Ok(()), _ => Err(SerializationError::InvalidData), @@ -436,7 +428,6 @@ impl SerializableToBytes for SecretKey { /// Detached BLS Signature #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct Signature(pub E::SignatureGroup); -// TODO: Serialization broken_derives!(Signature); // Actually the derive works for this one, not sure why. @@ -463,7 +454,6 @@ impl Signature { /// BLS Public Key #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct PublicKey(pub E::PublicKeyGroup); -// TODO: Serialization // impl PublicKey where E: DeserializePublicKey { // pub fn i_have_checked_this_proof_of_possession(self) -> PublicKey> { @@ -472,7 +462,6 @@ pub struct PublicKey(pub E::PublicKeyGroup); // } broken_derives!(PublicKey); -//serialization!(PublicKey,PublicKeyGroup,EngineBLS,EngineBLS); impl PublicKey { //const DESCRIPTION : &'static str = "A BLS signature"; @@ -502,7 +491,6 @@ impl Clone for KeypairVT { } } -// TODO: Serialization impl KeypairVT { /// Generate a `Keypair` pub fn generate(rng: R) -> Self { @@ -557,7 +545,6 @@ impl Clone for Keypair { } } -// TODO: Serialization impl Keypair { /// Generate a `Keypair` pub fn generate(rng: R) -> Self { @@ -633,7 +620,6 @@ pub struct SignedMessage { pub publickey: PublicKey, pub signature: Signature, } -// TODO: Serialization // borrow_wrapper!(Signature,SignatureGroup,signature); // borrow_wrapper!(PublicKey,PublicKeyGroup,publickey); @@ -651,13 +637,13 @@ impl Eq for SignedMessage {} impl<'a, E: EngineBLS> Signed for &'a SignedMessage { type E = E; - type M = Message; + type M = &'a Message; type PKG = PublicKey; fn messages_and_publickeys( self, - ) -> impl Iterator)> + ExactSizeIterator { - once((self.message.clone(), self.publickey)) // TODO: Avoid clone + ) -> impl Iterator)> + ExactSizeIterator { + once((&self.message, self.publickey)) } fn signature(&self) -> Signature { diff --git a/src/single_pop_aggregator.rs b/src/single_pop_aggregator.rs index 2ad44ea..f0fd20b 100644 --- a/src/single_pop_aggregator.rs +++ b/src/single_pop_aggregator.rs @@ -148,13 +148,13 @@ impl SignatureAggregatorAssumingPoP { impl<'a, E: EngineBLS> Signed for &'a SignatureAggregatorAssumingPoP { type E = E; - type M = Message; + type M = &'a Message; type PKG = PublicKey; fn messages_and_publickeys( self, - ) -> impl Iterator)> + ExactSizeIterator { - once((self.message.clone(), self.aggregated_publickey)) // TODO: Avoid clone + ) -> impl Iterator)> + ExactSizeIterator { + once((&self.message, self.aggregated_publickey)) } fn signature(&self) -> Signature { diff --git a/src/verifiers.rs b/src/verifiers.rs index 20fbb05..dc7534b 100644 --- a/src/verifiers.rs +++ b/src/verifiers.rs @@ -90,8 +90,6 @@ fn collect_messages_and_publickeys( /// parallel. This might mean (a) some sort function using /// `ops::IndexMut` instead of slices, and (b) wrapper types to make /// tuples of slices satisfy `ops::IndexMut`. -// TODO: Impl PartialEq, Eq, Hash for pairing::EncodedPoint -// to avoid struct H(E::PublicKeyGroup::Affine::Uncompressed); fn merge_by_signer( affine_publickeys: Vec>, messages: Vec>, @@ -122,7 +120,6 @@ fn normalize_publickeys( } } - /// Batch-normalize message points together with the aggregate signature, /// returning the affine messages and the affine signature separately. // TODO: Assess if we could cache normalized message hashes anyplace @@ -165,8 +162,7 @@ pub fn verify_unoptimized(s: S) -> bool { pub fn verify_simple(s: S) -> bool { let (signature, publickeys, messages) = collect_messages_and_publickeys(s); let affine_pks = PublicKeyProjective::::normalize_batch(&publickeys); - let (affine_msgs, affine_sig) = - normalize_messages_and_signature::(messages, signature); + let (affine_msgs, affine_sig) = normalize_messages_and_signature::(messages, signature); verify_normalized::(&affine_pks, &affine_msgs, affine_sig) } @@ -281,8 +277,7 @@ pub fn verify_using_aggregated_auxiliary_public_keys< let (merged_pks, merged_msgs) = merge_by_signer::(affine_publickeys, messages); // And verify the aggregate signature. - let (affine_msgs, affine_sig) = - normalize_messages_and_signature::(merged_msgs, signature); + let (affine_msgs, affine_sig) = normalize_messages_and_signature::(merged_msgs, signature); verify_normalized::(&merged_pks, &affine_msgs, affine_sig) } From 0ccb8c489b8e153a662f90f8eedcf9eea34b862b Mon Sep 17 00:00:00 2001 From: mertwole Date: Mon, 8 Jun 2026 15:27:39 +0200 Subject: [PATCH 4/6] Remove TODO about the choice of faster implementation --- src/single.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/single.rs b/src/single.rs index f5ef8d5..81f1c54 100644 --- a/src/single.rs +++ b/src/single.rs @@ -437,16 +437,8 @@ impl Signature { /// Verify a single BLS signature pub fn verify(&self, message: &Message, publickey: &PublicKey) -> bool { let publickey = E::prepare_public_key(publickey.0); - // TODO: Bentchmark these two variants - // Variant 1. Do not batch any normalizations let message = E::prepare_signature(message.hash_to_signature_curve::()); let signature = E::prepare_signature(self.0); - // Variant 2. Batch signature curve normalizations - // let mut s = [E::hash_to_signature_curve(message), signature.0]; - // E::SignatureCurve::batch_normalization(&s); - // let message = s[0].into_affine().prepare(); - // let signature = s[1].into_affine().prepare(); - // TODO: Compare benchmarks on variants E::verify_prepared(signature, &[(publickey, message)]) } } From 2522a395d5ea91b8d258f274ad165a628e41905a Mon Sep 17 00:00:00 2001 From: mertwole Date: Mon, 27 Jul 2026 12:32:28 +0200 Subject: [PATCH 5/6] Address review comments --- src/multi_pop_aggregator.rs | 5 +++-- src/single.rs | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/multi_pop_aggregator.rs b/src/multi_pop_aggregator.rs index 8e3bef7..1236688 100644 --- a/src/multi_pop_aggregator.rs +++ b/src/multi_pop_aggregator.rs @@ -34,8 +34,9 @@ // Aside about proof-of-possession in the DLOG setting // https://twitter.com/btcVeg/status/1085490561082183681 -use core::borrow::Borrow; // BorrowMut - // We use BTreeMap instead of BTreeMap for no_std compatibility. +use core::borrow::Borrow; // BorrowMuts + +// We use BTreeMap instead of HashMap for no_std compatibility. use alloc::collections::BTreeMap; use ark_ff::Zero; diff --git a/src/single.rs b/src/single.rs index 81f1c54..ed93a76 100644 --- a/src/single.rs +++ b/src/single.rs @@ -18,6 +18,9 @@ //! if we had seperate types for affine points, but if doing so //! improved performance enough then we instead suggest tweaking //! `CurveGroup::add_mixed` to test for normalized points. +//! +//! Serialization for Public Keys and Signatures is provided via +//! [`SerializableToBytes`](crate::serialize::SerializableToBytes) use alloc::{vec, vec::Vec}; From feab2bfe0c034e46c1167054f257bd0eb1a3f3c8 Mon Sep 17 00:00:00 2001 From: mertwole Date: Mon, 27 Jul 2026 12:58:27 +0200 Subject: [PATCH 6/6] Merge fixes --- src/lib.rs | 2 +- src/multi_pop_aggregator.rs | 323 --------------------------------- src/pop_aggregator.rs | 211 ++++++++++++++-------- src/single_pop_aggregator.rs | 340 ----------------------------------- 4 files changed, 140 insertions(+), 736 deletions(-) delete mode 100644 src/multi_pop_aggregator.rs delete mode 100644 src/single_pop_aggregator.rs diff --git a/src/lib.rs b/src/lib.rs index f5f391b..6fb2c8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,7 +303,7 @@ pub trait Signed: Sized { fn signature(&self) -> Signature; type M: Borrow; - type PKG: Borrow>; + type PKG: GeneralizedBLSPublicKey; /// Returns an iterator over messages and public key reference for /// pairings, often only partially aggregated. diff --git a/src/multi_pop_aggregator.rs b/src/multi_pop_aggregator.rs deleted file mode 100644 index 1236688..0000000 --- a/src/multi_pop_aggregator.rs +++ /dev/null @@ -1,323 +0,0 @@ -//! ## Aggregation of BLS signatures using proofs-of-possession -//! -//! In this module, we provide the linear flavor of aggregate -//! BLS signature in which the verifiers has previously checked -//! proofs-of-possession for all public keys. In other words, -//! we simply add up the signatures because the previously checked -//! proofs-of-possession for all signers prevent rogue key attacks. -//! See the security arguments in The Power of Proofs-of-Possession: -//! Securing Multiparty Signatures against Rogue-Key Attacks -//! by Thomas Ristenpart and Scott Yilek at https://eprint.iacr.org/2007/264.pdf -//! -//! These proof-of-possession are simply self-signed certificates, -//! so a BLS signature by each secret key on its own public key. -//! Importantly, the message for this self-signed certificates -//! must uniquely distinguish the public key for which the signature -//! establishes a proof-of-possession. -//! It follows that each proof-of-possession has a unique message, -//! so distinct message aggregation is optimal for verifying them. -//! -//! In this vein, we note that aggregation under proofs-of-possession -//! cannot improve performance when signers sign distinct messages, -//! so proofs-of-possession help with aggregating votes in a concensus -//! protocol, but should never be used for accounts on a block chain. -//! -//! We assume here that users provide their own data structure for -//! proofs-of-poossession. We provide more structure for users who -//! one bit per vote in a concensus protocol: -//! You first verify the proofs-of-possession when building a data -//! structure that holds the voters' keys. You implement the -//! `ProofsOfPossession` trait for this data strtcuture as well, -//! so that the `BitPoPSignedMessage` type provides a signature -//! data type with reasonable sanity checks. - -// Aside about proof-of-possession in the DLOG setting -// https://twitter.com/btcVeg/status/1085490561082183681 - -use core::borrow::Borrow; // BorrowMuts - -// We use BTreeMap instead of HashMap for no_std compatibility. -use alloc::collections::BTreeMap; - -use ark_ff::Zero; - -use super::verifiers::verify_with_distinct_messages; -use super::*; - -/// Batch or aggregate BLS signatures with attached messages and -/// signers, for whom we previously checked proofs-of-possession. -/// -/// In this type, we provide a high-risk low-level batching and -/// aggregation mechanism that merely adds up signatures under the -/// assumption that all required proofs-of-possession were previously -/// checked. -/// -/// We say a signing key has provided a proof-of-possession if the -/// verifier remembers having checked some self-signed certificate -/// by that key. It's insecure to use this aggregation strategy -/// without first cehcking proofs-of-possession. In particular -/// it is insecure to use this aggregation strategy when checking -/// proofs-of-possession, and could not improve performance anyways. -/// Distinct message aggregation is always optimal for checking -/// proofs-of-possession. Please see the module level doumentation -/// for additional discussion and notes on security. -/// -/// We foresee this type primarily being used to batch several -/// `BitPoPSignedMessage`s into one verification. We do not track -/// aggreggated public keys here, instead merging multiples signers -/// public keys anytime they sign the same message, so this type -/// essentially provides only fast batch verificartion. -/// In principle, our `add_*` methods suffice for building an actual -/// aggregate signature type. Yet, normally direct approaches like -/// `BitPoPSignedMessage` work better for aggregation because -/// the `ProofsOfPossession` trait tooling permits both enforce the -/// proofs-of-possession and provide a compact serialization. -/// We see no reason to support serialization for this type as present. -// -/// In principle, one might combine proof-of-possession with distinct -/// message assumptions, or other aggregation strategies, when -/// verifiers have only observed a subset of the proofs-of-possession, -/// but this sounds complex or worse fragile. -/// -// TODO: Implement gaussian elimination verification scheme. -use single::PublicKey; -/// ProofOfPossion trait which should be implemented by secret - -#[derive(Clone)] -pub struct MultiMessageSignatureAggregatorAssumingPoP { - messages_n_publickeys: BTreeMap>, - signature: Signature, -} - -impl MultiMessageSignatureAggregatorAssumingPoP { - pub fn new() -> MultiMessageSignatureAggregatorAssumingPoP { - MultiMessageSignatureAggregatorAssumingPoP { - messages_n_publickeys: BTreeMap::new(), - signature: Signature(E::SignatureGroup::zero()), - } - } - - /// Add only a `Signature` to our internal signature. - /// - /// Useful for constructing an aggregate signature, but we - /// recommend instead using a custom types like `BitPoPSignedMessage`. - pub fn add_signature(&mut self, signature: &Signature) { - self.signature.0 += &signature.0; - } - - /// Add only a `Message` and `PublicKey` to our internal data. - /// - /// Useful for constructing an aggregate signature, but we - /// recommend instead using a custom types like `BitPoPSignedMessage`. - pub fn add_message_n_publickey(&mut self, message: &Message, publickey: &PublicKey) { - self.messages_n_publickeys - .entry(message.clone()) - .and_modify(|pk0| pk0.0 += &publickey.0) - .or_insert(*publickey); - } - - /// Aggregage BLS signatures assuming they have proofs-of-possession - pub fn aggregate<'a, S>(&mut self, signed: &'a S) - where - &'a S: Signed, - <&'a S as Signed>::PKG: Borrow>, - { - let signature = signed.signature(); - for (message, pubickey) in signed.messages_and_publickeys() { - self.add_message_n_publickey(message.borrow(), pubickey.borrow()); - } - self.add_signature(&signature); - } -} - -impl<'a, E: EngineBLS> Signed for &'a MultiMessageSignatureAggregatorAssumingPoP { - type E = E; - - type M = &'a Message; - type PKG = &'a PublicKey; - - fn messages_and_publickeys( - self, - ) -> impl Iterator)> + ExactSizeIterator { - self.messages_n_publickeys.iter() - } - - fn signature(&self) -> Signature { - self.signature - } - - fn verify(self) -> bool { - // We have already aggregated distinct messages, so our distinct - // message verification code provides reasonable optimizations, - // except the public keys might not be normalized here. - // We foresee verification via gaussian elimination being faster, - // but requires affine keys or normalization. - verify_with_distinct_messages(self, true) - // TODO: verify_with_gaussian_elimination(self) - } -} - -#[cfg(test)] -mod tests { - - use crate::Keypair; - use crate::Message; - use crate::UsualBLS; - use rand::rngs::StdRng; - use rand::SeedableRng; - - use ark_bls12_381::Bls12_381; - - use super::*; - - #[test] - fn verify_aggregate_single_message_single_signer() { - let good = Message::new(b"ctx", b"test message"); - - let mut keypair = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair.sign(&good); - assert!(good_sig0.verify(&good, &keypair.public)); - } - - #[test] - fn verify_aggregate_single_message_multi_signers() { - let good = Message::new(b"ctx", b"test message"); - - let mut keypair0 = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair0.sign(&good); - - let mut keypair1 = Keypair::>::generate( - StdRng::from_seed([1u8; 32]), - ); - let good_sig1 = keypair1.sign(&good); - - let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); - aggregated_sigs.add_signature(&good_sig0); - aggregated_sigs.add_signature(&good_sig1); - - aggregated_sigs.add_message_n_publickey(&good, &keypair0.public); - aggregated_sigs.add_message_n_publickey(&good, &keypair1.public); - - assert!( - aggregated_sigs.verify() == true, - "good aggregated signature of a single message with multiple key does not verify" - ); - } - - #[test] - fn verify_aggregate_multi_messages_single_signer() { - let good0 = Message::new(b"ctx", b"Tab over Space"); - let good1 = Message::new(b"ctx", b"Space over Tab"); - - let mut keypair = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - - let good_sig0 = keypair.sign(&good0); - let good_sig1 = keypair.sign(&good1); - - let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); - aggregated_sigs.add_signature(&good_sig0); - aggregated_sigs.add_signature(&good_sig1); - - aggregated_sigs.add_message_n_publickey(&good0, &keypair.public); - aggregated_sigs.add_message_n_publickey(&good1, &keypair.public); - - assert!( - aggregated_sigs.verify() == true, - "good aggregated signature of multiple messages with a single key does not verify" - ); - } - - #[test] - fn verify_aggregate_multi_messages_multi_signers() { - let good0 = Message::new(b"ctx", b"in the beginning"); - let good1 = Message::new(b"ctx", b"there was a flying spaghetti monster"); - - let mut keypair0 = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair0.sign(&good0); - - let mut keypair1 = Keypair::>::generate( - StdRng::from_seed([1u8; 32]), - ); - let good_sig1 = keypair1.sign(&good1); - - let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); - aggregated_sigs.add_signature(&good_sig0); - aggregated_sigs.add_signature(&good_sig1); - - aggregated_sigs.add_message_n_publickey(&good0, &keypair0.public); - aggregated_sigs.add_message_n_publickey(&good1, &keypair1.public); - - assert!( - aggregated_sigs.verify() == true, - "good aggregated signature of multiple messages with multiple keys does not verify" - ); - } - - #[test] - fn verify_aggregate_single_message_repetative_signers() { - let good = Message::new(b"ctx", b"test message"); - - let mut keypair = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig = keypair.sign(&good); - - let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); - aggregated_sigs.add_signature(&good_sig); - aggregated_sigs.add_signature(&good_sig); - - aggregated_sigs.add_message_n_publickey(&good, &keypair.public); - aggregated_sigs.add_message_n_publickey(&good, &keypair.public); - - assert!( - aggregated_sigs.verify() == true, - "good aggregate of a repetitive signature does not verify" - ); - } - - #[test] - fn aggregate_of_signature_of_a_wrong_message_should_not_verify() { - let good0 = Message::new(b"ctx", b"Space over Tab"); - let bad1 = Message::new(b"ctx", b"Tab over Space"); - - let mut keypair0 = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair0.sign(&good0); - - let mut keypair1 = Keypair::>::generate( - StdRng::from_seed([1u8; 32]), - ); - let bad_sig1 = keypair1.sign(&bad1); - - let mut aggregated_sigs = MultiMessageSignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); - aggregated_sigs.add_signature(&good_sig0); - aggregated_sigs.add_signature(&bad_sig1); - - aggregated_sigs.add_message_n_publickey(&good0, &keypair0.public); - aggregated_sigs.add_message_n_publickey(&good0, &keypair1.public); - - assert!( - aggregated_sigs.verify() == false, - "aggregated signature of a wrong message should not verify" - ); - } -} diff --git a/src/pop_aggregator.rs b/src/pop_aggregator.rs index fda966f..2c8f6a7 100644 --- a/src/pop_aggregator.rs +++ b/src/pop_aggregator.rs @@ -112,7 +112,11 @@ impl SignatureAggregatorAssumingPoP { /// distinct message ends up paired with a single aggregated key. /// If the public key carries an auxiliary key in the signature group, /// it is automatically aggregated as well. - pub fn add_message_n_publickey(&mut self, message: &Message, publickey: &impl GeneralizedBLSPublicKey) { + pub fn add_message_n_publickey( + &mut self, + message: &Message, + publickey: &impl GeneralizedBLSPublicKey, + ) { let pk = publickey.public_key(); let aux = publickey.public_key_in_signature_group(); self.messages_n_publickeys @@ -146,9 +150,7 @@ impl SignatureAggregatorAssumingPoP { existing_aux.0 += &aux.0; Ok(()) } - Some(_) => { - Err("message already exists with a different public key") - } + Some(_) => Err("message already exists with a different public key"), None => { self.messages_n_publickeys .insert(message.clone(), (*publickey, *aux)); @@ -187,9 +189,15 @@ impl<'a, E: EngineBLS> Signed for &'a SignatureAggregatorAssumingPoP { type M = &'a Message; type PKG = &'a (PublicKey, PublicKeyInSignatureGroup); - type PKnM = alloc::collections::btree_map::Iter<'a, Message, (PublicKey, PublicKeyInSignatureGroup)>; - fn messages_and_publickeys(self) -> Self::PKnM { + fn messages_and_publickeys( + self, + ) -> impl Iterator< + Item = ( + &'a Message, + &'a (PublicKey, PublicKeyInSignatureGroup), + ), + > + ExactSizeIterator { self.messages_n_publickeys.iter() } @@ -216,8 +224,8 @@ mod tests { use crate::Message; use crate::TinyBLS; use crate::UsualBLS; - use rand::SeedableRng; use rand::rngs::StdRng; + use rand::SeedableRng; use sha2::Sha256; use ark_bls12_377::Bls12_377; @@ -229,8 +237,9 @@ mod tests { fn verify_aggregate_single_message_single_signer() { let good = Message::new(b"ctx", b"test message"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair.sign(&good); assert!(good_sig0.verify(&good, &keypair.public)); } @@ -239,17 +248,18 @@ mod tests { fn verify_aggregate_single_message_multi_signers() { let good = Message::new(b"ctx", b"test message"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let good_sig1 = keypair1.sign(&good); - let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); + let mut aggregated_sigs = + SignatureAggregatorAssumingPoP::>::new(); aggregated_sigs.add_signature(&good_sig0); aggregated_sigs.add_signature(&good_sig1); @@ -267,15 +277,15 @@ mod tests { let good0 = Message::new(b"ctx", b"Tab over Space"); let good1 = Message::new(b"ctx", b"Space over Tab"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair.sign(&good0); let good_sig1 = keypair.sign(&good1); - let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); + let mut aggregated_sigs = + SignatureAggregatorAssumingPoP::>::new(); aggregated_sigs.add_signature(&good_sig0); aggregated_sigs.add_signature(&good_sig1); @@ -293,17 +303,18 @@ mod tests { let good0 = Message::new(b"ctx", b"in the beginning"); let good1 = Message::new(b"ctx", b"there was a flying spaghetti monster"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good0); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let good_sig1 = keypair1.sign(&good1); - let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); + let mut aggregated_sigs = + SignatureAggregatorAssumingPoP::>::new(); aggregated_sigs.add_signature(&good_sig0); aggregated_sigs.add_signature(&good_sig1); @@ -320,13 +331,13 @@ mod tests { fn verify_aggregate_single_message_repetative_signers() { let good = Message::new(b"ctx", b"test message"); - let mut keypair = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig = keypair.sign(&good); - let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); + let mut aggregated_sigs = + SignatureAggregatorAssumingPoP::>::new(); aggregated_sigs.add_signature(&good_sig); aggregated_sigs.add_signature(&good_sig); @@ -344,17 +355,18 @@ mod tests { let good0 = Message::new(b"ctx", b"Space over Tab"); let bad1 = Message::new(b"ctx", b"Tab over Space"); - let mut keypair0 = - Keypair::>::generate(StdRng::from_seed([0u8; 32])); + let mut keypair0 = Keypair::>::generate( + StdRng::from_seed([0u8; 32]), + ); let good_sig0 = keypair0.sign(&good0); - let mut keypair1 = - Keypair::>::generate(StdRng::from_seed([1u8; 32])); + let mut keypair1 = Keypair::>::generate( + StdRng::from_seed([1u8; 32]), + ); let bad_sig1 = keypair1.sign(&bad1); - let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(); + let mut aggregated_sigs = + SignatureAggregatorAssumingPoP::>::new(); aggregated_sigs.add_signature(&good_sig0); aggregated_sigs.add_signature(&bad_sig1); @@ -372,7 +384,11 @@ mod tests { let message = Message::new(b"ctx", b"test message"); let mut keypairs: Vec<_> = (0..3) .into_iter() - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) + }) .collect(); let pub_keys_in_sig_grp: Vec> = keypairs .iter() @@ -406,7 +422,12 @@ mod tests { verifier_aggregator.add_message_n_publickey(msg, pk); } - let aggregated_pk = (&prover_aggregator).messages_and_publickeys().next().unwrap().1.0; + let aggregated_pk = (&prover_aggregator) + .messages_and_publickeys() + .next() + .unwrap() + .1 + .0; for aux in &pub_keys_in_sig_grp { verifier_aggregator .aggregate_aux_publickey_for_message_n_publickey(&message, &aggregated_pk, aux) @@ -425,13 +446,25 @@ mod tests { bad_verifier.add_message_n_publickey(msg, pk); } bad_verifier - .aggregate_aux_publickey_for_message_n_publickey(&message, &aggregated_pk, &pub_keys_in_sig_grp[1]) + .aggregate_aux_publickey_for_message_n_publickey( + &message, + &aggregated_pk, + &pub_keys_in_sig_grp[1], + ) .unwrap(); bad_verifier - .aggregate_aux_publickey_for_message_n_publickey(&message, &aggregated_pk, &pub_keys_in_sig_grp[1]) + .aggregate_aux_publickey_for_message_n_publickey( + &message, + &aggregated_pk, + &pub_keys_in_sig_grp[1], + ) .unwrap(); bad_verifier - .aggregate_aux_publickey_for_message_n_publickey(&message, &aggregated_pk, &pub_keys_in_sig_grp[2]) + .aggregate_aux_publickey_for_message_n_publickey( + &message, + &aggregated_pk, + &pub_keys_in_sig_grp[2], + ) .unwrap(); assert!( @@ -463,7 +496,11 @@ mod tests { .map(|i| Message::new(b"ctx", &[b'm', b'0' + i as u8])) .collect(); let mut keypairs: Vec<_> = (0..3) - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) + }) .collect(); let pub_keys_in_sig_grp: Vec> = keypairs .iter() @@ -478,7 +515,11 @@ mod tests { // Prover: each signer signs their own distinct message. let mut prover_aggregator = SignatureAggregatorAssumingPoP::::new(); - for ((k, m), aux) in keypairs.iter_mut().zip(messages.iter()).zip(pub_keys_in_sig_grp.iter()) { + for ((k, m), aux) in keypairs + .iter_mut() + .zip(messages.iter()) + .zip(pub_keys_in_sig_grp.iter()) + { prover_aggregator.add_signature(&k.sign(m)); prover_aggregator.add_message_n_publickey(m, &(k.public, *aux)); } @@ -543,7 +584,11 @@ mod tests { .map(|i| Message::new(b"ctx", &[b'm', b'0' + i as u8])) .collect(); let mut keypairs: Vec<_> = (0..3) - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) + }) .collect(); let pub_keys_in_sig_grp: Vec> = keypairs .iter() @@ -557,7 +602,11 @@ mod tests { // Prover: signs real messages honestly. let mut prover_aggregator = SignatureAggregatorAssumingPoP::::new(); - for ((i, k), aux) in keypairs.iter_mut().enumerate().zip(pub_keys_in_sig_grp.iter()) { + for ((i, k), aux) in keypairs + .iter_mut() + .enumerate() + .zip(pub_keys_in_sig_grp.iter()) + { prover_aggregator.add_signature(&k.sign(&real_messages[i])); prover_aggregator.add_message_n_publickey(&real_messages[i], &(k.public, *aux)); } @@ -579,7 +628,11 @@ mod tests { for (i, (_msg, pk)) in prover_entries.iter().enumerate() { let wrong_message = &real_messages[(i + 1) % real_messages.len()]; verifier_aggregator - .aggregate_aux_publickey_for_message_n_publickey(wrong_message, pk, &pub_keys_in_sig_grp[i]) + .aggregate_aux_publickey_for_message_n_publickey( + wrong_message, + pk, + &pub_keys_in_sig_grp[i], + ) .expect("public key should match"); } @@ -609,20 +662,25 @@ mod tests { .map(|i| Message::new(b"ctx", &[b'm', b'0' + i as u8])) .collect(); let mut keypairs: Vec<_> = (0..3) - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) + }) .collect(); // The verifier's list: every signer's public key together with // its auxiliary key in the signature group. - let signer_list: Vec<(PublicKey, PublicKeyInSignatureGroup)> = keypairs - .iter() - .map(|k| { - let aux = nugget::NuggetBLS::< - TinyBLS, - as EngineBLS>::SignatureGroup, - >::into_public_key_in_signature_group(k); - (k.public, aux) - }) - .collect(); + let signer_list: Vec<(PublicKey, PublicKeyInSignatureGroup)> = + keypairs + .iter() + .map(|k| { + let aux = nugget::NuggetBLS::< + TinyBLS, + as EngineBLS>::SignatureGroup, + >::into_public_key_in_signature_group(k); + (k.public, aux) + }) + .collect(); // Per-message participation bitfield: bit i set iff signer i // participated. m0 ← {0,1} = 0b011 ; m1 ← {1,2} = 0b110. @@ -740,18 +798,23 @@ mod tests { .map(|i| Message::new(b"ctx", &[b'm', b'0' + i as u8])) .collect(); let mut keypairs: Vec<_> = (0..2) - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) - .collect(); - let signer_list: Vec<(PublicKey, PublicKeyInSignatureGroup)> = keypairs - .iter() - .map(|k| { - let aux = nugget::NuggetBLS::< - TinyBLS, - as EngineBLS>::SignatureGroup, - >::into_public_key_in_signature_group(k); - (k.public, aux) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) }) .collect(); + let signer_list: Vec<(PublicKey, PublicKeyInSignatureGroup)> = + keypairs + .iter() + .map(|k| { + let aux = nugget::NuggetBLS::< + TinyBLS, + as EngineBLS>::SignatureGroup, + >::into_public_key_in_signature_group(k); + (k.public, aux) + }) + .collect(); // Same signers participate in every message. let bitfields: [u8; 2] = [0b11, 0b11]; @@ -891,7 +954,11 @@ mod tests { fn aux_key_verifier_rejects_aggregator_without_aux_keys() { let message = Message::new(b"ctx", b"test message"); let mut keypairs: Vec<_> = (0..3) - .map(|i| Keypair::>::generate(StdRng::from_seed([i; 32]))) + .map(|i| { + Keypair::>::generate(StdRng::from_seed( + [i; 32], + )) + }) .collect(); let mut aggregator = SignatureAggregatorAssumingPoP::::new(); diff --git a/src/single_pop_aggregator.rs b/src/single_pop_aggregator.rs deleted file mode 100644 index f0fd20b..0000000 --- a/src/single_pop_aggregator.rs +++ /dev/null @@ -1,340 +0,0 @@ -//! ## Aggregation of BLS signatures using proofs-of-possession -//! -//! In this module, we provide the linear flavor of aggregate -//! BLS signature in which the verifiers has previously checked -//! proofs-of-possession for all public keys. In other words, -//! we simply add up the signatures because the previously checked -//! proofs-of-possession for all signers prevent rogue key attacks. -//! See the security arguments in The Power of Proofs-of-Possession: -//! Securing Multiparty Signatures against Rogue-Key Attacks -//! by Thomas Ristenpart and Scott Yilek at https://eprint.iacr.org/2007/264.pdf -//! -//! These proof-of-possession are simply self-signed certificates, -//! so a BLS signature by each secret key on its own public key. -//! Importantly, the message for this self-signed certificates -//! must uniquely distinguish the public key for which the signature -//! establishes a proof-of-possession. -//! It follows that each proof-of-possession has a unique message, -//! so distinct message aggregation is optimal for verifying them. -//! -//! In this vein, we note that aggregation under proofs-of-possession -//! cannot improve performance when signers sign distinct messages, -//! so proofs-of-possession help with aggregating votes in a concensus -//! protocol, but should never be used for accounts on a block chain. -//! -//! We assume here that users provide their own data structure for -//! proofs-of-poossession. We provide more structure for users who -//! one bit per vote in a concensus protocol: -//! You first verify the proofs-of-possession when building a data -//! structure that holds the voters' keys. You implement the -//! `ProofsOfPossession` trait for this data strtcuture as well, -//! so that the `BitPoPSignedMessage` type provides a signature -//! data type with reasonable sanity checks. - -// Aside about proof-of-possession in the DLOG setting -// https://twitter.com/btcVeg/status/1085490561082183681 - -use ark_ff::Zero; - -use super::verifiers::{ - verify_using_aggregated_auxiliary_public_keys, verify_with_distinct_messages, -}; -use super::*; - -use digest::FixedOutputReset; - -/// Batch or aggregate BLS signatures with attached messages and -/// signers, for whom we previously checked proofs-of-possession. -/// -/// In this type, we provide a high-risk low-level batching and -/// aggregation mechanism that merely adds up signatures under the -/// assumption that all required proofs-of-possession were previously -/// checked. -/// -/// We say a signing key has provided a proof-of-possession if the -/// verifier remembers having checked some self-signed certificate -/// by that key. It's insecure to use this aggregation strategy -/// without first cehcking proofs-of-possession. In particular -/// it is insecure to use this aggregation strategy when checking -/// proofs-of-possession, and could not improve performance anyways. -/// Distinct message aggregation is always optimal for checking -/// proofs-of-possession. Please see the module level doumentation -/// for additional discussion and notes on security. -/// -/// We foresee this type primarily being used to batch several -/// `BitPoPSignedMessage`s into one verification. We do not track -/// aggreggated public keys here, instead merging multiples signers -/// public keys anytime they sign the same message, so this type -/// essentially provides only fast batch verificartion. -/// In principle, our `add_*` methods suffice for building an actual -/// aggregate signature type. Yet, normally direct approaches like -/// `BitPoPSignedMessage` work better for aggregation because -/// the `ProofsOfPossession` trait tooling permits both enforce the -/// proofs-of-possession and provide a compact serialization. -/// We see no reason to support serialization for this type as present. -/// message assumptions, or other aggre -/// -/// In principle, one might combine proof-of-possession with distinct -/// message assumptions, or other aggregation strategies, when -/// verifiers have only observed a subset of the proofs-of-possession, -/// but this sounds complex or worse fragile. -/// -/// TODO: Implement gaussian elimination verification scheme. -use core::iter::once; - -use nugget::PublicKeyInSignatureGroup; -use single::PublicKey; - -#[derive(Clone)] -pub struct SignatureAggregatorAssumingPoP { - message: Message, - aggregated_publickey: PublicKey, - signature: Signature, - aggregated_auxiliary_public_key: PublicKeyInSignatureGroup, -} - -impl SignatureAggregatorAssumingPoP { - pub fn new(message: Message) -> SignatureAggregatorAssumingPoP { - SignatureAggregatorAssumingPoP { - message: message, - aggregated_publickey: PublicKey(E::PublicKeyGroup::zero()), - signature: Signature(E::SignatureGroup::zero()), - aggregated_auxiliary_public_key: PublicKeyInSignatureGroup(E::SignatureGroup::zero()), - } - } - - /// Add only a `Signature` to our internal signature. - /// - /// Useful for constructing an aggregate signature, but we - pub fn add_signature(&mut self, signature: &Signature) { - self.signature.0 += &signature.0; - } - - /// Add only a `PublicKey` to our internal data. - /// - /// Useful for constructing an aggregate signature, but we - /// recommend instead using a custom types like `BitPoPSignedMessage`. - pub fn add_publickey(&mut self, publickey: &PublicKey) { - self.aggregated_publickey.0 += publickey.0; - } - - /// Aggregate the auxiliary public keys in the signature group to be used verification using aux key - pub fn add_auxiliary_public_key( - &mut self, - publickey_in_signature_group: &PublicKeyInSignatureGroup, - ) { - self.aggregated_auxiliary_public_key.0 += publickey_in_signature_group.0; - } - - /// Returns the aggergated public key. - /// - pub fn aggregated_publickey(&self) -> PublicKey { - self.aggregated_publickey - } - - pub fn verify_using_aggregated_auxiliary_public_keys< - RandomOracle: FixedOutputReset + Default + Clone, - >( - &self, - ) -> bool { - verify_using_aggregated_auxiliary_public_keys::( - self, - true, - self.aggregated_auxiliary_public_key.0, - ) - } -} - -impl<'a, E: EngineBLS> Signed for &'a SignatureAggregatorAssumingPoP { - type E = E; - - type M = &'a Message; - type PKG = PublicKey; - - fn messages_and_publickeys( - self, - ) -> impl Iterator)> + ExactSizeIterator { - once((&self.message, self.aggregated_publickey)) - } - - fn signature(&self) -> Signature { - self.signature - } - - fn verify(self) -> bool { - // We have already aggregated distinct messages, so our distinct - // message verification code provides reasonable optimizations, - // except the public keys might not be normalized here. - // We foresee verification via gaussian elimination being faster, - // but requires affine keys or normalization. - verify_with_distinct_messages(self, true) - // TODO: verify_with_gaussian_elimination(self) - } -} - -#[cfg(test)] -mod tests { - - use crate::EngineBLS; - use crate::Keypair; - use crate::Message; - use crate::TinyBLS; - use crate::UsualBLS; - use rand::rngs::StdRng; - use rand::SeedableRng; - use sha2::Sha256; - - use ark_bls12_377::Bls12_377; - use ark_bls12_381::Bls12_381; - - use super::*; - - #[test] - fn verify_aggregate_single_message_single_signer() { - let good = Message::new(b"ctx", b"test message"); - - let mut keypair = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair.sign(&good); - assert!(good_sig0.verify(&good, &keypair.public)); - } - - #[test] - fn verify_aggregate_single_message_multi_signers() { - let good = Message::new(b"ctx", b"test message"); - - let mut keypair0 = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair0.sign(&good); - - let mut keypair1 = Keypair::>::generate( - StdRng::from_seed([1u8; 32]), - ); - let good_sig1 = keypair1.sign(&good); - - let mut aggregated_sigs = - SignatureAggregatorAssumingPoP::>::new(good); - aggregated_sigs.add_signature(&good_sig0); - aggregated_sigs.add_signature(&good_sig1); - - aggregated_sigs.add_publickey(&keypair0.public); - aggregated_sigs.add_publickey(&keypair1.public); - - assert!( - aggregated_sigs.verify() == true, - "good aggregated signature of a single message with multiple key does not verify" - ); - } - - #[test] - fn verify_aggregate_single_message_repetative_signers() { - let good = Message::new(b"ctx", b"test message"); - - let mut keypair = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig = keypair.sign(&good); - - let mut aggregated_sigs = - SignatureAggregatorAssumingPoP::>::new(good); - aggregated_sigs.add_signature(&good_sig); - aggregated_sigs.add_signature(&good_sig); - - aggregated_sigs.add_publickey(&keypair.public); - aggregated_sigs.add_publickey(&keypair.public); - - assert!( - aggregated_sigs.verify() == true, - "good aggregate of a repetitive signature does not verify" - ); - } - - #[test] - fn aggregate_of_signature_of_a_wrong_message_should_not_verify() { - let good0 = Message::new(b"ctx", b"Space over Tab"); - let bad1 = Message::new(b"ctx", b"Tab over Space"); - - let mut keypair0 = Keypair::>::generate( - StdRng::from_seed([0u8; 32]), - ); - let good_sig0 = keypair0.sign(&good0); - - let mut keypair1 = Keypair::>::generate( - StdRng::from_seed([1u8; 32]), - ); - let bad_sig1 = keypair1.sign(&bad1); - - let mut aggregated_sigs = SignatureAggregatorAssumingPoP::< - UsualBLS, - >::new(good0); - aggregated_sigs.add_signature(&good_sig0); - aggregated_sigs.add_signature(&bad_sig1); - - aggregated_sigs.add_publickey(&keypair0.public); - aggregated_sigs.add_publickey(&keypair1.public); - - assert!( - aggregated_sigs.verify() == false, - "aggregated signature of a wrong message should not verify" - ); - } - - #[test] - fn test_aggregate_tiny_sigs_and_verify_in_g1() { - let message = Message::new(b"ctx", b"test message"); - let mut keypairs: Vec<_> = (0..3) - .into_iter() - .map(|i| { - Keypair::>::generate(StdRng::from_seed( - [i; 32], - )) - }) - .collect(); - let pub_keys_in_sig_grp: Vec> = keypairs - .iter() - .map(|k| { - nugget::NuggetBLS::< - TinyBLS, - as EngineBLS>::SignatureGroup, - >::into_public_key_in_signature_group(k) - }) - .collect(); - - let mut aggregator = SignatureAggregatorAssumingPoP::::new(message.clone()); - let mut aggregated_public_key = - PublicKey::(::PublicKeyGroup::zero()); - - for k in &mut keypairs { - aggregator.add_signature(&k.sign(&message)); - aggregated_public_key.0 += k.public.0; - } - - let mut verifier_aggregator = SignatureAggregatorAssumingPoP::::new(message); - - verifier_aggregator.add_signature(&aggregator.signature); - verifier_aggregator.add_publickey(&aggregated_public_key); - - for k in &pub_keys_in_sig_grp { - verifier_aggregator.add_auxiliary_public_key(k); - } - - assert!( - verifier_aggregator.verify_using_aggregated_auxiliary_public_keys::(), - "verifying with honest auxilary public key should pass" - ); - - //false aggregation in signature group should fails verification. - verifier_aggregator.add_auxiliary_public_key(&nugget::NuggetBLS::< - TinyBLS, - as EngineBLS>::SignatureGroup, - >::into_public_key_in_signature_group( - &keypairs[0] - )); - assert!( - !verifier_aggregator.verify_using_aggregated_auxiliary_public_keys::(), - "verification using non-matching auxilary public key should fail" - ); - } -}