//! Ed25519 keypair, signing, and verification. use crate::NodeId; use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey}; use serde::de::{SeqAccess, Visitor}; use serde::ser::SerializeTuple; use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// Ed25519 signing keypair. /// /// Wraps `ed25519_dalek::SigningKey`. The public key half is exposed as a /// `swactor_transport::NodeId` so peer identity is uniform across the /// distribution stack. #[derive(Clone)] pub struct Keypair { signing: SigningKey, } impl Keypair { /// Generate a fresh keypair from the OS RNG. pub fn generate() -> Self { let mut rng = rand_core::OsRng; Self { signing: SigningKey::generate(&mut rng), } } /// Reconstruct a keypair from its 32-byte secret seed. /// /// # Panics /// /// Panics if `bytes` is shorter than 32 bytes. Extra bytes are ignored. pub fn from_bytes(bytes: &[u8]) -> Self { let mut seed = [0u8; 32]; seed.copy_from_slice(&bytes[..32]); Self { signing: SigningKey::from_bytes(&seed), } } /// The 32-byte secret seed for this keypair. pub fn secret_bytes(&self) -> [u8; 32] { self.signing.to_bytes() } /// The public node identifier (raw ed25519 public key bytes). pub fn node_id(&self) -> NodeId { NodeId(self.signing.verifying_key().to_bytes()) } /// Sign `msg` with the secret half. Result is a 64-byte ed25519 signature. pub fn sign(&self, msg: &[u8]) -> Signature { let sig = self.signing.sign(msg); Signature(sig.to_bytes()) } } impl core::fmt::Debug for Keypair { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { // Don't leak the secret half through Debug. f.debug_struct("Keypair") .field("node_id", &self.node_id()) .finish_non_exhaustive() } } /// 64-byte ed25519 signature. #[derive(Clone, Copy, PartialEq, Eq)] pub struct Signature(pub [u8; 64]); impl Serialize for Signature { fn serialize(&self, ser: S) -> Result { let mut tup = ser.serialize_tuple(64)?; for byte in &self.0 { tup.serialize_element(byte)?; } tup.end() } } impl<'de> Deserialize<'de> for Signature { fn deserialize>(de: D) -> Result { struct SigVisitor; impl<'de> Visitor<'de> for SigVisitor { type Value = Signature; fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "a 64-byte ed25519 signature") } fn visit_seq>(self, mut seq: A) -> Result { let mut bytes = [0u8; 64]; for (i, byte) in bytes.iter_mut().enumerate() { *byte = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?; } Ok(Signature(bytes)) } } de.deserialize_tuple(64, SigVisitor) } } impl core::fmt::Debug for Signature { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Signature(")?; for b in &self.0[..4] { write!(f, "{b:02x}")?; } write!(f, "\u{2026})") } } /// Verify `msg` against `sig` using the public key encoded in `node_id`. /// /// Returns `false` if the key bytes are not a valid ed25519 point, the /// signature bytes are not a valid signature, or the check fails. pub fn verify(node_id: &NodeId, msg: &[u8], sig: &Signature) -> bool { let Ok(vk) = VerifyingKey::from_bytes(&node_id.0) else { return false; }; let sig = ed25519_dalek::Signature::from_bytes(&sig.0); vk.verify(msg, &sig).is_ok() } #[cfg(test)] mod tests { use super::*; #[test] fn sign_then_verify_succeeds() { let kp = Keypair::generate(); let sig = kp.sign(b"hello"); assert!(verify(&kp.node_id(), b"hello", &sig)); } #[test] fn verify_rejects_wrong_message() { let kp = Keypair::generate(); let sig = kp.sign(b"original"); assert!(!verify(&kp.node_id(), b"tampered", &sig)); } #[test] fn verify_rejects_wrong_key() { let a = Keypair::generate(); let b = Keypair::generate(); let sig = a.sign(b"msg"); assert!(!verify(&b.node_id(), b"msg", &sig)); } #[test] fn from_bytes_reproduces_identity() { let kp = Keypair::generate(); let restored = Keypair::from_bytes(&kp.secret_bytes()); assert_eq!(kp.node_id(), restored.node_id()); // And the signatures match too (ed25519 is deterministic). assert_eq!( kp.sign(b"deterministic").0, restored.sign(b"deterministic").0 ); } }