From e753478cd42a5ccd19931073caafd060ceecf293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 17 Mar 2025 17:48:45 +0300 Subject: [PATCH 01/11] aead: mergeall AEAD traits into one `Aead` trait --- aead/Cargo.toml | 4 +- aead/src/lib.rs | 299 ++++++++++++++++++++++++--------------------- aead/src/stream.rs | 12 +- 3 files changed, 171 insertions(+), 144 deletions(-) diff --git a/aead/Cargo.toml b/aead/Cargo.toml index 10c425dba..2665704b4 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 26faef2a3..b2454032a 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}; @@ -72,22 +68,31 @@ impl fmt::Display for Error { impl core::error::Error for Error {} /// Nonce: single-use value for ensuring ciphertexts are unique -pub type Nonce = Array::NonceSize>; +pub type Nonce = Array::NonceSize>; /// Tag: authentication code which ensures ciphertexts are authentic -pub type Tag = Array::TagSize>; +pub type Tag = Array::TagSize>; + +/// Enum which specifies tag position used by an AEAD algorithm. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum TagKind { + /// Postfix tag + Postfix, + /// Prefix tag + Prefix, +} -/// Authenticated Encryption with Associated Data (AEAD) algorithm core trait. -/// -/// Defines nonce, tag, and overhead sizes that are consumed by various other -/// `Aead*` traits. -pub trait AeadCore { +/// Authenticated Encryption with Associated Data (AEAD) algorithm. +pub trait Aead { /// The length of a nonce. type NonceSize: ArraySize; /// The maximum length of the tag. type TagSize: ArraySize; + /// The AEAD tag position. + const TAG_KIND: TagKind; + /// Generate a random nonce for this AEAD algorithm. /// /// AEAD algorithms accept a parameter to encryption/decryption called @@ -153,68 +158,78 @@ pub trait AeadCore { rng.try_fill_bytes(&mut nonce)?; Ok(nonce) } -} -/// Authenticated Encryption with Associated Data (AEAD) algorithm. -#[cfg(feature = "alloc")] -pub trait Aead: AeadCore { - /// Encrypt the given plaintext payload, and return the resulting - /// ciphertext as a vector of bytes. - /// - /// The [`Payload`] type can be used to provide Additional Associated Data - /// (AAD) along with the message: this is an optional bytestring which is - /// not encrypted, but *is* authenticated along with the message. Failure - /// to pass the same AAD that was used during encryption will cause - /// decryption to fail, which is useful if you would like to "bind" the - /// ciphertext to some other identifier, like a digital signature key - /// or other identifier. - /// - /// If you don't care about AAD and just want to encrypt a plaintext - /// message, `&[u8]` will automatically be coerced into a `Payload`: - /// - /// ```nobuild - /// 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. - fn encrypt<'msg, 'aad>( + /// 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 data in the provided mutable slice, returning the authentication tag. + fn encrypt_detached( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut [u8], + ) -> Result> { + self.encrypt_inout_detached(nonce, associated_data, buffer.into()) + } + + /// Decrypt the data in the provided mutable slice, returning an error in the event the + /// provided authentication tag is invalid for the given ciphertext (i.e. ciphertext + /// is modified/unauthentic) + fn decrypt_detached( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut [u8], + tag: &Tag, + ) -> Result<()> { + self.decrypt_inout_detached(nonce, associated_data, buffer.into(), tag) + } + + /// Encrypt the given plaintext payload, returning the authentication tag and writing + /// the resulting ciphertext to `buffer_out`. + fn encrypt_b2b_detached<'msg, 'aad>( &self, nonce: &Nonce, plaintext: impl Into>, - ) -> Result>; + buffer_out: &mut [u8], + ) -> Result> { + let payload = plaintext.into(); + let inout_buf = InOutBuf::new(payload.msg, buffer_out).map_err(|_| Error)?; + self.encrypt_inout_detached(nonce, payload.aad, inout_buf) + } - /// Decrypt the given ciphertext slice, and return the resulting plaintext - /// as a vector of bytes. - /// - /// See notes on [`Aead::encrypt()`] about allowable message payloads and - /// Associated Additional Data (AAD). - /// - /// If you have no AAD, you can call this as follows: - /// - /// ```nobuild - /// 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. - fn decrypt<'msg, 'aad>( + /// Decrypt Decrypt the given ciphertext slice and write result to `buffer_out`, returning + /// an error in the event the provided authentication tag is invalid for the given ciphertext + /// (i.e. ciphertext is modified/unauthentic) or in the event if the buffers have different + /// length. + fn decrypt_b2b_detached<'msg, 'aad>( &self, nonce: &Nonce, ciphertext: impl Into>, - ) -> Result>; -} + buffer_out: &mut [u8], + tag: &Tag, + ) -> Result<()> { + let payload = ciphertext.into(); + let inout_buf = InOutBuf::new(payload.msg, buffer_out).map_err(|_| Error)?; + self.decrypt_inout_detached(nonce, payload.aad, inout_buf, tag) + } -/// 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 @@ -229,7 +244,25 @@ pub trait AeadInPlace: AeadCore { nonce: &Nonce, associated_data: &[u8], buffer: &mut dyn Buffer, - ) -> Result<()>; + ) -> Result<()> { + match Self::TAG_KIND { + TagKind::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_detached(nonce, associated_data, msg)?; + tag_dst.copy_from_slice(&tag); + } + TagKind::Postfix => { + let tag = self.encrypt_detached(nonce, associated_data, buffer.as_mut())?; + 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. @@ -241,40 +274,51 @@ pub trait AeadInPlace: AeadCore { 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 {} + ) -> Result<()> { + let tag_size = Self::TagSize::USIZE; + let tagless_len = buffer.len().checked_sub(tag_size).ok_or(Error)?; + + match Self::TAG_KIND { + TagKind::Prefix => { + let (msg, tag) = buffer.as_mut().split_at_mut(tag_size); + let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); + self.decrypt_detached(nonce, associated_data, msg, &tag)?; + buffer.as_mut().copy_within(tag_size.., 0); + } + TagKind::Postfix => { + let (msg, tag) = buffer.as_mut().split_at_mut(tagless_len); + let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); + self.decrypt_detached(nonce, associated_data, msg, &tag)?; + } + } + buffer.truncate(tagless_len); + Ok(()) + } -#[cfg(feature = "alloc")] -impl Aead for Alg { + /// Encrypt the given plaintext payload, and return the resulting + /// ciphertext as a vector of bytes. + /// + /// The [`Payload`] type can be used to provide Additional Associated Data + /// (AAD) along with the message: this is an optional bytestring which is + /// not encrypted, but *is* authenticated along with the message. Failure + /// to pass the same AAD that was used during encryption will cause + /// decryption to fail, which is useful if you would like to "bind" the + /// ciphertext to some other identifier, like a digital signature key + /// or other identifier. + /// + /// If you don't care about AAD and just want to encrypt a plaintext + /// message, `&[u8]` will automatically be coerced into a `Payload`: + /// + /// ```nobuild + /// 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: &Nonce, @@ -287,6 +331,24 @@ impl Aead for Alg { Ok(buffer) } + /// Decrypt the given ciphertext slice, and return the resulting plaintext + /// as a vector of bytes. + /// + /// See notes on [`Aead::encrypt()`] about allowable message payloads and + /// Associated Additional Data (AAD). + /// + /// If you have no AAD, you can call this as follows: + /// + /// ```nobuild + /// 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: &Nonce, @@ -299,39 +361,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 +369,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 +381,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"" } @@ -442,5 +469,5 @@ mod tests { /// 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 5ee90c5f8..4282c4145 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::{Aead, Buffer, Error, Key, KeyInit, Result}; use core::ops::{AddAssign, Sub}; use crypto_common::array::{Array, ArraySize}; @@ -19,12 +19,12 @@ pub type Nonce = Array>; /// Size of a nonce as used by a STREAM construction, sans the overhead of /// the STREAM protocol itself. pub type NonceSize = - <::NonceSize as Sub<>::NonceOverhead>>::Output; + <::NonceSize as Sub<>::NonceOverhead>>::Output; /// Create a new STREAM from the provided AEAD. pub trait NewStream: StreamPrimitive where - A: AeadInPlace, + A: Aead, 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: Aead, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -157,7 +157,7 @@ macro_rules! impl_stream_object { #[derive(Debug)] pub struct $name where - A: AeadInPlace, + A: Aead, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, @@ -171,7 +171,7 @@ macro_rules! impl_stream_object { impl $name where - A: AeadInPlace, + A: Aead, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, From a90f857abb88464a52756ab31c90515bc96d7e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 17 Mar 2025 17:53:05 +0300 Subject: [PATCH 02/11] Fix docs --- aead/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index b2454032a..9317079c0 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -137,7 +137,7 @@ pub trait Aead { /// Generate a random nonce for this AEAD algorithm using the specified [`CryptoRng`]. /// - /// See [`AeadCore::generate_nonce`] documentation for requirements for + /// See [`Aead::generate_nonce`] documentation for requirements for /// random nonces. #[cfg(feature = "rand_core")] fn generate_nonce_with_rng(rng: &mut R) -> Nonce { @@ -148,7 +148,7 @@ pub trait Aead { /// Generate a random nonce for this AEAD algorithm using the specified [`TryCryptoRng`]. /// - /// See [`AeadCore::generate_nonce`] documentation for requirements for + /// See [`Aead::generate_nonce`] documentation for requirements for /// random nonces. #[cfg(feature = "rand_core")] fn try_generate_nonce_with_rng( From d8bd99a8aba46106b1f70fcdc724984d0cfbf367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Mar 2025 00:36:23 +0300 Subject: [PATCH 03/11] Remove detached helper methods --- aead/src/lib.rs | 60 ++++--------------------------------------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 9317079c0..9eb165a13 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -178,58 +178,6 @@ pub trait Aead { tag: &Tag, ) -> Result<()>; - /// Encrypt the data in the provided mutable slice, returning the authentication tag. - fn encrypt_detached( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - ) -> Result> { - self.encrypt_inout_detached(nonce, associated_data, buffer.into()) - } - - /// Decrypt the data in the provided mutable slice, returning an error in the event the - /// provided authentication tag is invalid for the given ciphertext (i.e. ciphertext - /// is modified/unauthentic) - fn decrypt_detached( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - tag: &Tag, - ) -> Result<()> { - self.decrypt_inout_detached(nonce, associated_data, buffer.into(), tag) - } - - /// Encrypt the given plaintext payload, returning the authentication tag and writing - /// the resulting ciphertext to `buffer_out`. - fn encrypt_b2b_detached<'msg, 'aad>( - &self, - nonce: &Nonce, - plaintext: impl Into>, - buffer_out: &mut [u8], - ) -> Result> { - let payload = plaintext.into(); - let inout_buf = InOutBuf::new(payload.msg, buffer_out).map_err(|_| Error)?; - self.encrypt_inout_detached(nonce, payload.aad, inout_buf) - } - - /// Decrypt Decrypt the given ciphertext slice and write result to `buffer_out`, returning - /// an error in the event the provided authentication tag is invalid for the given ciphertext - /// (i.e. ciphertext is modified/unauthentic) or in the event if the buffers have different - /// length. - fn decrypt_b2b_detached<'msg, 'aad>( - &self, - nonce: &Nonce, - ciphertext: impl Into>, - buffer_out: &mut [u8], - tag: &Tag, - ) -> Result<()> { - let payload = ciphertext.into(); - let inout_buf = InOutBuf::new(payload.msg, buffer_out).map_err(|_| Error)?; - self.decrypt_inout_detached(nonce, payload.aad, inout_buf, tag) - } - /// Encrypt the given buffer containing a plaintext message in-place. /// /// The buffer must have sufficient capacity to store the ciphertext @@ -253,11 +201,11 @@ pub trait Aead { 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_detached(nonce, associated_data, msg)?; + let tag = self.encrypt_inout_detached(nonce, associated_data, msg.into())?; tag_dst.copy_from_slice(&tag); } TagKind::Postfix => { - let tag = self.encrypt_detached(nonce, associated_data, buffer.as_mut())?; + let tag = self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?; buffer.extend_from_slice(tag.as_slice())?; } } @@ -282,13 +230,13 @@ pub trait Aead { TagKind::Prefix => { let (msg, tag) = buffer.as_mut().split_at_mut(tag_size); let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); - self.decrypt_detached(nonce, associated_data, msg, &tag)?; + self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?; buffer.as_mut().copy_within(tag_size.., 0); } TagKind::Postfix => { let (msg, tag) = buffer.as_mut().split_at_mut(tagless_len); let tag = Tag::::try_from(&*tag).expect("tag length mismatch"); - self.decrypt_detached(nonce, associated_data, msg, &tag)?; + self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?; } } buffer.truncate(tagless_len); From 24de07d84015106e697eed637ec3fe84aec6f7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Mar 2025 00:39:04 +0300 Subject: [PATCH 04/11] Rename `TagKind` to `TagPosition` --- aead/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 9eb165a13..52d14ecce 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -75,7 +75,7 @@ pub type Tag = Array::TagSize>; /// Enum which specifies tag position used by an AEAD algorithm. #[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum TagKind { +pub enum TagPosition { /// Postfix tag Postfix, /// Prefix tag @@ -91,7 +91,7 @@ pub trait Aead { type TagSize: ArraySize; /// The AEAD tag position. - const TAG_KIND: TagKind; + const TAG_POSITION: TagPosition; /// Generate a random nonce for this AEAD algorithm. /// @@ -193,8 +193,8 @@ pub trait Aead { associated_data: &[u8], buffer: &mut dyn Buffer, ) -> Result<()> { - match Self::TAG_KIND { - TagKind::Prefix => { + match Self::TAG_POSITION { + TagPosition::Prefix => { let msg_len = buffer.len(); buffer.extend_from_slice(&Tag::::default())?; let buffer = buffer.as_mut(); @@ -204,7 +204,7 @@ pub trait Aead { let tag = self.encrypt_inout_detached(nonce, associated_data, msg.into())?; tag_dst.copy_from_slice(&tag); } - TagKind::Postfix => { + TagPosition::Postfix => { let tag = self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?; buffer.extend_from_slice(tag.as_slice())?; } @@ -226,14 +226,14 @@ pub trait Aead { let tag_size = Self::TagSize::USIZE; let tagless_len = buffer.len().checked_sub(tag_size).ok_or(Error)?; - match Self::TAG_KIND { - TagKind::Prefix => { + match Self::TAG_POSITION { + TagPosition::Prefix => { let (msg, tag) = 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); } - TagKind::Postfix => { + 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)?; From ab81592aa620036feaf545a55df7a9bf7b64dace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Mar 2025 00:41:13 +0300 Subject: [PATCH 05/11] fmt --- aead/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 52d14ecce..4e58a3261 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -205,7 +205,8 @@ pub trait Aead { tag_dst.copy_from_slice(&tag); } TagPosition::Postfix => { - let tag = self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?; + let tag = + self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?; buffer.extend_from_slice(tag.as_slice())?; } } From f2214ebf01e231cb75366d16e8e2fbd077e5e16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Mar 2025 19:50:17 +0300 Subject: [PATCH 06/11] Return the split --- aead/src/lib.rs | 139 ++++++++++++++++++++++++++++----------------- aead/src/stream.rs | 12 ++-- 2 files changed, 93 insertions(+), 58 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 4e58a3261..2a7c52e85 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -68,10 +68,10 @@ impl fmt::Display for Error { impl core::error::Error for Error {} /// Nonce: single-use value for ensuring ciphertexts are unique -pub type Nonce = Array::NonceSize>; +pub type Nonce = Array::NonceSize>; /// Tag: authentication code which ensures ciphertexts are authentic -pub type Tag = Array::TagSize>; +pub type Tag = Array::TagSize>; /// Enum which specifies tag position used by an AEAD algorithm. #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -83,7 +83,7 @@ pub enum TagPosition { } /// Authenticated Encryption with Associated Data (AEAD) algorithm. -pub trait Aead { +pub trait AeadCore { /// The length of a nonce. type NonceSize: ArraySize; @@ -137,7 +137,7 @@ pub trait Aead { /// Generate a random nonce for this AEAD algorithm using the specified [`CryptoRng`]. /// - /// See [`Aead::generate_nonce`] documentation for requirements for + /// See [`AeadCore::generate_nonce`] documentation for requirements for /// random nonces. #[cfg(feature = "rand_core")] fn generate_nonce_with_rng(rng: &mut R) -> Nonce { @@ -148,7 +148,7 @@ pub trait Aead { /// Generate a random nonce for this AEAD algorithm using the specified [`TryCryptoRng`]. /// - /// See [`Aead::generate_nonce`] documentation for requirements for + /// See [`AeadCore::generate_nonce`] documentation for requirements for /// random nonces. #[cfg(feature = "rand_core")] fn try_generate_nonce_with_rng( @@ -158,7 +158,10 @@ pub trait Aead { rng.try_fill_bytes(&mut nonce)?; Ok(nonce) } +} +/// In-place 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, @@ -177,7 +180,11 @@ pub trait Aead { buffer: InOutBuf<'_, '_, u8>, tag: &Tag, ) -> Result<()>; +} +/// Authenticated Encryption with Associated Data (AEAD) algorithm. +#[cfg(feature = "alloc")] +pub trait Aead: AeadCore { /// Encrypt the given buffer containing a plaintext message in-place. /// /// The buffer must have sufficient capacity to store the ciphertext @@ -187,6 +194,80 @@ pub trait Aead { /// /// 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<()>; + + /// Encrypt the given plaintext payload, and return the resulting + /// ciphertext as a vector of bytes. + /// + /// The [`Payload`] type can be used to provide Additional Associated Data + /// (AAD) along with the message: this is an optional bytestring which is + /// not encrypted, but *is* authenticated along with the message. Failure + /// to pass the same AAD that was used during encryption will cause + /// decryption to fail, which is useful if you would like to "bind" the + /// ciphertext to some other identifier, like a digital signature key + /// or other identifier. + /// + /// If you don't care about AAD and just want to encrypt a plaintext + /// message, `&[u8]` will automatically be coerced into a `Payload`: + /// + /// ```nobuild + /// 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. + fn encrypt<'msg, 'aad>( + &self, + nonce: &Nonce, + plaintext: impl Into>, + ) -> Result>; + + /// Decrypt the given ciphertext slice, and return the resulting plaintext + /// as a vector of bytes. + /// + /// See notes on [`Aead::encrypt()`] about allowable message payloads and + /// Associated Additional Data (AAD). + /// + /// If you have no AAD, you can call this as follows: + /// + /// ```nobuild + /// 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. + fn decrypt<'msg, 'aad>( + &self, + nonce: &Nonce, + ciphertext: impl Into>, + ) -> Result>; +} + +#[cfg(feature = "alloc")] +impl Aead for T { fn encrypt_in_place( &self, nonce: &Nonce, @@ -213,11 +294,6 @@ pub trait Aead { 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, @@ -244,30 +320,6 @@ pub trait Aead { Ok(()) } - /// Encrypt the given plaintext payload, and return the resulting - /// ciphertext as a vector of bytes. - /// - /// The [`Payload`] type can be used to provide Additional Associated Data - /// (AAD) along with the message: this is an optional bytestring which is - /// not encrypted, but *is* authenticated along with the message. Failure - /// to pass the same AAD that was used during encryption will cause - /// decryption to fail, which is useful if you would like to "bind" the - /// ciphertext to some other identifier, like a digital signature key - /// or other identifier. - /// - /// If you don't care about AAD and just want to encrypt a plaintext - /// message, `&[u8]` will automatically be coerced into a `Payload`: - /// - /// ```nobuild - /// 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: &Nonce, @@ -280,24 +332,6 @@ pub trait Aead { Ok(buffer) } - /// Decrypt the given ciphertext slice, and return the resulting plaintext - /// as a vector of bytes. - /// - /// See notes on [`Aead::encrypt()`] about allowable message payloads and - /// Associated Additional Data (AAD). - /// - /// If you have no AAD, you can call this as follows: - /// - /// ```nobuild - /// 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: &Nonce, @@ -319,6 +353,7 @@ pub trait Aead { /// 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. #[derive(Debug)] +#[cfg(feature = "alloc")] pub struct Payload<'msg, 'aad> { /// Message to be encrypted/decrypted pub msg: &'msg [u8], diff --git a/aead/src/stream.rs b/aead/src/stream.rs index 4282c4145..b965108bc 100644 --- a/aead/src/stream.rs +++ b/aead/src/stream.rs @@ -6,7 +6,7 @@ #![allow(clippy::upper_case_acronyms)] -use crate::{Aead, 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}; @@ -19,12 +19,12 @@ pub type Nonce = Array>; /// Size of a nonce as used by a STREAM construction, sans the overhead of /// the STREAM protocol itself. pub type NonceSize = - <::NonceSize as Sub<>::NonceOverhead>>::Output; + <::NonceSize as Sub<>::NonceOverhead>>::Output; /// Create a new STREAM from the provided AEAD. pub trait NewStream: StreamPrimitive where - A: Aead, + 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: Aead, + A: AeadInOut, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -157,7 +157,7 @@ macro_rules! impl_stream_object { #[derive(Debug)] pub struct $name where - A: Aead, + A: AeadInOut, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, @@ -171,7 +171,7 @@ macro_rules! impl_stream_object { impl $name where - A: Aead, + A: AeadInOut, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, From c0fd08f3b509cae14e97575986e51428d9b50079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Mar 2025 19:57:18 +0300 Subject: [PATCH 07/11] tweak code --- aead/src/lib.rs | 43 +++++++++++++++---------------------------- aead/src/stream.rs | 10 +++++----- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 2a7c52e85..c7d4aeeb7 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -183,7 +183,6 @@ pub trait AeadInOut: AeadCore { } /// Authenticated Encryption with Associated Data (AEAD) algorithm. -#[cfg(feature = "alloc")] pub trait Aead: AeadCore { /// Encrypt the given buffer containing a plaintext message in-place. /// @@ -236,11 +235,18 @@ pub trait Aead: AeadCore { /// 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: &Nonce, plaintext: impl Into>, - ) -> Result>; + ) -> Result> { + let payload = plaintext.into(); + let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize()); + buffer.extend_from_slice(payload.msg); + self.encrypt_in_place(nonce, payload.aad, &mut buffer)?; + Ok(buffer) + } /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. @@ -259,14 +265,19 @@ pub trait Aead: AeadCore { /// 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: &Nonce, ciphertext: impl Into>, - ) -> Result>; + ) -> Result> { + let payload = ciphertext.into(); + let mut buffer = Vec::from(payload.msg); + self.decrypt_in_place(nonce, payload.aad, &mut buffer)?; + Ok(buffer) + } } -#[cfg(feature = "alloc")] impl Aead for T { fn encrypt_in_place( &self, @@ -319,29 +330,6 @@ impl Aead for T { buffer.truncate(tagless_len); Ok(()) } - - fn encrypt<'msg, 'aad>( - &self, - nonce: &Nonce, - plaintext: impl Into>, - ) -> Result> { - let payload = plaintext.into(); - let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize()); - buffer.extend_from_slice(payload.msg); - self.encrypt_in_place(nonce, payload.aad, &mut buffer)?; - Ok(buffer) - } - - fn decrypt<'msg, 'aad>( - &self, - nonce: &Nonce, - ciphertext: impl Into>, - ) -> Result> { - let payload = ciphertext.into(); - let mut buffer = Vec::from(payload.msg); - self.decrypt_in_place(nonce, payload.aad, &mut buffer)?; - Ok(buffer) - } } /// AEAD payloads (message + AAD). @@ -353,7 +341,6 @@ impl Aead 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. #[derive(Debug)] -#[cfg(feature = "alloc")] pub struct Payload<'msg, 'aad> { /// Message to be encrypted/decrypted pub msg: &'msg [u8], diff --git a/aead/src/stream.rs b/aead/src/stream.rs index b965108bc..3a17704f4 100644 --- a/aead/src/stream.rs +++ b/aead/src/stream.rs @@ -6,7 +6,7 @@ #![allow(clippy::upper_case_acronyms)] -use crate::{AeadCore, AeadInOut, Buffer, Error, Key, KeyInit, Result}; +use crate::{Aead, AeadCore, 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: AeadInOut, + A: Aead, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -49,7 +49,7 @@ where /// Deliberately immutable and stateless to permit parallel operation. pub trait StreamPrimitive where - A: AeadInOut, + A: Aead, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -157,7 +157,7 @@ macro_rules! impl_stream_object { #[derive(Debug)] pub struct $name where - A: AeadInOut, + A: Aead, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, @@ -171,7 +171,7 @@ macro_rules! impl_stream_object { impl $name where - A: AeadInOut, + A: Aead, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, From 9e7b6d7945da8778bd7e341955fd7f160e6ea8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Mar 2025 22:30:49 +0300 Subject: [PATCH 08/11] fix `decrypt_in_place` --- aead/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index c7d4aeeb7..5b7ada016 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -316,7 +316,7 @@ impl Aead for T { match Self::TAG_POSITION { TagPosition::Prefix => { - let (msg, tag) = buffer.as_mut().split_at_mut(tag_size); + 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); From 3b091cf967ed0e28f8abecadd24cbaa19b6768ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Thu, 20 Mar 2025 16:30:27 +0300 Subject: [PATCH 09/11] Move in_place methods to AeadInOut --- aead/src/lib.rs | 127 ++++++++++++++++++++++----------------------- aead/src/stream.rs | 10 ++-- 2 files changed, 68 insertions(+), 69 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 5b7ada016..82407362f 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -180,10 +180,7 @@ pub trait AeadInOut: AeadCore { buffer: InOutBuf<'_, '_, u8>, tag: &Tag, ) -> Result<()>; -} -/// Authenticated Encryption with Associated Data (AEAD) algorithm. -pub trait Aead: AeadCore { /// Encrypt the given buffer containing a plaintext message in-place. /// /// The buffer must have sufficient capacity to store the ciphertext @@ -198,7 +195,26 @@ pub trait Aead: AeadCore { nonce: &Nonce, associated_data: &[u8], buffer: &mut dyn Buffer, - ) -> Result<()>; + ) -> 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. @@ -210,8 +226,31 @@ pub trait Aead: AeadCore { nonce: &Nonce, associated_data: &[u8], buffer: &mut dyn Buffer, - ) -> Result<()>; + ) -> 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 { /// Encrypt the given plaintext payload, and return the resulting /// ciphertext as a vector of bytes. /// @@ -235,18 +274,11 @@ pub trait Aead: AeadCore { /// 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: &Nonce, plaintext: impl Into>, - ) -> Result> { - let payload = plaintext.into(); - let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize()); - buffer.extend_from_slice(payload.msg); - self.encrypt_in_place(nonce, payload.aad, &mut buffer)?; - Ok(buffer) - } + ) -> Result>; /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. @@ -265,70 +297,36 @@ pub trait Aead: AeadCore { /// 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: &Nonce, ciphertext: impl Into>, - ) -> Result> { - let payload = ciphertext.into(); - let mut buffer = Vec::from(payload.msg); - self.decrypt_in_place(nonce, payload.aad, &mut buffer)?; - Ok(buffer) - } + ) -> Result>; } +#[cfg(feature = "alloc")] impl Aead for T { - fn encrypt_in_place( + fn encrypt<'msg, 'aad>( &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(()) + plaintext: impl Into>, + ) -> Result> { + let payload = plaintext.into(); + let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize()); + buffer.extend_from_slice(payload.msg); + self.encrypt_in_place(nonce, payload.aad, &mut buffer)?; + Ok(buffer) } - fn decrypt_in_place( + fn decrypt<'msg, 'aad>( &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(()) + ciphertext: impl Into>, + ) -> Result> { + let payload = ciphertext.into(); + let mut buffer = Vec::from(payload.msg); + self.decrypt_in_place(nonce, payload.aad, &mut buffer)?; + Ok(buffer) } } @@ -434,6 +432,7 @@ impl Buffer for heapless::Vec { } } +#[cfg(feature = "alloc")] #[cfg(test)] mod tests { use super::*; diff --git a/aead/src/stream.rs b/aead/src/stream.rs index 3a17704f4..27ef603ef 100644 --- a/aead/src/stream.rs +++ b/aead/src/stream.rs @@ -6,7 +6,7 @@ #![allow(clippy::upper_case_acronyms)] -use crate::{Aead, AeadCore, Buffer, Error, Key, KeyInit, Result}; +use crate::{AeadInOut, AeadCore, 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: Aead, + 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: Aead, + A: AeadInOut, A::NonceSize: Sub, NonceSize: ArraySize, { @@ -157,7 +157,7 @@ macro_rules! impl_stream_object { #[derive(Debug)] pub struct $name where - A: Aead, + A: AeadInOut, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, @@ -171,7 +171,7 @@ macro_rules! impl_stream_object { impl $name where - A: Aead, + A: AeadInOut, S: StreamPrimitive, A::NonceSize: Sub<>::NonceOverhead>, NonceSize: ArraySize, From 28e4eebc34a8b73c5c68879a10e6832cca654445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Thu, 20 Mar 2025 16:30:54 +0300 Subject: [PATCH 10/11] fmt --- aead/src/stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aead/src/stream.rs b/aead/src/stream.rs index 27ef603ef..b965108bc 100644 --- a/aead/src/stream.rs +++ b/aead/src/stream.rs @@ -6,7 +6,7 @@ #![allow(clippy::upper_case_acronyms)] -use crate::{AeadInOut, AeadCore, 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}; From 4813ff4979d4bbbaf311d56c5fe951faeca35abd Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Fri, 21 Mar 2025 15:54:06 +0300 Subject: [PATCH 11/11] Tweak `AeadInOut` description --- aead/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 82407362f..b61fbf023 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -160,7 +160,7 @@ pub trait AeadCore { } } -/// In-place AEAD trait which handles the authentication tag as a return value/separate parameter. +/// 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(