async_encrypted_stream/
lib.rs

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