Skip to main content

sigil_protocol/
vault.rs

1//! Vault — encrypted storage for intercepted secrets.
2//!
3//! SIGIL defines the envelope format and provider trait.
4//! Implementations choose their own encryption backend
5//! (RSA+ChaCha20, AES-GCM, HSM, etc.).
6
7use serde::{Deserialize, Serialize};
8
9/// A sealed vault entry — the SIGIL envelope format.
10///
11/// This is the standard format that all SIGIL-compliant vaults produce.
12/// The `ciphertext` field is opaque to the protocol; only the vault
13/// provider knows how to decrypt it.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct VaultEntry {
16    /// Unique identifier for this vault entry.
17    pub id: String,
18    /// Encrypted data (format determined by VaultProvider implementation).
19    pub ciphertext: Vec<u8>,
20    /// Human-readable description for the audit trail.
21    pub description: String,
22    /// ISO 8601 timestamp of when the entry was created.
23    pub created_at: String,
24    /// Tags for categorization (e.g., "api-key", "iban", "pin").
25    pub tags: Vec<String>,
26}
27
28/// Trait for encrypted vault storage backends.
29///
30/// Implementors provide their own encryption scheme.
31/// The protocol only requires encrypt/decrypt/exists operations.
32///
33/// # Example
34///
35/// ```rust,no_run
36/// use sigil_protocol::{VaultProvider, VaultEntry};
37///
38/// struct HsmVault { /* HSM connection */ }
39///
40/// impl VaultProvider for HsmVault {
41///     fn encrypt(&self, plaintext: &[u8], description: &str) -> anyhow::Result<VaultEntry> {
42///         // Encrypt via HSM and return sealed entry
43///         todo!()
44///     }
45///     fn decrypt(&self, id: &str) -> anyhow::Result<Vec<u8>> {
46///         // Decrypt via HSM
47///         todo!()
48///     }
49///     fn exists(&self, id: &str) -> bool { false }
50/// }
51/// ```
52pub trait VaultProvider: Send + Sync {
53    /// Encrypt plaintext and store as a sealed vault entry.
54    ///
55    /// Returns the `VaultEntry` with metadata. The ciphertext format
56    /// is implementation-specific.
57    fn encrypt(&self, plaintext: &[u8], description: &str) -> anyhow::Result<VaultEntry>;
58
59    /// Decrypt a vault entry by its ID.
60    ///
61    /// Returns the original plaintext bytes.
62    fn decrypt(&self, id: &str) -> anyhow::Result<Vec<u8>>;
63
64    /// Check if a vault entry exists.
65    fn exists(&self, id: &str) -> bool;
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn vault_entry_serializes() {
74        let entry = VaultEntry {
75            id: "test-123".to_string(),
76            ciphertext: vec![0xDE, 0xAD, 0xBE, 0xEF],
77            description: "Test secret".to_string(),
78            created_at: "2026-01-01T00:00:00Z".to_string(),
79            tags: vec!["test".to_string()],
80        };
81        let json = serde_json::to_string(&entry).unwrap();
82        let parsed: VaultEntry = serde_json::from_str(&json).unwrap();
83        assert_eq!(parsed.id, "test-123");
84        assert_eq!(parsed.ciphertext, vec![0xDE, 0xAD, 0xBE, 0xEF]);
85    }
86}