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}