Rust · 11910 bytes Raw Blame History
1 //! AES-256-GCM encryption implementation for ZephyrFS
2 //!
3 //! Provides segment-level encryption using AES-256 in GCM mode for authenticated encryption.
4 //! Each file segment is encrypted with its own derived key for maximum security isolation.
5
6 use crate::crypto::{AesParams, KeyHierarchy, SecureBytes};
7 use anyhow::{Context, Result};
8 use ring::aead::{Aad, BoundKey, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey, AES_256_GCM};
9 use ring::error::Unspecified;
10 use ring::rand::{SecureRandom, SystemRandom};
11 use serde::{Deserialize, Serialize};
12 use zeroize::{Zeroize, ZeroizeOnDrop};
13
14 /// Default segment size for file encryption (1MB)
15 pub const DEFAULT_SEGMENT_SIZE: usize = 1024 * 1024; // 1MB
16
17 /// Encrypted data container with all necessary metadata for decryption
18 #[derive(Debug, Clone, Serialize, Deserialize)]
19 pub struct EncryptedData {
20 /// Segment index in the original file
21 pub segment_index: u64,
22 /// Encrypted data payload
23 pub ciphertext: Vec<u8>,
24 /// Nonce used for this encryption
25 pub nonce: [u8; 12],
26 /// Additional authenticated data (AAD) - segment metadata
27 pub aad: Vec<u8>,
28 /// Key derivation path for this segment
29 pub key_path: Vec<u32>,
30 }
31
32 /// File-level encryption operations
33 pub struct FileEncryption {
34 params: AesParams,
35 segment_size: usize,
36 rng: SystemRandom,
37 }
38
39 impl FileEncryption {
40 pub fn new(params: &AesParams) -> Self {
41 Self {
42 params: params.clone(),
43 segment_size: DEFAULT_SEGMENT_SIZE,
44 rng: SystemRandom::new(),
45 }
46 }
47
48 pub fn with_segment_size(params: &AesParams, segment_size: usize) -> Self {
49 Self {
50 params: params.clone(),
51 segment_size,
52 rng: SystemRandom::new(),
53 }
54 }
55
56 /// Encrypt file data into multiple encrypted segments
57 pub fn encrypt_to_segments(
58 &self,
59 file_data: &[u8],
60 key_hierarchy: &KeyHierarchy,
61 ) -> Result<Vec<EncryptedData>> {
62 let mut encrypted_segments = Vec::new();
63 let total_segments = (file_data.len() + self.segment_size - 1) / self.segment_size;
64
65 for (segment_index, chunk) in file_data.chunks(self.segment_size).enumerate() {
66 let segment_encryption = SegmentEncryption::new(&self.params, &self.rng);
67 let key_path = vec![segment_index as u32]; // Simple path for now
68
69 let encrypted_data = segment_encryption.encrypt_segment(
70 chunk,
71 segment_index as u64,
72 key_hierarchy,
73 &key_path,
74 )?;
75
76 encrypted_segments.push(encrypted_data);
77 }
78
79 Ok(encrypted_segments)
80 }
81
82 /// Decrypt segments back to original file data
83 pub fn decrypt_from_segments(
84 &self,
85 encrypted_segments: &[EncryptedData],
86 key_hierarchy: &KeyHierarchy,
87 ) -> Result<Vec<u8>> {
88 // Sort segments by index to ensure correct order
89 let mut sorted_segments = encrypted_segments.to_vec();
90 sorted_segments.sort_by_key(|s| s.segment_index);
91
92 let mut decrypted_data = Vec::new();
93
94 for encrypted_segment in sorted_segments {
95 let segment_encryption = SegmentEncryption::new(&self.params, &self.rng);
96 let decrypted_chunk = segment_encryption.decrypt_segment(
97 &encrypted_segment,
98 key_hierarchy,
99 )?;
100
101 decrypted_data.extend_from_slice(&decrypted_chunk);
102 }
103
104 Ok(decrypted_data)
105 }
106 }
107
108 /// Segment-level encryption operations
109 pub struct SegmentEncryption {
110 params: AesParams,
111 rng: SystemRandom,
112 }
113
114 impl SegmentEncryption {
115 pub fn new(params: &AesParams, rng: &SystemRandom) -> Self {
116 Self {
117 params: params.clone(),
118 rng: SystemRandom::new(), // Create new instance for thread safety
119 }
120 }
121
122 /// Encrypt a single file segment
123 pub fn encrypt_segment(
124 &self,
125 segment_data: &[u8],
126 segment_index: u64,
127 key_hierarchy: &KeyHierarchy,
128 key_path: &[u32],
129 ) -> Result<EncryptedData> {
130 // Derive segment-specific key
131 let segment_key = key_hierarchy.derive_segment_key(key_path)
132 .context("Failed to derive segment key")?;
133
134 // Generate random nonce
135 let mut nonce_bytes = [0u8; 12];
136 self.rng.fill(&mut nonce_bytes)
137 .map_err(|_| anyhow::anyhow!("Failed to generate random nonce"))?;
138
139 let nonce = Nonce::assume_unique_for_key(nonce_bytes);
140
141 // Prepare AAD (Additional Authenticated Data) with segment metadata
142 let aad_data = self.prepare_aad(segment_index, key_path)?;
143 let aad = Aad::from(&aad_data);
144
145 // Create sealing key
146 let unbound_key = UnboundKey::new(&AES_256_GCM, segment_key.as_bytes())
147 .map_err(|_| anyhow::anyhow!("Failed to create encryption key"))?;
148
149 let mut sealing_key = SealingKey::new(unbound_key, NonceCounter::new());
150
151 // Encrypt the data
152 let mut ciphertext = segment_data.to_vec();
153 sealing_key.seal_in_place_append_tag(aad, &mut ciphertext)
154 .map_err(|_| anyhow::anyhow!("Failed to encrypt segment"))?;
155
156 Ok(EncryptedData {
157 segment_index,
158 ciphertext,
159 nonce: nonce_bytes,
160 aad: aad_data,
161 key_path: key_path.to_vec(),
162 })
163 }
164
165 /// Decrypt a single encrypted segment
166 pub fn decrypt_segment(
167 &self,
168 encrypted_data: &EncryptedData,
169 key_hierarchy: &KeyHierarchy,
170 ) -> Result<Vec<u8>> {
171 // Derive the same segment key used for encryption
172 let segment_key = key_hierarchy.derive_segment_key(&encrypted_data.key_path)
173 .context("Failed to derive segment key for decryption")?;
174
175 // Recreate nonce
176 let nonce = Nonce::assume_unique_for_key(encrypted_data.nonce);
177 let aad = Aad::from(&encrypted_data.aad);
178
179 // Create opening key
180 let unbound_key = UnboundKey::new(&AES_256_GCM, segment_key.as_bytes())
181 .map_err(|_| anyhow::anyhow!("Failed to create decryption key"))?;
182
183 let mut opening_key = OpeningKey::new(unbound_key, NonceCounter::new());
184
185 // Decrypt the data
186 let mut ciphertext = encrypted_data.ciphertext.clone();
187 let plaintext = opening_key.open_in_place(aad, &mut ciphertext)
188 .map_err(|_| anyhow::anyhow!("Failed to decrypt segment - authentication failed"))?;
189
190 Ok(plaintext.to_vec())
191 }
192
193 /// Prepare Additional Authenticated Data (AAD) for GCM
194 fn prepare_aad(&self, segment_index: u64, key_path: &[u32]) -> Result<Vec<u8>> {
195 let mut aad = Vec::new();
196
197 // Add segment index to AAD
198 aad.extend_from_slice(&segment_index.to_le_bytes());
199
200 // Add key path to AAD for additional security
201 for path_element in key_path {
202 aad.extend_from_slice(&path_element.to_le_bytes());
203 }
204
205 // Add version identifier for future compatibility
206 aad.extend_from_slice(b"ZephyrFS-v1");
207
208 Ok(aad)
209 }
210 }
211
212 /// Nonce counter for GCM operations - ensures nonces are never reused
213 #[derive(ZeroizeOnDrop)]
214 struct NonceCounter {
215 counter: u64,
216 }
217
218 impl NonceCounter {
219 fn new() -> Self {
220 Self { counter: 0 }
221 }
222 }
223
224 impl NonceSequence for NonceCounter {
225 fn advance(&mut self) -> Result<Nonce, Unspecified> {
226 self.counter += 1;
227
228 if self.counter == u64::MAX {
229 return Err(Unspecified);
230 }
231
232 let mut nonce_bytes = [0u8; 12];
233 nonce_bytes[4..].copy_from_slice(&self.counter.to_be_bytes());
234
235 Ok(Nonce::assume_unique_for_key(nonce_bytes))
236 }
237 }
238
239 impl Zeroize for NonceCounter {
240 fn zeroize(&mut self) {
241 self.counter = 0;
242 }
243 }
244
245 #[cfg(test)]
246 mod tests {
247 use super::*;
248 use crate::crypto::key_derivation::{KeyDerivation, DerivedKey};
249 use crate::crypto::CryptoParams;
250
251 fn create_test_hierarchy() -> Result<KeyHierarchy> {
252 let params = CryptoParams::default();
253 let key_derivation = KeyDerivation::new(&params.scrypt_params);
254 let derived_key = key_derivation.derive_from_password("test_password_123")?;
255 KeyHierarchy::new(derived_key)
256 }
257
258 #[test]
259 fn test_segment_encryption_roundtrip() -> Result<()> {
260 let params = CryptoParams::default().aes_params;
261 let hierarchy = create_test_hierarchy()?;
262 let rng = SystemRandom::new();
263
264 let segment_encryption = SegmentEncryption::new(&params, &rng);
265 let test_data = b"Hello, ZephyrFS! This is test segment data for encryption.";
266 let key_path = vec![42];
267
268 // Encrypt
269 let encrypted = segment_encryption.encrypt_segment(
270 test_data,
271 1,
272 &hierarchy,
273 &key_path,
274 )?;
275
276 // Decrypt
277 let decrypted = segment_encryption.decrypt_segment(&encrypted, &hierarchy)?;
278
279 assert_eq!(test_data, decrypted.as_slice());
280 assert_eq!(encrypted.segment_index, 1);
281 assert_eq!(encrypted.key_path, vec![42]);
282
283 Ok(())
284 }
285
286 #[test]
287 fn test_file_encryption_multiple_segments() -> Result<()> {
288 let params = CryptoParams::default().aes_params;
289 let hierarchy = create_test_hierarchy()?;
290
291 let file_encryption = FileEncryption::with_segment_size(&params, 100); // Small segments for testing
292 let test_data = b"This is a longer test file that should be split into multiple segments for encryption testing. Each segment will be encrypted independently with its own derived key.";
293
294 // Encrypt file
295 let encrypted_segments = file_encryption.encrypt_to_segments(test_data, &hierarchy)?;
296
297 // Should have multiple segments
298 assert!(encrypted_segments.len() > 1);
299
300 // Decrypt file
301 let decrypted_data = file_encryption.decrypt_from_segments(&encrypted_segments, &hierarchy)?;
302
303 assert_eq!(test_data, decrypted_data.as_slice());
304
305 Ok(())
306 }
307
308 #[test]
309 fn test_encryption_authentication_failure() -> Result<()> {
310 let params = CryptoParams::default().aes_params;
311 let hierarchy = create_test_hierarchy()?;
312 let rng = SystemRandom::new();
313
314 let segment_encryption = SegmentEncryption::new(&params, &rng);
315 let test_data = b"Test data for authentication failure";
316 let key_path = vec![1];
317
318 // Encrypt
319 let mut encrypted = segment_encryption.encrypt_segment(
320 test_data,
321 0,
322 &hierarchy,
323 &key_path,
324 )?;
325
326 // Tamper with ciphertext
327 encrypted.ciphertext[0] ^= 1;
328
329 // Decrypt should fail due to authentication failure
330 let result = segment_encryption.decrypt_segment(&encrypted, &hierarchy);
331 assert!(result.is_err());
332
333 Ok(())
334 }
335
336 #[test]
337 fn test_different_segments_different_keys() -> Result<()> {
338 let params = CryptoParams::default().aes_params;
339 let hierarchy = create_test_hierarchy()?;
340 let rng = SystemRandom::new();
341
342 let segment_encryption = SegmentEncryption::new(&params, &rng);
343 let test_data = b"Same data, different keys";
344
345 // Encrypt with different key paths
346 let encrypted1 = segment_encryption.encrypt_segment(
347 test_data, 0, &hierarchy, &[1]
348 )?;
349
350 let encrypted2 = segment_encryption.encrypt_segment(
351 test_data, 0, &hierarchy, &[2]
352 )?;
353
354 // Ciphertexts should be different even with same plaintext
355 assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
356 assert_ne!(encrypted1.key_path, encrypted2.key_path);
357
358 Ok(())
359 }
360 }