diff --git a/aead/Cargo.toml b/aead/Cargo.toml index beea5f4a2..95c278147 100644 --- a/aead/Cargo.toml +++ b/aead/Cargo.toml @@ -12,3 +12,7 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = { version = "0.12", default-features = false } + +[features] +default = ["alloc"] +alloc = [] diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 4bc0fe43b..9c16bc529 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -14,13 +14,50 @@ #![no_std] +#[cfg(feature = "alloc")] extern crate alloc; pub use generic_array; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; +// Define the default implementation for both `Aead::encrypt()` and +// `AeadMut::encrypt()`. Uses a macro to gloss over `&self` vs `&mut self`. +#[cfg(feature = "alloc")] +macro_rules! encrypt_to_postfix_tagged_vec { + ($aead:expr, $nonce:expr, $payload:expr) => {{ + let payload = $payload.into(); + let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize()); + buffer.extend_from_slice(payload.msg); + + let tag = $aead.encrypt_in_place_detached($nonce, payload.aad, &mut buffer)?; + buffer.extend_from_slice(tag.as_slice()); + Ok(buffer) + }}; +} + +// Define the default implementation for both `Aead::decrypt()` and +// `AeadMut::decrypt()`. Uses a macro to gloss over `&self` vs `&mut self`. +#[cfg(feature = "alloc")] +macro_rules! decrypt_postfix_tagged_ciphertext_to_vec { + ($aead:expr, $nonce:expr, $payload:expr) => {{ + let payload = $payload.into(); + + if payload.msg.len() < Self::TagSize::to_usize() { + return Err(Error); + } + + let tag_start = payload.msg.len() - Self::TagSize::to_usize(); + let mut buffer = Vec::from(&payload.msg[..tag_start]); + let tag = GenericArray::from_slice(&payload.msg[tag_start..]); + $aead.decrypt_in_place_detached($nonce, payload.aad, &mut buffer, tag)?; + + Ok(buffer) + }}; +} + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Error; @@ -36,7 +73,7 @@ pub trait NewAead { /// Authenticated Encryption with Associated Data (AEAD) algorithm. /// /// This trait is intended for use with stateless AEAD algorithms. The -/// `AeadMut` trait provides a stateful interface. +/// [`AeadMut`] trait provides a stateful interface. pub trait Aead { /// The length of a nonce. type NonceSize: ArrayLength; @@ -64,11 +101,27 @@ pub trait Aead { /// let plaintext = b"Top secret message, handle with care"; /// let ciphertext = cipher.encrypt(nonce, plaintext); /// ``` + /// + /// The default implementation assumes a postfix tag (ala AES-GCM, + /// AES-GCM-SIV, ChaCha20Poly1305). [`Aead`] implementations which do not + /// use a postfix tag will need to override this to correctly assemble the + /// ciphertext message. + #[cfg(feature = "alloc")] fn encrypt<'msg, 'aad>( &self, nonce: &GenericArray, plaintext: impl Into>, - ) -> Result, Error>; + ) -> Result, Error> { + encrypt_to_postfix_tagged_vec!(self, nonce, plaintext) + } + + /// Encrypt the data in-place, returning the authentication tag + fn encrypt_in_place_detached( + &self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + ) -> Result, Error>; /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. @@ -82,11 +135,30 @@ pub trait Aead { /// let ciphertext = b"..."; /// let plaintext = cipher.decrypt(nonce, ciphertext)?; /// ``` + /// + /// The default implementation assumes a postfix tag (ala AES-GCM, + /// AES-GCM-SIV, ChaCha20Poly1305). [`Aead`] implementations which do not + /// use a postfix tag will need to override this to correctly parse the + /// ciphertext message. + #[cfg(feature = "alloc")] fn decrypt<'msg, 'aad>( &self, nonce: &GenericArray, ciphertext: impl Into>, - ) -> Result, Error>; + ) -> Result, Error> { + decrypt_postfix_tagged_ciphertext_to_vec!(self, nonce, ciphertext) + } + + /// Decrypt the data in-place, returning an error in the event the provided + /// authentication tag does not match the given ciphertext (i.e. ciphertext + /// is modified/unauthentic) + fn decrypt_in_place_detached( + &self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + tag: &GenericArray, + ) -> Result<(), Error>; } /// Stateful Authenticated Encryption with Associated Data algorithm. @@ -104,22 +176,47 @@ pub trait AeadMut { /// /// See notes on [`Aead::encrypt()`] about allowable message payloads and /// Associated Additional Data (AAD). + #[cfg(feature = "alloc")] fn encrypt<'msg, 'aad>( &mut self, nonce: &GenericArray, plaintext: impl Into>, - ) -> Result, Error>; + ) -> Result, Error> { + encrypt_to_postfix_tagged_vec!(self, nonce, plaintext) + } + + /// Encrypt the data in-place, returning the authentication tag + fn encrypt_in_place_detached( + &mut self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + ) -> Result, Error>; /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. /// /// See notes on [`Aead::encrypt()`] and [`Aead::decrypt()`] about allowable /// message payloads and Associated Additional Data (AAD). + #[cfg(feature = "alloc")] fn decrypt<'msg, 'aad>( &mut self, nonce: &GenericArray, ciphertext: impl Into>, - ) -> Result, Error>; + ) -> Result, Error> { + decrypt_postfix_tagged_ciphertext_to_vec!(self, nonce, ciphertext) + } + + /// Decrypt the data in-place, returning an error in the event the provided + /// authentication tag does not match the given ciphertext (i.e. ciphertext + /// is modified/unauthentic) + fn decrypt_in_place_detached( + &mut self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + tag: &GenericArray, + ) -> Result<(), Error>; } /// A blanket implementation of the Stateful AEAD interface for Stateless @@ -131,6 +228,7 @@ impl AeadMut for Algo { /// Encrypt the given plaintext slice, and return the resulting ciphertext /// as a vector of bytes. + #[cfg(feature = "alloc")] fn encrypt<'msg, 'aad>( &mut self, nonce: &GenericArray, @@ -139,8 +237,19 @@ impl AeadMut for Algo { ::encrypt(self, nonce, plaintext) } + /// Encrypt the data in-place, returning the authentication tag + fn encrypt_in_place_detached( + &mut self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + ) -> Result, Error> { + ::encrypt_in_place_detached(self, nonce, associated_data, buffer) + } + /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. + #[cfg(feature = "alloc")] fn decrypt<'msg, 'aad>( &mut self, nonce: &GenericArray, @@ -148,6 +257,19 @@ impl AeadMut for Algo { ) -> Result, Error> { ::decrypt(self, nonce, ciphertext) } + + /// Decrypt the data in-place, returning an error in the event the provided + /// authentication tag does not match the given ciphertext (i.e. ciphertext + /// is modified/unauthentic) + fn decrypt_in_place_detached( + &mut self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + tag: &GenericArray, + ) -> Result<(), Error> { + ::decrypt_in_place_detached(self, nonce, associated_data, buffer, tag) + } } /// AEAD payloads are a combination of a message (plaintext or ciphertext)