Skip to main content

async_encrypted_stream/
read_half.rs

1use aead_stream::{Decryptor, NonceSize, StreamPrimitive};
2use chacha20poly1305::aead::{array::ArraySize, AeadInOut};
3use pin_project_lite::pin_project;
4use std::{ops::Sub, pin::Pin, task::ready};
5
6use tokio::io::{AsyncBufRead, AsyncRead};
7
8use crate::DEFAULT_BUFFER_SIZE;
9
10pin_project! {
11    /// Async Encryption Read Half
12    pub struct ReadHalf<T, U> {
13
14        #[pin]
15        inner: T,
16        decryptor: U,
17        buffer: Vec<u8>,
18        pos: usize,
19        cap: usize,
20
21        // Decrypted bytes from a message that didn't fully fit in the caller's read buffer,
22        // waiting to be delivered on subsequent `poll_read` calls.
23        overflow: Vec<u8>
24    }
25}
26
27impl<T, A, S> ReadHalf<T, Decryptor<A, S>>
28where
29    S: StreamPrimitive<A>,
30    A: AeadInOut,
31    A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
32    NonceSize<A, S>: ArraySize,
33{
34    pub fn new(inner: T, decryptor: Decryptor<A, S>) -> Self {
35        Self::with_capacity(inner, decryptor, DEFAULT_BUFFER_SIZE)
36    }
37    pub fn with_capacity(inner: T, decryptor: Decryptor<A, S>, size: usize) -> Self {
38        Self {
39            inner,
40            decryptor,
41            buffer: vec![0u8; size],
42            pos: 0,
43            cap: 0,
44            overflow: Vec::new(),
45        }
46    }
47
48    /// Produce a value if there is enough data in the internal buffer
49    ///
50    /// When a value is produced, it will advance the buffer to the position for the next value.
51    fn produce(mut self: Pin<&mut Self>) -> std::io::Result<Option<Vec<u8>>> {
52        if self.cap <= self.pos {
53            return Ok(None);
54        }
55
56        if self.cap - self.pos < 4 {
57            // Not enough bytes buffered yet to even read the length prefix.
58            self.adjust_buffer(4);
59            return Ok(None);
60        }
61
62        // Producing a value is a relatively simple operation.
63        // Read 4 bytes from the buffer and cast to a u32 as the length of the message.
64        // If there is enough bytes in the buffer, read the bytes and decrypt the message.
65        //
66        // Then advance the buffer to the next position (4 + length)
67        //
68        // If there isn't enough bytes to produce a message, just return None
69
70        let mut length_bytes = [0u8; 4];
71        length_bytes.copy_from_slice(&self.buffer[self.pos..self.pos + 4]);
72        let length = u32::from_le_bytes(length_bytes) as usize;
73
74        let me = self.as_mut().project();
75        if *me.cap >= *me.pos + length + 4 {
76            let decrypted = me
77                .decryptor
78                .decrypt_next(&me.buffer[*me.pos + 4..*me.pos + 4 + length])
79                .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
80
81            *me.pos += 4 + length;
82            if *me.pos == *me.cap {
83                *me.pos = 0;
84                *me.cap = 0;
85            }
86
87            Ok(Some(decrypted))
88        } else {
89            self.adjust_buffer(length + 4);
90            Ok(None)
91        }
92    }
93
94    /// Adjusts the buffer to fit the next full message.
95    ///
96    /// When the buffer reach a position where the length of the message is greater than the buffer
97    /// available capacity, it is necessary to reset the buffer position to 0 and move the bytes
98    /// available to the beginning of the buffer, freeing buffer capacity to be filled.
99    ///
100    /// It is also possible that the message length is bigger than the buffer full size, in this
101    /// case the buffer will be resized to double it's full capacity. This operation should not
102    /// be necessary because the writter is limited to write 1024 bytes long messages
103    fn adjust_buffer(self: Pin<&mut Self>, desired_additional: usize) {
104        let me = self.project();
105        if *me.cap + desired_additional >= me.buffer.len() && *me.pos > 0 {
106            me.buffer.copy_within(*me.pos..*me.cap, 0);
107            *me.cap -= *me.pos;
108            *me.pos = 0;
109        }
110
111        if *me.pos + desired_additional > me.buffer.len() {
112            me.buffer.resize(me.buffer.len() * 2, 0);
113        }
114    }
115
116    /// Return the contents of the internal buffer at the current position, for diagnostic
117    /// purposes.
118    ///
119    /// For each message available in the buffer, the first 4 bytes are the message length encoded
120    /// as a **little endian** u32. The end of the buffer may contain incomplete data.
121    pub fn buffer(&self) -> &[u8] {
122        &self.buffer[self.pos..]
123    }
124}
125
126impl<T, A, S> AsyncRead for ReadHalf<T, Decryptor<A, S>>
127where
128    T: AsyncRead,
129    S: StreamPrimitive<A>,
130    A: AeadInOut,
131    A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
132    NonceSize<A, S>: ArraySize,
133{
134    /// The poll read simply tries to produce a value from the internal buffer.
135    /// If no value is produced, it then tries to poll more bytes from the inner reader
136    ///
137    /// If a decrypted message is larger than the caller's buffer, the remainder is held in an
138    /// internal overflow buffer and delivered on subsequent calls, instead of being discarded.
139    ///
140    /// This function may return a [std::io::ErrorKind::InvalidData] if it is not possible to decrypt
141    /// the message, in this case, further read attempts will always produce the same error.
142    fn poll_read(
143        mut self: Pin<&mut Self>,
144        cx: &mut std::task::Context<'_>,
145        buf: &mut tokio::io::ReadBuf<'_>,
146    ) -> std::task::Poll<std::io::Result<()>> {
147        loop {
148            if !self.overflow.is_empty() {
149                let me = self.as_mut().project();
150                let n = std::cmp::min(me.overflow.len(), buf.remaining());
151                buf.put_slice(&me.overflow[..n]);
152                me.overflow.drain(..n);
153                return std::task::Poll::Ready(Ok(()));
154            }
155
156            if let Some(decrypted) = self.as_mut().produce()? {
157                let n = std::cmp::min(decrypted.len(), buf.remaining());
158                buf.put_slice(&decrypted[..n]);
159                if n < decrypted.len() {
160                    let me = self.as_mut().project();
161                    me.overflow.extend_from_slice(&decrypted[n..]);
162                }
163                return std::task::Poll::Ready(Ok(()));
164            }
165
166            if ready!(self.as_mut().poll_fill_buf(cx))?.is_empty() {
167                return std::task::Poll::Ready(Ok(()));
168            }
169        }
170    }
171}
172
173impl<R: AsyncRead, A, S> tokio::io::AsyncBufRead for ReadHalf<R, Decryptor<A, S>>
174where
175    S: StreamPrimitive<A>,
176    A: AeadInOut,
177    A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
178    NonceSize<A, S>: ArraySize,
179{
180    fn poll_fill_buf(
181        self: Pin<&mut Self>,
182        cx: &mut std::task::Context<'_>,
183    ) -> std::task::Poll<std::io::Result<&[u8]>> {
184        let me = self.project();
185
186        let mut buf = tokio::io::ReadBuf::new(&mut me.buffer[*me.cap..]);
187        ready!(me.inner.poll_read(cx, &mut buf))?;
188        if !buf.filled().is_empty() {
189            *me.cap += buf.filled().len();
190        }
191
192        std::task::Poll::Ready(Ok(&me.buffer[*me.pos..*me.cap]))
193    }
194
195    fn consume(self: Pin<&mut Self>, amt: usize) {
196        let me = self.project();
197        *me.pos += amt;
198        if *me.pos >= *me.cap {
199            *me.pos = 0;
200            *me.cap = 0;
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use std::{assert_eq, time::Duration};
208
209    use aead_stream::{DecryptorLE31, EncryptorLE31};
210    use chacha20poly1305::{KeyInit, XChaCha20Poly1305};
211    use tokio::io::{AsyncReadExt, AsyncWriteExt};
212
213    use crate::get_key;
214
215    use super::*;
216
217    #[tokio::test]
218    pub async fn test_crypto_stream_read_half() {
219        let key: [u8; 32] = get_key("key", "group");
220        let start_nonce = [0u8; 20];
221
222        let (rx, mut tx) = tokio::io::duplex(100);
223
224        tokio::spawn(async move {
225            let encrypted_content = {
226                let mut encryptor: EncryptorLE31<XChaCha20Poly1305> =
227                    EncryptorLE31::from_aead(
228                        XChaCha20Poly1305::new((&key).into()),
229                        (&start_nonce).into(),
230                    );
231
232                let mut expected = Vec::new();
233
234                for data in ["some content", "some other content", "even more content"] {
235                    let mut encrypted = encryptor.encrypt_next(data.as_bytes()).unwrap();
236                    expected.extend((encrypted.len() as u32).to_le_bytes());
237                    expected.append(&mut encrypted);
238                }
239
240                expected
241            };
242
243            for chunk in encrypted_content.chunks(10) {
244                let _ = tx.write(chunk).await;
245                tokio::time::sleep(Duration::from_millis(20)).await;
246            }
247        });
248
249        tokio::time::sleep(Duration::from_millis(20)).await;
250
251        let decryptor = DecryptorLE31::from_aead(
252            XChaCha20Poly1305::new((&key).into()),
253            (&start_nonce).into(),
254        );
255        let mut reader = ReadHalf::new(rx, decryptor);
256
257        let mut plain_content = String::new();
258        let _ = reader.read_to_string(&mut plain_content).await;
259
260        assert_eq!(
261            plain_content,
262            "some contentsome other contenteven more content"
263        );
264    }
265
266    #[tokio::test]
267    pub async fn test_read_invalid_data() {
268        let key: [u8; 32] = get_key("key", "group");
269        let start_nonce = [0u8; 20];
270
271        let (rx, _tx) = tokio::io::duplex(100);
272
273        let decryptor = DecryptorLE31::from_aead(
274            XChaCha20Poly1305::new((&key).into()),
275            (&start_nonce).into(),
276        );
277        let mut reader = ReadHalf::new(rx, decryptor);
278        let mut reader_data = Vec::from_iter(10u32.to_le_bytes());
279        reader_data.extend_from_slice(&[0u8; 20]);
280
281        reader.cap = reader_data.len();
282        reader.buffer = reader_data;
283
284        let mut buf = [0u8; 1024];
285
286        assert!(reader.read(&mut buf).await.is_err());
287        assert!(reader.read(&mut buf).await.is_err());
288    }
289
290    #[tokio::test]
291    pub async fn test_read_with_header_split_near_buffer_end() {
292        let key: [u8; 32] = get_key("key", "group");
293        let start_nonce = [0u8; 20];
294
295        let mut encryptor: EncryptorLE31<XChaCha20Poly1305> =
296            EncryptorLE31::from_aead(
297                XChaCha20Poly1305::new((&key).into()),
298                (&start_nonce).into(),
299            );
300
301        let mut record1 = {
302            let mut encrypted = encryptor.encrypt_next("hi".as_bytes()).unwrap();
303            let mut record = Vec::new();
304            record.extend((encrypted.len() as u32).to_le_bytes());
305            record.append(&mut encrypted);
306            record
307        };
308
309        let record2 = {
310            let mut encrypted = encryptor.encrypt_next("there".as_bytes()).unwrap();
311            let mut record = Vec::new();
312            record.extend((encrypted.len() as u32).to_le_bytes());
313            record.append(&mut encrypted);
314            record
315        };
316
317        // Only the first 2 bytes of message 2's 4-byte length header will already be
318        // sitting in the internal buffer; the rest arrives later through the reader.
319        let (record2_head, record2_tail) = record2.split_at(2);
320
321        let (rx, mut tx) = tokio::io::duplex(4096);
322        tx.write_all(record2_tail).await.unwrap();
323
324        let decryptor = DecryptorLE31::from_aead(
325            XChaCha20Poly1305::new((&key).into()),
326            (&start_nonce).into(),
327        );
328        let mut reader = ReadHalf::with_capacity(rx, decryptor, record1.len() + record2_head.len());
329
330        // Pre-load message 1 in full, plus the first 2 bytes of message 2's header,
331        // filling the internal buffer to exactly its capacity. Once message 1 is
332        // consumed, `pos` sits only 2 bytes away from the end of the buffer, with
333        // less than 4 bytes available for message 2's header.
334        record1.extend_from_slice(record2_head);
335        reader.buffer = record1.clone();
336        reader.cap = record1.len();
337        reader.pos = 0;
338
339        let mut buf = [0u8; 1024];
340        let n = reader.read(&mut buf).await.unwrap();
341        assert_eq!(&buf[..n], b"hi");
342
343        let n = reader.read(&mut buf).await.unwrap();
344        assert_eq!(&buf[..n], b"there");
345    }
346
347    #[tokio::test]
348    pub async fn test_read_buffer_smaller_than_message() {
349        let key: [u8; 32] = get_key("key", "group");
350        let start_nonce = [0u8; 20];
351
352        let (rx, mut tx) = tokio::io::duplex(4096);
353
354        tokio::spawn(async move {
355            let mut encryptor: EncryptorLE31<XChaCha20Poly1305> =
356                EncryptorLE31::from_aead(
357                    XChaCha20Poly1305::new((&key).into()),
358                    (&start_nonce).into(),
359                );
360
361            let content = "a".repeat(500);
362            let mut encrypted = encryptor.encrypt_next(content.as_bytes()).unwrap();
363            let mut encrypted_content = Vec::new();
364            encrypted_content.extend((encrypted.len() as u32).to_le_bytes());
365            encrypted_content.append(&mut encrypted);
366
367            let _ = tx.write_all(&encrypted_content).await;
368        });
369
370        let decryptor = DecryptorLE31::from_aead(
371            XChaCha20Poly1305::new((&key).into()),
372            (&start_nonce).into(),
373        );
374        let mut reader = ReadHalf::new(rx, decryptor);
375
376        // The caller's buffer is far smaller than the 500-byte decrypted message, so
377        // delivering it must span several `read` calls instead of erroring/dropping data.
378        let mut plain_content = String::new();
379        let mut small_buf = [0u8; 64];
380        loop {
381            let n = reader.read(&mut small_buf).await.unwrap();
382            if n == 0 {
383                break;
384            }
385            plain_content.push_str(std::str::from_utf8(&small_buf[..n]).unwrap());
386        }
387
388        assert_eq!(plain_content, "a".repeat(500));
389    }
390}