//! Service identity management using Ed25519. use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use rand::rngs::OsRng; use sha2::{Digest, Sha256}; /// A service participant's identity (Ed25519 keypair). #[derive(Clone)] pub struct ServiceIdentity { signing_key: SigningKey, } impl ServiceIdentity { /// Generate a new random identity. pub fn generate() -> Self { use rand::RngCore; let mut secret = [0u8; 32]; OsRng.fill_bytes(&mut secret); let signing_key = SigningKey::from_bytes(&secret); Self { signing_key } } /// Create from an existing secret key. pub fn from_secret(secret: &[u8; 32]) -> Self { let signing_key = SigningKey::from_bytes(secret); Self { signing_key } } /// Get the 32-byte public key. pub fn public_key(&self) -> [u8; 32] { self.signing_key.verifying_key().to_bytes() } /// Get the 32-byte secret key (for persistence). pub fn secret_key(&self) -> [u8; 32] { self.signing_key.to_bytes() } /// Compute the 16-byte mesh address from the public key. pub fn address(&self) -> [u8; 16] { compute_address(&self.public_key()) } /// Sign a message. pub fn sign(&self, message: &[u8]) -> [u8; 64] { let sig = self.signing_key.sign(message); sig.to_bytes() } /// Verify a signature against a public key. pub fn verify(public_key: &[u8; 32], message: &[u8], signature: &[u8; 64]) -> bool { let Ok(verifying_key) = VerifyingKey::from_bytes(public_key) else { return false; }; let sig = Signature::from_bytes(signature); verifying_key.verify(message, &sig).is_ok() } } /// Compute a 16-byte mesh address from a 32-byte public key. /// /// Address = SHA-256(public_key)[0..16] pub fn compute_address(public_key: &[u8; 32]) -> [u8; 16] { let hash = Sha256::digest(public_key); let mut addr = [0u8; 16]; addr.copy_from_slice(&hash[..16]); addr } impl std::fmt::Debug for ServiceIdentity { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ServiceIdentity") .field("address", &hex::encode(self.address())) .finish() } } // Hex encoding for debug output mod hex { pub fn encode(bytes: impl AsRef<[u8]>) -> String { bytes.as_ref().iter().map(|b| format!("{b:02x}")).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn generate_and_sign() { let id = ServiceIdentity::generate(); let msg = b"hello world"; let sig = id.sign(msg); assert!(ServiceIdentity::verify(&id.public_key(), msg, &sig)); } #[test] fn address_is_deterministic() { let id = ServiceIdentity::generate(); let addr1 = id.address(); let addr2 = compute_address(&id.public_key()); assert_eq!(addr1, addr2); } #[test] fn wrong_message_fails() { let id = ServiceIdentity::generate(); let sig = id.sign(b"correct"); assert!(!ServiceIdentity::verify(&id.public_key(), b"wrong", &sig)); } #[test] fn roundtrip_secret() { let id = ServiceIdentity::generate(); let secret = id.secret_key(); let restored = ServiceIdentity::from_secret(&secret); assert_eq!(id.public_key(), restored.public_key()); } }