New mesh_node.rs providing a production-ready node: - MeshNodeBuilder for fluent configuration - MeshConfig integration for all settings - MeshMetrics tracking for all operations - Rate limiting on incoming messages - Backpressure controller - Graceful shutdown via ShutdownCoordinator - Optional FappRouter based on capabilities - MeshRouter for envelope routing - TransportManager for multi-transport support Key APIs: - MeshNodeBuilder::new().fapp_relay().build() - node.process_incoming() with rate limiting + metrics - node.gc() for store/routing table cleanup - node.shutdown() for graceful termination 222 tests passing (203 lib + 3 fapp_flow + 16 multi_node)
120 lines
3.3 KiB
Rust
120 lines
3.3 KiB
Rust
//! 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());
|
|
}
|
|
}
|