Skip to main content

async_encrypted_stream/
lib.rs

1#![doc = include_str!("../README.md")]
2use std::ops::Sub;
3
4pub use aead_stream;
5pub use chacha20poly1305;
6
7use aead_stream::{Decryptor, Encryptor, NewStream, NonceSize, StreamPrimitive};
8use chacha20poly1305::aead::{
9    AeadInOut,
10    array::{Array, ArraySize},
11};
12
13use tokio::io::{AsyncRead, AsyncWrite};
14
15mod read_half;
16pub use read_half::ReadHalf;
17
18mod write_half;
19pub use write_half::WriteHalf;
20
21pub const DEFAULT_BUFFER_SIZE: usize = 4096;
22pub const DEFAULT_CHUNK_SIZE: usize = 1024;
23
24pub type EncryptedStream<R, W, A, S> =
25    (ReadHalf<R, Decryptor<A, S>>, WriteHalf<W, Encryptor<A, S>>);
26
27/// Creates a pair of [ReadHalf] and [WriteHalf] with default buffer size of
28/// [self::DEFAULT_BUFFER_SIZE] and chunk size of [self::DEFAULT_CHUNK_SIZE]  
29///
30/// ```rust
31/// use async_encrypted_stream::aead_stream::{DecryptorLE31, EncryptorLE31};
32/// use async_encrypted_stream::chacha20poly1305::XChaCha20Poly1305;
33///
34/// use async_encrypted_stream::{ReadHalf, WriteHalf, encrypted_stream};
35///
36/// let key = [0u8; 32];
37/// let nonce = [0u8; 20];
38///
39/// let (rx, tx) = tokio::io::duplex(4096);
40/// let (mut reader, mut writer): (
41///     ReadHalf<_, DecryptorLE31<XChaCha20Poly1305>>,
42///     WriteHalf<_, EncryptorLE31<XChaCha20Poly1305>>,
43/// ) = encrypted_stream(rx, tx, (&key).into(), (&nonce).into());
44/// ````
45pub fn encrypted_stream<R: AsyncRead, W: AsyncWrite, A, S>(
46    read: R,
47    write: W,
48    key: &Array<u8, A::KeySize>,
49    nonce: &Array<u8, NonceSize<A, S>>,
50) -> EncryptedStream<R, W, A, S>
51where
52    S: StreamPrimitive<A> + NewStream<A>,
53    A: AeadInOut + chacha20poly1305::KeyInit,
54    A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
55    NonceSize<A, S>: ArraySize,
56{
57    encrypted_stream_with_capacity(
58        read,
59        write,
60        key,
61        nonce,
62        DEFAULT_BUFFER_SIZE,
63        DEFAULT_CHUNK_SIZE,
64    )
65}
66
67/// Creates a pair of [ReadHalf] and [WriteHalf] with default buffer size of
68/// `buffer_size` and chunk size of `chunk_size`  
69///
70/// ```rust
71/// use async_encrypted_stream::aead_stream::{DecryptorLE31, EncryptorLE31};
72/// use async_encrypted_stream::chacha20poly1305::XChaCha20Poly1305;
73///
74/// use async_encrypted_stream::{ReadHalf, WriteHalf, encrypted_stream_with_capacity};
75///
76/// let key = [0u8; 32];
77/// let nonce = [0u8; 20];
78///
79/// let (rx, tx) = tokio::io::duplex(4096);
80/// let (mut reader, mut writer): (
81///     ReadHalf<_, DecryptorLE31<XChaCha20Poly1305>>,
82///     WriteHalf<_, EncryptorLE31<XChaCha20Poly1305>>,
83/// ) = encrypted_stream_with_capacity(rx, tx, (&key).into(), (&nonce).into(), 4096,
84/// 512);
85/// ````
86pub fn encrypted_stream_with_capacity<R: AsyncRead, W: AsyncWrite, A, S>(
87    read: R,
88    write: W,
89    key: &Array<u8, A::KeySize>,
90    nonce: &Array<u8, NonceSize<A, S>>,
91    buffer_size: usize,
92    chunk_size: usize,
93) -> EncryptedStream<R, W, A, S>
94where
95    S: StreamPrimitive<A> + NewStream<A>,
96    A: AeadInOut + chacha20poly1305::KeyInit,
97    A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
98    NonceSize<A, S>: ArraySize,
99{
100    let encryptor = Encryptor::new(key, nonce);
101    let decryptor = Decryptor::new(key, nonce);
102
103    (
104        ReadHalf::with_capacity(read, decryptor, buffer_size),
105        WriteHalf::with_capacity(write, encryptor, buffer_size, chunk_size),
106    )
107}
108
109#[cfg(test)]
110fn get_key<const S: usize>(plain_key: &str, salt: &str) -> [u8; S] {
111    const ITERATIONS: u32 = 4096;
112    pbkdf2::pbkdf2_hmac_array::<sha2::Sha256, S>(plain_key.as_bytes(), salt.as_bytes(), ITERATIONS)
113}
114
115#[cfg(test)]
116mod tests {
117    use std::{assert_eq, time::Duration};
118
119    use aead_stream::{DecryptorLE31, EncryptorLE31};
120    use chacha20poly1305::XChaCha20Poly1305;
121    use tokio::io::{AsyncReadExt, AsyncWriteExt};
122
123    use super::*;
124
125    #[tokio::test]
126    pub async fn test_big_transfer() {
127        let key: [u8; 32] = get_key("key", "group");
128        let nonce = [0u8; 20];
129
130        let (rx, tx) = tokio::io::duplex(4096);
131        let (mut reader, mut writer): (
132            ReadHalf<_, DecryptorLE31<XChaCha20Poly1305>>,
133            WriteHalf<_, EncryptorLE31<XChaCha20Poly1305>>,
134        ) = super::encrypted_stream(rx, tx, (&key).into(), (&nonce).into());
135
136        let size = 1024 * 4;
137        tokio::spawn(async move {
138            let content = vec![100u8; size];
139            let _ = writer.write(&content).await;
140            let _ = writer.flush().await;
141        });
142
143        tokio::time::sleep(Duration::from_millis(100)).await;
144
145        let mut bytes_expected = size;
146        let mut read_buf = vec![0u8; 1024];
147        while let Ok(bytes_read) = reader.read(&mut read_buf[..]).await {
148            if bytes_read == 0 {
149                break;
150            }
151
152            assert!(read_buf[..bytes_read].iter().all(|b| *b == 100));
153            bytes_expected -= bytes_read;
154        }
155
156        assert_eq!(0, bytes_expected);
157    }
158}