Skip to main content

oxigdal_security/encryption/
envelope.rs

1//! Envelope encryption for KMS integration.
2
3use crate::encryption::{AtRestEncryptor, EncryptedData, EncryptionAlgorithm};
4use crate::error::{Result, SecurityError};
5use serde::{Deserialize, Serialize};
6
7/// Key encryption key (KEK) provider trait.
8pub trait KekProvider: Send + Sync {
9    /// Encrypt a data encryption key (DEK).
10    fn encrypt_dek(&self, dek: &[u8]) -> Result<Vec<u8>>;
11
12    /// Decrypt a data encryption key (DEK).
13    fn decrypt_dek(&self, encrypted_dek: &[u8]) -> Result<Vec<u8>>;
14
15    /// Get the KEK ID.
16    fn kek_id(&self) -> &str;
17}
18
19/// Envelope encrypted data.
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct EnvelopeEncryptedData {
22    /// Encrypted data encryption key (DEK).
23    pub encrypted_dek: Vec<u8>,
24    /// KEK ID used to encrypt the DEK.
25    pub kek_id: String,
26    /// Encrypted payload.
27    pub encrypted_payload: EncryptedData,
28    /// DEK algorithm.
29    pub dek_algorithm: EncryptionAlgorithm,
30}
31
32impl EnvelopeEncryptedData {
33    /// Serialize to JSON bytes.
34    pub fn to_json_bytes(&self) -> Result<Vec<u8>> {
35        serde_json::to_vec(self).map_err(SecurityError::from)
36    }
37
38    /// Deserialize from JSON bytes.
39    pub fn from_json_bytes(bytes: &[u8]) -> Result<Self> {
40        serde_json::from_slice(bytes).map_err(SecurityError::from)
41    }
42}
43
44/// Envelope encryptor using envelope encryption pattern.
45pub struct EnvelopeEncryptor {
46    kek_provider: Box<dyn KekProvider>,
47    dek_algorithm: EncryptionAlgorithm,
48}
49
50impl EnvelopeEncryptor {
51    /// Create a new envelope encryptor.
52    pub fn new(kek_provider: Box<dyn KekProvider>, dek_algorithm: EncryptionAlgorithm) -> Self {
53        Self {
54            kek_provider,
55            dek_algorithm,
56        }
57    }
58
59    /// Encrypt data using envelope encryption.
60    pub fn encrypt(&self, plaintext: &[u8], aad: Option<&[u8]>) -> Result<EnvelopeEncryptedData> {
61        // Generate random DEK
62        let dek = AtRestEncryptor::generate_key(self.dek_algorithm);
63
64        // Encrypt plaintext with DEK
65        let dek_id = uuid::Uuid::new_v4().to_string();
66        let encryptor = AtRestEncryptor::new(self.dek_algorithm, dek.clone(), dek_id)?;
67        let encrypted_payload = encryptor.encrypt(plaintext, aad)?;
68
69        // Encrypt DEK with KEK
70        let encrypted_dek = self.kek_provider.encrypt_dek(&dek)?;
71
72        Ok(EnvelopeEncryptedData {
73            encrypted_dek,
74            kek_id: self.kek_provider.kek_id().to_string(),
75            encrypted_payload,
76            dek_algorithm: self.dek_algorithm,
77        })
78    }
79
80    /// Decrypt data using envelope encryption.
81    pub fn decrypt(&self, envelope: &EnvelopeEncryptedData) -> Result<Vec<u8>> {
82        // Verify KEK ID matches
83        if envelope.kek_id != self.kek_provider.kek_id() {
84            return Err(SecurityError::decryption(format!(
85                "KEK ID mismatch: expected {}, got {}",
86                self.kek_provider.kek_id(),
87                envelope.kek_id
88            )));
89        }
90
91        // Decrypt DEK with KEK
92        let dek = self.kek_provider.decrypt_dek(&envelope.encrypted_dek)?;
93
94        // Decrypt payload with DEK
95        let dek_id = envelope.encrypted_payload.metadata.key_id.clone();
96        let encryptor = AtRestEncryptor::new(envelope.dek_algorithm, dek, dek_id)?;
97        encryptor.decrypt(&envelope.encrypted_payload)
98    }
99
100    /// Get the KEK ID.
101    pub fn kek_id(&self) -> &str {
102        self.kek_provider.kek_id()
103    }
104
105    /// Get the DEK algorithm.
106    pub fn dek_algorithm(&self) -> EncryptionAlgorithm {
107        self.dek_algorithm
108    }
109}
110
111/// In-memory KEK provider for testing and development.
112pub struct InMemoryKekProvider {
113    kek_id: String,
114    encryptor: AtRestEncryptor,
115}
116
117impl InMemoryKekProvider {
118    /// Create a new in-memory KEK provider.
119    pub fn new(kek_id: String) -> Result<Self> {
120        let kek = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
121        let encryptor = AtRestEncryptor::new(EncryptionAlgorithm::Aes256Gcm, kek, kek_id.clone())?;
122
123        Ok(Self { kek_id, encryptor })
124    }
125
126    /// Create with a specific KEK.
127    pub fn with_kek(kek_id: String, kek: Vec<u8>) -> Result<Self> {
128        let encryptor = AtRestEncryptor::new(EncryptionAlgorithm::Aes256Gcm, kek, kek_id.clone())?;
129
130        Ok(Self { kek_id, encryptor })
131    }
132}
133
134impl KekProvider for InMemoryKekProvider {
135    fn encrypt_dek(&self, dek: &[u8]) -> Result<Vec<u8>> {
136        let encrypted = self.encryptor.encrypt(dek, None)?;
137        encrypted.to_json_bytes()
138    }
139
140    fn decrypt_dek(&self, encrypted_dek: &[u8]) -> Result<Vec<u8>> {
141        let encrypted = EncryptedData::from_json_bytes(encrypted_dek)?;
142        self.encryptor.decrypt(&encrypted)
143    }
144
145    fn kek_id(&self) -> &str {
146        &self.kek_id
147    }
148}
149
150/// Multi-region KEK provider for disaster recovery.
151pub struct MultiRegionKekProvider {
152    kek_id: String,
153    primary: Box<dyn KekProvider>,
154    secondary: Option<Box<dyn KekProvider>>,
155}
156
157impl MultiRegionKekProvider {
158    /// Create a new multi-region KEK provider.
159    pub fn new(
160        kek_id: String,
161        primary: Box<dyn KekProvider>,
162        secondary: Option<Box<dyn KekProvider>>,
163    ) -> Self {
164        Self {
165            kek_id,
166            primary,
167            secondary,
168        }
169    }
170
171    /// Encrypt with both primary and secondary (for migration).
172    pub fn encrypt_with_both(&self, dek: &[u8]) -> Result<(Vec<u8>, Option<Vec<u8>>)> {
173        let primary_encrypted = self.primary.encrypt_dek(dek)?;
174        let secondary_encrypted = if let Some(ref secondary) = self.secondary {
175            Some(secondary.encrypt_dek(dek)?)
176        } else {
177            None
178        };
179
180        Ok((primary_encrypted, secondary_encrypted))
181    }
182}
183
184impl KekProvider for MultiRegionKekProvider {
185    fn encrypt_dek(&self, dek: &[u8]) -> Result<Vec<u8>> {
186        self.primary.encrypt_dek(dek)
187    }
188
189    fn decrypt_dek(&self, encrypted_dek: &[u8]) -> Result<Vec<u8>> {
190        // Try primary first
191        match self.primary.decrypt_dek(encrypted_dek) {
192            Ok(dek) => Ok(dek),
193            Err(e) => {
194                // Fallback to secondary if available
195                if let Some(ref secondary) = self.secondary {
196                    secondary.decrypt_dek(encrypted_dek)
197                } else {
198                    Err(e)
199                }
200            }
201        }
202    }
203
204    fn kek_id(&self) -> &str {
205        &self.kek_id
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn test_in_memory_kek_provider() {
215        let provider =
216            InMemoryKekProvider::new("test-kek".to_string()).expect("Failed to create provider");
217
218        let dek = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
219        let encrypted_dek = provider.encrypt_dek(&dek).expect("Encryption failed");
220
221        assert_ne!(encrypted_dek, dek);
222
223        let decrypted_dek = provider
224            .decrypt_dek(&encrypted_dek)
225            .expect("Decryption failed");
226        assert_eq!(decrypted_dek, dek);
227    }
228
229    #[test]
230    fn test_envelope_encryption() {
231        let kek_provider =
232            InMemoryKekProvider::new("test-kek".to_string()).expect("Failed to create provider");
233        let encryptor =
234            EnvelopeEncryptor::new(Box::new(kek_provider), EncryptionAlgorithm::Aes256Gcm);
235
236        let plaintext = b"sensitive data";
237        let envelope = encryptor
238            .encrypt(plaintext, None)
239            .expect("Encryption failed");
240
241        assert_ne!(envelope.encrypted_payload.ciphertext, plaintext);
242        assert!(!envelope.encrypted_dek.is_empty());
243
244        let decrypted = encryptor.decrypt(&envelope).expect("Decryption failed");
245        assert_eq!(decrypted, plaintext);
246    }
247
248    #[test]
249    fn test_envelope_with_aad() {
250        let kek_provider =
251            InMemoryKekProvider::new("test-kek".to_string()).expect("Failed to create provider");
252        let encryptor =
253            EnvelopeEncryptor::new(Box::new(kek_provider), EncryptionAlgorithm::Aes256Gcm);
254
255        let plaintext = b"sensitive data";
256        let aad = b"additional data";
257        let envelope = encryptor
258            .encrypt(plaintext, Some(aad))
259            .expect("Encryption failed");
260
261        let decrypted = encryptor.decrypt(&envelope).expect("Decryption failed");
262        assert_eq!(decrypted, plaintext);
263    }
264
265    #[test]
266    fn test_multi_region_kek_provider() {
267        let primary =
268            InMemoryKekProvider::new("primary-kek".to_string()).expect("Failed to create primary");
269        let secondary = InMemoryKekProvider::new("secondary-kek".to_string())
270            .expect("Failed to create secondary");
271
272        let multi = MultiRegionKekProvider::new(
273            "multi-kek".to_string(),
274            Box::new(primary),
275            Some(Box::new(secondary)),
276        );
277
278        let dek = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
279        let encrypted_dek = multi.encrypt_dek(&dek).expect("Encryption failed");
280
281        let decrypted_dek = multi
282            .decrypt_dek(&encrypted_dek)
283            .expect("Decryption failed");
284        assert_eq!(decrypted_dek, dek);
285    }
286
287    #[test]
288    fn test_envelope_serialization() {
289        let kek_provider =
290            InMemoryKekProvider::new("test-kek".to_string()).expect("Failed to create provider");
291        let encryptor =
292            EnvelopeEncryptor::new(Box::new(kek_provider), EncryptionAlgorithm::Aes256Gcm);
293
294        let plaintext = b"sensitive data";
295        let envelope = encryptor
296            .encrypt(plaintext, None)
297            .expect("Encryption failed");
298
299        let json = envelope.to_json_bytes().expect("Serialization failed");
300        let deserialized =
301            EnvelopeEncryptedData::from_json_bytes(&json).expect("Deserialization failed");
302
303        let decrypted = encryptor.decrypt(&deserialized).expect("Decryption failed");
304        assert_eq!(decrypted, plaintext);
305    }
306}