File size: 2,854 Bytes
103262d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// 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<WormSeal, SnapAddrError> {
    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<WormReceipt, SnapAddrError> {
    crate::receipt::generate_receipt(value)
}

/// Verify a WORM receipt.
pub fn verify_receipt(receipt: &WormReceipt) -> Result<bool, SnapAddrError> {
    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());
    }
}