use crate::{ generic_public_key::{GenericPublicKey, TPublicKey}, Error, Hash256, }; use serde::de::{Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use serde_utils::hex::encode as hex_encode; use ssz::{Decode, Encode}; use std::fmt; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use tree_hash::TreeHash; /// The byte-length of a BLS signature when serialized in compressed form. pub const SIGNATURE_BYTES_LEN: usize = 96; /// The byte-length of a BLS signature when serialized in uncompressed form. pub const SIGNATURE_UNCOMPRESSED_BYTES_LEN: usize = 192; /// Represents the signature at infinity. pub const INFINITY_SIGNATURE: [u8; SIGNATURE_BYTES_LEN] = [ 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; /// The compressed bytes used to represent `GenericSignature::empty()`. pub const NONE_SIGNATURE: [u8; SIGNATURE_BYTES_LEN] = [0; SIGNATURE_BYTES_LEN]; /// Implemented on some struct from a BLS library so it may be used as the `point` in an /// `GenericSignature`. pub trait TSignature: Sized + Clone { /// Serialize `self` as compressed bytes. fn serialize(&self) -> [u8; SIGNATURE_BYTES_LEN]; /// Serialize `self` as uncompressed bytes. fn serialize_uncompressed(&self) -> [u8; SIGNATURE_UNCOMPRESSED_BYTES_LEN]; /// Deserialize `self` from compressed bytes. fn deserialize(bytes: &[u8]) -> Result; /// Returns `true` if `self` is a signature across `msg` by `pubkey`. fn verify(&self, pubkey: &GenericPublicKey, msg: Hash256) -> bool; } /// A BLS signature that is generic across: /// /// - `Pub`: A BLS public key. /// - `Sig`: A BLS signature. /// /// Provides generic functionality whilst deferring all serious cryptographic operations to the /// generics. #[derive(Clone, PartialEq, Eq)] pub struct GenericSignature { /// The underlying point which performs *actual* cryptographic operations. point: Option, /// True if this point is equal to the `INFINITY_SIGNATURE`. pub(crate) is_infinity: bool, _phantom: PhantomData, } impl GenericSignature where Sig: TSignature, { /// Initialize self to the "empty" value. This value is serialized as all-zeros. /// /// ## Notes /// /// This function is not necessarily useful from a BLS cryptography perspective, it mostly /// exists to satisfy the Eth2 specification which expects the all-zeros serialization to be /// meaningful. pub fn empty() -> Self { Self { point: None, is_infinity: false, _phantom: PhantomData, } } /// Returns `true` if `self` is equal to the "empty" value. /// /// E.g., `Self::empty().is_empty() == true` pub fn is_empty(&self) -> bool { self.point.is_none() } /// Initialize self to the point-at-infinity. /// /// In general `AggregateSignature::infinity` should be used in favour of this function. pub fn infinity() -> Result { Self::deserialize(&INFINITY_SIGNATURE) } /// Returns `true` if `self` is equal to the point at infinity. pub fn is_infinity(&self) -> bool { self.is_infinity } /// Returns a reference to the underlying BLS point. pub fn point(&self) -> Option<&Sig> { self.point.as_ref() } /// Instantiates `Self` from a `point`. pub fn from_point(point: Sig, is_infinity: bool) -> Self { Self { point: Some(point), is_infinity, _phantom: PhantomData, } } /// Serialize `self` as compressed bytes. pub fn serialize(&self) -> [u8; SIGNATURE_BYTES_LEN] { if let Some(point) = &self.point { point.serialize() } else { NONE_SIGNATURE } } /// Serialize `self` as compressed bytes. pub fn serialize_uncompressed(&self) -> Option<[u8; SIGNATURE_UNCOMPRESSED_BYTES_LEN]> { self.point.as_ref().map(|point| point.serialize_uncompressed()) } /// Deserialize `self` from compressed bytes. pub fn deserialize(bytes: &[u8]) -> Result { let point = if bytes == &NONE_SIGNATURE[..] { None } else { Some(Sig::deserialize(bytes)?) }; Ok(Self { point, is_infinity: bytes == &INFINITY_SIGNATURE[..], _phantom: PhantomData, }) } } impl GenericSignature where Sig: TSignature, Pub: TPublicKey + Clone, { /// Returns `true` if `self` is a signature across `msg` by `pubkey`. pub fn verify(&self, pubkey: &GenericPublicKey, msg: Hash256) -> bool { if let Some(point) = &self.point { point.verify(pubkey.point(), msg) } else { false } } } impl> Encode for GenericSignature { impl_ssz_encode!(SIGNATURE_BYTES_LEN); } impl> Decode for GenericSignature { impl_ssz_decode!(SIGNATURE_BYTES_LEN); } impl> TreeHash for GenericSignature { impl_tree_hash!(SIGNATURE_BYTES_LEN); } /// Hashes the `self.serialize()` bytes. impl> Hash for GenericSignature { fn hash(&self, state: &mut H) { self.serialize().hash(state); } } impl> fmt::Display for GenericSignature { impl_display!(); } impl> std::str::FromStr for GenericSignature { impl_from_str!(); } impl> Serialize for GenericSignature { impl_serde_serialize!(); } impl<'de, PublicKey, T: TSignature> Deserialize<'de> for GenericSignature { impl_serde_deserialize!(); } impl> fmt::Debug for GenericSignature { impl_debug!(); } #[cfg(feature = "arbitrary")] impl + 'static> arbitrary::Arbitrary<'_> for GenericSignature { impl_arbitrary!(SIGNATURE_BYTES_LEN); }