rustfs_crypto/encdec/
stream_io.rs1#![allow(deprecated)] use crate::encdec::id::ID;
22use crate::error::Error;
23use aes_gcm::{
24 Aes256Gcm,
25 aead::{AeadCore, AeadInPlace, KeyInit as _, array::Array},
26};
27use chacha20poly1305::ChaCha20Poly1305;
28
29const STREAM_IO_HEADER_LEN: usize = 41;
30const SIO_BUF_SIZE: usize = 16384;
31const SIO_NONCE_PREFIX_LEN: usize = 8;
32const AES_GCM_OVERHEAD: usize = 16;
33const CHACHA_OVERHEAD: usize = 16;
34
35pub fn decrypt_stream_io(password: &[u8], data: &[u8]) -> Result<Vec<u8>, Error> {
37 if data.len() < STREAM_IO_HEADER_LEN {
38 return Err(Error::ErrUnexpectedHeader);
39 }
40 let salt = &data[0..32];
41 let id = ID::try_from(data[32])?;
42 let nonce_prefix = &data[33..41];
43 let body = &data[STREAM_IO_HEADER_LEN..];
44
45 let key = id.get_key(password, salt)?;
46
47 match id {
48 ID::Argon2idChaCHa20Poly1305 => decrypt_stream(
49 ChaCha20Poly1305::new_from_slice(&key).map_err(|e| Error::ErrInvalidInput(e.to_string()))?,
50 nonce_prefix,
51 body,
52 CHACHA_OVERHEAD,
53 ),
54 _ => decrypt_stream(
55 Aes256Gcm::new_from_slice(&key).map_err(|e| Error::ErrInvalidInput(e.to_string()))?,
56 nonce_prefix,
57 body,
58 AES_GCM_OVERHEAD,
59 ),
60 }
61}
62
63fn decrypt_stream<A>(aead: A, nonce_prefix: &[u8], body: &[u8], overhead: usize) -> Result<Vec<u8>, Error>
64where
65 A: AeadInPlace,
66{
67 let ciphertext_len = SIO_BUF_SIZE + overhead;
68 let ad = build_associated_data(&aead, nonce_prefix)?;
69 let mut plain = Vec::with_capacity(body.len());
70
71 let mut seq_num: u32 = 1;
72 let mut pos = 0;
73
74 while pos < body.len() {
75 let remaining = body.len() - pos;
76 let frag_len = remaining.min(ciphertext_len);
77 let is_last = (pos + frag_len) == body.len();
78
79 if frag_len < overhead {
80 return Err(Error::ErrDecryptFailed(aes_gcm::aead::Error));
81 }
82
83 let mut nonce = [0u8; 12];
84 nonce[0..SIO_NONCE_PREFIX_LEN].copy_from_slice(nonce_prefix);
85 nonce[8..12].copy_from_slice(&seq_num.to_le_bytes());
86
87 let mut ad_mut = ad.clone();
88 ad_mut[0] = if is_last { 0x80 } else { 0x00 };
89
90 let fragment = &body[pos..pos + frag_len];
91 let tag_len = overhead;
92 let (ct, tag) = fragment.split_at(frag_len - tag_len);
93
94 let mut buffer = ct.to_vec();
95 let nonce_arr = Array::<u8, <A as AeadCore>::NonceSize>::try_from(&nonce[..])
96 .map_err(|_| Error::ErrDecryptFailed(aes_gcm::aead::Error))?;
97 let tag_arr =
98 Array::<u8, <A as AeadCore>::TagSize>::try_from(tag).map_err(|_| Error::ErrDecryptFailed(aes_gcm::aead::Error))?;
99 aead.decrypt_in_place_detached(&nonce_arr, &ad_mut, &mut buffer, &tag_arr)
100 .map_err(|_| Error::ErrDecryptFailed(aes_gcm::aead::Error))?;
101 plain.extend_from_slice(&buffer);
102
103 pos += frag_len;
104 seq_num += 1;
105
106 if is_last {
107 break;
108 }
109 }
110
111 Ok(plain)
112}
113
114fn build_associated_data<A>(aead: &A, nonce_prefix: &[u8]) -> Result<Vec<u8>, Error>
115where
116 A: AeadInPlace,
117{
118 let mut nonce = [0u8; 12];
119 nonce[0..SIO_NONCE_PREFIX_LEN].copy_from_slice(nonce_prefix);
120 nonce[8..12].copy_from_slice(&0u32.to_le_bytes());
121
122 let nonce_arr = Array::<u8, <A as AeadCore>::NonceSize>::try_from(&nonce[..])
123 .map_err(|_| Error::ErrEncryptFailed(aes_gcm::aead::Error))?;
124 let mut empty: [u8; 0] = [];
125 let tag = aead
126 .encrypt_in_place_detached(&nonce_arr, &[] as &[u8], &mut empty)
127 .map_err(Error::ErrEncryptFailed)?;
128
129 let mut ad = vec![0u8; 1 + tag.len()];
130 ad[0] = 0x00;
131 ad[1..].copy_from_slice(tag.as_slice());
132 Ok(ad)
133}
134
135pub fn encrypt_stream_io(password: &[u8], data: &[u8]) -> Result<Vec<u8>, Error> {
137 let salt: [u8; 32] = rand::random();
138
139 #[cfg(feature = "fips")]
140 let id = ID::Pbkdf2AESGCM;
141
142 #[cfg(not(feature = "fips"))]
143 let id = if crate::encdec::encrypt::native_aes() {
144 ID::Argon2idAESGCM
145 } else {
146 ID::Argon2idChaCHa20Poly1305
147 };
148
149 let key = id.get_key(password, &salt)?;
150 let nonce_prefix: [u8; SIO_NONCE_PREFIX_LEN] = rand::random();
151
152 let mut out = Vec::with_capacity(STREAM_IO_HEADER_LEN + data.len() + 32);
153 out.extend_from_slice(&salt);
154 out.push(id as u8);
155 out.extend_from_slice(&nonce_prefix);
156
157 match id {
158 ID::Argon2idChaCHa20Poly1305 => encrypt_stream(
159 ChaCha20Poly1305::new_from_slice(&key).map_err(|e| Error::ErrInvalidInput(e.to_string()))?,
160 &nonce_prefix,
161 data,
162 &mut out,
163 CHACHA_OVERHEAD,
164 )?,
165 _ => encrypt_stream(
166 Aes256Gcm::new_from_slice(&key).map_err(|e| Error::ErrInvalidInput(e.to_string()))?,
167 &nonce_prefix,
168 data,
169 &mut out,
170 AES_GCM_OVERHEAD,
171 )?,
172 }
173
174 Ok(out)
175}
176
177fn encrypt_stream<A>(
178 aead: A,
179 nonce_prefix: &[u8; SIO_NONCE_PREFIX_LEN],
180 data: &[u8],
181 out: &mut Vec<u8>,
182 _overhead: usize,
183) -> Result<(), Error>
184where
185 A: AeadInPlace,
186{
187 let ad = build_associated_data(&aead, nonce_prefix)?;
188 let mut seq_num: u32 = 1;
189 let mut pos = 0;
190
191 while pos < data.len() {
192 let remaining = data.len() - pos;
193 let is_last = remaining <= SIO_BUF_SIZE;
194
195 let chunk_len = if is_last { remaining } else { SIO_BUF_SIZE };
196 let chunk = &data[pos..pos + chunk_len];
197
198 let mut nonce = [0u8; 12];
199 nonce[0..SIO_NONCE_PREFIX_LEN].copy_from_slice(nonce_prefix);
200 nonce[8..12].copy_from_slice(&seq_num.to_le_bytes());
201
202 let mut ad_mut = ad.clone();
203 ad_mut[0] = if is_last { 0x80 } else { 0x00 };
204
205 let mut buffer = chunk.to_vec();
206 let nonce_arr = Array::<u8, <A as AeadCore>::NonceSize>::try_from(&nonce[..])
207 .map_err(|_| Error::ErrEncryptFailed(aes_gcm::aead::Error))?;
208 let tag = aead
209 .encrypt_in_place_detached(&nonce_arr, &ad_mut, &mut buffer)
210 .map_err(Error::ErrEncryptFailed)?;
211 out.extend_from_slice(&buffer);
212 out.extend_from_slice(tag.as_slice());
213
214 pos += chunk_len;
215 seq_num += 1;
216 }
217
218 Ok(())
219}