From f046a326b89be130e1c67886bd0ce3445781a3f5 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 18 Dec 2018 17:04:54 +1100 Subject: [PATCH 1/5] Implement "newtype" wrappers for BLS structs --- beacon_chain/utils/bls/Cargo.toml | 1 + .../utils/bls/src/aggregate_signature.rs | 72 +++++++++++++++++ beacon_chain/utils/bls/src/lib.rs | 9 ++- beacon_chain/utils/bls/src/signature.rs | 81 +++++++++++++++++++ 4 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 beacon_chain/utils/bls/src/aggregate_signature.rs create mode 100644 beacon_chain/utils/bls/src/signature.rs diff --git a/beacon_chain/utils/bls/Cargo.toml b/beacon_chain/utils/bls/Cargo.toml index 1199efc15..81d467a46 100644 --- a/beacon_chain/utils/bls/Cargo.toml +++ b/beacon_chain/utils/bls/Cargo.toml @@ -6,3 +6,4 @@ authors = ["Paul Hauner "] [dependencies] bls-aggregates = { git = "https://github.com/sigp/signature-schemes" } hashing = { path = "../hashing" } +ssz = { path = "../ssz" } diff --git a/beacon_chain/utils/bls/src/aggregate_signature.rs b/beacon_chain/utils/bls/src/aggregate_signature.rs new file mode 100644 index 000000000..0970ce56f --- /dev/null +++ b/beacon_chain/utils/bls/src/aggregate_signature.rs @@ -0,0 +1,72 @@ +use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream}; +use super::{AggregatePublicKey, Signature}; +use bls_aggregates::AggregateSignature as RawAggregateSignature; + +/// A BLS aggregate signature. +/// +/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ +/// serialization). +#[derive(Debug, PartialEq, Clone)] +pub struct AggregateSignature(RawAggregateSignature); + +impl AggregateSignature { + /// Instantiate a new AggregateSignature. + pub fn new() -> Self { + AggregateSignature(RawAggregateSignature::new()) + } + + /// Add (aggregate) a signature to the `AggregateSignature`. + pub fn add(&mut self, signature: &Signature) { + self.0.add(signature.as_raw()) + } + + /// Verify the `AggregateSignature` against an `AggregatePublicKey`. + /// + /// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys + /// that signed the `AggregateSignature`. + pub fn verify(&mut self, msg: &[u8], avk: &AggregatePublicKey) -> bool { + self.0.verify(msg, avk) + } +} + +impl Default for AggregateSignature { + /// A "default" signature is a signature across an empty message by a secret key of 48 zeros. + fn default() -> Self { + AggregateSignature::new() + } +} + +impl Encodable for AggregateSignature { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.0.as_bytes()); + } +} + +impl Decodable for AggregateSignature { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (sig_bytes, i) = decode_ssz_list(bytes, i)?; + let raw_sig = + RawAggregateSignature::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?; + Ok((AggregateSignature(raw_sig), i)) + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::super::{Keypair, Signature}; + use super::*; + + #[test] + pub fn test_ssz_round_trip() { + let keypair = Keypair::random(); + + let mut original = AggregateSignature::new(); + original.add(&Signature::new(&[42, 42], &keypair.sk)); + + let bytes = ssz_encode(&original); + let (decoded, _) = AggregateSignature::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_chain/utils/bls/src/lib.rs b/beacon_chain/utils/bls/src/lib.rs index dcd2a9d29..15abca098 100644 --- a/beacon_chain/utils/bls/src/lib.rs +++ b/beacon_chain/utils/bls/src/lib.rs @@ -1,12 +1,17 @@ extern crate bls_aggregates; extern crate hashing; +extern crate ssz; + +mod aggregate_signature; +mod signature; + +pub use aggregate_signature::AggregateSignature; +pub use signature::Signature; pub use self::bls_aggregates::AggregatePublicKey; -pub use self::bls_aggregates::AggregateSignature; pub use self::bls_aggregates::Keypair; pub use self::bls_aggregates::PublicKey; pub use self::bls_aggregates::SecretKey; -pub use self::bls_aggregates::Signature; pub const BLS_AGG_SIG_BYTE_SIZE: usize = 97; diff --git a/beacon_chain/utils/bls/src/signature.rs b/beacon_chain/utils/bls/src/signature.rs new file mode 100644 index 000000000..bfeaca45e --- /dev/null +++ b/beacon_chain/utils/bls/src/signature.rs @@ -0,0 +1,81 @@ +use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream}; +use bls_aggregates::{PublicKey, SecretKey, Signature as RawSignature}; + +/// A single BLS signature. +/// +/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ +/// serialization). +#[derive(Debug, PartialEq, Clone)] +pub struct Signature(RawSignature); + +impl Signature { + /// Instantiate a new Signature from a message and a SecretKey. + pub fn new(msg: &[u8], sk: &SecretKey) -> Self { + Signature(RawSignature::new(msg, sk)) + } + + /// Instantiate a new Signature from a message and a SecretKey, where the message has already + /// been hashed. + pub fn new_hashed(msg_hashed: &[u8], sk: &SecretKey) -> Self { + Signature(RawSignature::new_hashed(msg_hashed, sk)) + } + + /// Verify the Signature against a PublicKey. + pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> bool { + self.0.verify(msg, pk) + } + + /// Verify the Signature against a PublicKey, where the message has already been hashed. + pub fn verify_hashed(&self, msg_hash: &[u8], pk: &PublicKey) -> bool { + self.0.verify_hashed(msg_hash, pk) + } + + /// Returns the underlying signature. + pub fn as_raw(&self) -> &RawSignature { + &self.0 + } +} + +impl Default for Signature { + /// A "default" signature is a signature across an empty message by a secret key of 48 zeros. + fn default() -> Self { + let sk = match SecretKey::from_bytes(&[0; 48]) { + Ok(key) => key, + _ => unreachable!(), // Key is static, should not fail. + }; + Signature(RawSignature::new(&[], &sk)) + } +} + +impl Encodable for Signature { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.0.as_bytes()); + } +} + +impl Decodable for Signature { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (sig_bytes, i) = decode_ssz_list(bytes, i)?; + let raw_sig = RawSignature::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?; + Ok((Signature(raw_sig), i)) + } +} + +#[cfg(test)] +mod tests { + use super::super::ssz::ssz_encode; + use super::super::Keypair; + use super::*; + + #[test] + pub fn test_ssz_round_trip() { + let keypair = Keypair::random(); + + let original = Signature::new(&[42, 42], &keypair.sk); + + let bytes = ssz_encode(&original); + let (decoded, _) = Signature::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } +} From 92ed2ffc22b4ca750ebd7a41f9bb74f969faf515 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 18 Dec 2018 17:30:34 +1100 Subject: [PATCH 2/5] Update Attestation to use new bls structs --- beacon_chain/types/src/attestation.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/beacon_chain/types/src/attestation.rs b/beacon_chain/types/src/attestation.rs index 2c2015cd3..40ee2173c 100644 --- a/beacon_chain/types/src/attestation.rs +++ b/beacon_chain/types/src/attestation.rs @@ -1,6 +1,6 @@ use super::attestation_data::SSZ_ATTESTION_DATA_LENGTH; use super::bls::{AggregateSignature, BLS_AGG_SIG_BYTE_SIZE}; -use super::ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream, LENGTH_BYTES}; +use super::ssz::{Decodable, DecodeError, Encodable, SszStream, LENGTH_BYTES}; use super::{AttestationData, Bitfield}; pub const MIN_SSZ_ATTESTION_RECORD_LENGTH: usize = { @@ -23,7 +23,7 @@ impl Encodable for Attestation { s.append(&self.data); s.append(&self.participation_bitfield); s.append(&self.custody_bitfield); - s.append_vec(&self.aggregate_sig.as_bytes()); + s.append(&self.aggregate_sig); } } @@ -32,9 +32,7 @@ impl Decodable for Attestation { let (data, i) = AttestationData::ssz_decode(bytes, i)?; let (participation_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; let (custody_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; - let (agg_sig_bytes, i) = decode_ssz_list(bytes, i)?; - let aggregate_sig = - AggregateSignature::from_bytes(&agg_sig_bytes).map_err(|_| DecodeError::TooShort)?; // also could be TooLong + let (aggregate_sig, i) = AggregateSignature::ssz_decode(bytes, i)?; let attestation_record = Self { data, From 7f00f167e3f1e2cd9bae6e8e6f53c5cb6ab6339b Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 18 Dec 2018 17:55:21 +1100 Subject: [PATCH 3/5] Fix error in agg sig newtype --- beacon_chain/utils/bls/src/aggregate_signature.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_chain/utils/bls/src/aggregate_signature.rs b/beacon_chain/utils/bls/src/aggregate_signature.rs index 0970ce56f..5cc9c3c21 100644 --- a/beacon_chain/utils/bls/src/aggregate_signature.rs +++ b/beacon_chain/utils/bls/src/aggregate_signature.rs @@ -24,7 +24,7 @@ impl AggregateSignature { /// /// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys /// that signed the `AggregateSignature`. - pub fn verify(&mut self, msg: &[u8], avk: &AggregatePublicKey) -> bool { + pub fn verify(&self, msg: &[u8], avk: &AggregatePublicKey) -> bool { self.0.verify(msg, avk) } } From 810156a0fb347ffc54ddbd71b1e970ae772d175f Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 26 Dec 2018 08:46:37 +1100 Subject: [PATCH 4/5] Remove Default impls for BLS newtypes --- beacon_chain/utils/bls/src/aggregate_signature.rs | 7 ------- beacon_chain/utils/bls/src/signature.rs | 11 ----------- 2 files changed, 18 deletions(-) diff --git a/beacon_chain/utils/bls/src/aggregate_signature.rs b/beacon_chain/utils/bls/src/aggregate_signature.rs index 5cc9c3c21..656b7e4ad 100644 --- a/beacon_chain/utils/bls/src/aggregate_signature.rs +++ b/beacon_chain/utils/bls/src/aggregate_signature.rs @@ -29,13 +29,6 @@ impl AggregateSignature { } } -impl Default for AggregateSignature { - /// A "default" signature is a signature across an empty message by a secret key of 48 zeros. - fn default() -> Self { - AggregateSignature::new() - } -} - impl Encodable for AggregateSignature { fn ssz_append(&self, s: &mut SszStream) { s.append_vec(&self.0.as_bytes()); diff --git a/beacon_chain/utils/bls/src/signature.rs b/beacon_chain/utils/bls/src/signature.rs index bfeaca45e..ebdb5b817 100644 --- a/beacon_chain/utils/bls/src/signature.rs +++ b/beacon_chain/utils/bls/src/signature.rs @@ -36,17 +36,6 @@ impl Signature { } } -impl Default for Signature { - /// A "default" signature is a signature across an empty message by a secret key of 48 zeros. - fn default() -> Self { - let sk = match SecretKey::from_bytes(&[0; 48]) { - Ok(key) => key, - _ => unreachable!(), // Key is static, should not fail. - }; - Signature(RawSignature::new(&[], &sk)) - } -} - impl Encodable for Signature { fn ssz_append(&self, s: &mut SszStream) { s.append_vec(&self.0.as_bytes()); From 4330acdd96a548186adba93f2b3a15603108406a Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 26 Dec 2018 08:54:26 +1100 Subject: [PATCH 5/5] Tidy up var name in BLS --- beacon_chain/utils/bls/src/aggregate_signature.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon_chain/utils/bls/src/aggregate_signature.rs b/beacon_chain/utils/bls/src/aggregate_signature.rs index 656b7e4ad..2e3630268 100644 --- a/beacon_chain/utils/bls/src/aggregate_signature.rs +++ b/beacon_chain/utils/bls/src/aggregate_signature.rs @@ -24,8 +24,8 @@ impl AggregateSignature { /// /// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys /// that signed the `AggregateSignature`. - pub fn verify(&self, msg: &[u8], avk: &AggregatePublicKey) -> bool { - self.0.verify(msg, avk) + pub fn verify(&self, msg: &[u8], aggregate_public_key: &AggregatePublicKey) -> bool { + self.0.verify(msg, aggregate_public_key) } }