async_encrypted_stream/
lib.rs1#![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
20pub 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
60pub 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}