dcrypt_symmetric/streaming/
gcm.rs

1//! Streaming AES-GCM implementations
2
3use super::{StreamingDecrypt, StreamingEncrypt};
4use crate::aead::gcm::{Aes128Gcm, Aes256Gcm, GcmNonce};
5use crate::aes::keys::{Aes128Key, Aes256Key};
6use crate::cipher::{Aead, SymmetricCipher};
7use crate::error::{validate_stream_state, Result, SymmetricResultExt};
8use std::io::{Read, Write};
9
10/// Streaming encryption API for AES-128-GCM with secure nonce management
11pub struct Aes128GcmEncryptStream<W: Write> {
12    writer: W,
13    cipher: Aes128Gcm,
14    buffer: Vec<u8>,
15    finalized: bool,
16    aad: Option<Vec<u8>>,
17    // Counter for deriving unique nonces
18    counter: u32,
19    // Base nonce - used to derive per-chunk nonces
20    base_nonce: GcmNonce,
21}
22
23impl<W: Write> Aes128GcmEncryptStream<W> {
24    /// Creates a new encryption stream
25    pub fn new(writer: W, key: &Aes128Key, aad: Option<&[u8]>) -> Result<Self> {
26        // Create cipher with proper error handling
27        let cipher = Aes128Gcm::new(key)?;
28        let base_nonce = Aes128Gcm::generate_nonce();
29
30        // Write base nonce to the beginning of the stream
31        let mut w = writer;
32        w.write_all(base_nonce.as_bytes()).map_io_err()?;
33
34        Ok(Self {
35            writer: w,
36            cipher,
37            buffer: Vec::with_capacity(16384), // 16 KB buffer
38            finalized: false,
39            aad: aad.map(|a| a.to_vec()),
40            counter: 0,
41            base_nonce,
42        })
43    }
44
45    /// Derives a unique nonce for the current chunk
46    fn derive_chunk_nonce(&self) -> GcmNonce {
47        // Create a derived nonce by XORing the counter with the base nonce
48        let mut nonce_bytes = *self.base_nonce.as_bytes();
49        let counter_bytes = self.counter.to_be_bytes();
50
51        // XOR the last 4 bytes with the counter
52        for i in 0..4 {
53            nonce_bytes[8 + i] ^= counter_bytes[i];
54        }
55
56        GcmNonce::new(nonce_bytes)
57    }
58
59    /// Flushes the internal buffer, encrypting and writing data
60    fn flush_buffer(&mut self) -> Result<()> {
61        if self.buffer.is_empty() {
62            return Ok(());
63        }
64
65        // Generate a unique nonce for this chunk using counter
66        let chunk_nonce = self.derive_chunk_nonce();
67
68        // Encrypt the buffered data with the unique nonce
69        let ciphertext = self
70            .cipher
71            .encrypt(&chunk_nonce, &self.buffer, self.aad.as_deref())?;
72
73        // Write the chunk nonce indicator followed by ciphertext length and data
74        self.writer.write_all(&[1]).map_io_err()?; // 1 = has chunk nonce
75
76        // Write the chunk counter (used to derive the nonce)
77        let counter_bytes = self.counter.to_be_bytes();
78        self.writer.write_all(&counter_bytes).map_io_err()?;
79
80        // Write the length of the ciphertext followed by the ciphertext itself
81        let len = (ciphertext.len() as u32).to_be_bytes();
82        self.writer.write_all(&len).map_io_err()?;
83        self.writer.write_all(&ciphertext).map_io_err()?;
84
85        // Increment counter for next chunk
86        self.counter += 1;
87
88        // Clear the buffer
89        self.buffer.clear();
90
91        Ok(())
92    }
93}
94
95impl<W: Write> StreamingEncrypt<W> for Aes128GcmEncryptStream<W> {
96    /// Writes plaintext data to the stream
97    fn write(&mut self, data: &[u8]) -> Result<()> {
98        validate_stream_state(!self.finalized, "stream write", "stream already finalized")?;
99
100        // Add data to internal buffer
101        self.buffer.extend_from_slice(data);
102
103        // If buffer exceeds 16 KB, encrypt and write a chunk
104        if self.buffer.len() >= 16384 {
105            self.flush_buffer()?;
106        }
107
108        Ok(())
109    }
110
111    /// Finalizes the stream, encrypting any remaining data
112    fn finalize(mut self) -> Result<W> {
113        validate_stream_state(
114            !self.finalized,
115            "stream finalize",
116            "stream already finalized",
117        )?;
118
119        // Flush any remaining data
120        self.flush_buffer()?;
121
122        // Write a zero marker to indicate end of data
123        self.writer.write_all(&[0]).map_io_err()?;
124
125        self.finalized = true;
126        Ok(self.writer)
127    }
128}
129
130/// Streaming decryption API for AES-128-GCM with secure nonce handling
131pub struct Aes128GcmDecryptStream<R: Read> {
132    reader: R,
133    cipher: Aes128Gcm,
134    base_nonce: GcmNonce,
135    finished: bool,
136    aad: Option<Vec<u8>>,
137}
138
139impl<R: Read> Aes128GcmDecryptStream<R> {
140    /// Creates a new decryption stream
141    pub fn new(mut reader: R, key: &Aes128Key, aad: Option<&[u8]>) -> Result<Self> {
142        // Read the base nonce from the beginning of the stream
143        let mut nonce_bytes = [0u8; 12];
144        reader.read_exact(&mut nonce_bytes).map_io_err()?;
145
146        let base_nonce = GcmNonce::new(nonce_bytes);
147        // Create cipher with proper error handling
148        let cipher = Aes128Gcm::new(key)?;
149
150        Ok(Self {
151            reader,
152            cipher,
153            base_nonce,
154            finished: false,
155            aad: aad.map(|a| a.to_vec()),
156        })
157    }
158
159    /// Derives a chunk nonce from the base nonce and counter
160    fn derive_chunk_nonce(&self, counter: u32) -> GcmNonce {
161        let mut nonce_bytes = *self.base_nonce.as_bytes();
162        let counter_bytes = counter.to_be_bytes();
163
164        // XOR the last 4 bytes with the counter
165        for i in 0..4 {
166            nonce_bytes[8 + i] ^= counter_bytes[i];
167        }
168
169        GcmNonce::new(nonce_bytes)
170    }
171}
172
173impl<R: Read> StreamingDecrypt<R> for Aes128GcmDecryptStream<R> {
174    /// Reads and decrypts data from the stream
175    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
176        if self.finished {
177            return Ok(0);
178        }
179
180        // Read the chunk marker
181        let mut marker = [0u8; 1];
182        self.reader.read_exact(&mut marker).map_io_err()?;
183
184        // Check if we've reached the end of the stream
185        if marker[0] == 0 {
186            self.finished = true;
187            return Ok(0);
188        }
189
190        // Read the chunk counter
191        let mut counter_bytes = [0u8; 4];
192        self.reader.read_exact(&mut counter_bytes).map_io_err()?;
193        let counter = u32::from_be_bytes(counter_bytes);
194
195        // Derive the nonce for this chunk
196        let chunk_nonce = self.derive_chunk_nonce(counter);
197
198        // Read the length of the ciphertext
199        let mut len_bytes = [0u8; 4];
200        self.reader.read_exact(&mut len_bytes).map_io_err()?;
201        let len = u32::from_be_bytes(len_bytes) as usize;
202
203        // Read the ciphertext
204        let mut ciphertext = vec![0u8; len];
205        self.reader.read_exact(&mut ciphertext).map_io_err()?;
206
207        // Decrypt the chunk using the derived nonce
208        let plaintext = self
209            .cipher
210            .decrypt(&chunk_nonce, &ciphertext, self.aad.as_deref())?;
211
212        // Copy to output buffer
213        let to_copy = plaintext.len().min(buf.len());
214        buf[..to_copy].copy_from_slice(&plaintext[..to_copy]);
215
216        Ok(to_copy)
217    }
218}
219
220/// Streaming encryption API for AES-256-GCM with secure nonce management
221pub struct Aes256GcmEncryptStream<W: Write> {
222    writer: W,
223    cipher: Aes256Gcm,
224    buffer: Vec<u8>,
225    finalized: bool,
226    aad: Option<Vec<u8>>,
227    // Counter for deriving unique nonces
228    counter: u32,
229    // Base nonce - used to derive per-chunk nonces
230    base_nonce: GcmNonce,
231}
232
233impl<W: Write> Aes256GcmEncryptStream<W> {
234    /// Creates a new encryption stream
235    pub fn new(writer: W, key: &Aes256Key, aad: Option<&[u8]>) -> Result<Self> {
236        // Create cipher with proper error handling
237        let cipher = Aes256Gcm::new(key)?;
238        let base_nonce = Aes256Gcm::generate_nonce();
239
240        // Write base nonce to the beginning of the stream
241        let mut w = writer;
242        w.write_all(base_nonce.as_bytes()).map_io_err()?;
243
244        Ok(Self {
245            writer: w,
246            cipher,
247            buffer: Vec::with_capacity(16384), // 16 KB buffer
248            finalized: false,
249            aad: aad.map(|a| a.to_vec()),
250            counter: 0,
251            base_nonce,
252        })
253    }
254
255    /// Derives a unique nonce for the current chunk
256    fn derive_chunk_nonce(&self) -> GcmNonce {
257        // Create a derived nonce by XORing the counter with the base nonce
258        let mut nonce_bytes = *self.base_nonce.as_bytes();
259        let counter_bytes = self.counter.to_be_bytes();
260
261        // XOR the last 4 bytes with the counter
262        for i in 0..4 {
263            nonce_bytes[8 + i] ^= counter_bytes[i];
264        }
265
266        GcmNonce::new(nonce_bytes)
267    }
268
269    /// Flushes the internal buffer, encrypting and writing data
270    fn flush_buffer(&mut self) -> Result<()> {
271        if self.buffer.is_empty() {
272            return Ok(());
273        }
274
275        // Generate a unique nonce for this chunk using counter
276        let chunk_nonce = self.derive_chunk_nonce();
277
278        // Encrypt the buffered data with the unique nonce
279        let ciphertext = self
280            .cipher
281            .encrypt(&chunk_nonce, &self.buffer, self.aad.as_deref())?;
282
283        // Write the chunk nonce indicator followed by ciphertext length and data
284        self.writer.write_all(&[1]).map_io_err()?; // 1 = has chunk nonce
285
286        // Write the chunk counter (used to derive the nonce)
287        let counter_bytes = self.counter.to_be_bytes();
288        self.writer.write_all(&counter_bytes).map_io_err()?;
289
290        // Write the length of the ciphertext followed by the ciphertext itself
291        let len = (ciphertext.len() as u32).to_be_bytes();
292        self.writer.write_all(&len).map_io_err()?;
293        self.writer.write_all(&ciphertext).map_io_err()?;
294
295        // Increment counter for next chunk
296        self.counter += 1;
297
298        // Clear the buffer
299        self.buffer.clear();
300
301        Ok(())
302    }
303}
304
305impl<W: Write> StreamingEncrypt<W> for Aes256GcmEncryptStream<W> {
306    /// Writes plaintext data to the stream
307    fn write(&mut self, data: &[u8]) -> Result<()> {
308        validate_stream_state(!self.finalized, "stream write", "stream already finalized")?;
309
310        // Add data to internal buffer
311        self.buffer.extend_from_slice(data);
312
313        // If buffer exceeds 16 KB, encrypt and write a chunk
314        if self.buffer.len() >= 16384 {
315            self.flush_buffer()?;
316        }
317
318        Ok(())
319    }
320
321    /// Finalizes the stream, encrypting any remaining data
322    fn finalize(mut self) -> Result<W> {
323        validate_stream_state(
324            !self.finalized,
325            "stream finalize",
326            "stream already finalized",
327        )?;
328
329        // Flush any remaining data
330        self.flush_buffer()?;
331
332        // Write a zero marker to indicate end of data
333        self.writer.write_all(&[0]).map_io_err()?;
334
335        self.finalized = true;
336        Ok(self.writer)
337    }
338}
339
340/// Streaming decryption API for AES-256-GCM with secure nonce handling
341pub struct Aes256GcmDecryptStream<R: Read> {
342    reader: R,
343    cipher: Aes256Gcm,
344    base_nonce: GcmNonce,
345    finished: bool,
346    aad: Option<Vec<u8>>,
347}
348
349impl<R: Read> Aes256GcmDecryptStream<R> {
350    /// Creates a new decryption stream
351    pub fn new(mut reader: R, key: &Aes256Key, aad: Option<&[u8]>) -> Result<Self> {
352        // Read the base nonce from the beginning of the stream
353        let mut nonce_bytes = [0u8; 12];
354        reader.read_exact(&mut nonce_bytes).map_io_err()?;
355
356        let base_nonce = GcmNonce::new(nonce_bytes);
357        // Create cipher with proper error handling
358        let cipher = Aes256Gcm::new(key)?;
359
360        Ok(Self {
361            reader,
362            cipher,
363            base_nonce,
364            finished: false,
365            aad: aad.map(|a| a.to_vec()),
366        })
367    }
368
369    /// Derives a chunk nonce from the base nonce and counter
370    fn derive_chunk_nonce(&self, counter: u32) -> GcmNonce {
371        let mut nonce_bytes = *self.base_nonce.as_bytes();
372        let counter_bytes = counter.to_be_bytes();
373
374        // XOR the last 4 bytes with the counter
375        for i in 0..4 {
376            nonce_bytes[8 + i] ^= counter_bytes[i];
377        }
378
379        GcmNonce::new(nonce_bytes)
380    }
381}
382
383impl<R: Read> StreamingDecrypt<R> for Aes256GcmDecryptStream<R> {
384    /// Reads and decrypts data from the stream
385    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
386        if self.finished {
387            return Ok(0);
388        }
389
390        // Read the chunk marker
391        let mut marker = [0u8; 1];
392        self.reader.read_exact(&mut marker).map_io_err()?;
393
394        // Check if we've reached the end of the stream
395        if marker[0] == 0 {
396            self.finished = true;
397            return Ok(0);
398        }
399
400        // Read the chunk counter
401        let mut counter_bytes = [0u8; 4];
402        self.reader.read_exact(&mut counter_bytes).map_io_err()?;
403        let counter = u32::from_be_bytes(counter_bytes);
404
405        // Derive the nonce for this chunk
406        let chunk_nonce = self.derive_chunk_nonce(counter);
407
408        // Read the length of the ciphertext
409        let mut len_bytes = [0u8; 4];
410        self.reader.read_exact(&mut len_bytes).map_io_err()?;
411        let len = u32::from_be_bytes(len_bytes) as usize;
412
413        // Read the ciphertext
414        let mut ciphertext = vec![0u8; len];
415        self.reader.read_exact(&mut ciphertext).map_io_err()?;
416
417        // Decrypt the chunk using the derived nonce
418        let plaintext = self
419            .cipher
420            .decrypt(&chunk_nonce, &ciphertext, self.aad.as_deref())?;
421
422        // Copy to output buffer
423        let to_copy = plaintext.len().min(buf.len());
424        buf[..to_copy].copy_from_slice(&plaintext[..to_copy]);
425
426        Ok(to_copy)
427    }
428}
429
430/// Encrypts a file using AES-128-GCM
431pub fn encrypt_file_aes128<R: Read, W: Write>(
432    mut reader: R,
433    writer: W,
434    key: &Aes128Key,
435    aad: Option<&[u8]>,
436) -> Result<()> {
437    // Create stream with proper error handling
438    let mut stream = Aes128GcmEncryptStream::new(writer, key, aad)?;
439
440    let mut buffer = [0u8; 8192];
441    loop {
442        let bytes_read = reader.read(&mut buffer).map_io_err()?;
443        if bytes_read == 0 {
444            break;
445        }
446
447        stream.write(&buffer[..bytes_read])?;
448    }
449
450    stream.finalize()?;
451    Ok(())
452}
453
454/// Decrypts a file using AES-128-GCM
455pub fn decrypt_file_aes128<R: Read, W: Write>(
456    reader: R,
457    mut writer: W,
458    key: &Aes128Key,
459    aad: Option<&[u8]>,
460) -> Result<()> {
461    let mut stream = Aes128GcmDecryptStream::new(reader, key, aad)?;
462
463    let mut buffer = [0u8; 8192];
464    loop {
465        let bytes_read = stream.read(&mut buffer)?;
466        if bytes_read == 0 {
467            break;
468        }
469
470        writer.write_all(&buffer[..bytes_read]).map_io_err()?;
471    }
472
473    Ok(())
474}
475
476/// Encrypts a file using AES-256-GCM
477pub fn encrypt_file_aes256<R: Read, W: Write>(
478    mut reader: R,
479    writer: W,
480    key: &Aes256Key,
481    aad: Option<&[u8]>,
482) -> Result<()> {
483    // Create stream with proper error handling
484    let mut stream = Aes256GcmEncryptStream::new(writer, key, aad)?;
485
486    let mut buffer = [0u8; 8192];
487    loop {
488        let bytes_read = reader.read(&mut buffer).map_io_err()?;
489        if bytes_read == 0 {
490            break;
491        }
492
493        stream.write(&buffer[..bytes_read])?;
494    }
495
496    stream.finalize()?;
497    Ok(())
498}
499
500/// Decrypts a file using AES-256-GCM
501pub fn decrypt_file_aes256<R: Read, W: Write>(
502    reader: R,
503    mut writer: W,
504    key: &Aes256Key,
505    aad: Option<&[u8]>,
506) -> Result<()> {
507    let mut stream = Aes256GcmDecryptStream::new(reader, key, aad)?;
508
509    let mut buffer = [0u8; 8192];
510    loop {
511        let bytes_read = stream.read(&mut buffer)?;
512        if bytes_read == 0 {
513            break;
514        }
515
516        writer.write_all(&buffer[..bytes_read]).map_io_err()?;
517    }
518
519    Ok(())
520}