dcrypt_symmetric/streaming/
chacha20poly1305.rs1use crate::aead::chacha20poly1305::{
4 ChaCha20Poly1305Cipher, ChaCha20Poly1305Key, ChaCha20Poly1305Nonce,
5};
6use crate::cipher::{Aead, SymmetricCipher};
7use crate::error::{validate_stream_state, Result, SymmetricResultExt};
8use crate::streaming::{StreamingDecrypt, StreamingEncrypt};
9use std::io::{Read, Write};
10
11pub struct ChaCha20Poly1305EncryptStream<W: Write> {
13 writer: W,
14 cipher: ChaCha20Poly1305Cipher,
15 buffer: Vec<u8>,
16 finalized: bool,
17 aad: Option<Vec<u8>>,
18 counter: u32,
20 base_nonce: ChaCha20Poly1305Nonce,
22}
23
24impl<W: Write> ChaCha20Poly1305EncryptStream<W> {
25 pub fn new(writer: W, key: &ChaCha20Poly1305Key, aad: Option<&[u8]>) -> Result<Self> {
27 let cipher = ChaCha20Poly1305Cipher::new(key)?;
29 let base_nonce = ChaCha20Poly1305Cipher::generate_nonce();
30
31 let mut w = writer;
33 w.write_all(base_nonce.as_bytes()).map_io_err()?;
35
36 Ok(Self {
37 writer: w,
38 cipher,
39 buffer: Vec::with_capacity(16384), finalized: false,
41 aad: aad.map(|a| a.to_vec()),
42 counter: 0,
43 base_nonce,
44 })
45 }
46
47 fn derive_chunk_nonce(&self) -> ChaCha20Poly1305Nonce {
49 let mut nonce_bytes = *self.base_nonce.as_bytes();
51 let counter_bytes = self.counter.to_be_bytes();
52
53 for i in 0..4 {
55 nonce_bytes[8 + i] ^= counter_bytes[i];
56 }
57
58 ChaCha20Poly1305Nonce::new(nonce_bytes)
59 }
60
61 fn flush_buffer(&mut self) -> Result<()> {
63 if self.buffer.is_empty() {
64 return Ok(());
65 }
66
67 let chunk_nonce = self.derive_chunk_nonce();
69
70 let ciphertext = self
72 .cipher
73 .encrypt(&chunk_nonce, &self.buffer, self.aad.as_deref())?;
74
75 self.writer.write_all(&[1]).map_io_err()?; let counter_bytes = self.counter.to_be_bytes();
80 self.writer.write_all(&counter_bytes).map_io_err()?;
81
82 let len = (ciphertext.len() as u32).to_be_bytes();
84 self.writer.write_all(&len).map_io_err()?;
85 self.writer.write_all(&ciphertext).map_io_err()?;
86
87 self.counter += 1;
89
90 self.buffer.clear();
92
93 Ok(())
94 }
95}
96
97impl<W: Write> StreamingEncrypt<W> for ChaCha20Poly1305EncryptStream<W> {
98 fn write(&mut self, data: &[u8]) -> Result<()> {
100 validate_stream_state(!self.finalized, "stream write", "stream already finalized")?;
101
102 self.buffer.extend_from_slice(data);
104
105 if self.buffer.len() >= 16384 {
107 self.flush_buffer()?;
108 }
109
110 Ok(())
111 }
112
113 fn finalize(mut self) -> Result<W> {
115 validate_stream_state(
116 !self.finalized,
117 "stream finalize",
118 "stream already finalized",
119 )?;
120
121 self.flush_buffer()?;
123
124 self.writer.write_all(&[0]).map_io_err()?;
126
127 self.finalized = true;
128 Ok(self.writer)
129 }
130}
131
132pub struct ChaCha20Poly1305DecryptStream<R: Read> {
134 reader: R,
135 cipher: ChaCha20Poly1305Cipher,
136 base_nonce: ChaCha20Poly1305Nonce,
137 finished: bool,
138 aad: Option<Vec<u8>>,
139}
140
141impl<R: Read> ChaCha20Poly1305DecryptStream<R> {
142 pub fn new(mut reader: R, key: &ChaCha20Poly1305Key, aad: Option<&[u8]>) -> Result<Self> {
144 let mut nonce_bytes = [0u8; 12];
146 reader.read_exact(&mut nonce_bytes).map_io_err()?;
147
148 let base_nonce = ChaCha20Poly1305Nonce::new(nonce_bytes);
149 let cipher = ChaCha20Poly1305Cipher::new(key)?;
151
152 Ok(Self {
153 reader,
154 cipher,
155 base_nonce,
156 finished: false,
157 aad: aad.map(|a| a.to_vec()),
158 })
159 }
160
161 fn derive_chunk_nonce(&self, counter: u32) -> ChaCha20Poly1305Nonce {
163 let mut nonce_bytes = *self.base_nonce.as_bytes();
164 let counter_bytes = counter.to_be_bytes();
165
166 for i in 0..4 {
168 nonce_bytes[8 + i] ^= counter_bytes[i];
169 }
170
171 ChaCha20Poly1305Nonce::new(nonce_bytes)
172 }
173}
174
175impl<R: Read> StreamingDecrypt<R> for ChaCha20Poly1305DecryptStream<R> {
176 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
178 if self.finished {
179 return Ok(0);
180 }
181
182 let mut marker = [0u8; 1];
184 self.reader.read_exact(&mut marker).map_io_err()?;
185
186 if marker[0] == 0 {
188 self.finished = true;
189 return Ok(0);
190 }
191
192 let mut counter_bytes = [0u8; 4];
194 self.reader.read_exact(&mut counter_bytes).map_io_err()?;
195 let counter = u32::from_be_bytes(counter_bytes);
196
197 let chunk_nonce = self.derive_chunk_nonce(counter);
199
200 let mut len_bytes = [0u8; 4];
202 self.reader.read_exact(&mut len_bytes).map_io_err()?;
203 let len = u32::from_be_bytes(len_bytes) as usize;
204
205 let mut ciphertext = vec![0u8; len];
207 self.reader.read_exact(&mut ciphertext).map_io_err()?;
208
209 let plaintext = self
211 .cipher
212 .decrypt(&chunk_nonce, &ciphertext, self.aad.as_deref())?;
213
214 let to_copy = plaintext.len().min(buf.len());
216 buf[..to_copy].copy_from_slice(&plaintext[..to_copy]);
217
218 Ok(to_copy)
219 }
220}
221
222pub fn encrypt_file<R: Read, W: Write>(
224 mut reader: R,
225 writer: W,
226 key: &ChaCha20Poly1305Key,
227 aad: Option<&[u8]>,
228) -> Result<()> {
229 let mut stream = ChaCha20Poly1305EncryptStream::new(writer, key, aad)?;
231
232 let mut buffer = [0u8; 8192];
233 loop {
234 let bytes_read = reader.read(&mut buffer).map_io_err()?;
235 if bytes_read == 0 {
236 break;
237 }
238
239 stream.write(&buffer[..bytes_read])?;
240 }
241
242 stream.finalize()?;
243 Ok(())
244}
245
246pub fn decrypt_file<R: Read, W: Write>(
248 reader: R,
249 mut writer: W,
250 key: &ChaCha20Poly1305Key,
251 aad: Option<&[u8]>,
252) -> Result<()> {
253 let mut stream = ChaCha20Poly1305DecryptStream::new(reader, key, aad)?;
254
255 let mut buffer = [0u8; 8192];
256 loop {
257 let bytes_read = stream.read(&mut buffer)?;
258 if bytes_read == 0 {
259 break;
260 }
261
262 writer.write_all(&buffer[..bytes_read]).map_io_err()?;
263 }
264
265 Ok(())
266}