// worm.rs — WORM (Write-Once-Read-Many) artifact sealing // // Non-recursive. Every artifact is sealed with SHA-256. use sha2::{Sha256, Digest}; use crate::WormReceipt; use crate::SnapAddrError; /// WORM seal for an artifact. #[derive(Debug, Clone)] pub struct WormSeal { pub hash: String, pub content_hash: String, pub receipt_hash: String, } /// Seal an artifact with WORM. pub fn seal_artifact(content: &[u8]) -> WormSeal { let mut hasher = Sha256::new(); hasher.update(content); let content_hash = hex::encode(hasher.finalize()); WormSeal { hash: content_hash.clone(), content_hash, receipt_hash: String::new(), } } /// Seal a receipt. pub fn seal_receipt(receipt: &WormReceipt) -> Result { let receipt_json = serde_json::to_string(receipt) .map_err(|e| SnapAddrError::CanonicalFailed(e.to_string()))?; let content_seal = seal_artifact(receipt_json.as_bytes()); let mut hasher = Sha256::new(); hasher.update(receipt_json.as_bytes()); let receipt_hash = hex::encode(hasher.finalize()); Ok(WormSeal { hash: content_seal.hash.clone(), content_hash: content_seal.content_hash, receipt_hash, }) } /// Verify a WORM seal against content. pub fn verify_seal(content: &[u8], seal: &WormSeal) -> bool { let mut hasher = Sha256::new(); hasher.update(content); let computed = hex::encode(hasher.finalize()); computed == seal.content_hash } /// Generate a WORM receipt for a JSON value. pub fn generate_receipt(value: &serde_json::Value) -> Result { crate::receipt::generate_receipt(value) } /// Verify a WORM receipt. pub fn verify_receipt(receipt: &WormReceipt) -> Result { crate::receipt::verify_receipt(receipt) } #[cfg(test)] mod tests { use super::*; #[test] fn test_seal_artifact() { let content = b"test content"; let seal = seal_artifact(content); assert_eq!(seal.content_hash.len(), 64); assert!(verify_seal(content, &seal)); } #[test] fn test_seal_tampered() { let content = b"test content"; let seal = seal_artifact(content); assert!(!verify_seal(b"tampered content", &seal)); } #[test] fn test_seal_receipt() { let v = serde_json::json!({"test": 42}); let receipt = generate_receipt(&v).unwrap(); let seal = seal_receipt(&receipt).unwrap(); assert_eq!(seal.receipt_hash.len(), 64); } #[test] fn test_verify_receipt() { let v = serde_json::json!({"test": 42}); let receipt = generate_receipt(&v).unwrap(); assert!(verify_receipt(&receipt).unwrap()); } }