//! Signature types for Synor blockchain. //! //! All signatures in Synor use the hybrid Ed25519+Dilithium3 scheme for //! quantum-resistant security. Both components must be valid for a signature //! to be considered valid. use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use thiserror::Error; /// Ed25519 signature size in bytes. pub const ED25519_SIGNATURE_SIZE: usize = 64; /// A hybrid signature combining Ed25519 and Dilithium3. /// /// Both signatures must be valid for the overall signature to be valid. /// This provides security against both classical and quantum attacks: /// - Ed25519: Fast, compact, proven security against classical computers /// - Dilithium3: NIST-standardized, quantum-resistant lattice-based signature /// /// An attacker must break BOTH algorithms to forge a signature. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct HybridSignature { /// Ed25519 signature (64 bytes). pub ed25519_signature: Vec, /// Dilithium3 signature (variable length, ~3293 bytes). pub dilithium_signature: Vec, } impl HybridSignature { /// Creates a new hybrid signature from components. pub fn new(ed25519: [u8; 64], dilithium: Vec) -> Self { HybridSignature { ed25519_signature: ed25519.to_vec(), dilithium_signature: dilithium, } } /// Creates a new hybrid signature from Vec components. pub fn from_vecs(ed25519: Vec, dilithium: Vec) -> Self { HybridSignature { ed25519_signature: ed25519, dilithium_signature: dilithium, } } /// Returns the total size of the signature in bytes. pub fn size(&self) -> usize { self.ed25519_signature.len() + self.dilithium_signature.len() } /// Returns the Ed25519 signature component. pub fn ed25519(&self) -> &[u8] { &self.ed25519_signature } /// Returns the Dilithium signature component. pub fn dilithium(&self) -> &[u8] { &self.dilithium_signature } /// Returns the Ed25519 component bytes (for compatibility). pub fn ed25519_bytes(&self) -> &[u8] { &self.ed25519_signature } /// Returns the Ed25519 component as a fixed-size array if valid. pub fn ed25519_array(&self) -> Option<[u8; 64]> { if self.ed25519_signature.len() != 64 { return None; } let mut arr = [0u8; 64]; arr.copy_from_slice(&self.ed25519_signature); Some(arr) } /// Returns the Dilithium component bytes. pub fn dilithium_bytes(&self) -> &[u8] { &self.dilithium_signature } /// Serializes the signature to bytes. pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::with_capacity(self.size()); bytes.extend_from_slice(&self.ed25519_signature); bytes.extend_from_slice(&self.dilithium_signature); bytes } /// Deserializes a signature from bytes. pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() < ED25519_SIGNATURE_SIZE { return Err(SignatureError::InvalidLength { expected: ED25519_SIGNATURE_SIZE, got: bytes.len(), }); } let ed25519 = bytes[..ED25519_SIGNATURE_SIZE].to_vec(); let dilithium = bytes[ED25519_SIGNATURE_SIZE..].to_vec(); // Validate that we have a Dilithium signature if dilithium.is_empty() { return Err(SignatureError::MissingComponent( "Dilithium signature required for hybrid signature".to_string(), )); } Ok(HybridSignature { ed25519_signature: ed25519, dilithium_signature: dilithium, }) } /// Returns true (all signatures are hybrid). pub fn is_hybrid(&self) -> bool { true } } impl std::fmt::Debug for HybridSignature { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let ed_preview = if self.ed25519_signature.len() >= 8 { hex::encode(&self.ed25519_signature[..8]) } else { hex::encode(&self.ed25519_signature) }; f.debug_struct("HybridSignature") .field("ed25519", &ed_preview) .field("dilithium_len", &self.dilithium_signature.len()) .finish() } } impl std::fmt::Display for HybridSignature { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let ed_hex = if self.ed25519_signature.len() >= 8 { hex::encode(&self.ed25519_signature[..8]) } else { hex::encode(&self.ed25519_signature) }; write!(f, "HybridSig({}...)", ed_hex) } } /// Type alias for signature - all Synor signatures are hybrid. /// /// This alias exists for API clarity and future compatibility. /// All signatures must contain both Ed25519 and Dilithium components. pub type Signature = HybridSignature; /// Errors related to signatures. #[derive(Debug, Error)] pub enum SignatureError { #[error("Invalid signature length: expected at least {expected}, got {got}")] InvalidLength { expected: usize, got: usize }, #[error("Invalid signature format")] InvalidFormat, #[error("Signature verification failed")] VerificationFailed, #[error("Missing required component: {0}")] MissingComponent(String), #[error("Ed25519 verification failed")] Ed25519VerificationFailed, #[error("Dilithium verification failed")] DilithiumVerificationFailed, } #[cfg(test)] mod tests { use super::*; #[test] fn test_hybrid_signature_roundtrip() { let sig = HybridSignature::new([42u8; 64], vec![1, 2, 3, 4, 5]); let bytes = sig.to_bytes(); let recovered = HybridSignature::from_bytes(&bytes).unwrap(); assert_eq!(sig.ed25519_signature, recovered.ed25519_signature); assert_eq!(sig.dilithium_signature, recovered.dilithium_signature); } #[test] fn test_signature_borsh() { let sig = HybridSignature::new([1u8; 64], vec![2u8; 100]); let serialized = borsh::to_vec(&sig).unwrap(); let deserialized: HybridSignature = borsh::from_slice(&serialized).unwrap(); assert_eq!(sig.ed25519_bytes(), deserialized.ed25519_bytes()); assert_eq!(sig.dilithium_bytes(), deserialized.dilithium_bytes()); } #[test] fn test_signature_size() { let sig = HybridSignature::new([0u8; 64], vec![0u8; 3293]); assert_eq!(sig.size(), 64 + 3293); } #[test] fn test_signature_is_always_hybrid() { let sig = HybridSignature::new([0u8; 64], vec![0u8; 100]); assert!(sig.is_hybrid()); } #[test] fn test_missing_dilithium_fails() { // Only Ed25519 bytes, no Dilithium let bytes = [0u8; 64]; let result = HybridSignature::from_bytes(&bytes); assert!(result.is_err()); } #[test] fn test_type_alias() { // Signature is now a type alias for HybridSignature let sig: Signature = HybridSignature::new([1u8; 64], vec![2u8; 50]); assert_eq!(sig.size(), 64 + 50); } }