use crate::{ generic_public_key::{GenericPublicKey, TPublicKey}, Error, Hash256, }; use serde::de::{Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use serde_hex::{encode as hex_encode, PrefixedHexVisitor}; use ssz::{Decode, Encode}; use std::fmt; 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; /// 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]; /// 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)] 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() } /// Returns a reference to the underlying BLS point. pub(crate) fn point(&self) -> Option<&Sig> { self.point.as_ref() } /// Instantiates `Self` from a `point`. pub(crate) 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 } } /// 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 self.is_infinity && pubkey.is_infinity { return true; } 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); } 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); }