diff --git a/eth2/utils/bls/src/aggregate_public_key.rs b/eth2/utils/bls/src/aggregate_public_key.rs new file mode 100644 index 000000000..dcb08126c --- /dev/null +++ b/eth2/utils/bls/src/aggregate_public_key.rs @@ -0,0 +1,24 @@ +use super::PublicKey; +use bls_aggregates::AggregatePublicKey as RawAggregatePublicKey; + +/// A single BLS signature. +/// +/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ +/// serialization). +#[derive(Debug, Clone)] +pub struct AggregatePublicKey(RawAggregatePublicKey); + +impl AggregatePublicKey { + pub fn new() -> Self { + AggregatePublicKey(RawAggregatePublicKey::new()) + } + + pub fn add(&mut self, public_key: &PublicKey) { + self.0.add(public_key.as_raw()) + } + + /// Returns the underlying signature. + pub fn as_raw(&self) -> &RawAggregatePublicKey { + &self.0 + } +} diff --git a/eth2/utils/bls/src/aggregate_signature.rs b/eth2/utils/bls/src/aggregate_signature.rs index 4ee79d0aa..2d8776353 100644 --- a/eth2/utils/bls/src/aggregate_signature.rs +++ b/eth2/utils/bls/src/aggregate_signature.rs @@ -1,5 +1,7 @@ use super::{AggregatePublicKey, Signature}; -use bls_aggregates::AggregateSignature as RawAggregateSignature; +use bls_aggregates::{ + AggregatePublicKey as RawAggregatePublicKey, AggregateSignature as RawAggregateSignature, +}; use serde::ser::{Serialize, Serializer}; use ssz::{ decode_ssz_list, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash, @@ -33,7 +35,38 @@ impl AggregateSignature { domain: u64, aggregate_public_key: &AggregatePublicKey, ) -> bool { - self.0.verify(msg, domain, aggregate_public_key) + self.0.verify(msg, domain, aggregate_public_key.as_raw()) + } + + /// Verify this AggregateSignature against multiple AggregatePublickeys with multiple Messages. + /// + /// All PublicKeys related to a Message should be aggregated into one AggregatePublicKey. + /// Each AggregatePublicKey has a 1:1 ratio with a 32 byte Message. + pub fn verify_multiple( + &self, + messages: &[&[u8]], + domain: u64, + aggregate_public_keys: &[&AggregatePublicKey], + ) -> bool { + // TODO: the API for `RawAggregatePublicKey` shoudn't need to take an owned + // `AggregatePublicKey`. There is an issue to fix this, but in the meantime we need to + // clone. + // + // https://github.com/sigp/signature-schemes/issues/10 + let aggregate_public_keys: Vec = aggregate_public_keys + .iter() + .map(|pk| pk.as_raw()) + .cloned() + .collect(); + + // Messages are concatenated into one long message. + let mut msg: Vec = vec![]; + for message in messages { + msg.extend_from_slice(message); + } + + self.0 + .verify_multiple(&msg[..], domain, &aggregate_public_keys[..]) } } diff --git a/eth2/utils/bls/src/lib.rs b/eth2/utils/bls/src/lib.rs index 8f2e9fac0..865b8d82d 100644 --- a/eth2/utils/bls/src/lib.rs +++ b/eth2/utils/bls/src/lib.rs @@ -1,20 +1,20 @@ extern crate bls_aggregates; extern crate ssz; +mod aggregate_public_key; mod aggregate_signature; mod keypair; mod public_key; mod secret_key; mod signature; +pub use crate::aggregate_public_key::AggregatePublicKey; pub use crate::aggregate_signature::AggregateSignature; pub use crate::keypair::Keypair; pub use crate::public_key::PublicKey; pub use crate::secret_key::SecretKey; pub use crate::signature::Signature; -pub use self::bls_aggregates::AggregatePublicKey; - pub const BLS_AGG_SIG_BYTE_SIZE: usize = 96; use ssz::ssz_encode;