diff --git a/aws-lc-rs/src/aead.rs b/aws-lc-rs/src/aead.rs index d78d86da2df..665b16431b9 100644 --- a/aws-lc-rs/src/aead.rs +++ b/aws-lc-rs/src/aead.rs @@ -114,16 +114,9 @@ //! assert_eq!(plaintext, decrypted_plaintext); //! ``` -use crate::{derive_debug_via_id, fips::indicator_check, hkdf, iv::FixedLength}; -use std::{fmt::Debug, ptr::null}; - -use crate::error::Unspecified; +use crate::{derive_debug_via_id, error::Unspecified, hkdf}; use aead_ctx::AeadCtx; -use aws_lc::{ - EVP_AEAD_CTX_open, EVP_AEAD_CTX_open_gather, EVP_AEAD_CTX_seal, EVP_AEAD_CTX_seal_scatter, -}; -use std::mem::MaybeUninit; -use std::ops::RangeFrom; +use core::{fmt::Debug, ops::RangeFrom}; mod aead_ctx; mod aes_gcm; @@ -135,6 +128,7 @@ mod poly1305; pub mod quic; mod rand_nonce; mod tls; +mod unbound_key; pub use self::{ aes_gcm::{AES_128_GCM, AES_128_GCM_SIV, AES_256_GCM, AES_256_GCM_SIV}, @@ -142,6 +136,7 @@ pub use self::{ nonce::{Nonce, NONCE_LEN}, rand_nonce::RandomizedNonceKey, tls::{TlsProtocolId, TlsRecordOpeningKey, TlsRecordSealingKey}, + unbound_key::UnboundKey, }; /// A sequences of unique nonces. @@ -196,7 +191,7 @@ impl BoundKey for OpeningKey { #[inline] fn algorithm(&self) -> &'static Algorithm { - self.key.algorithm + self.key.algorithm() } } @@ -229,6 +224,7 @@ impl OpeningKey { /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been /// overwritten in an unspecified way. #[inline] + #[allow(clippy::needless_pass_by_value)] pub fn open_in_place<'in_out, A>( &mut self, aad: Aad, @@ -237,7 +233,8 @@ impl OpeningKey { where A: AsRef<[u8]>, { - self.open_within(aad, in_out, 0..) + self.key + .open_within(self.nonce_sequence.advance()?, aad.as_ref(), in_out, 0..) } /// Authenticates and decrypts (“opens”) data in place, with a shift. @@ -293,6 +290,7 @@ impl OpeningKey { /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been /// overwritten in an unspecified way. #[inline] + #[allow(clippy::needless_pass_by_value)] pub fn open_within<'in_out, A>( &mut self, aad: Aad, @@ -302,59 +300,15 @@ impl OpeningKey { where A: AsRef<[u8]>, { - open_within( - self.key.algorithm(), - self.key.get_inner_key(), + self.key.open_within( self.nonce_sequence.advance()?, - aad, + aad.as_ref(), in_out, ciphertext_and_tag, ) } } -#[inline] -fn open_within<'in_out, A: AsRef<[u8]>>( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Nonce, - Aad(aad): Aad, - in_out: &'in_out mut [u8], - ciphertext_and_tag: RangeFrom, -) -> Result<&'in_out mut [u8], Unspecified> { - let in_prefix_len = ciphertext_and_tag.start; - let ciphertext_and_tag_len = in_out.len().checked_sub(in_prefix_len).ok_or(Unspecified)?; - let ciphertext_len = ciphertext_and_tag_len - .checked_sub(alg.tag_len()) - .ok_or(Unspecified)?; - check_per_nonce_max_bytes(alg, ciphertext_len)?; - - match ctx { - AeadCtx::AES_128_GCM_RANDNONCE(_) | AeadCtx::AES_256_GCM_RANDNONCE(_) => { - aead_open_combined_randnonce( - alg, - ctx, - nonce, - Aad::from(aad.as_ref()), - &mut in_out[in_prefix_len..], - ) - } - _ => aead_open_combined( - alg, - ctx, - nonce, - Aad::from(aad.as_ref()), - &mut in_out[in_prefix_len..], - ), - }?; - - // shift the plaintext to the left - in_out.copy_within(in_prefix_len..in_prefix_len + ciphertext_len, 0); - - // `ciphertext_len` is also the plaintext length. - Ok(&mut in_out[..ciphertext_len]) -} - /// An AEAD key for encrypting and signing ("sealing"), bound to a nonce /// sequence. /// @@ -377,7 +331,7 @@ impl BoundKey for SealingKey { #[inline] fn algorithm(&self) -> &'static Algorithm { - self.key.algorithm + self.key.algorithm() } } @@ -441,14 +395,9 @@ impl SealingKey { A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - seal_in_place_append_tag( - self.algorithm(), - self.key.get_inner_key(), - Some(self.nonce_sequence.advance()?), - Aad::from(aad.as_ref()), - in_out, - ) - .map(|_| ()) + self.key + .seal_in_place_append_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out) + .map(|_| ()) } /// Encrypts and signs (“seals”) data in place. @@ -480,119 +429,10 @@ impl SealingKey { where A: AsRef<[u8]>, { - seal_in_place_separate_tag( - self.algorithm(), - self.key.get_inner_key(), - Some(self.nonce_sequence.advance()?), - Aad::from(aad.as_ref()), - in_out, - ) - .map(|(_, tag)| tag) - } -} - -#[inline] -fn seal_in_place_append_tag<'a, InOut>( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Option, - aad: Aad<&[u8]>, - in_out: &'a mut InOut, -) -> Result -where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, -{ - check_per_nonce_max_bytes(alg, in_out.as_mut().len())?; - match nonce { - Some(nonce) => aead_seal_combined(alg, ctx, nonce, aad, in_out), - None => aead_seal_combined_randnonce(alg, ctx, aad, in_out), - } -} - -#[inline] -fn seal_in_place_separate_tag( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Option, - aad: Aad<&[u8]>, - in_out: &mut [u8], -) -> Result<(Nonce, Tag), Unspecified> { - check_per_nonce_max_bytes(alg, in_out.len())?; - match nonce { - Some(nonce) => aead_seal_separate(alg, ctx, nonce, aad, in_out), - None => aead_seal_separate_randnonce(alg, ctx, aad, in_out), - } -} - -#[inline] -fn seal_in_place_separate_scatter( - alg: &'static Algorithm, - key: &UnboundKey, - nonce: Nonce, - aad: Aad<&[u8]>, - in_out: &mut [u8], - extra_in: &[u8], - extra_out_and_tag: &mut [u8], -) -> Result<(), Unspecified> { - check_per_nonce_max_bytes(key.algorithm, in_out.len())?; - let key_inner_ref = key.get_inner_key(); - aead_seal_separate_scatter( - alg, - key_inner_ref, - nonce, - aad, - in_out, - extra_in, - extra_out_and_tag, - ) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_seal_separate_scatter( - alg: &'static Algorithm, - key: &AeadCtx, - nonce: Nonce, - aad: Aad<&[u8]>, - in_out: &mut [u8], - extra_in: &[u8], - extra_out_and_tag: &mut [u8], -) -> Result<(), Unspecified> { - // ensure that the extra lengths match - { - let actual = extra_in.len() + alg.tag_len(); - let expected = extra_out_and_tag.len(); - - if actual != expected { - return Err(Unspecified); - } + self.key + .seal_in_place_separate_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out) + .map(|(_, tag)| tag) } - - let aead_ctx = key.as_ref(); - let aad_slice = aad.as_ref(); - let nonce = nonce.as_ref(); - let mut out_tag_len = extra_out_and_tag.len(); - - if 1 != unsafe { - EVP_AEAD_CTX_seal_scatter( - *aead_ctx.as_const(), - in_out.as_mut_ptr(), - extra_out_and_tag.as_mut_ptr(), - &mut out_tag_len, - extra_out_and_tag.len(), - nonce.as_ptr(), - nonce.len(), - in_out.as_ptr(), - in_out.len(), - extra_in.as_ptr(), - extra_in.len(), - aad_slice.as_ptr(), - aad_slice.len(), - ) - } { - return Err(Unspecified); - } - Ok(()) } /// The additionally authenticated data (AAD) for an opening or sealing @@ -627,55 +467,6 @@ impl Aad<[u8; 0]> { } } -/// An AEAD key without a designated role or nonce sequence. -pub struct UnboundKey { - inner: AeadCtx, - algorithm: &'static Algorithm, -} - -#[allow(clippy::missing_fields_in_debug)] -impl Debug for UnboundKey { - fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { - f.debug_struct("UnboundKey") - .field("algorithm", &self.algorithm) - .finish() - } -} - -impl UnboundKey { - /// Constructs an `UnboundKey`. - /// # Errors - /// `error::Unspecified` if `key_bytes.len() != algorithm.key_len()`. - pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { - Ok(Self { - inner: (algorithm.init)(key_bytes, algorithm.tag_len())?, - algorithm, - }) - } - - #[inline] - fn get_inner_key(&self) -> &AeadCtx { - &self.inner - } - - /// The key's AEAD algorithm. - #[inline] - #[must_use] - pub fn algorithm(&self) -> &'static Algorithm { - self.algorithm - } -} - -impl From> for UnboundKey { - fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self { - let mut key_bytes = [0; MAX_KEY_LEN]; - let key_bytes = &mut key_bytes[..okm.len().key_len]; - let algorithm = *okm.len(); - okm.fill(key_bytes).unwrap(); - Self::new(algorithm, key_bytes).unwrap() - } -} - impl hkdf::KeyType for &'static Algorithm { #[inline] fn len(&self) -> usize { @@ -745,6 +536,7 @@ impl LessSafeKey { /// # Errors /// `error::Unspecified` when ciphertext is invalid. #[inline] + #[allow(clippy::needless_pass_by_value)] pub fn open_within<'in_out, A>( &self, nonce: Nonce, @@ -755,14 +547,38 @@ impl LessSafeKey { where A: AsRef<[u8]>, { - open_within( - self.key.algorithm, - self.key.get_inner_key(), - nonce, - aad, - in_out, - ciphertext_and_tag, - ) + self.key + .open_within(nonce, aad.as_ref(), in_out, ciphertext_and_tag) + } + + /// Authenticates and decrypts (“opens”) data into another provided slice. + /// + /// `aad` is the additional authenticated data (AAD), if any. + /// + /// On input, `in_ciphertext` must be the ciphertext. The tag must be provided in + /// `in_tag`. + /// + /// The `out_plaintext` length must match the provided `in_ciphertext`. + /// + /// # Errors + /// `error::Unspecified` when ciphertext is invalid. In this case, `out_plaintext` may + /// have been overwritten in an unspecified way. + /// + #[inline] + #[allow(clippy::needless_pass_by_value)] + pub fn open_separate_gather( + &self, + nonce: Nonce, + aad: Aad, + in_ciphertext: &[u8], + in_tag: &[u8], + out_plaintext: &mut [u8], + ) -> Result<(), Unspecified> + where + A: AsRef<[u8]>, + { + self.key + .open_separate_gather(&nonce, aad.as_ref(), in_ciphertext, in_tag, out_plaintext) } /// Deprecated. Renamed to `seal_in_place_append_tag()`. @@ -812,14 +628,9 @@ impl LessSafeKey { A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - seal_in_place_append_tag( - self.algorithm(), - self.key.get_inner_key(), - Some(nonce), - Aad::from(aad.as_ref()), - in_out, - ) - .map(|_| ()) + self.key + .seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out) + .map(|_| ()) } /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an @@ -845,14 +656,9 @@ impl LessSafeKey { where A: AsRef<[u8]>, { - seal_in_place_separate_tag( - self.algorithm(), - self.key.get_inner_key(), - Some(nonce), - Aad::from(aad.as_ref()), - in_out, - ) - .map(|(_, tag)| tag) + self.key + .seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out) + .map(|(_, tag)| tag) } /// Encrypts and signs (“seals”) data in place with extra plaintext. @@ -887,11 +693,9 @@ impl LessSafeKey { where A: AsRef<[u8]>, { - seal_in_place_separate_scatter( - self.algorithm(), - &self.key, + self.key.seal_in_place_separate_scatter( nonce, - Aad::from(aad.as_ref()), + aad.as_ref(), in_out, extra_in, extra_out_and_tag, @@ -902,7 +706,7 @@ impl LessSafeKey { #[inline] #[must_use] pub fn algorithm(&self) -> &'static Algorithm { - self.key.algorithm + self.key.algorithm() } } @@ -991,304 +795,6 @@ const TAG_LEN: usize = 16; /// The maximum length of a tag for the algorithms in this module. pub const MAX_TAG_LEN: usize = TAG_LEN; -/// The maximum length of a nonce returned by our AEAD API. -const MAX_NONCE_LEN: usize = NONCE_LEN; - -/// The maximum required tag buffer needed if using AWS-LC generated nonce construction -const MAX_TAG_NONCE_BUFFER_LEN: usize = MAX_TAG_LEN + MAX_NONCE_LEN; - -#[inline] -#[must_use] -const fn u64_from_usize(x: usize) -> u64 { - x as u64 -} - -#[inline] -fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), Unspecified> { - if u64_from_usize(in_out_len) > alg.max_input_len { - return Err(Unspecified); - } - Ok(()) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_seal_combined( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Nonce, - aad: Aad<&[u8]>, - in_out: &mut InOut, -) -> Result -where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, -{ - let plaintext_len = in_out.as_mut().len(); - - let alg_tag_len = alg.tag_len(); - - debug_assert!(alg_tag_len <= MAX_TAG_LEN); - - let tag_buffer = [0u8; MAX_TAG_LEN]; - - in_out.extend(tag_buffer[..alg_tag_len].iter()); - - let mut out_len = MaybeUninit::::uninit(); - let mut_in_out = in_out.as_mut(); - let aad_str = aad.0; - - { - let nonce = nonce.as_ref(); - - debug_assert_eq!(nonce.len(), alg.nonce_len()); - - if 1 != indicator_check!(unsafe { - EVP_AEAD_CTX_seal( - *ctx.as_ref().as_const(), - mut_in_out.as_mut_ptr(), - out_len.as_mut_ptr(), - plaintext_len + alg_tag_len, - nonce.as_ptr(), - nonce.len(), - mut_in_out.as_ptr(), - plaintext_len, - aad_str.as_ptr(), - aad_str.len(), - ) - }) { - return Err(Unspecified); - } - } - - Ok(nonce) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_seal_combined_randnonce( - alg: &'static Algorithm, - key: &AeadCtx, - aad: Aad<&[u8]>, - in_out: &mut InOut, -) -> Result -where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, -{ - let mut tag_buffer = [0u8; MAX_TAG_NONCE_BUFFER_LEN]; - - let mut out_tag_len = MaybeUninit::::uninit(); - let aad_str = aad.0; - - { - let plaintext_len = in_out.as_mut().len(); - let in_out = in_out.as_mut(); - - if 1 != indicator_check!(unsafe { - EVP_AEAD_CTX_seal_scatter( - *key.as_ref().as_const(), - in_out.as_mut_ptr(), - tag_buffer.as_mut_ptr(), - out_tag_len.as_mut_ptr(), - tag_buffer.len(), - null(), - 0, - in_out.as_ptr(), - plaintext_len, - null(), - 0, - aad_str.as_ptr(), - aad_str.len(), - ) - }) { - return Err(Unspecified); - } - } - - let tag_len = alg.tag_len(); - let nonce_len = alg.nonce_len(); - - let nonce = Nonce(FixedLength::::try_from( - &tag_buffer[tag_len..tag_len + nonce_len], - )?); - - in_out.extend(&tag_buffer[..tag_len]); - - Ok(nonce) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_seal_separate( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Nonce, - aad: Aad<&[u8]>, - in_out: &mut [u8], -) -> Result<(Nonce, Tag), Unspecified> { - let aad_slice = aad.as_ref(); - let mut tag = [0u8; MAX_TAG_LEN]; - let mut out_tag_len = MaybeUninit::::uninit(); - { - let nonce = nonce.as_ref(); - - debug_assert_eq!(nonce.len(), alg.nonce_len()); - - if 1 != indicator_check!(unsafe { - EVP_AEAD_CTX_seal_scatter( - *ctx.as_ref().as_const(), - in_out.as_mut_ptr(), - tag.as_mut_ptr(), - out_tag_len.as_mut_ptr(), - tag.len(), - nonce.as_ptr(), - nonce.len(), - in_out.as_ptr(), - in_out.len(), - null(), - 0usize, - aad_slice.as_ptr(), - aad_slice.len(), - ) - }) { - return Err(Unspecified); - } - } - Ok((nonce, Tag(tag, unsafe { out_tag_len.assume_init() }))) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_seal_separate_randnonce( - alg: &'static Algorithm, - ctx: &AeadCtx, - aad: Aad<&[u8]>, - in_out: &mut [u8], -) -> Result<(Nonce, Tag), Unspecified> { - let aad_slice = aad.as_ref(); - let mut tag_buffer = [0u8; MAX_TAG_NONCE_BUFFER_LEN]; - - debug_assert!(alg.tag_len() + alg.nonce_len() <= tag_buffer.len()); - - let mut out_tag_len = MaybeUninit::::uninit(); - - if 1 != indicator_check!(unsafe { - EVP_AEAD_CTX_seal_scatter( - *ctx.as_ref().as_const(), - in_out.as_mut_ptr(), - tag_buffer.as_mut_ptr(), - out_tag_len.as_mut_ptr(), - tag_buffer.len(), - null(), - 0, - in_out.as_ptr(), - in_out.len(), - null(), - 0usize, - aad_slice.as_ptr(), - aad_slice.len(), - ) - }) { - return Err(Unspecified); - } - - let tag_len = alg.tag_len(); - let nonce_len = alg.nonce_len(); - - let nonce = Nonce(FixedLength::::try_from( - &tag_buffer[tag_len..tag_len + nonce_len], - )?); - - let mut tag = [0u8; MAX_TAG_LEN]; - tag.copy_from_slice(&tag_buffer[..tag_len]); - - Ok((nonce, Tag(tag, tag_len))) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_open_combined( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Nonce, - aad: Aad<&[u8]>, - in_out: &mut [u8], -) -> Result<(), Unspecified> { - let nonce = nonce.as_ref(); - - debug_assert_eq!(nonce.len(), alg.nonce_len()); - - let plaintext_len = in_out.len() - alg.tag_len(); - - let aad_str = aad.0; - let mut out_len = MaybeUninit::::uninit(); - if 1 != indicator_check!(unsafe { - EVP_AEAD_CTX_open( - *ctx.as_ref().as_const(), - in_out.as_mut_ptr(), - out_len.as_mut_ptr(), - plaintext_len, - nonce.as_ptr(), - nonce.len(), - in_out.as_ptr(), - plaintext_len + alg.tag_len(), - aad_str.as_ptr(), - aad_str.len(), - ) - }) { - return Err(Unspecified); - } - - Ok(()) -} - -#[inline] -#[allow(clippy::needless_pass_by_value)] -pub(crate) fn aead_open_combined_randnonce( - alg: &'static Algorithm, - ctx: &AeadCtx, - nonce: Nonce, - aad: Aad<&[u8]>, - in_out: &mut [u8], -) -> Result<(), Unspecified> { - let nonce = nonce.as_ref(); - - let alg_nonce_len = alg.nonce_len(); - let alg_tag_len = alg.tag_len(); - - debug_assert_eq!(nonce.len(), alg_nonce_len); - debug_assert!(alg_tag_len + alg_nonce_len <= MAX_TAG_NONCE_BUFFER_LEN); - - let plaintext_len = in_out.len() - alg_tag_len; - - let mut tag_buffer = [0u8; MAX_TAG_NONCE_BUFFER_LEN]; - - tag_buffer[..alg_tag_len].copy_from_slice(&in_out[plaintext_len..plaintext_len + alg_tag_len]); - tag_buffer[alg_tag_len..alg_tag_len + alg_nonce_len].copy_from_slice(nonce); - - let tag_slice = &tag_buffer[0..alg_tag_len + alg_nonce_len]; - - let aad_str = aad.0; - - if 1 != indicator_check!(unsafe { - EVP_AEAD_CTX_open_gather( - *ctx.as_ref().as_const(), - in_out.as_mut_ptr(), - null(), - 0, - in_out.as_ptr(), - plaintext_len, - tag_slice.as_ptr(), - tag_slice.len(), - aad_str.as_ptr(), - aad_str.len(), - ) - }) { - return Err(Unspecified); - } - - Ok(()) -} - #[cfg(test)] mod tests { use super::*; diff --git a/aws-lc-rs/src/aead/rand_nonce.rs b/aws-lc-rs/src/aead/rand_nonce.rs index 67f75357cef..aae1c57a9fd 100644 --- a/aws-lc-rs/src/aead/rand_nonce.rs +++ b/aws-lc-rs/src/aead/rand_nonce.rs @@ -1,13 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use crate::{error::Unspecified, iv::FixedLength}; -use std::fmt::Debug; +use crate::error::Unspecified; +use core::fmt::Debug; -use super::{ - aead_ctx::AeadCtx, open_within, seal_in_place_append_tag, seal_in_place_separate_tag, Aad, - Algorithm, AlgorithmID, Nonce, Tag, NONCE_LEN, -}; +use super::{aead_ctx::AeadCtx, Aad, Algorithm, AlgorithmID, Nonce, Tag, UnboundKey}; /// AEAD Cipher key using a randomized nonce. /// @@ -19,7 +16,7 @@ use super::{ /// /// Prefer this type in place of `LessSafeKey`, `OpeningKey`, `SealingKey`. pub struct RandomizedNonceKey { - ctx: AeadCtx, + key: UnboundKey, algorithm: &'static Algorithm, } @@ -42,7 +39,10 @@ impl RandomizedNonceKey { | AlgorithmID::AES_256_GCM_SIV | AlgorithmID::CHACHA20_POLY1305 => return Err(Unspecified), }?; - Ok(Self { ctx, algorithm }) + Ok(Self { + key: UnboundKey::from(ctx), + algorithm, + }) } /// Authenticates and decrypts (“opens”) data in place. @@ -55,6 +55,7 @@ impl RandomizedNonceKey { /// # Errors /// `error::Unspecified` when ciphertext is invalid. #[inline] + #[allow(clippy::needless_pass_by_value)] pub fn open_in_place<'in_out, A>( &self, nonce: Nonce, @@ -64,7 +65,7 @@ impl RandomizedNonceKey { where A: AsRef<[u8]>, { - open_within(self.algorithm, &self.ctx, nonce, aad, in_out, 0..) + self.key.open_within(nonce, aad.as_ref(), in_out, 0..) } /// Encrypts and signs (“seals”) data in place, appending the tag to the @@ -92,13 +93,8 @@ impl RandomizedNonceKey { A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - seal_in_place_append_tag( - self.algorithm, - &self.ctx, - None, - Aad::from(aad.as_ref()), - in_out, - ) + self.key + .seal_in_place_append_tag(None, aad.as_ref(), in_out) } /// Encrypts and signs (“seals”) data in place. @@ -127,18 +123,8 @@ impl RandomizedNonceKey { where A: AsRef<[u8]>, { - let nonce = if let AlgorithmID::CHACHA20_POLY1305 = self.algorithm.id { - Some(Nonce(FixedLength::::new()?)) - } else { - None - }; - seal_in_place_separate_tag( - self.algorithm, - &self.ctx, - nonce, - Aad::from(aad.as_ref()), - in_out, - ) + self.key + .seal_in_place_separate_tag(None, aad.as_ref(), in_out) } /// The key's AEAD algorithm. diff --git a/aws-lc-rs/src/aead/tls.rs b/aws-lc-rs/src/aead/tls.rs index 6002087adf1..e9328e7f8f9 100644 --- a/aws-lc-rs/src/aead/tls.rs +++ b/aws-lc-rs/src/aead/tls.rs @@ -1,15 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use std::fmt::Debug; - -use crate::error::Unspecified; - use super::{ aead_ctx::{self, AeadCtx}, - open_within, seal_in_place_append_tag, seal_in_place_separate_tag, Aad, Algorithm, AlgorithmID, - Nonce, Tag, + Aad, Algorithm, AlgorithmID, Nonce, Tag, UnboundKey, }; +use crate::error::Unspecified; +use core::fmt::Debug; /// The Transport Layer Security (TLS) protocol version. #[allow(clippy::module_name_repetitions)] @@ -38,8 +35,7 @@ pub struct TlsRecordSealingKey { // The choice here was either wrap the underlying EVP_AEAD_CTX in a Mutex as done here, // or force this type to !Sync. Since this is an implementation detail of AWS-LC // we have optex to manage this behavior internally. - ctx: AeadCtx, - algorithm: &'static Algorithm, + key: UnboundKey, protocol: TlsProtocolId, } @@ -83,8 +79,7 @@ impl TlsRecordSealingKey { ) => Err(Unspecified), }?; Ok(Self { - ctx, - algorithm, + key: UnboundKey::from(ctx), protocol, }) } @@ -109,14 +104,9 @@ impl TlsRecordSealingKey { A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - seal_in_place_append_tag( - self.algorithm, - &self.ctx, - Some(nonce), - Aad::from(aad.as_ref()), - in_out, - ) - .map(|_| ()) + self.key + .seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out) + .map(|_| ()) } /// Encrypts and signs (“seals”) data in place. @@ -146,21 +136,16 @@ impl TlsRecordSealingKey { where A: AsRef<[u8]>, { - seal_in_place_separate_tag( - self.algorithm, - &self.ctx, - Some(nonce), - Aad::from(aad.as_ref()), - in_out, - ) - .map(|(_, tag)| tag) + self.key + .seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out) + .map(|(_, tag)| tag) } /// The key's AEAD algorithm. #[inline] #[must_use] pub fn algorithm(&self) -> &'static Algorithm { - self.algorithm + self.key.algorithm() } /// The key's associated `TlsProtocolId`. @@ -174,7 +159,7 @@ impl TlsRecordSealingKey { impl Debug for TlsRecordSealingKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TlsRecordSealingKey") - .field("algorithm", &self.algorithm) + .field("key", &self.key) .field("protocol", &self.protocol) .finish() } @@ -195,8 +180,7 @@ pub struct TlsRecordOpeningKey { // The choice here was either wrap the underlying EVP_AEAD_CTX in a Mutex as done here, // or force this type to !Sync. Since this is an implementation detail of AWS-LC // we have optex to manage this behavior internally. - ctx: AeadCtx, - algorithm: &'static Algorithm, + key: UnboundKey, protocol: TlsProtocolId, } @@ -240,8 +224,7 @@ impl TlsRecordOpeningKey { ) => Err(Unspecified), }?; Ok(Self { - ctx, - algorithm, + key: UnboundKey::from(ctx), protocol, }) } @@ -254,6 +237,7 @@ impl TlsRecordOpeningKey { /// # Errors /// `error::Unspecified` when ciphertext is invalid. #[inline] + #[allow(clippy::needless_pass_by_value)] pub fn open_in_place<'in_out, A>( &self, nonce: Nonce, @@ -263,14 +247,14 @@ impl TlsRecordOpeningKey { where A: AsRef<[u8]>, { - open_within(self.algorithm, &self.ctx, nonce, aad, in_out, 0..) + self.key.open_within(nonce, aad.as_ref(), in_out, 0..) } /// The key's AEAD algorithm. #[inline] #[must_use] pub fn algorithm(&self) -> &'static Algorithm { - self.algorithm + self.key.algorithm() } /// The key's associated `TlsProtocolId`. @@ -284,7 +268,7 @@ impl TlsRecordOpeningKey { impl Debug for TlsRecordOpeningKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TlsRecordOpeningKey") - .field("algorithm", &self.algorithm) + .field("key", &self.key) .field("protocol", &self.protocol) .finish() } diff --git a/aws-lc-rs/src/aead/unbound_key.rs b/aws-lc-rs/src/aead/unbound_key.rs new file mode 100644 index 00000000000..637148e8d6f --- /dev/null +++ b/aws-lc-rs/src/aead/unbound_key.rs @@ -0,0 +1,505 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +use super::{aead_ctx::AeadCtx, Algorithm, Nonce, MAX_KEY_LEN, MAX_TAG_LEN, NONCE_LEN}; +use super::{Tag, AES_128_GCM, AES_128_GCM_SIV, AES_256_GCM, AES_256_GCM_SIV, CHACHA20_POLY1305}; +use crate::iv::FixedLength; +use crate::{error::Unspecified, fips::indicator_check, hkdf}; +use aws_lc::{ + EVP_AEAD_CTX_open, EVP_AEAD_CTX_open_gather, EVP_AEAD_CTX_seal, EVP_AEAD_CTX_seal_scatter, +}; +use core::fmt::Debug; +use core::{mem::MaybeUninit, ops::RangeFrom, ptr::null}; + +/// The maximum length of a nonce returned by our AEAD API. +const MAX_NONCE_LEN: usize = NONCE_LEN; + +/// The maximum required tag buffer needed if using AWS-LC generated nonce construction +const MAX_TAG_NONCE_BUFFER_LEN: usize = MAX_TAG_LEN + MAX_NONCE_LEN; + +/// An AEAD key without a designated role or nonce sequence. +pub struct UnboundKey { + ctx: AeadCtx, + algorithm: &'static Algorithm, +} + +#[allow(clippy::missing_fields_in_debug)] +impl Debug for UnboundKey { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_struct("UnboundKey") + .field("algorithm", &self.algorithm) + .finish() + } +} + +impl UnboundKey { + /// Constructs an `UnboundKey`. + /// # Errors + /// `error::Unspecified` if `key_bytes.len() != algorithm.key_len()`. + pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { + Ok(Self { + ctx: (algorithm.init)(key_bytes, algorithm.tag_len())?, + algorithm, + }) + } + + #[inline] + pub(crate) fn open_within<'in_out>( + &self, + nonce: Nonce, + aad: &[u8], + in_out: &'in_out mut [u8], + ciphertext_and_tag: RangeFrom, + ) -> Result<&'in_out mut [u8], Unspecified> { + let in_prefix_len = ciphertext_and_tag.start; + let ciphertext_and_tag_len = in_out.len().checked_sub(in_prefix_len).ok_or(Unspecified)?; + let ciphertext_len = ciphertext_and_tag_len + .checked_sub(self.algorithm().tag_len()) + .ok_or(Unspecified)?; + self.check_per_nonce_max_bytes(ciphertext_len)?; + + match self.ctx { + AeadCtx::AES_128_GCM_RANDNONCE(_) | AeadCtx::AES_256_GCM_RANDNONCE(_) => { + self.open_combined_randnonce(nonce, aad, &mut in_out[in_prefix_len..]) + } + _ => self.open_combined(nonce, aad.as_ref(), &mut in_out[in_prefix_len..]), + }?; + + // shift the plaintext to the left + in_out.copy_within(in_prefix_len..in_prefix_len + ciphertext_len, 0); + + // `ciphertext_len` is also the plaintext length. + Ok(&mut in_out[..ciphertext_len]) + } + + #[inline] + pub(crate) fn open_separate_gather( + &self, + nonce: &Nonce, + aad: &[u8], + in_ciphertext: &[u8], + in_tag: &[u8], + out_plaintext: &mut [u8], + ) -> Result<(), Unspecified> { + self.check_per_nonce_max_bytes(in_ciphertext.len())?; + + // ensure that the lengths match + { + let actual = in_ciphertext.len(); + let expected = out_plaintext.len(); + + if actual != expected { + return Err(Unspecified); + } + } + + unsafe { + let aead_ctx = self.ctx.as_ref(); + let nonce = nonce.as_ref(); + + if 1 != EVP_AEAD_CTX_open_gather( + *aead_ctx.as_const(), + out_plaintext.as_mut_ptr(), + nonce.as_ptr(), + nonce.len(), + in_ciphertext.as_ptr(), + in_ciphertext.len(), + in_tag.as_ptr(), + in_tag.len(), + aad.as_ptr(), + aad.len(), + ) { + return Err(Unspecified); + } + Ok(()) + } + } + + #[inline] + pub(crate) fn seal_in_place_append_tag<'a, InOut>( + &self, + nonce: Option, + aad: &[u8], + in_out: &'a mut InOut, + ) -> Result + where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + self.check_per_nonce_max_bytes(in_out.as_mut().len())?; + match nonce { + Some(nonce) => self.seal_combined(nonce, aad, in_out), + None => self.seal_combined_randnonce(aad, in_out), + } + } + + #[inline] + pub(crate) fn seal_in_place_separate_tag( + &self, + nonce: Option, + aad: &[u8], + in_out: &mut [u8], + ) -> Result<(Nonce, Tag), Unspecified> { + self.check_per_nonce_max_bytes(in_out.len())?; + match nonce { + Some(nonce) => self.seal_separate(nonce, aad, in_out), + None => self.seal_separate_randnonce(aad, in_out), + } + } + + #[inline] + #[allow(clippy::needless_pass_by_value)] + pub(crate) fn seal_in_place_separate_scatter( + &self, + nonce: Nonce, + aad: &[u8], + in_out: &mut [u8], + extra_in: &[u8], + extra_out_and_tag: &mut [u8], + ) -> Result<(), Unspecified> { + self.check_per_nonce_max_bytes(in_out.len())?; + // ensure that the extra lengths match + { + let actual = extra_in.len() + self.algorithm().tag_len(); + let expected = extra_out_and_tag.len(); + + if actual != expected { + return Err(Unspecified); + } + } + + let nonce = nonce.as_ref(); + let mut out_tag_len = extra_out_and_tag.len(); + + if 1 != unsafe { + EVP_AEAD_CTX_seal_scatter( + *self.ctx.as_ref().as_const(), + in_out.as_mut_ptr(), + extra_out_and_tag.as_mut_ptr(), + &mut out_tag_len, + extra_out_and_tag.len(), + nonce.as_ptr(), + nonce.len(), + in_out.as_ptr(), + in_out.len(), + extra_in.as_ptr(), + extra_in.len(), + aad.as_ptr(), + aad.len(), + ) + } { + return Err(Unspecified); + } + Ok(()) + } + + /// The key's AEAD algorithm. + #[inline] + #[must_use] + pub fn algorithm(&self) -> &'static Algorithm { + self.algorithm + } + + #[inline] + pub(crate) fn check_per_nonce_max_bytes(&self, in_out_len: usize) -> Result<(), Unspecified> { + if in_out_len as u64 > self.algorithm().max_input_len { + return Err(Unspecified); + } + Ok(()) + } + + #[inline] + #[allow(clippy::needless_pass_by_value)] + fn open_combined( + &self, + nonce: Nonce, + aad: &[u8], + in_out: &mut [u8], + ) -> Result<(), Unspecified> { + let nonce = nonce.as_ref(); + + debug_assert_eq!(nonce.len(), self.algorithm().nonce_len()); + + let plaintext_len = in_out.len() - self.algorithm().tag_len(); + + let mut out_len = MaybeUninit::::uninit(); + if 1 != indicator_check!(unsafe { + EVP_AEAD_CTX_open( + *self.ctx.as_ref().as_const(), + in_out.as_mut_ptr(), + out_len.as_mut_ptr(), + plaintext_len, + nonce.as_ptr(), + nonce.len(), + in_out.as_ptr(), + plaintext_len + self.algorithm().tag_len(), + aad.as_ptr(), + aad.len(), + ) + }) { + return Err(Unspecified); + } + + Ok(()) + } + + #[inline] + #[allow(clippy::needless_pass_by_value)] + fn open_combined_randnonce( + &self, + nonce: Nonce, + aad: &[u8], + in_out: &mut [u8], + ) -> Result<(), Unspecified> { + let nonce = nonce.as_ref(); + + let alg_nonce_len = self.algorithm().nonce_len(); + let alg_tag_len = self.algorithm().tag_len(); + + debug_assert_eq!(nonce.len(), alg_nonce_len); + debug_assert!(alg_tag_len + alg_nonce_len <= MAX_TAG_NONCE_BUFFER_LEN); + + let plaintext_len = in_out.len() - alg_tag_len; + + let mut tag_buffer = [0u8; MAX_TAG_NONCE_BUFFER_LEN]; + + tag_buffer[..alg_tag_len] + .copy_from_slice(&in_out[plaintext_len..plaintext_len + alg_tag_len]); + tag_buffer[alg_tag_len..alg_tag_len + alg_nonce_len].copy_from_slice(nonce); + + let tag_slice = &tag_buffer[0..alg_tag_len + alg_nonce_len]; + + if 1 != indicator_check!(unsafe { + EVP_AEAD_CTX_open_gather( + *self.ctx.as_ref().as_const(), + in_out.as_mut_ptr(), + null(), + 0, + in_out.as_ptr(), + plaintext_len, + tag_slice.as_ptr(), + tag_slice.len(), + aad.as_ptr(), + aad.len(), + ) + }) { + return Err(Unspecified); + } + + Ok(()) + } + + #[inline] + fn seal_combined( + &self, + nonce: Nonce, + aad: &[u8], + in_out: &mut InOut, + ) -> Result + where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + let plaintext_len = in_out.as_mut().len(); + + let alg_tag_len = self.algorithm().tag_len(); + + debug_assert!(alg_tag_len <= MAX_TAG_LEN); + + let tag_buffer = [0u8; MAX_TAG_LEN]; + + in_out.extend(tag_buffer[..alg_tag_len].iter()); + + let mut out_len = MaybeUninit::::uninit(); + let mut_in_out = in_out.as_mut(); + + { + let nonce = nonce.as_ref(); + + debug_assert_eq!(nonce.len(), self.algorithm().nonce_len()); + + if 1 != indicator_check!(unsafe { + EVP_AEAD_CTX_seal( + *self.ctx.as_ref().as_const(), + mut_in_out.as_mut_ptr(), + out_len.as_mut_ptr(), + plaintext_len + alg_tag_len, + nonce.as_ptr(), + nonce.len(), + mut_in_out.as_ptr(), + plaintext_len, + aad.as_ptr(), + aad.len(), + ) + }) { + return Err(Unspecified); + } + } + + Ok(nonce) + } + + #[inline] + fn seal_combined_randnonce( + &self, + aad: &[u8], + in_out: &mut InOut, + ) -> Result + where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + let mut tag_buffer = [0u8; MAX_TAG_NONCE_BUFFER_LEN]; + + let mut out_tag_len = MaybeUninit::::uninit(); + + { + let plaintext_len = in_out.as_mut().len(); + let in_out = in_out.as_mut(); + + if 1 != indicator_check!(unsafe { + EVP_AEAD_CTX_seal_scatter( + *self.ctx.as_ref().as_const(), + in_out.as_mut_ptr(), + tag_buffer.as_mut_ptr(), + out_tag_len.as_mut_ptr(), + tag_buffer.len(), + null(), + 0, + in_out.as_ptr(), + plaintext_len, + null(), + 0, + aad.as_ptr(), + aad.len(), + ) + }) { + return Err(Unspecified); + } + } + + let tag_len = self.algorithm().tag_len(); + let nonce_len = self.algorithm().nonce_len(); + + let nonce = Nonce(FixedLength::::try_from( + &tag_buffer[tag_len..tag_len + nonce_len], + )?); + + in_out.extend(&tag_buffer[..tag_len]); + + Ok(nonce) + } + + #[inline] + fn seal_separate( + &self, + nonce: Nonce, + aad: &[u8], + in_out: &mut [u8], + ) -> Result<(Nonce, Tag), Unspecified> { + let mut tag = [0u8; MAX_TAG_LEN]; + let mut out_tag_len = MaybeUninit::::uninit(); + { + let nonce = nonce.as_ref(); + + debug_assert_eq!(nonce.len(), self.algorithm().nonce_len()); + + if 1 != indicator_check!(unsafe { + EVP_AEAD_CTX_seal_scatter( + *self.ctx.as_ref().as_const(), + in_out.as_mut_ptr(), + tag.as_mut_ptr(), + out_tag_len.as_mut_ptr(), + tag.len(), + nonce.as_ptr(), + nonce.len(), + in_out.as_ptr(), + in_out.len(), + null(), + 0usize, + aad.as_ptr(), + aad.len(), + ) + }) { + return Err(Unspecified); + } + } + Ok((nonce, Tag(tag, unsafe { out_tag_len.assume_init() }))) + } + + #[inline] + fn seal_separate_randnonce( + &self, + aad: &[u8], + in_out: &mut [u8], + ) -> Result<(Nonce, Tag), Unspecified> { + let mut tag_buffer = [0u8; MAX_TAG_NONCE_BUFFER_LEN]; + + debug_assert!( + self.algorithm().tag_len() + self.algorithm().nonce_len() <= tag_buffer.len() + ); + + let mut out_tag_len = MaybeUninit::::uninit(); + + if 1 != indicator_check!(unsafe { + EVP_AEAD_CTX_seal_scatter( + *self.ctx.as_ref().as_const(), + in_out.as_mut_ptr(), + tag_buffer.as_mut_ptr(), + out_tag_len.as_mut_ptr(), + tag_buffer.len(), + null(), + 0, + in_out.as_ptr(), + in_out.len(), + null(), + 0usize, + aad.as_ptr(), + aad.len(), + ) + }) { + return Err(Unspecified); + } + + let tag_len = self.algorithm().tag_len(); + let nonce_len = self.algorithm().nonce_len(); + + let nonce = Nonce(FixedLength::::try_from( + &tag_buffer[tag_len..tag_len + nonce_len], + )?); + + let mut tag = [0u8; MAX_TAG_LEN]; + tag.copy_from_slice(&tag_buffer[..tag_len]); + + Ok((nonce, Tag(tag, tag_len))) + } +} + +impl From for UnboundKey { + fn from(value: AeadCtx) -> Self { + let algorithm = match value { + AeadCtx::AES_128_GCM(_) + | AeadCtx::AES_128_GCM_TLS12(_) + | AeadCtx::AES_128_GCM_TLS13(_) + | AeadCtx::AES_128_GCM_RANDNONCE(_) => &AES_128_GCM, + AeadCtx::AES_128_GCM_SIV(_) => &AES_128_GCM_SIV, + AeadCtx::AES_256_GCM(_) + | AeadCtx::AES_256_GCM_RANDNONCE(_) + | AeadCtx::AES_256_GCM_TLS12(_) + | AeadCtx::AES_256_GCM_TLS13(_) => &AES_256_GCM, + AeadCtx::AES_256_GCM_SIV(_) => &AES_256_GCM_SIV, + AeadCtx::CHACHA20_POLY1305(_) => &CHACHA20_POLY1305, + }; + Self { + ctx: value, + algorithm, + } + } +} + +impl From> for UnboundKey { + fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self { + let mut key_bytes = [0; MAX_KEY_LEN]; + let key_bytes = &mut key_bytes[..okm.len().key_len]; + let algorithm = *okm.len(); + okm.fill(key_bytes).unwrap(); + Self::new(algorithm, key_bytes).unwrap() + } +} diff --git a/aws-lc-rs/src/test.rs b/aws-lc-rs/src/test.rs index 7cf1063bccb..e724abd1050 100644 --- a/aws-lc-rs/src/test.rs +++ b/aws-lc-rs/src/test.rs @@ -279,6 +279,7 @@ macro_rules! test_file { } /// A test input file. +#[derive(Clone, Copy)] pub struct File<'a> { /// The name (path) of the file. pub file_name: &'a str, diff --git a/aws-lc-rs/tests/aead_test.rs b/aws-lc-rs/tests/aead_test.rs index 68476d86fc6..f39663f37ec 100644 --- a/aws-lc-rs/tests/aead_test.rs +++ b/aws-lc-rs/tests/aead_test.rs @@ -11,102 +11,62 @@ use mirai_annotations::unrecoverable; #[test] fn aead_aes_gcm_128() { - test_aead( + test_aead_all( &aead::AES_128_GCM, - seal_with_key, - open_with_key, - test_file!("data/aead_aes_128_gcm_tests.txt"), - ); - test_aead( - &aead::AES_128_GCM, - seal_with_less_safe_key, - open_with_less_safe_key, - test_file!("data/aead_aes_128_gcm_tests.txt"), - ); - test_aead( - &aead::AES_128_GCM, - seal_with_less_safe_key_scatter, - open_with_less_safe_key, test_file!("data/aead_aes_128_gcm_tests.txt"), ); } #[test] fn aead_aes_gcm_256() { - test_aead( - &aead::AES_256_GCM, - seal_with_key, - open_with_key, - test_file!("data/aead_aes_256_gcm_tests.txt"), - ); - test_aead( - &aead::AES_256_GCM, - seal_with_less_safe_key, - open_with_less_safe_key, - test_file!("data/aead_aes_256_gcm_tests.txt"), - ); - test_aead( + test_aead_all( &aead::AES_256_GCM, - seal_with_less_safe_key_scatter, - open_with_less_safe_key, test_file!("data/aead_aes_256_gcm_tests.txt"), ); } #[test] fn aead_aes_gcm_siv_256() { - test_aead( + test_aead_all( &aead::AES_256_GCM_SIV, - seal_with_key, - open_with_key, - test_file!("data/aes_256_gcm_siv_tests.txt"), - ); - test_aead( - &aead::AES_256_GCM_SIV, - seal_with_less_safe_key, - open_with_less_safe_key, test_file!("data/aes_256_gcm_siv_tests.txt"), ); } #[test] fn aead_aes_gcm_siv_128() { - test_aead( - &aead::AES_128_GCM_SIV, - seal_with_key, - open_with_key, - test_file!("data/aes_128_gcm_siv_tests.txt"), - ); - test_aead( + test_aead_all( &aead::AES_128_GCM_SIV, - seal_with_less_safe_key, - open_with_less_safe_key, test_file!("data/aes_128_gcm_siv_tests.txt"), ); } #[test] fn aead_chacha20_poly1305() { - test_aead( + test_aead_all( &aead::CHACHA20_POLY1305, - seal_with_key, - open_with_key, - test_file!("data/aead_chacha20_poly1305_tests.txt"), - ); - test_aead( - &aead::CHACHA20_POLY1305, - seal_with_less_safe_key, - open_with_less_safe_key, - test_file!("data/aead_chacha20_poly1305_tests.txt"), - ); - test_aead( - &aead::CHACHA20_POLY1305, - seal_with_less_safe_key_scatter, - open_with_less_safe_key, test_file!("data/aead_chacha20_poly1305_tests.txt"), ); } +/// Tests all combinations of sealer and opener functions +fn test_aead_all(aead_alg: &'static aead::Algorithm, test_file: test::File) { + let mut sealers = vec![seal_with_key, seal_with_less_safe_key]; + let mut openers = vec![open_with_key, open_with_less_safe_key]; + + // SIV doesn't support scatter/gather APIs + if !(aead_alg == &aead::AES_128_GCM_SIV || aead_alg == &aead::AES_256_GCM_SIV) { + sealers.push(seal_with_less_safe_key_scatter); + openers.push(open_with_less_safe_key_gather); + } + + for seal in &sealers { + for open in &openers { + test_aead(aead_alg, seal, open, test_file); + } + } +} + #[allow(clippy::too_many_lines)] fn test_aead( aead_alg: &'static aead::Algorithm, @@ -463,6 +423,27 @@ fn open_with_less_safe_key<'a>( key.open_within(nonce, aad, in_out, ciphertext_and_tag) } +fn open_with_less_safe_key_gather<'a>( + algorithm: &'static aead::Algorithm, + key: &[u8], + nonce: aead::Nonce, + aad: aead::Aad<&[u8]>, + in_out: &'a mut [u8], + ciphertext_and_tag: RangeFrom, +) -> Result<&'a mut [u8], error::Unspecified> { + let key = make_less_safe_key(algorithm, key); + + // clone the ciphertext and tag to a separate buffer, since it doesn't get modified in place + let ciphertext = in_out[ciphertext_and_tag].to_vec(); + let (in_ciphertext, in_tag) = ciphertext.split_at(ciphertext.len() - algorithm.tag_len()); + + let out_plaintext = &mut in_out[..in_ciphertext.len()]; + + key.open_separate_gather(nonce, aad, in_ciphertext, in_tag, out_plaintext)?; + + Ok(out_plaintext) +} + #[allow(clippy::range_plus_one)] fn test_aead_key_sizes(aead_alg: &'static aead::Algorithm) { let key_len = aead_alg.key_len();