diff --git a/aead/Cargo.toml b/aead/Cargo.toml index 10c425db..2665704b 100644 --- a/aead/Cargo.toml +++ b/aead/Cargo.toml @@ -17,16 +17,16 @@ such as AES-GCM as ChaCha20Poly1305, which provide a high-level API [dependencies] crypto-common = { version = "0.2.0-rc.1", path = "../crypto-common" } +inout = "0.2.0-rc.4" # optional dependencies arrayvec = { version = "0.7", optional = true, default-features = false } blobby = { version = "0.4.0-pre.0", optional = true } bytes = { version = "1", optional = true, default-features = false } heapless = { version = "0.8", optional = true, default-features = false } -inout = { version = "0.2.0-rc.4", optional = true, default-features = false } [features] -default = ["inout", "rand_core"] +default = ["rand_core"] alloc = [] dev = ["blobby"] os_rng = ["crypto-common/os_rng", "rand_core"] diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 26faef2a..b61fbf02 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -34,22 +34,18 @@ pub use bytes; pub use crypto_common::rand_core; #[cfg(feature = "heapless")] pub use heapless; -#[cfg(feature = "inout")] pub use inout; use core::fmt; -use crypto_common::array::{Array, ArraySize}; +use crypto_common::array::{Array, ArraySize, typenum::Unsigned}; +use inout::InOutBuf; #[cfg(feature = "alloc")] use alloc::vec::Vec; #[cfg(feature = "bytes")] use bytes::BytesMut; -#[cfg(any(feature = "alloc", feature = "inout"))] -use crypto_common::array::typenum::Unsigned; #[cfg(feature = "os_rng")] use crypto_common::rand_core::{OsError, OsRng, TryRngCore}; -#[cfg(feature = "inout")] -use inout::InOutBuf; #[cfg(feature = "rand_core")] use rand_core::{CryptoRng, TryCryptoRng}; @@ -77,10 +73,16 @@ pub type Nonce = Array::NonceSize>; /// Tag: authentication code which ensures ciphertexts are authentic pub type Tag = Array::TagSize>; -/// Authenticated Encryption with Associated Data (AEAD) algorithm core trait. -/// -/// Defines nonce, tag, and overhead sizes that are consumed by various other -/// `Aead*` traits. +/// Enum which specifies tag position used by an AEAD algorithm. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum TagPosition { + /// Postfix tag + Postfix, + /// Prefix tag + Prefix, +} + +/// Authenticated Encryption with Associated Data (AEAD) algorithm. pub trait AeadCore { /// The length of a nonce. type NonceSize: ArraySize; @@ -88,6 +90,9 @@ pub trait AeadCore { /// The maximum length of the tag. type TagSize: ArraySize; + /// The AEAD tag position. + const TAG_POSITION: TagPosition; + /// Generate a random nonce for this AEAD algorithm. /// /// AEAD algorithms accept a parameter to encryption/decryption called @@ -155,6 +160,94 @@ pub trait AeadCore { } } +/// In-place and inout AEAD trait which handles the authentication tag as a return value/separate parameter. +pub trait AeadInOut: AeadCore { + /// Encrypt the data in the provided [`InOutBuf`], returning the authentication tag. + fn encrypt_inout_detached( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: InOutBuf<'_, '_, u8>, + ) -> Result>; + + /// Decrypt the data in the provided [`InOutBuf`], returning an error in the event the + /// provided authentication tag is invalid for the given ciphertext (i.e. ciphertext + /// is modified/unauthentic) + fn decrypt_inout_detached( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: InOutBuf<'_, '_, u8>, + tag: &Tag, + ) -> Result<()>; + + /// Encrypt the given buffer containing a plaintext message in-place. + /// + /// The buffer must have sufficient capacity to store the ciphertext + /// message, which will always be larger than the original plaintext. + /// The exact size needed is cipher-dependent, but generally includes + /// the size of an authentication tag. + /// + /// Returns an error if the buffer has insufficient capacity to store the + /// resulting ciphertext message. + fn encrypt_in_place( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut dyn Buffer, + ) -> Result<()> { + match Self::TAG_POSITION { + TagPosition::Prefix => { + let msg_len = buffer.len(); + buffer.extend_from_slice(&Tag::::default())?; + let buffer = buffer.as_mut(); + let tag_size = Self::TagSize::USIZE; + buffer.copy_within(..msg_len, tag_size); + let (tag_dst, msg) = buffer.split_at_mut(tag_size); + let tag = self.encrypt_inout_detached(nonce, associated_data, msg.into())?; + tag_dst.copy_from_slice(&tag); + } + TagPosition::Postfix => { + let tag = + self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?; + buffer.extend_from_slice(tag.as_slice())?; + } + } + Ok(()) + } + + /// Decrypt the message in-place, returning an error in the event the + /// provided authentication tag does not match the given ciphertext. + /// + /// The buffer will be truncated to the length of the original plaintext + /// message upon success. + fn decrypt_in_place( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut dyn Buffer, + ) -> Result<()> { + let tag_size = Self::TagSize::USIZE; + let tagless_len = buffer.len().checked_sub(tag_size).ok_or(Error)?; + + match Self::TAG_POSITION { + TagPosition::Prefix => { + let (tag, msg) = buffer.as_mut().split_at_mut(tag_size); + let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); + self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?; + buffer.as_mut().copy_within(tag_size.., 0); + } + TagPosition::Postfix => { + let (msg, tag) = buffer.as_mut().split_at_mut(tagless_len); + let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); + self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?; + } + } + buffer.truncate(tagless_len); + Ok(()) + } +} + /// Authenticated Encryption with Associated Data (AEAD) algorithm. #[cfg(feature = "alloc")] pub trait Aead: AeadCore { @@ -211,70 +304,8 @@ pub trait Aead: AeadCore { ) -> Result>; } -/// In-place AEAD trait. -/// -/// This trait is both object safe and has no dependencies on `alloc` or `std`. -pub trait AeadInPlace: AeadCore { - /// Encrypt the given buffer containing a plaintext message in-place. - /// - /// The buffer must have sufficient capacity to store the ciphertext - /// message, which will always be larger than the original plaintext. - /// The exact size needed is cipher-dependent, but generally includes - /// the size of an authentication tag. - /// - /// Returns an error if the buffer has insufficient capacity to store the - /// resulting ciphertext message. - fn encrypt_in_place( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()>; - - /// Decrypt the message in-place, returning an error in the event the - /// provided authentication tag does not match the given ciphertext. - /// - /// The buffer will be truncated to the length of the original plaintext - /// message upon success. - fn decrypt_in_place( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()>; -} - -/// In-place AEAD trait which handles the authentication tag as a return value/separate parameter. -#[cfg(feature = "inout")] -pub trait AeadInOut: AeadCore { - /// Encrypt the data in the provided [`InOutBuf`], returning the authentication tag. - fn encrypt_inout_detached( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: InOutBuf<'_, '_, u8>, - ) -> Result>; - - /// Decrypt the data in the provided [`InOutBuf`], returning an error in the event the - /// provided authentication tag is invalid for the given ciphertext (i.e. ciphertext - /// is modified/unauthentic) - fn decrypt_inout_detached( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: InOutBuf<'_, '_, u8>, - tag: &Tag, - ) -> Result<()>; -} - -/// Marker trait for AEAD algorithms which append the authentication tag to the end of the -/// ciphertext message. -/// -/// This is the common convention for AEAD algorithms. -pub trait PostfixTagged {} - #[cfg(feature = "alloc")] -impl Aead for Alg { +impl Aead for T { fn encrypt<'msg, 'aad>( &self, nonce: &Nonce, @@ -299,39 +330,6 @@ impl Aead for Alg { } } -#[cfg(feature = "inout")] -impl AeadInPlace for T { - fn encrypt_in_place( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - let tag = self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?; - buffer.extend_from_slice(tag.as_slice())?; - Ok(()) - } - - fn decrypt_in_place( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - let tag_pos = buffer - .len() - .checked_sub(Self::TagSize::to_usize()) - .ok_or(Error)?; - - let (msg, tag) = buffer.as_mut().split_at_mut(tag_pos); - let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); - - self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?; - buffer.truncate(tag_pos); - Ok(()) - } -} - /// AEAD payloads (message + AAD). /// /// Combination of a message (plaintext or ciphertext) and @@ -340,7 +338,6 @@ impl AeadInPlace for T { /// /// If you don't care about AAD, you can pass a `&[u8]` as the payload to /// `encrypt`/`decrypt` and it will automatically be coerced to this type. -#[cfg(feature = "alloc")] #[derive(Debug)] pub struct Payload<'msg, 'aad> { /// Message to be encrypted/decrypted @@ -353,7 +350,6 @@ pub struct Payload<'msg, 'aad> { pub aad: &'aad [u8], } -#[cfg(feature = "alloc")] impl<'msg> From<&'msg [u8]> for Payload<'msg, '_> { fn from(msg: &'msg [u8]) -> Self { Self { msg, aad: b"" } @@ -436,11 +432,12 @@ impl Buffer for heapless::Vec { } } +#[cfg(feature = "alloc")] #[cfg(test)] mod tests { use super::*; /// Ensure that `AeadInPlace` is object-safe #[allow(dead_code)] - type DynAeadInPlace = dyn AeadInPlace; + type DynAeadInPlace = dyn Aead; } diff --git a/aead/src/stream.rs b/aead/src/stream.rs index 5ee90c5f..b965108b 100644 --- a/aead/src/stream.rs +++ b/aead/src/stream.rs @@ -6,7 +6,7 @@ #![allow(clippy::upper_case_acronyms)] -use crate::{AeadCore, AeadInPlace, Buffer, Error, Key, KeyInit, Result}; +use crate::{AeadCore, AeadInOut, Buffer, Error, Key, KeyInit, Result}; use core::ops::{AddAssign, Sub}; use crypto_common::array::{Array, ArraySize}; @@ -24,7 +24,7 @@ pub type NonceSize = /// Create a new STREAM from the provided AEAD. pub trait NewStream: StreamPrimitive where - A: AeadInPlace, + A: AeadInOut, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -49,7 +49,7 @@ where /// Deliberately immutable and stateless to permit parallel operation. pub trait StreamPrimitive where - A: AeadInPlace, + A: AeadInOut, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -157,7 +157,7 @@ macro_rules! impl_stream_object { #[derive(Debug)] pub struct $name where - A: AeadInPlace, + A: AeadInOut, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, @@ -171,7 +171,7 @@ macro_rules! impl_stream_object { impl $name where - A: AeadInPlace, + A: AeadInOut, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize,