//! Verification framework for building trust in decentralized services. //! //! Verification levels: //! - 0: None (bare announce) //! - 1: Self-asserted (profile URL, metadata) //! - 2: Endorsed by trusted peers //! - 3: Registry-verified (KBV for therapists, trade registry for craftsmen) use serde::{Deserialize, Serialize}; use crate::identity::ServiceIdentity; /// Verification levels (higher = more trusted). #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] #[repr(u8)] pub enum VerificationLevel { #[default] None = 0, SelfAsserted = 1, PeerEndorsed = 2, RegistryVerified = 3, } impl From for VerificationLevel { fn from(value: u8) -> Self { match value { 1 => VerificationLevel::SelfAsserted, 2 => VerificationLevel::PeerEndorsed, 3.. => VerificationLevel::RegistryVerified, _ => VerificationLevel::None, } } } /// A verification attestation attached to a service message. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Verification { /// Verification level. pub level: u8, /// Verifier's mesh address. pub verifier_address: [u8; 16], /// What is being verified (e.g., "license", "identity"). pub claim: String, /// Optional external reference (URL, registry ID). #[serde(default, skip_serializing_if = "Option::is_none")] pub reference: Option, /// Signature over (level || sender_address || claim). pub signature: Vec, /// Timestamp of verification. pub timestamp: u64, /// Optional expiry timestamp. #[serde(default, skip_serializing_if = "Option::is_none")] pub expires: Option, } impl Verification { /// Create a new peer endorsement. pub fn peer_endorsement( verifier: &ServiceIdentity, subject_address: &[u8; 16], claim: impl Into, ) -> Self { Self::new( verifier, VerificationLevel::PeerEndorsed, subject_address, claim, None, ) } /// Create a registry verification. pub fn registry( verifier: &ServiceIdentity, subject_address: &[u8; 16], claim: impl Into, reference: impl Into, ) -> Self { Self::new( verifier, VerificationLevel::RegistryVerified, subject_address, claim, Some(reference.into()), ) } /// Create a new verification. pub fn new( verifier: &ServiceIdentity, level: VerificationLevel, subject_address: &[u8; 16], claim: impl Into, reference: Option, ) -> Self { use std::time::{SystemTime, UNIX_EPOCH}; let claim = claim.into(); let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_secs(); let signable = Self::signable_bytes(level as u8, subject_address, &claim); let signature = verifier.sign(&signable).to_vec(); Self { level: level as u8, verifier_address: verifier.address(), claim, reference, signature, timestamp, expires: None, } } /// Set expiry time. pub fn with_expiry(mut self, expires: u64) -> Self { self.expires = Some(expires); self } /// Create signable bytes. fn signable_bytes(level: u8, subject_address: &[u8; 16], claim: &str) -> Vec { let mut buf = Vec::with_capacity(17 + claim.len()); buf.push(level); buf.extend_from_slice(subject_address); buf.extend_from_slice(claim.as_bytes()); buf } /// Verify this attestation. pub fn verify(&self, verifier_public_key: &[u8; 32], subject_address: &[u8; 16]) -> bool { use crate::identity::compute_address; // Verify verifier address matches key if compute_address(verifier_public_key) != self.verifier_address { return false; } // Check expiry if let Some(expires) = self.expires { use std::time::{SystemTime, UNIX_EPOCH}; let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_secs(); if now > expires { return false; } } let sig: [u8; 64] = match self.signature.as_slice().try_into() { Ok(s) => s, Err(_) => return false, }; let signable = Self::signable_bytes(self.level, subject_address, &self.claim); ServiceIdentity::verify(verifier_public_key, &signable, &sig) } } /// Set of known trusted verifiers (registries, endorsers). #[derive(Default)] pub struct TrustedVerifiers { /// Known public keys with their trust level. verifiers: Vec, } /// A trusted verifier entry. #[derive(Clone)] pub struct TrustedVerifier { pub public_key: [u8; 32], pub address: [u8; 16], pub name: String, pub max_level: VerificationLevel, } impl TrustedVerifiers { /// Create empty set. pub fn new() -> Self { Self::default() } /// Add a trusted verifier. pub fn add( &mut self, public_key: [u8; 32], name: impl Into, max_level: VerificationLevel, ) { use crate::identity::compute_address; self.verifiers.push(TrustedVerifier { public_key, address: compute_address(&public_key), name: name.into(), max_level, }); } /// Find a verifier by address. pub fn find_by_address(&self, address: &[u8; 16]) -> Option<&TrustedVerifier> { self.verifiers.iter().find(|v| &v.address == address) } /// Verify a verification against known trusted verifiers. /// Returns the effective level (or 0 if not trusted). pub fn check(&self, verification: &Verification, subject_address: &[u8; 16]) -> u8 { let Some(verifier) = self.find_by_address(&verification.verifier_address) else { return 0; }; // Level cannot exceed verifier's max let claimed_level = verification.level.min(verifier.max_level as u8); // Actually verify the signature if verification.verify(&verifier.public_key, subject_address) { claimed_level } else { 0 } } /// Get the highest trusted verification level from a list. pub fn highest_level( &self, verifications: &[Verification], subject_address: &[u8; 16], ) -> VerificationLevel { verifications .iter() .map(|v| self.check(v, subject_address)) .max() .map(VerificationLevel::from) .unwrap_or(VerificationLevel::None) } } #[cfg(test)] mod tests { use super::*; #[test] fn peer_endorsement_roundtrip() { let verifier = ServiceIdentity::generate(); let subject_address = [1u8; 16]; let v = Verification::peer_endorsement(&verifier, &subject_address, "good_actor"); assert!(v.verify(&verifier.public_key(), &subject_address)); assert_eq!(v.level, VerificationLevel::PeerEndorsed as u8); } #[test] fn trusted_verifiers_check() { let verifier = ServiceIdentity::generate(); let subject_address = [2u8; 16]; let mut trusted = TrustedVerifiers::new(); trusted.add(verifier.public_key(), "Test Registry", VerificationLevel::RegistryVerified); let v = Verification::registry(&verifier, &subject_address, "licensed", "REG-12345"); let level = trusted.check(&v, &subject_address); assert_eq!(level, VerificationLevel::RegistryVerified as u8); } #[test] fn untrusted_verifier_returns_zero() { let verifier = ServiceIdentity::generate(); let subject_address = [3u8; 16]; let trusted = TrustedVerifiers::new(); // Empty let v = Verification::registry(&verifier, &subject_address, "licensed", "REG-999"); let level = trusted.check(&v, &subject_address); assert_eq!(level, 0); } #[test] fn expired_verification_fails() { let verifier = ServiceIdentity::generate(); let subject_address = [4u8; 16]; let v = Verification::peer_endorsement(&verifier, &subject_address, "trusted") .with_expiry(1); // Expired in 1970 assert!(!v.verify(&verifier.public_key(), &subject_address)); } }