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
27pub 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
67pub 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}