1use crate::{with_output_array, with_output_vec, with_output_vec_fallible, FfiMutSlice, FfiSlice};
48use alloc::vec::Vec;
49
50#[derive(Debug)]
52pub struct InvalidCiphertext;
53
54pub trait Aead {
56 type Tag: AsRef<[u8]>;
59
60 type Nonce: AsRef<[u8]>;
63
64 fn seal(&self, nonce: &Self::Nonce, plaintext: &[u8], ad: &[u8]) -> Vec<u8>;
68
69 fn seal_in_place(&self, nonce: &Self::Nonce, plaintext: &mut [u8], ad: &[u8]) -> Self::Tag;
74
75 fn open(&self, nonce: &Self::Nonce, ciphertext: &[u8], ad: &[u8]) -> Option<Vec<u8>>;
80
81 fn open_in_place(
85 &self,
86 nonce: &Self::Nonce,
87 ciphertext: &mut [u8],
88 tag: &Self::Tag,
89 ad: &[u8],
90 ) -> Result<(), InvalidCiphertext>;
91}
92
93pub struct Aes128Gcm(EvpAead<16, 12, 16>);
95aead_algo!(Aes128Gcm, EVP_aead_aes_128_gcm, 16, 12, 16);
96
97pub struct Aes256Gcm(EvpAead<32, 12, 16>);
99aead_algo!(Aes256Gcm, EVP_aead_aes_256_gcm, 32, 12, 16);
100
101pub struct Aes128GcmSiv(EvpAead<16, 12, 16>);
103aead_algo!(Aes128GcmSiv, EVP_aead_aes_128_gcm_siv, 16, 12, 16);
104
105pub struct Aes256GcmSiv(EvpAead<32, 12, 16>);
107aead_algo!(Aes256GcmSiv, EVP_aead_aes_256_gcm_siv, 32, 12, 16);
108
109pub struct Chacha20Poly1305(EvpAead<32, 12, 16>);
111aead_algo!(Chacha20Poly1305, EVP_aead_chacha20_poly1305, 32, 12, 16);
112
113pub struct XChacha20Poly1305(EvpAead<32, 24, 16>);
115aead_algo!(XChacha20Poly1305, EVP_aead_xchacha20_poly1305, 32, 24, 16);
116
117struct EvpAead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize>(
119 *mut bssl_sys::EVP_AEAD_CTX,
120);
121
122#[allow(clippy::unwrap_used)]
123impl<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize>
124 EvpAead<KEY_LEN, NONCE_LEN, TAG_LEN>
125{
126 unsafe fn new(key: &[u8; KEY_LEN], evp_aead: *const bssl_sys::EVP_AEAD) -> Self {
128 let ptr =
132 unsafe { bssl_sys::EVP_AEAD_CTX_new(evp_aead, key.as_ffi_ptr(), key.len(), TAG_LEN) };
133 assert!(!ptr.is_null());
134 Self(ptr)
135 }
136
137 fn seal(&self, nonce: &[u8; NONCE_LEN], plaintext: &[u8], ad: &[u8]) -> Vec<u8> {
138 let max_output = plaintext.len() + TAG_LEN;
139 unsafe {
140 with_output_vec(max_output, |out_buf| {
141 let mut out_len = 0usize;
142 let result = bssl_sys::EVP_AEAD_CTX_seal(
147 self.0,
148 out_buf,
149 &mut out_len,
150 max_output,
151 nonce.as_ffi_ptr(),
152 nonce.len(),
153 plaintext.as_ffi_ptr(),
154 plaintext.len(),
155 ad.as_ffi_ptr(),
156 ad.len(),
157 );
158 assert_eq!(result, 1);
160 assert_eq!(out_len, max_output);
163 out_len
165 })
166 }
167 }
168
169 fn seal_in_place(
170 &self,
171 nonce: &[u8; NONCE_LEN],
172 plaintext: &mut [u8],
173 ad: &[u8],
174 ) -> [u8; TAG_LEN] {
175 unsafe {
179 with_output_array(|tag, tag_len| {
180 let mut out_tag_len = 0usize;
181 let result = bssl_sys::EVP_AEAD_CTX_seal_scatter(
182 self.0,
183 plaintext.as_mut_ffi_ptr(),
184 tag,
185 &mut out_tag_len,
186 tag_len,
187 nonce.as_ffi_ptr(),
188 nonce.len(),
189 plaintext.as_ffi_ptr(),
190 plaintext.len(),
191 core::ptr::null(),
192 0,
193 ad.as_ffi_ptr(),
194 ad.len(),
195 );
196 assert_eq!(result, 1);
199 assert_eq!(out_tag_len, TAG_LEN);
201 })
202 }
203 }
204
205 fn open(&self, nonce: &[u8; NONCE_LEN], ciphertext: &[u8], ad: &[u8]) -> Option<Vec<u8>> {
206 if ciphertext.len() < TAG_LEN {
207 return None;
208 }
209 let max_output = ciphertext.len() - TAG_LEN;
210
211 unsafe {
212 with_output_vec_fallible(max_output, |out_buf| {
213 let mut out_len = 0usize;
214 let result = bssl_sys::EVP_AEAD_CTX_open(
219 self.0,
220 out_buf,
221 &mut out_len,
222 max_output,
223 nonce.as_ffi_ptr(),
224 nonce.len(),
225 ciphertext.as_ffi_ptr(),
226 ciphertext.len(),
227 ad.as_ffi_ptr(),
228 ad.len(),
229 );
230 if result == 1 {
231 Some(out_len)
233 } else {
234 None
235 }
236 })
237 }
238 }
239
240 fn open_in_place(
241 &self,
242 nonce: &[u8; NONCE_LEN],
243 ciphertext: &mut [u8],
244 tag: &[u8; TAG_LEN],
245 ad: &[u8],
246 ) -> Result<(), InvalidCiphertext> {
247 let result = unsafe {
250 bssl_sys::EVP_AEAD_CTX_open_gather(
251 self.0,
252 ciphertext.as_mut_ffi_ptr(),
253 nonce.as_ffi_ptr(),
254 nonce.len(),
255 ciphertext.as_ffi_ptr(),
256 ciphertext.len(),
257 tag.as_ffi_ptr(),
258 tag.len(),
259 ad.as_ffi_ptr(),
260 ad.len(),
261 )
262 };
263 if result == 1 {
264 Ok(())
265 } else {
266 Err(InvalidCiphertext)
267 }
268 }
269}
270
271impl<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize> Drop
272 for EvpAead<KEY_LEN, NONCE_LEN, TAG_LEN>
273{
274 fn drop(&mut self) {
275 unsafe { bssl_sys::EVP_AEAD_CTX_free(self.0) }
278 }
279}
280
281#[cfg(test)]
282mod test {
283 use super::*;
284 use crate::test_helpers::{decode_hex, decode_hex_into_vec};
285
286 fn check_aead_invariants<
287 const NONCE_LEN: usize,
288 const TAG_LEN: usize,
289 A: Aead<Nonce = [u8; NONCE_LEN], Tag = [u8; TAG_LEN]>,
290 >(
291 aead: A,
292 ) {
293 let plaintext = b"plaintext";
294 let ad = b"additional data";
295 let nonce: A::Nonce = [0u8; NONCE_LEN];
296
297 let mut ciphertext = aead.seal(&nonce, plaintext, ad);
298 let plaintext2 = aead
299 .open(&nonce, ciphertext.as_slice(), ad)
300 .expect("should decrypt");
301 assert_eq!(plaintext, plaintext2.as_slice());
302
303 ciphertext[0] ^= 1;
304 assert!(aead.open(&nonce, ciphertext.as_slice(), ad).is_none());
305 ciphertext[0] ^= 1;
306
307 let (ciphertext_in_place, tag_slice) =
308 ciphertext.as_mut_slice().split_at_mut(plaintext.len());
309 let tag: [u8; TAG_LEN] = tag_slice.try_into().unwrap();
310 aead.open_in_place(&nonce, ciphertext_in_place, &tag, ad)
311 .expect("should decrypt");
312 assert_eq!(plaintext, ciphertext_in_place);
313
314 let tag = aead.seal_in_place(&nonce, ciphertext_in_place, ad);
315 aead.open_in_place(&nonce, ciphertext_in_place, &tag, ad)
316 .expect("should decrypt");
317 assert_eq!(plaintext, ciphertext_in_place);
318
319 assert!(aead.open(&nonce, b"tooshort", b"").is_none());
320 }
321
322 #[test]
323 fn aes_128_gcm_invariants() {
324 check_aead_invariants(Aes128Gcm::new(&[0u8; 16]));
325 }
326
327 #[test]
328 fn aes_256_gcm_invariants() {
329 check_aead_invariants(Aes256Gcm::new(&[0u8; 32]));
330 }
331
332 #[test]
333 fn aes_128_gcm_siv_invariants() {
334 check_aead_invariants(Aes128GcmSiv::new(&[0u8; 16]));
335 }
336
337 #[test]
338 fn aes_256_gcm_siv_invariants() {
339 check_aead_invariants(Aes256GcmSiv::new(&[0u8; 32]));
340 }
341
342 #[test]
343 fn chacha20_poly1305_invariants() {
344 check_aead_invariants(Chacha20Poly1305::new(&[0u8; 32]));
345 }
346
347 #[test]
348 fn xchacha20_poly1305_invariants() {
349 check_aead_invariants(XChacha20Poly1305::new(&[0u8; 32]));
350 }
351
352 struct TestCase<const KEY_LEN: usize, const NONCE_LEN: usize> {
353 key: [u8; KEY_LEN],
354 nonce: [u8; NONCE_LEN],
355 msg: Vec<u8>,
356 ad: Vec<u8>,
357 ciphertext: Vec<u8>,
358 }
359
360 fn check_test_cases<
361 const KEY_LEN: usize,
362 const NONCE_LEN: usize,
363 const TAG_LEN: usize,
364 F: Fn(&[u8; KEY_LEN]) -> Box<dyn Aead<Nonce = [u8; NONCE_LEN], Tag = [u8; TAG_LEN]>>,
365 >(
366 new_func: F,
367 test_cases: &[TestCase<KEY_LEN, NONCE_LEN>],
368 ) {
369 for (test_num, test) in test_cases.iter().enumerate() {
370 let ctx = new_func(&test.key);
371 let ciphertext = ctx.seal(&test.nonce, test.msg.as_slice(), test.ad.as_slice());
372 assert_eq!(ciphertext, test.ciphertext, "Failed on test #{}", test_num);
373
374 let plaintext = ctx
375 .open(&test.nonce, ciphertext.as_slice(), test.ad.as_slice())
376 .unwrap();
377 assert_eq!(plaintext, test.msg, "Decrypt failed on test #{}", test_num);
378 }
379 }
380
381 #[test]
382 fn aes_128_gcm_siv() {
383 let test_cases: &[TestCase<16, 12>] = &[
384 TestCase {
385 key: decode_hex("01000000000000000000000000000000"),
388 nonce: decode_hex("030000000000000000000000"),
389 msg: Vec::new(),
390 ad: Vec::new(),
391 ciphertext: decode_hex_into_vec("dc20e2d83f25705bb49e439eca56de25"),
392 },
393 TestCase {
394 key: decode_hex("01000000000000000000000000000000"),
396 nonce: decode_hex("030000000000000000000000"),
397 msg: decode_hex_into_vec("0100000000000000"),
398 ad: Vec::new(),
399 ciphertext: decode_hex_into_vec("b5d839330ac7b786578782fff6013b815b287c22493a364c"),
400 },
401 TestCase {
402 key: decode_hex("01000000000000000000000000000000"),
404 nonce: decode_hex("030000000000000000000000"),
405 msg: decode_hex_into_vec("02000000"),
406 ad: decode_hex_into_vec("010000000000000000000000"),
407 ciphertext: decode_hex_into_vec("a8fe3e8707eb1f84fb28f8cb73de8e99e2f48a14"),
408 },
409 ];
410
411 check_test_cases(|key| Box::new(Aes128GcmSiv::new(key)), test_cases);
412 }
413
414 #[test]
415 fn aes_256_gcm_siv() {
416 let test_cases: &[TestCase<32, 12>] = &[
417 TestCase {
418 key: decode_hex("0100000000000000000000000000000000000000000000000000000000000000"),
421 nonce: decode_hex("030000000000000000000000"),
422 msg: decode_hex_into_vec("0100000000000000"),
423 ad: Vec::new(),
424 ciphertext: decode_hex_into_vec("c2ef328e5c71c83b843122130f7364b761e0b97427e3df28"),
425 },
426 TestCase {
427 key: decode_hex("0100000000000000000000000000000000000000000000000000000000000000"),
429 nonce: decode_hex("030000000000000000000000"),
430 msg: decode_hex_into_vec("010000000000000000000000"),
431 ad: Vec::new(),
432 ciphertext: decode_hex_into_vec(
433 "9aab2aeb3faa0a34aea8e2b18ca50da9ae6559e48fd10f6e5c9ca17e",
434 ),
435 },
436 TestCase {
437 key: decode_hex("0100000000000000000000000000000000000000000000000000000000000000"),
439 nonce: decode_hex("030000000000000000000000"),
440 msg: decode_hex_into_vec("02000000"),
441 ad: decode_hex_into_vec("010000000000000000000000"),
442 ciphertext: decode_hex_into_vec("22b3f4cd1835e517741dfddccfa07fa4661b74cf"),
443 },
444 ];
445
446 check_test_cases(|key| Box::new(Aes256GcmSiv::new(key)), test_cases);
447 }
448
449 #[test]
450 fn aes_128_gcm() {
451 let test_cases: &[TestCase<16, 12>] = &[
452 TestCase {
453 key: decode_hex("d480429666d48b400633921c5407d1d1"),
455 nonce: decode_hex("3388c676dc754acfa66e172a"),
456 msg: Vec::new(),
457 ad: Vec::new(),
458 ciphertext: decode_hex_into_vec("7d7daf44850921a34e636b01adeb104f"),
459 },
460 TestCase {
461 key: decode_hex("3881e7be1bb3bbcaff20bdb78e5d1b67"),
463 nonce: decode_hex("dcf5b7ae2d7552e2297fcfa9"),
464 msg: decode_hex_into_vec("0a2714aa7d"),
465 ad: decode_hex_into_vec("c60c64bbf7"),
466 ciphertext: decode_hex_into_vec("5626f96ecbff4c4f1d92b0abb1d0820833d9eb83c7"),
467 },
468 ];
469
470 check_test_cases(|key| Box::new(Aes128Gcm::new(key)), test_cases);
471 }
472
473 #[test]
474 fn aes_256_gcm() {
475 let test_cases: &[TestCase<32, 12>] = &[
476 TestCase {
477 key: decode_hex("e5ac4a32c67e425ac4b143c83c6f161312a97d88d634afdf9f4da5bd35223f01"),
479 nonce: decode_hex("5bf11a0951f0bfc7ea5c9e58"),
480 msg: Vec::new(),
481 ad: Vec::new(),
482 ciphertext: decode_hex_into_vec("d7cba289d6d19a5af45dc13857016bac"),
483 },
484 TestCase {
485 key: decode_hex("73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"),
487 nonce: decode_hex("a330a184fc245812f4820caa"),
488 msg: decode_hex_into_vec("f0535fe211"),
489 ad: decode_hex_into_vec("e91428be04"),
490 ciphertext: decode_hex_into_vec("e9b8a896da9115ed79f26a030c14947b3e454db9e7"),
491 },
492 ];
493
494 check_test_cases(|key| Box::new(Aes256Gcm::new(key)), test_cases);
495 }
496}