synor/crates/synor-crypto/src/signature.rs
Gulshan Yadav 48949ebb3f Initial commit: Synor blockchain monorepo
A complete blockchain implementation featuring:
- synord: Full node with GHOSTDAG consensus
- explorer-web: Modern React blockchain explorer with 3D DAG visualization
- CLI wallet and tools
- Smart contract SDK and example contracts (DEX, NFT, token)
- WASM crypto library for browser/mobile
2026-01-08 05:22:17 +05:30

225 lines
7.1 KiB
Rust

//! 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<u8>,
/// Dilithium3 signature (variable length, ~3293 bytes).
pub dilithium_signature: Vec<u8>,
}
impl HybridSignature {
/// Creates a new hybrid signature from components.
pub fn new(ed25519: [u8; 64], dilithium: Vec<u8>) -> Self {
HybridSignature {
ed25519_signature: ed25519.to_vec(),
dilithium_signature: dilithium,
}
}
/// Creates a new hybrid signature from Vec components.
pub fn from_vecs(ed25519: Vec<u8>, dilithium: Vec<u8>) -> 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<u8> {
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<Self, SignatureError> {
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);
}
}