From 0a483d176f3f0dd91ec2a37f85e96254319b9973 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, 18 Nov 2024 16:52:20 +0300 Subject: [PATCH 1/9] aead: rework traits --- Cargo.lock | 1 + aead/Cargo.toml | 1 + aead/README.md | 38 ++- aead/src/dyn_aead.rs | 201 ++++++++++++ aead/src/lib.rs | 734 ++++++++++++++++++++++--------------------- aead/src/stream.rs | 12 +- 6 files changed, 624 insertions(+), 363 deletions(-) create mode 100644 aead/src/dyn_aead.rs diff --git a/Cargo.lock b/Cargo.lock index 136d93a91..03e744fe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ dependencies = [ "bytes", "crypto-common 0.2.0-rc.1", "heapless", + "inout 0.2.0-rc.2", ] [[package]] diff --git a/aead/Cargo.toml b/aead/Cargo.toml index 9cfaa2ff9..952375860 100644 --- a/aead/Cargo.toml +++ b/aead/Cargo.toml @@ -17,6 +17,7 @@ rust-version = "1.81" [dependencies] crypto-common = "0.2.0-rc.0" +inout = "0.2.0-rc.1" # optional dependencies arrayvec = { version = "0.7", optional = true, default-features = false } diff --git a/aead/README.md b/aead/README.md index 0a0dd045a..ecbe59d7c 100644 --- a/aead/README.md +++ b/aead/README.md @@ -13,9 +13,43 @@ able to execute [chosen-ciphertext attacks]. The resulting security property, [ciphertext indistinguishability], is considered a basic requirement for modern cryptographic implementations. -See [RustCrypto/AEADs] for cipher implementations which use this trait. +See [RustCrypto/AEADs] for cipher implementations which implement traits from +this crate. -[Documentation][docs-link] +## Nonces: ⚠️ Security Warning ⚠️ + +AEAD algorithms accept a parameter to encryption/decryption called +a "nonce" which must be unique every time encryption is performed and +never repeated for the same key. The nonce is often prepended to the +ciphertext. The nonce used to produce a given ciphertext must be passed +to the decryption function in order for it to decrypt correctly. + +AEAD algorithms often fail catastrophically if nonces are ever repeated +for the same key (with SIV modes being a "misuse-resistent" exception). + +Nonces don't necessarily have to be random, but it is one strategy +which is often used in practice. + +Using random nonces runs the risk of repeating them unless the nonce +size is particularly large, e.g. 192-bit extended nonces used by the +`XChaCha20Poly1305` and `XSalsa20Poly1305` constructions. + +[NIST SP 800-38D] recommends the following for 128-bit nonces: + +> The total number of invocations of the authenticated encryption +> function shall not exceed 2^32, including all IV lengths and all +> instances of the authenticated encryption function with the given key. + +Following this guideline, only 4,294,967,296 messages with random +nonces can be encrypted under a given key. While this bound is high, +it's possible to encounter in practice, and systems which might +reach it should consider alternatives to purely random nonces, like +a counter or a combination of a random nonce + counter. + +See the [`aead-stream`] crate for a ready-made implementation of the latter. + +[NIST SP 800-38D]: https://csrc.nist.gov/publications/detail/sp/800-38d/final +[`aead-stream`]: https://docs.rs/aead-stream ## Minimum Supported Rust Version diff --git a/aead/src/dyn_aead.rs b/aead/src/dyn_aead.rs new file mode 100644 index 000000000..79a2da20e --- /dev/null +++ b/aead/src/dyn_aead.rs @@ -0,0 +1,201 @@ +use inout::{InOutBuf, InOutBufReserved}; + +use crate::{Aead, Buffer, Error, Result}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +mod sealed { + pub trait Sealed {} +} + +/// Object-safe variant of the [`Aead`] trait. +/// +/// This trait is implemented automaticlly for all types which implement the [`Aead`] trait. +pub trait DynAead: sealed::Sealed { + fn postfix_encrypt_inout<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: InOutBufReserved<'_, 'out, u8>, + ) -> Result<&'out mut [u8]>; + + fn postfix_decrypt_inout<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: InOutBuf<'_, 'out, u8>, + ) -> Result<&'out mut [u8]>; + + fn postfix_encrypt_inplace<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: &'out mut [u8], + plaintext_len: usize, + ) -> Result<&'out mut [u8]>; + + fn postfix_decrypt_inplace<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]>; + + fn postfix_encrypt_to_buf<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + plaintext: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]>; + + fn postfix_decrypt_to_buf<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + ciphertext: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]>; + + fn encrypt_to_buffer2( + &self, + nonce: &[u8], + associated_data: &[u8], + plaintext: &[u8], + buffer: &mut dyn Buffer, + ) -> Result<()>; + + fn decrypt_to_buffer2( + &self, + nonce: &[u8], + associated_data: &[u8], + ciphertext: &[u8], + buffer: &mut dyn Buffer, + ) -> Result<()>; + + #[cfg(feature = "alloc")] + fn encrypt_to_vec( + &self, + nonce: &[u8], + associated_data: &[u8], + plaintext: &[u8], + ) -> Result>; + + #[cfg(feature = "alloc")] + fn decrypt_to_vec( + &self, + nonce: &[u8], + associated_data: &[u8], + ciphertext: &[u8], + ) -> Result>; +} + +impl sealed::Sealed for T {} + +impl DynAead for T { + fn postfix_encrypt_inout<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: InOutBufReserved<'_, 'out, u8>, + ) -> Result<&'out mut [u8]> { + let nonce = nonce.try_into().map_err(|_| Error)?; + Aead::postfix_encrypt_inout(self, nonce, associated_data, buffer) + } + + fn postfix_decrypt_inout<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: InOutBuf<'_, 'out, u8>, + ) -> Result<&'out mut [u8]> { + let nonce = nonce.try_into().map_err(|_| Error)?; + Aead::postfix_decrypt_inout(self, nonce, associated_data, buffer) + } + + fn postfix_encrypt_inplace<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: &'out mut [u8], + plaintext_len: usize, + ) -> Result<&'out mut [u8]> { + let nonce = nonce.try_into().map_err(|_| Error)?; + Aead::postfix_encrypt_inplace(self, nonce, associated_data, buffer, plaintext_len) + } + + fn postfix_decrypt_inplace<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]> { + let nonce = nonce.try_into().map_err(|_| Error)?; + Aead::postfix_decrypt_inplace(self, nonce, associated_data, buffer) + } + + fn postfix_encrypt_to_buf<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + plaintext: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]> { + let nonce = nonce.try_into().map_err(|_| Error)?; + Aead::postfix_encrypt_to_buf(self, nonce, associated_data, plaintext, buffer) + } + + fn postfix_decrypt_to_buf<'out>( + &self, + nonce: &[u8], + associated_data: &[u8], + ciphertext: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]> { + let nonce = nonce.try_into().map_err(|_| Error)?; + Aead::postfix_decrypt_to_buf(self, nonce, associated_data, ciphertext, buffer) + } + + fn encrypt_to_buffer2( + &self, + nonce: &[u8], + aad: &[u8], + msg: &[u8], + buffer: &mut dyn Buffer, + ) -> Result<()> { + let nonce = nonce.try_into().map_err(|_| Error)?; + let payload = crate::Payload { aad, msg }; + Aead::encrypt_to_buffer(self, nonce, payload, buffer) + } + + fn decrypt_to_buffer2( + &self, + nonce: &[u8], + aad: &[u8], + msg: &[u8], + buffer: &mut dyn Buffer, + ) -> Result<()> { + let nonce = nonce.try_into().map_err(|_| Error)?; + let payload = crate::Payload { aad, msg }; + Aead::decrypt_to_buffer(self, nonce, payload, buffer) + } + + #[cfg(feature = "alloc")] + fn encrypt_to_vec(&self, nonce: &[u8], aad: &[u8], msg: &[u8]) -> Result> { + let nonce = nonce.try_into().map_err(|_| Error)?; + let payload = crate::Payload { aad, msg }; + Aead::encrypt_to_vec(self, nonce, payload) + } + + #[cfg(feature = "alloc")] + fn decrypt_to_vec(&self, nonce: &[u8], aad: &[u8], msg: &[u8]) -> Result> { + let nonce = nonce.try_into().map_err(|_| Error)?; + let payload = crate::Payload { aad, msg }; + Aead::decrypt_to_vec(self, nonce, payload) + } +} + +// Ensure that `DynAead` is an object-safe trait +#[allow(dead_code)] +fn foo(_: &dyn DynAead) {} diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 2a3560c21..2556b99a0 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -5,10 +5,10 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] -#![forbid(unsafe_code)] +// #![forbid(unsafe_code)] #![warn( clippy::unwrap_used, - missing_docs, + // missing_docs, rust_2018_idioms, missing_debug_implementations )] @@ -16,41 +16,48 @@ #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "bytes")] +use bytes::BytesMut; + +#[cfg(feature = "arrayvec")] +use arrayvec::ArrayVec; + #[cfg(feature = "dev")] pub mod dev; +mod dyn_aead; + +pub use dyn_aead::DynAead; + pub mod stream; pub use crypto_common::{ + self, array::{self, typenum::consts}, Key, KeyInit, KeySizeUser, }; -#[cfg(feature = "arrayvec")] -pub use arrayvec; -#[cfg(feature = "bytes")] -pub use bytes; -#[cfg(feature = "getrandom")] -pub use crypto_common::rand_core::OsRng; -#[cfg(feature = "heapless")] -pub use heapless; - #[cfg(feature = "rand_core")] pub use crypto_common::rand_core; +use inout::{InOutBuf, InOutBufReserved}; use core::fmt; use crypto_common::array::{typenum::Unsigned, Array, ArraySize}; #[cfg(feature = "alloc")] use alloc::vec::Vec; -#[cfg(feature = "bytes")] -use bytes::BytesMut; #[cfg(feature = "getrandom")] use crypto_common::getrandom; #[cfg(feature = "rand_core")] use rand_core::CryptoRngCore; -/// Error type. +/// Nonce: single-use value for ensuring ciphertexts are unique +pub type Nonce = Array::NonceSize>; + +/// Tag: authentication code which ensures ciphertexts are authentic +pub type Tag = Array::TagSize>; + +/// Authenticated Encryption with Associated Data (AEAD) error type. /// /// This type is deliberately opaque as to avoid potential side-channel /// leakage (e.g. padding oracle). @@ -68,96 +75,269 @@ 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>; - -/// 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. -pub trait AeadCore { +/// Authenticated Encryption with Associated Data (AEAD) algorithm trait. +pub trait Aead { /// The length of a nonce. type NonceSize: ArraySize; - /// The maximum length of the tag. + /// The length of a tag. type TagSize: ArraySize; - /// The upper bound amount of additional space required to support a - /// ciphertext vs. a plaintext. - type CiphertextOverhead: ArraySize + Unsigned; + /// Constant which defines whether AEAD specification appends or prepends tags. + /// + /// It influences the behavior of the [`Aead::encrypt_to_vec`], [`Aead::decrypt_to_vec`], + /// [`Aead::encrypt_to_buffer`], and [`Aead::decrypt_to_buffer`] methods. + /// + /// If the specification does not explicitly specify tag kind, we default to postfix tags. + const IS_POSTFIX: bool = true; + + /// Encrypt the [`InOutBuf`] data, returning the authentication tag. + fn detached_encrypt_inout( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: InOutBuf<'_, '_, u8>, + ) -> Result>; - /// Generate a random nonce for this AEAD algorithm. + /// Decrypt the [`InOutBuf`] data, returning an error in the event the provided + /// authentication tag does not match the given ciphertext. + fn detached_decrypt_inout( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: InOutBuf<'_, '_, u8>, + tag: &Tag, + ) -> Result<()>; + + /// Encrypt the data in-place, returning the authentication tag. + #[inline] + fn detached_encrypt_inplace( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut [u8], + ) -> Result> { + self.detached_encrypt_inout(nonce, associated_data, buffer.into()) + } + + /// Encrypt the data in-place, returning an error in the event the provided + /// authentication tag does not match the given ciphertext. + #[inline] + fn detached_decrypt_inplace( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut [u8], + tag: &Tag, + ) -> Result<()> { + self.detached_decrypt_inout(nonce, associated_data, buffer.into(), tag) + } + + /// Encrypt the data buffer-to-buffer, returning the authentication tag. + #[inline] + fn detached_encrypt_to_buf( + &self, + nonce: &Nonce, + associated_data: &[u8], + src: &[u8], + dst: &mut [u8], + ) -> Result> { + let buf = InOutBuf::new(src, dst).map_err(|_| Error)?; + self.detached_encrypt_inout(nonce, associated_data, buf) + } + + /// Encrypt the data buffer-to-buffer, returning an error in the event the provided + /// authentication tag does not match the given ciphertext. + #[inline] + fn detached_decrypt_to_buf( + &self, + nonce: &Nonce, + associated_data: &[u8], + src: &[u8], + dst: &mut [u8], + tag: &Tag, + ) -> Result<()> { + let buf = InOutBuf::new(src, dst).map_err(|_| Error)?; + self.detached_decrypt_inout(nonce, associated_data, buf, tag) + } + + /// Encrypt the [`InOutBufReserved`] data, append the authentication tag, and return + /// the resulting byte slice. /// - /// AEAD algorithms accept a parameter to encryption/decryption called - /// a "nonce" which must be unique every time encryption is performed and - /// never repeated for the same key. The nonce is often prepended to the - /// ciphertext. The nonce used to produce a given ciphertext must be passed - /// to the decryption function in order for it to decrypt correctly. + /// `buffer` should have at least [`TagSize`][Aead::TagSize] bytes of additional output + /// capacity; otherwise, the method will return an error. /// - /// Nonces don't necessarily have to be random, but it is one strategy - /// which is implemented by this function. + /// The returned byte slice is guaranteed to point to the output of `buffer`. + #[inline] + fn postfix_encrypt_inout<'out>( + &self, + nonce: &Nonce, + associated_data: &[u8], + mut buffer: InOutBufReserved<'_, 'out, u8>, + ) -> Result<&'out mut [u8]> { + let (msg, tail) = split_reserved(&mut buffer); + let tag_len = Self::TagSize::USIZE; + let tag_dst = tail.get_mut(..tag_len).ok_or(Error)?; + let res_len = msg.len() + tag_len; + let tag = self.detached_encrypt_inout(nonce, associated_data, msg)?; + tag_dst.copy_from_slice(&tag); + + let out_buf = into_out_buf2(buffer); + Ok(&mut out_buf[..res_len]) + } + + /// Decrypt the [`InOutBuf`] data, verify the appended authentication tag, and return + /// the resulting byte slice in case of success. /// - /// # ⚠️Security Warning + /// Returns an error if the provided authentication tag does not match the given ciphertext + /// or if the size of `buffer` is smaller than the tag size. /// - /// AEAD algorithms often fail catastrophically if nonces are ever repeated - /// (with SIV modes being an exception). + /// The returned byte slice is guaranteed to point to the output of `buffer`. + #[inline] + fn postfix_decrypt_inout<'out>( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: InOutBuf<'_, 'out, u8>, + ) -> Result<&'out mut [u8]> { + let tag_len = Self::TagSize::USIZE; + let ct_len = buffer.len().checked_sub(tag_len).ok_or(Error)?; + let (mut buf, tag) = buffer.split_at(ct_len); + let tag = tag.get_in().try_into().expect("tag has correct length"); + self.detached_decrypt_inout(nonce, associated_data, buf.reborrow(), tag)?; + Ok(into_out_buf(buf)) + } + + /// Encrypt the plaintext data of length `plaintext_len` residing at the beggining of `buffer` + /// in-place, append the authentication tag, and return the resulting byte slice. /// - /// Using random nonces runs the risk of repeating them unless the nonce - /// size is particularly large (e.g. 192-bit extended nonces used by the - /// `XChaCha20Poly1305` and `XSalsa20Poly1305` constructions. + /// `buffer` should have at least [`TagSize`][Aead::TagSize] bytes of additional output + /// capacity; otherwise, the method will return an error. /// - /// [NIST SP 800-38D] recommends the following: + /// The returned byte slice is guaranteed to point to `buffer`. + #[inline] + fn postfix_encrypt_inplace<'out>( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &'out mut [u8], + plaintext_len: usize, + ) -> Result<&'out mut [u8]> { + let tag_len = Self::TagSize::USIZE; + let res_len = plaintext_len + tag_len; + let buf = buffer.get_mut(..res_len).ok_or(Error)?; + let (msg, tag_dst) = buf.split_at_mut(plaintext_len); + let tag = self.detached_encrypt_inout(nonce, associated_data, msg.into())?; + tag_dst.copy_from_slice(&tag); + Ok(buf) + } + + /// Decrypt the data in `buffer` in-place, verify the appended authentication tag, and return + /// the resulting byte slice in case of success. /// - /// > The total number of invocations of the authenticated encryption - /// > function shall not exceed 2^32, including all IV lengths and all - /// > instances of the authenticated encryption function with the given key. + /// Returns an error if the provided authentication tag does not match the given ciphertext + /// or if the size of `buffer` is smaller than the tag size. /// - /// Following this guideline, only 4,294,967,296 messages with random - /// nonces can be encrypted under a given key. While this bound is high, - /// it's possible to encounter in practice, and systems which might - /// reach it should consider alternatives to purely random nonces, like - /// a counter or a combination of a random nonce + counter. + /// The returned byte slice is guaranteed to point to the output of `buffer`. + #[inline] + fn postfix_decrypt_inplace<'out>( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]> { + let tag_len = Self::TagSize::USIZE; + let ct_len = buffer.len().checked_sub(tag_len).ok_or(Error)?; + let (buf, tag) = buffer.split_at_mut(ct_len); + let tag = (&*tag).try_into().expect("tag has correct length"); + self.detached_decrypt_inout(nonce, associated_data, buf.into(), tag)?; + Ok(buf) + } + + /// Encrypt the data in `plaintext`, write resulting ciphertext to `buffer`, append + /// the authentication tag, and return the resulting byte slice. /// - /// See the [`stream`] module for a ready-made implementation of the latter. + /// `buffer` should have at least [`TagSize`][Aead::TagSize] bytes of additional capacity; + /// otherwise, the method will return an error. /// - /// [NIST SP 800-38D]: https://csrc.nist.gov/publications/detail/sp/800-38d/final - #[cfg(feature = "getrandom")] - fn generate_nonce() -> core::result::Result, getrandom::Error> - where - Nonce: Default, - { - let mut nonce = Nonce::::default(); - getrandom::getrandom(&mut nonce)?; - Ok(nonce) + /// The returned byte slice is guaranteed to point to the output of `buffer`. + #[inline] + fn postfix_encrypt_to_buf<'out>( + &self, + nonce: &Nonce, + associated_data: &[u8], + plaintext: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]> { + let tag_len = Self::TagSize::USIZE; + let res_len = plaintext.len() + tag_len; + let buf = buffer.get_mut(..res_len).ok_or(Error)?; + let (msg_dst, tag_dst) = buf.split_at_mut(plaintext.len()); + let inout_buf = InOutBuf::new(plaintext, msg_dst).expect("ct_dst has correct length"); + let tag = self.detached_encrypt_inout(nonce, associated_data, inout_buf)?; + tag_dst.copy_from_slice(&tag); + Ok(buf) } - /// Generate a random nonce for this AEAD algorithm using the specified - /// [`CryptoRngCore`]. + /// Decrypt the data in `ciphertext`, write resulting ciphertext to `buffer`, verify + /// the appended authentication tag, and return the resulting byte slice in case of success. /// - /// See [`AeadCore::generate_nonce`] documentation for requirements for - /// random nonces. - #[cfg(feature = "rand_core")] - fn generate_nonce_with_rng( - rng: &mut impl CryptoRngCore, - ) -> core::result::Result, rand_core::Error> - where - Nonce: Default, - { - let mut nonce = Nonce::::default(); - rng.try_fill_bytes(&mut nonce)?; - Ok(nonce) + /// Returns an error if the provided authentication tag does not match the given ciphertext, + /// if the size of `ciphertext` is smaller than the tag size, or if the size of `buffer` is + /// too small for resulting plaintext (i.e. it should have capacity of at least + /// `ciphertext.len() - tag_size`). + /// + /// The returned byte slice is guaranteed to point to the output of `buffer`. + #[inline] + fn postfix_decrypt_to_buf<'out>( + &self, + nonce: &Nonce, + associated_data: &[u8], + ciphertext: &[u8], + buffer: &'out mut [u8], + ) -> Result<&'out mut [u8]> { + let tag_len = Self::TagSize::USIZE; + let pt_len = ciphertext.len().checked_sub(tag_len).ok_or(Error)?; + let pt_dst = buffer.get_mut(..pt_len).ok_or(Error)?; + let (ct, tag) = ciphertext.split_at(pt_len); + let tag = tag.try_into().expect("tag has correct length"); + let buf = InOutBuf::new(ct, pt_dst).expect("buffers have the same length"); + self.detached_decrypt_inout(nonce, associated_data, buf, tag)?; + Ok(pt_dst) + } + + /// Encrypt the data in `buffer`, and append the authentication tag to it. + /// + /// `buffer` is a generic [`Buffer`] type. See the trait docs for more information. + #[inline] + fn postfix_encrypt_buffer( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut impl Buffer, + ) -> Result<()> { + let tag = self.detached_encrypt_inplace(nonce, associated_data, buffer.as_mut())?; + buffer.extend_from_slice(&tag) + } + + /// Decrypt the data in `buffer`, verify the appended authentication tag, and truncate `buffer` + /// to contain only the resulting plaintext. + /// + /// Returns an error if the provided authentication tag does not match the given ciphertext, + /// or if the length of `buffer` is smaller than the tag size. + #[inline] + fn postfix_decrypt_buffer( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut impl Buffer, + ) -> Result<()> { + let pt = self.postfix_decrypt_inplace(nonce, associated_data, buffer.as_mut())?; + let pt_len = pt.len(); + buffer.truncate(pt_len); + Ok(()) } -} -/// Authenticated Encryption with Associated Data (AEAD) algorithm. -/// -/// This trait is intended for use with stateless AEAD algorithms. The -/// [`AeadMut`] trait provides a stateful interface. -#[cfg(feature = "alloc")] -pub trait Aead: AeadCore { /// Encrypt the given plaintext payload, and return the resulting /// ciphertext as a vector of bytes. /// @@ -181,16 +361,22 @@ 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. - fn encrypt<'msg, 'aad>( + #[cfg(feature = "alloc")] + #[inline] + fn encrypt_to_vec<'msg, 'aad>( &self, nonce: &Nonce, - plaintext: impl Into>, - ) -> Result>; + pt_payload: impl Into>, + ) -> Result> { + let mut buf = Vec::new(); + self.encrypt_to_buffer(nonce, pt_payload, &mut buf)?; + Ok(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 + /// See notes on [`Aead::encrypt_to_vec()`] about allowable message payloads and /// Associated Additional Data (AAD). /// /// If you have no AAD, you can call this as follows: @@ -204,265 +390,107 @@ 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. - fn decrypt<'msg, 'aad>( + #[cfg(feature = "alloc")] + #[inline] + fn decrypt_to_vec<'msg, 'aad>( &self, nonce: &Nonce, - ciphertext: impl Into>, - ) -> Result>; -} - -/// Stateful Authenticated Encryption with Associated Data algorithm. -#[cfg(feature = "alloc")] -pub trait AeadMut: AeadCore { - /// Encrypt the given plaintext slice, and return the resulting ciphertext - /// as a vector of bytes. - /// - /// See notes on [`Aead::encrypt()`] about allowable message payloads and - /// Associated Additional Data (AAD). - fn encrypt<'msg, 'aad>( - &mut 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()`] and [`Aead::decrypt()`] about allowable - /// message payloads and Associated Additional Data (AAD). - fn decrypt<'msg, 'aad>( - &mut self, - nonce: &Nonce, - ciphertext: impl Into>, - ) -> Result>; -} - -/// Implement the `decrypt_in_place` method on [`AeadInPlace`] and -/// [`AeadMutInPlace]`, using a macro to gloss over the `&self` vs `&mut self`. -/// -/// Assumes a postfix authentication tag. AEAD ciphers which do not use a -/// postfix authentication tag will need to define their own implementation. -macro_rules! impl_decrypt_in_place { - ($aead:expr, $nonce:expr, $aad:expr, $buffer:expr) => {{ - 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"); - - $aead.decrypt_in_place_detached($nonce, $aad, msg, &tag)?; - $buffer.truncate(tag_pos); - Ok(()) - }}; -} + ct_payload: impl Into>, + ) -> Result> { + let mut buf = Vec::new(); + self.decrypt_to_buffer(nonce, ct_payload, &mut buf)?; + Ok(buf) + } -/// In-place stateless 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( + #[inline] + fn encrypt_to_buffer<'msg, 'aad, B: Buffer + ?Sized>( &self, nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, + pt_payload: impl Into>, + buffer: &mut B, ) -> Result<()> { - let tag = self.encrypt_in_place_detached(nonce, associated_data, buffer.as_mut())?; - buffer.extend_from_slice(tag.as_slice())?; + let Payload { msg: pt, aad } = pt_payload.into(); + let tag_len = Self::TagSize::USIZE; + buffer.resize(pt.len() + tag_len)?; + let (ct_dst, tag_dst) = if Self::IS_POSTFIX { + buffer.as_mut().split_at_mut(pt.len()) + } else { + buffer.as_mut().split_at_mut(tag_len) + }; + let tag = self.detached_encrypt_to_buf(nonce, aad, pt, ct_dst)?; + tag_dst.copy_from_slice(&tag); Ok(()) } - /// Encrypt the data in-place, returning the authentication tag - fn encrypt_in_place_detached( - &self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - ) -> 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( + #[inline] + fn decrypt_to_buffer<'msg, 'aad, B: Buffer + ?Sized>( &self, nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, + ct_payload: impl Into>, + buffer: &mut B, ) -> Result<()> { - impl_decrypt_in_place!(self, nonce, associated_data, buffer) + let Payload { msg: ct_tag, aad } = ct_payload.into(); + let tag_len = Self::TagSize::USIZE; + let pt_len = ct_tag.len().checked_sub(tag_len).ok_or(Error)?; + buffer.resize(pt_len)?; + let (ct, tag) = if Self::IS_POSTFIX { + ct_tag.split_at(pt_len) + } else { + ct_tag.split_at(tag_len) + }; + let tag = tag.try_into().expect("tag has correct length"); + self.detached_decrypt_to_buf(nonce, aad, ct, buffer.as_mut(), tag)?; + Ok(()) } - /// Decrypt the message 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: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - tag: &Tag, - ) -> Result<()>; -} - -/// In-place stateful AEAD trait. -/// -/// This trait is both object safe and has no dependencies on `alloc` or `std`. -pub trait AeadMutInPlace: 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. + /// Generate a random nonce for this AEAD algorithm. /// - /// Returns an error if the buffer has insufficient capacity to store the - /// resulting ciphertext message. - fn encrypt_in_place( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut impl Buffer, - ) -> Result<()> { - let tag = self.encrypt_in_place_detached(nonce, associated_data, buffer.as_mut())?; - buffer.extend_from_slice(tag.as_slice())?; - Ok(()) + /// See the crate-level documentation for requirements for random nonces. + #[cfg(feature = "getrandom")] + fn generate_nonce() -> core::result::Result, getrandom::Error> { + let mut nonce = Nonce::::default(); + getrandom::getrandom(&mut nonce)?; + Ok(nonce) } - /// Encrypt the data in-place, returning the authentication tag - fn encrypt_in_place_detached( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - ) -> Result>; - - /// Decrypt the message in-place, returning an error in the event the - /// provided authentication tag does not match the given ciphertext. + /// Generate a random nonce for this AEAD algorithm using the specified [`CryptoRngCore`]. /// - /// The buffer will be truncated to the length of the original plaintext - /// message upon success. - fn decrypt_in_place( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut impl Buffer, - ) -> Result<()> { - impl_decrypt_in_place!(self, nonce, associated_data, buffer) + /// See the crate-level documentation for requirements for random nonces. + #[cfg(feature = "rand_core")] + fn generate_nonce_with_rng( + rng: &mut impl CryptoRngCore, + ) -> core::result::Result, rand_core::Error> { + let mut nonce = Nonce::::default(); + rng.try_fill_bytes(&mut nonce)?; + Ok(nonce) } - - /// 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: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - tag: &Tag, - ) -> Result<()>; } -#[cfg(feature = "alloc")] -impl Aead for Alg { - 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) +// TODO: move to `inout` +fn split_reserved<'a>( + buf: &'a mut InOutBufReserved<'_, '_, u8>, +) -> (InOutBuf<'a, 'a, u8>, &'a mut [u8]) { + let in_len = buf.get_in_len(); + let out_len = buf.get_out_len(); + let in_ptr = buf.get_in().as_ptr(); + let out_ptr = buf.get_out().as_mut_ptr(); + unsafe { + let body = InOutBuf::from_raw(in_ptr, out_ptr, in_len); + let tail = core::slice::from_raw_parts_mut(out_ptr.add(in_len), out_len - in_len); + (body, tail) } } -#[cfg(feature = "alloc")] -impl AeadMut for Alg { - fn encrypt<'msg, 'aad>( - &mut 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>( - &mut 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) - } +fn into_out_buf<'out>(buf: InOutBuf<'_, 'out, u8>) -> &'out mut [u8] { + let out_len = buf.len(); + let (_, out_ptr) = buf.into_raw(); + unsafe { core::slice::from_raw_parts_mut(out_ptr, out_len) } } -impl AeadMutInPlace for Alg { - fn encrypt_in_place( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut impl Buffer, - ) -> Result<()> { - ::encrypt_in_place(self, nonce, associated_data, buffer) - } - - fn encrypt_in_place_detached( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - ) -> Result> { - ::encrypt_in_place_detached(self, nonce, associated_data, buffer) - } - - fn decrypt_in_place( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut impl Buffer, - ) -> Result<()> { - ::decrypt_in_place(self, nonce, associated_data, buffer) - } - - fn decrypt_in_place_detached( - &mut self, - nonce: &Nonce, - associated_data: &[u8], - buffer: &mut [u8], - tag: &Tag, - ) -> Result<()> { - ::decrypt_in_place_detached(self, nonce, associated_data, buffer, tag) - } +fn into_out_buf2<'out>(buf: InOutBufReserved<'_, 'out, u8>) -> &'out mut [u8] { + let out_len = buf.get_out_len(); + let (_, out_ptr) = buf.into_raw(); + unsafe { core::slice::from_raw_parts_mut(out_ptr, out_len) } } /// AEAD payloads (message + AAD). @@ -473,7 +501,6 @@ impl AeadMutInPlace for Alg { /// /// 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 @@ -488,6 +515,7 @@ pub struct Payload<'msg, 'aad> { #[cfg(feature = "alloc")] impl<'msg> From<&'msg [u8]> for Payload<'msg, '_> { + #[inline] fn from(msg: &'msg [u8]) -> Self { Self { msg, aad: b"" } } @@ -497,16 +525,11 @@ impl<'msg> From<&'msg [u8]> for Payload<'msg, '_> { /// /// This trait defines the set of methods needed to support in-place operations /// on a `Vec`-like data type. -pub trait Buffer: AsRef<[u8]> + AsMut<[u8]> { - /// Get the length of the buffer - fn len(&self) -> usize { - self.as_ref().len() - } - - /// Is the buffer empty? - fn is_empty(&self) -> bool { - self.as_ref().is_empty() - } +pub trait Buffer: AsMut<[u8]> { + /// Resizes buffer to the requested length. + /// + /// If buffer is smaller than `len`, fills it with zeros. Otherwise, truncates it to `len`. + fn resize(&mut self, len: usize) -> Result<()>; /// Extend this buffer from the given slice fn extend_from_slice(&mut self, other: &[u8]) -> Result<()>; @@ -517,6 +540,11 @@ pub trait Buffer: AsRef<[u8]> + AsMut<[u8]> { #[cfg(feature = "alloc")] impl Buffer for Vec { + fn resize(&mut self, len: usize) -> Result<()> { + Vec::resize(self, len, 0); + Ok(()) + } + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { Vec::extend_from_slice(self, other); Ok(()) @@ -529,12 +557,9 @@ impl Buffer for Vec { #[cfg(feature = "bytes")] impl Buffer for BytesMut { - fn len(&self) -> usize { - BytesMut::len(self) - } - - fn is_empty(&self) -> bool { - BytesMut::is_empty(self) + fn resize(&mut self, len: usize) -> Result<()> { + BytesMut::resize(self, len, 0); + Ok(()) } fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { @@ -548,18 +573,32 @@ impl Buffer for BytesMut { } #[cfg(feature = "arrayvec")] -impl Buffer for arrayvec::ArrayVec { +impl Buffer for ArrayVec { + fn resize(&mut self, len: usize) -> Result<()> { + if let Some(ext_len) = len.checked_sub(self.len()) { + let buf = &[0u8; N][..ext_len]; + self.try_extend_from_slice(buf).map_err(|_| Error) + } else { + self.truncate(len); + Ok(()) + } + } + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - arrayvec::ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error) + ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error) } fn truncate(&mut self, len: usize) { - arrayvec::ArrayVec::truncate(self, len); + ArrayVec::truncate(self, len); } } #[cfg(feature = "heapless")] impl Buffer for heapless::Vec { + fn resize(&mut self, len: usize) -> Result<()> { + heapless::Vec::resize(self, len, 0).map_err(|_| Error) + } + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { heapless::Vec::extend_from_slice(self, other).map_err(|_| Error) } @@ -568,18 +607,3 @@ impl Buffer for heapless::Vec { heapless::Vec::truncate(self, len); } } - -#[cfg(test)] -mod tests { - use super::*; - - /// Ensure that `AeadInPlace` is object-safe - #[allow(dead_code)] - type DynAeadInPlace = - dyn AeadInPlace; - - /// Ensure that `AeadMutInPlace` is object-safe - #[allow(dead_code)] - type DynAeadMutInPlace = - dyn AeadMutInPlace; -} 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 a69680850502971d50c0f69ff088ef673c2b4dca 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, 20 Nov 2024 16:26:37 +0300 Subject: [PATCH 2/9] Remove `DynAead` --- aead/src/dyn_aead.rs | 201 ------------------------------------------- aead/src/lib.rs | 4 - 2 files changed, 205 deletions(-) delete mode 100644 aead/src/dyn_aead.rs diff --git a/aead/src/dyn_aead.rs b/aead/src/dyn_aead.rs deleted file mode 100644 index 79a2da20e..000000000 --- a/aead/src/dyn_aead.rs +++ /dev/null @@ -1,201 +0,0 @@ -use inout::{InOutBuf, InOutBufReserved}; - -use crate::{Aead, Buffer, Error, Result}; - -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -mod sealed { - pub trait Sealed {} -} - -/// Object-safe variant of the [`Aead`] trait. -/// -/// This trait is implemented automaticlly for all types which implement the [`Aead`] trait. -pub trait DynAead: sealed::Sealed { - fn postfix_encrypt_inout<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: InOutBufReserved<'_, 'out, u8>, - ) -> Result<&'out mut [u8]>; - - fn postfix_decrypt_inout<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: InOutBuf<'_, 'out, u8>, - ) -> Result<&'out mut [u8]>; - - fn postfix_encrypt_inplace<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: &'out mut [u8], - plaintext_len: usize, - ) -> Result<&'out mut [u8]>; - - fn postfix_decrypt_inplace<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: &'out mut [u8], - ) -> Result<&'out mut [u8]>; - - fn postfix_encrypt_to_buf<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - plaintext: &[u8], - buffer: &'out mut [u8], - ) -> Result<&'out mut [u8]>; - - fn postfix_decrypt_to_buf<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - ciphertext: &[u8], - buffer: &'out mut [u8], - ) -> Result<&'out mut [u8]>; - - fn encrypt_to_buffer2( - &self, - nonce: &[u8], - associated_data: &[u8], - plaintext: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()>; - - fn decrypt_to_buffer2( - &self, - nonce: &[u8], - associated_data: &[u8], - ciphertext: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()>; - - #[cfg(feature = "alloc")] - fn encrypt_to_vec( - &self, - nonce: &[u8], - associated_data: &[u8], - plaintext: &[u8], - ) -> Result>; - - #[cfg(feature = "alloc")] - fn decrypt_to_vec( - &self, - nonce: &[u8], - associated_data: &[u8], - ciphertext: &[u8], - ) -> Result>; -} - -impl sealed::Sealed for T {} - -impl DynAead for T { - fn postfix_encrypt_inout<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: InOutBufReserved<'_, 'out, u8>, - ) -> Result<&'out mut [u8]> { - let nonce = nonce.try_into().map_err(|_| Error)?; - Aead::postfix_encrypt_inout(self, nonce, associated_data, buffer) - } - - fn postfix_decrypt_inout<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: InOutBuf<'_, 'out, u8>, - ) -> Result<&'out mut [u8]> { - let nonce = nonce.try_into().map_err(|_| Error)?; - Aead::postfix_decrypt_inout(self, nonce, associated_data, buffer) - } - - fn postfix_encrypt_inplace<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: &'out mut [u8], - plaintext_len: usize, - ) -> Result<&'out mut [u8]> { - let nonce = nonce.try_into().map_err(|_| Error)?; - Aead::postfix_encrypt_inplace(self, nonce, associated_data, buffer, plaintext_len) - } - - fn postfix_decrypt_inplace<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - buffer: &'out mut [u8], - ) -> Result<&'out mut [u8]> { - let nonce = nonce.try_into().map_err(|_| Error)?; - Aead::postfix_decrypt_inplace(self, nonce, associated_data, buffer) - } - - fn postfix_encrypt_to_buf<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - plaintext: &[u8], - buffer: &'out mut [u8], - ) -> Result<&'out mut [u8]> { - let nonce = nonce.try_into().map_err(|_| Error)?; - Aead::postfix_encrypt_to_buf(self, nonce, associated_data, plaintext, buffer) - } - - fn postfix_decrypt_to_buf<'out>( - &self, - nonce: &[u8], - associated_data: &[u8], - ciphertext: &[u8], - buffer: &'out mut [u8], - ) -> Result<&'out mut [u8]> { - let nonce = nonce.try_into().map_err(|_| Error)?; - Aead::postfix_decrypt_to_buf(self, nonce, associated_data, ciphertext, buffer) - } - - fn encrypt_to_buffer2( - &self, - nonce: &[u8], - aad: &[u8], - msg: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - let nonce = nonce.try_into().map_err(|_| Error)?; - let payload = crate::Payload { aad, msg }; - Aead::encrypt_to_buffer(self, nonce, payload, buffer) - } - - fn decrypt_to_buffer2( - &self, - nonce: &[u8], - aad: &[u8], - msg: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - let nonce = nonce.try_into().map_err(|_| Error)?; - let payload = crate::Payload { aad, msg }; - Aead::decrypt_to_buffer(self, nonce, payload, buffer) - } - - #[cfg(feature = "alloc")] - fn encrypt_to_vec(&self, nonce: &[u8], aad: &[u8], msg: &[u8]) -> Result> { - let nonce = nonce.try_into().map_err(|_| Error)?; - let payload = crate::Payload { aad, msg }; - Aead::encrypt_to_vec(self, nonce, payload) - } - - #[cfg(feature = "alloc")] - fn decrypt_to_vec(&self, nonce: &[u8], aad: &[u8], msg: &[u8]) -> Result> { - let nonce = nonce.try_into().map_err(|_| Error)?; - let payload = crate::Payload { aad, msg }; - Aead::decrypt_to_vec(self, nonce, payload) - } -} - -// Ensure that `DynAead` is an object-safe trait -#[allow(dead_code)] -fn foo(_: &dyn DynAead) {} diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 2556b99a0..117358bb0 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -25,10 +25,6 @@ use arrayvec::ArrayVec; #[cfg(feature = "dev")] pub mod dev; -mod dyn_aead; - -pub use dyn_aead::DynAead; - pub mod stream; pub use crypto_common::{ From 0c74a0e88b5ec8cfd0c4a072cb977dafd9ffffcd 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, 20 Nov 2024 17:27:10 +0300 Subject: [PATCH 3/9] Remove the `stream` module --- aead/src/lib.rs | 2 - aead/src/stream.rs | 311 --------------------------------------------- 2 files changed, 313 deletions(-) delete mode 100644 aead/src/stream.rs diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 117358bb0..5b26fb315 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -25,8 +25,6 @@ use arrayvec::ArrayVec; #[cfg(feature = "dev")] pub mod dev; -pub mod stream; - pub use crypto_common::{ self, array::{self, typenum::consts}, diff --git a/aead/src/stream.rs b/aead/src/stream.rs deleted file mode 100644 index 4282c4145..000000000 --- a/aead/src/stream.rs +++ /dev/null @@ -1,311 +0,0 @@ -//! Streaming AEAD support. -//! -//! See the [`aead-stream`] crate for a generic implementation of the STREAM construction. -//! -//! [`aead-stream`]: https://docs.rs/aead-stream - -#![allow(clippy::upper_case_acronyms)] - -use crate::{Aead, Buffer, Error, Key, KeyInit, Result}; -use core::ops::{AddAssign, Sub}; -use crypto_common::array::{Array, ArraySize}; - -#[cfg(feature = "alloc")] -use {crate::Payload, alloc::vec::Vec, crypto_common::array::typenum::Unsigned}; - -/// Nonce as used by a given AEAD construction and STREAM primitive. -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; - -/// Create a new STREAM from the provided AEAD. -pub trait NewStream: StreamPrimitive -where - A: Aead, - A::NonceSize: Sub, - NonceSize: ArraySize, -{ - /// Create a new STREAM with the given key and nonce. - fn new(key: &Key, nonce: &Nonce) -> Self - where - A: KeyInit, - Self: Sized, - { - Self::from_aead(A::new(key), nonce) - } - - /// Create a new STREAM from the given AEAD cipher. - fn from_aead(aead: A, nonce: &Nonce) -> Self; -} - -/// Low-level STREAM implementation. -/// -/// This trait provides a particular "flavor" of STREAM, as there are -/// different ways the specifics of the construction can be implemented. -/// -/// Deliberately immutable and stateless to permit parallel operation. -pub trait StreamPrimitive -where - A: Aead, - A::NonceSize: Sub, - NonceSize: ArraySize, -{ - /// Number of bytes this STREAM primitive requires from the nonce. - type NonceOverhead: ArraySize; - - /// Type used as the STREAM counter. - type Counter: AddAssign + Copy + Default + Eq; - - /// Value to use when incrementing the STREAM counter (i.e. one) - const COUNTER_INCR: Self::Counter; - - /// Maximum value of the STREAM counter. - const COUNTER_MAX: Self::Counter; - - /// Encrypt an AEAD message in-place at the given position in the STREAM. - fn encrypt_in_place( - &self, - position: Self::Counter, - last_block: bool, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()>; - - /// Decrypt an AEAD message in-place at the given position in the STREAM. - fn decrypt_in_place( - &self, - position: Self::Counter, - last_block: bool, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()>; - - /// Encrypt the given plaintext payload, and return the resulting - /// ciphertext as a vector of bytes. - #[cfg(feature = "alloc")] - fn encrypt<'msg, 'aad>( - &self, - position: Self::Counter, - last_block: bool, - plaintext: impl Into>, - ) -> Result> { - let payload = plaintext.into(); - let mut buffer = Vec::with_capacity(payload.msg.len() + A::TagSize::to_usize()); - buffer.extend_from_slice(payload.msg); - self.encrypt_in_place(position, last_block, payload.aad, &mut buffer)?; - Ok(buffer) - } - - /// Decrypt the given ciphertext slice, and return the resulting plaintext - /// as a vector of bytes. - #[cfg(feature = "alloc")] - fn decrypt<'msg, 'aad>( - &self, - position: Self::Counter, - last_block: bool, - ciphertext: impl Into>, - ) -> Result> { - let payload = ciphertext.into(); - let mut buffer = Vec::from(payload.msg); - self.decrypt_in_place(position, last_block, payload.aad, &mut buffer)?; - Ok(buffer) - } - - /// Obtain [`Encryptor`] for this [`StreamPrimitive`]. - fn encryptor(self) -> Encryptor - where - Self: Sized, - { - Encryptor::from_stream_primitive(self) - } - - /// Obtain [`Decryptor`] for this [`StreamPrimitive`]. - fn decryptor(self) -> Decryptor - where - Self: Sized, - { - Decryptor::from_stream_primitive(self) - } -} - -/// Implement a stateful STREAM object (i.e. encryptor or decryptor) -macro_rules! impl_stream_object { - ( - $name:ident, - $next_method:tt, - $next_in_place_method:tt, - $last_method:tt, - $last_in_place_method:tt, - $op:tt, - $in_place_op:tt, - $op_desc:expr, - $obj_desc:expr - ) => { - #[doc = "Stateful STREAM object which can"] - #[doc = $op_desc] - #[doc = "AEAD messages one-at-a-time."] - #[doc = ""] - #[doc = "This corresponds to the "] - #[doc = $obj_desc] - #[doc = "object as defined in the paper"] - #[doc = "[Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance][1]."] - #[doc = ""] - #[doc = "[1]: https://eprint.iacr.org/2015/189.pdf"] - #[derive(Debug)] - pub struct $name - where - A: Aead, - S: StreamPrimitive, - A::NonceSize: Sub<>::NonceOverhead>, - NonceSize: ArraySize, - { - /// Underlying STREAM primitive. - stream: S, - - /// Current position in the STREAM. - position: S::Counter, - } - - impl $name - where - A: Aead, - S: StreamPrimitive, - A::NonceSize: Sub<>::NonceOverhead>, - NonceSize: ArraySize, - { - #[doc = "Create a"] - #[doc = $obj_desc] - #[doc = "object from the given AEAD key and nonce."] - pub fn new(key: &Key, nonce: &Nonce) -> Self - where - A: KeyInit, - S: NewStream, - { - Self::from_stream_primitive(S::new(key, nonce)) - } - - #[doc = "Create a"] - #[doc = $obj_desc] - #[doc = "object from the given AEAD primitive."] - pub fn from_aead(aead: A, nonce: &Nonce) -> Self - where - A: KeyInit, - S: NewStream, - { - Self::from_stream_primitive(S::from_aead(aead, nonce)) - } - - #[doc = "Create a"] - #[doc = $obj_desc] - #[doc = "object from the given STREAM primitive."] - pub fn from_stream_primitive(stream: S) -> Self { - Self { - stream, - position: Default::default(), - } - } - - #[doc = "Use the underlying AEAD to"] - #[doc = $op_desc] - #[doc = "the next AEAD message in this STREAM, returning the"] - #[doc = "result as a [`Vec`]."] - #[cfg(feature = "alloc")] - pub fn $next_method<'msg, 'aad>( - &mut self, - payload: impl Into>, - ) -> Result> { - if self.position == S::COUNTER_MAX { - // Counter overflow. Note that the maximum counter value is - // deliberately disallowed, as it would preclude being able - // to encrypt a last block (i.e. with `$last_in_place_method`) - return Err(Error); - } - - let result = self.stream.$op(self.position, false, payload)?; - - // Note: overflow checked above - self.position += S::COUNTER_INCR; - Ok(result) - } - - #[doc = "Use the underlying AEAD to"] - #[doc = $op_desc] - #[doc = "the next AEAD message in this STREAM in-place."] - pub fn $next_in_place_method( - &mut self, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - if self.position == S::COUNTER_MAX { - // Counter overflow. Note that the maximum counter value is - // deliberately disallowed, as it would preclude being able - // to encrypt a last block (i.e. with `$last_in_place_method`) - return Err(Error); - } - - self.stream - .$in_place_op(self.position, false, associated_data, buffer)?; - - // Note: overflow checked above - self.position += S::COUNTER_INCR; - Ok(()) - } - - #[doc = "Use the underlying AEAD to"] - #[doc = $op_desc] - #[doc = "the last AEAD message in this STREAM,"] - #[doc = "consuming the "] - #[doc = $obj_desc] - #[doc = "object in order to prevent further use."] - #[cfg(feature = "alloc")] - pub fn $last_method<'msg, 'aad>( - self, - payload: impl Into>, - ) -> Result> { - self.stream.$op(self.position, true, payload) - } - - #[doc = "Use the underlying AEAD to"] - #[doc = $op_desc] - #[doc = "the last AEAD message in this STREAM in-place,"] - #[doc = "consuming the "] - #[doc = $obj_desc] - #[doc = "object in order to prevent further use."] - pub fn $last_in_place_method( - self, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - self.stream - .$in_place_op(self.position, true, associated_data, buffer) - } - } - }; -} - -impl_stream_object!( - Encryptor, - encrypt_next, - encrypt_next_in_place, - encrypt_last, - encrypt_last_in_place, - encrypt, - encrypt_in_place, - "encrypt", - "ℰ STREAM encryptor" -); - -impl_stream_object!( - Decryptor, - decrypt_next, - decrypt_next_in_place, - decrypt_last, - decrypt_last_in_place, - decrypt, - decrypt_in_place, - "decrypt", - "𝒟 STREAM decryptor" -); From 472a462329aa15f113a329c723849df271f6a3f5 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, 20 Nov 2024 20:20:58 +0300 Subject: [PATCH 4/9] use `into_out` methods --- Cargo.lock | 12 ++++++++++-- aead/Cargo.toml | 2 +- aead/src/lib.rs | 19 +++---------------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03e744fe1..1ddc0ee1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ "bytes", "crypto-common 0.2.0-rc.1", "heapless", - "inout 0.2.0-rc.2", + "inout 0.2.0-rc.2 (git+https://github.com/RustCrypto/utils)", ] [[package]] @@ -223,7 +223,7 @@ version = "0.5.0-pre.7" dependencies = [ "blobby", "crypto-common 0.2.0-rc.1", - "inout 0.2.0-rc.2", + "inout 0.2.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize", ] @@ -736,6 +736,14 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "inout" +version = "0.2.0-rc.2" +source = "git+https://github.com/RustCrypto/utils#8bb6c50988ba6c32d92e4a8e640027ab3c0920cd" +dependencies = [ + "hybrid-array", +] + [[package]] name = "itoa" version = "1.0.11" diff --git a/aead/Cargo.toml b/aead/Cargo.toml index 952375860..c1315371f 100644 --- a/aead/Cargo.toml +++ b/aead/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.81" [dependencies] crypto-common = "0.2.0-rc.0" -inout = "0.2.0-rc.1" +inout = { version = "0.2.0-rc.1", git = "https://github.com/RustCrypto/utils" } # optional dependencies arrayvec = { version = "0.7", optional = true, default-features = false } diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 5b26fb315..9c0dcc4ab 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -175,9 +175,8 @@ pub trait Aead { let res_len = msg.len() + tag_len; let tag = self.detached_encrypt_inout(nonce, associated_data, msg)?; tag_dst.copy_from_slice(&tag); - - let out_buf = into_out_buf2(buffer); - Ok(&mut out_buf[..res_len]) + let res = &mut buffer.into_out()[..res_len]; + Ok(res) } /// Decrypt the [`InOutBuf`] data, verify the appended authentication tag, and return @@ -199,7 +198,7 @@ pub trait Aead { let (mut buf, tag) = buffer.split_at(ct_len); let tag = tag.get_in().try_into().expect("tag has correct length"); self.detached_decrypt_inout(nonce, associated_data, buf.reborrow(), tag)?; - Ok(into_out_buf(buf)) + Ok(buf.into_out()) } /// Encrypt the plaintext data of length `plaintext_len` residing at the beggining of `buffer` @@ -475,18 +474,6 @@ fn split_reserved<'a>( } } -fn into_out_buf<'out>(buf: InOutBuf<'_, 'out, u8>) -> &'out mut [u8] { - let out_len = buf.len(); - let (_, out_ptr) = buf.into_raw(); - unsafe { core::slice::from_raw_parts_mut(out_ptr, out_len) } -} - -fn into_out_buf2<'out>(buf: InOutBufReserved<'_, 'out, u8>) -> &'out mut [u8] { - let out_len = buf.get_out_len(); - let (_, out_ptr) = buf.into_raw(); - unsafe { core::slice::from_raw_parts_mut(out_ptr, out_len) } -} - /// AEAD payloads (message + AAD). /// /// Combination of a message (plaintext or ciphertext) and From aca70a0a18cb271bd4d507b298892b035190e810 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, 20 Nov 2024 20:39:35 +0300 Subject: [PATCH 5/9] Use `split_reserved` from `inout` and restore `forbid(unsafe_code)` --- Cargo.lock | 2 +- aead/src/lib.rs | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ddc0ee1e..ac9117c1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,7 +739,7 @@ dependencies = [ [[package]] name = "inout" version = "0.2.0-rc.2" -source = "git+https://github.com/RustCrypto/utils#8bb6c50988ba6c32d92e4a8e640027ab3c0920cd" +source = "git+https://github.com/RustCrypto/utils#794835232842098d2003acb5b12f8218e5a1fe01" dependencies = [ "hybrid-array", ] diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 9c0dcc4ab..c8e26a52f 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -5,7 +5,7 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] -// #![forbid(unsafe_code)] +#![forbid(unsafe_code)] #![warn( clippy::unwrap_used, // missing_docs, @@ -169,7 +169,7 @@ pub trait Aead { associated_data: &[u8], mut buffer: InOutBufReserved<'_, 'out, u8>, ) -> Result<&'out mut [u8]> { - let (msg, tail) = split_reserved(&mut buffer); + let (msg, tail) = buffer.split_reserved(); let tag_len = Self::TagSize::USIZE; let tag_dst = tail.get_mut(..tag_len).ok_or(Error)?; let res_len = msg.len() + tag_len; @@ -459,21 +459,6 @@ pub trait Aead { } } -// TODO: move to `inout` -fn split_reserved<'a>( - buf: &'a mut InOutBufReserved<'_, '_, u8>, -) -> (InOutBuf<'a, 'a, u8>, &'a mut [u8]) { - let in_len = buf.get_in_len(); - let out_len = buf.get_out_len(); - let in_ptr = buf.get_in().as_ptr(); - let out_ptr = buf.get_out().as_mut_ptr(); - unsafe { - let body = InOutBuf::from_raw(in_ptr, out_ptr, in_len); - let tail = core::slice::from_raw_parts_mut(out_ptr.add(in_len), out_len - in_len); - (body, tail) - } -} - /// AEAD payloads (message + AAD). /// /// Combination of a message (plaintext or ciphertext) and From a917ee3b4feb44673132ef4ad2d41f967c339f37 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, 20 Nov 2024 20:48:23 +0300 Subject: [PATCH 6/9] Move `Buffer` into a separate module --- aead/src/buffer.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++ aead/src/lib.rs | 96 ++------------------------------------------- 2 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 aead/src/buffer.rs diff --git a/aead/src/buffer.rs b/aead/src/buffer.rs new file mode 100644 index 000000000..8e983aea4 --- /dev/null +++ b/aead/src/buffer.rs @@ -0,0 +1,97 @@ +use crate::{Error, Result}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +#[cfg(feature = "bytes")] +use bytes::BytesMut; + +#[cfg(feature = "arrayvec")] +use arrayvec::ArrayVec; + +/// In-place encryption/decryption byte buffers. +/// +/// This trait defines the set of methods needed to support in-place operations +/// on a `Vec`-like data type. +pub trait Buffer: AsMut<[u8]> { + /// Resizes buffer to the requested length. + /// + /// If buffer is smaller than `len`, fills it with zeros. Otherwise, truncates it to `len`. + fn resize(&mut self, len: usize) -> Result<()>; + + /// Extend this buffer from the given slice + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()>; + + /// Truncate this buffer to the given size + fn truncate(&mut self, len: usize); +} + +#[cfg(feature = "alloc")] +impl Buffer for Vec { + fn resize(&mut self, len: usize) -> Result<()> { + Vec::resize(self, len, 0); + Ok(()) + } + + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { + Vec::extend_from_slice(self, other); + Ok(()) + } + + fn truncate(&mut self, len: usize) { + Vec::truncate(self, len); + } +} + +#[cfg(feature = "bytes")] +impl Buffer for BytesMut { + fn resize(&mut self, len: usize) -> Result<()> { + BytesMut::resize(self, len, 0); + Ok(()) + } + + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { + BytesMut::extend_from_slice(self, other); + Ok(()) + } + + fn truncate(&mut self, len: usize) { + BytesMut::truncate(self, len); + } +} + +#[cfg(feature = "arrayvec")] +impl Buffer for ArrayVec { + fn resize(&mut self, len: usize) -> Result<()> { + if let Some(ext_len) = len.checked_sub(self.len()) { + let buf = &[0u8; N][..ext_len]; + self.try_extend_from_slice(buf).map_err(|_| Error) + } else { + self.truncate(len); + Ok(()) + } + } + + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { + ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error) + } + + fn truncate(&mut self, len: usize) { + ArrayVec::truncate(self, len); + } +} + +#[cfg(feature = "heapless")] +impl Buffer for heapless::Vec { + fn resize(&mut self, len: usize) -> Result<()> { + heapless::Vec::resize(self, len, 0).map_err(|_| Error) + } + + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { + heapless::Vec::extend_from_slice(self, other).map_err(|_| Error) + } + + fn truncate(&mut self, len: usize) { + heapless::Vec::truncate(self, len); + } +} diff --git a/aead/src/lib.rs b/aead/src/lib.rs index c8e26a52f..4fe253b3b 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -16,15 +16,12 @@ #[cfg(feature = "alloc")] extern crate alloc; -#[cfg(feature = "bytes")] -use bytes::BytesMut; - -#[cfg(feature = "arrayvec")] -use arrayvec::ArrayVec; - #[cfg(feature = "dev")] pub mod dev; +mod buffer; +pub use buffer::Buffer; + pub use crypto_common::{ self, array::{self, typenum::consts}, @@ -486,90 +483,3 @@ impl<'msg> From<&'msg [u8]> for Payload<'msg, '_> { Self { msg, aad: b"" } } } - -/// In-place encryption/decryption byte buffers. -/// -/// This trait defines the set of methods needed to support in-place operations -/// on a `Vec`-like data type. -pub trait Buffer: AsMut<[u8]> { - /// Resizes buffer to the requested length. - /// - /// If buffer is smaller than `len`, fills it with zeros. Otherwise, truncates it to `len`. - fn resize(&mut self, len: usize) -> Result<()>; - - /// Extend this buffer from the given slice - fn extend_from_slice(&mut self, other: &[u8]) -> Result<()>; - - /// Truncate this buffer to the given size - fn truncate(&mut self, len: usize); -} - -#[cfg(feature = "alloc")] -impl Buffer for Vec { - fn resize(&mut self, len: usize) -> Result<()> { - Vec::resize(self, len, 0); - Ok(()) - } - - fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - Vec::extend_from_slice(self, other); - Ok(()) - } - - fn truncate(&mut self, len: usize) { - Vec::truncate(self, len); - } -} - -#[cfg(feature = "bytes")] -impl Buffer for BytesMut { - fn resize(&mut self, len: usize) -> Result<()> { - BytesMut::resize(self, len, 0); - Ok(()) - } - - fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - BytesMut::extend_from_slice(self, other); - Ok(()) - } - - fn truncate(&mut self, len: usize) { - BytesMut::truncate(self, len); - } -} - -#[cfg(feature = "arrayvec")] -impl Buffer for ArrayVec { - fn resize(&mut self, len: usize) -> Result<()> { - if let Some(ext_len) = len.checked_sub(self.len()) { - let buf = &[0u8; N][..ext_len]; - self.try_extend_from_slice(buf).map_err(|_| Error) - } else { - self.truncate(len); - Ok(()) - } - } - - fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error) - } - - fn truncate(&mut self, len: usize) { - ArrayVec::truncate(self, len); - } -} - -#[cfg(feature = "heapless")] -impl Buffer for heapless::Vec { - fn resize(&mut self, len: usize) -> Result<()> { - heapless::Vec::resize(self, len, 0).map_err(|_| Error) - } - - fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - heapless::Vec::extend_from_slice(self, other).map_err(|_| Error) - } - - fn truncate(&mut self, len: usize) { - heapless::Vec::truncate(self, len); - } -} From 1e55bec6044b5c32a45143024a25b24f2064d58b 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, 20 Nov 2024 20:55:07 +0300 Subject: [PATCH 7/9] Reduce code duplication --- aead/src/lib.rs | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 4fe253b3b..92154440b 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -213,13 +213,8 @@ pub trait Aead { buffer: &'out mut [u8], plaintext_len: usize, ) -> Result<&'out mut [u8]> { - let tag_len = Self::TagSize::USIZE; - let res_len = plaintext_len + tag_len; - let buf = buffer.get_mut(..res_len).ok_or(Error)?; - let (msg, tag_dst) = buf.split_at_mut(plaintext_len); - let tag = self.detached_encrypt_inout(nonce, associated_data, msg.into())?; - tag_dst.copy_from_slice(&tag); - Ok(buf) + let buffer = InOutBufReserved::from_mut_slice(buffer, plaintext_len).map_err(|_| Error)?; + self.postfix_encrypt_inout(nonce, associated_data, buffer) } /// Decrypt the data in `buffer` in-place, verify the appended authentication tag, and return @@ -236,12 +231,7 @@ pub trait Aead { associated_data: &[u8], buffer: &'out mut [u8], ) -> Result<&'out mut [u8]> { - let tag_len = Self::TagSize::USIZE; - let ct_len = buffer.len().checked_sub(tag_len).ok_or(Error)?; - let (buf, tag) = buffer.split_at_mut(ct_len); - let tag = (&*tag).try_into().expect("tag has correct length"); - self.detached_decrypt_inout(nonce, associated_data, buf.into(), tag)?; - Ok(buf) + self.postfix_decrypt_inout(nonce, associated_data, buffer.into()) } /// Encrypt the data in `plaintext`, write resulting ciphertext to `buffer`, append @@ -259,14 +249,8 @@ pub trait Aead { plaintext: &[u8], buffer: &'out mut [u8], ) -> Result<&'out mut [u8]> { - let tag_len = Self::TagSize::USIZE; - let res_len = plaintext.len() + tag_len; - let buf = buffer.get_mut(..res_len).ok_or(Error)?; - let (msg_dst, tag_dst) = buf.split_at_mut(plaintext.len()); - let inout_buf = InOutBuf::new(plaintext, msg_dst).expect("ct_dst has correct length"); - let tag = self.detached_encrypt_inout(nonce, associated_data, inout_buf)?; - tag_dst.copy_from_slice(&tag); - Ok(buf) + let buffer = InOutBufReserved::from_slices(plaintext, buffer).map_err(|_| Error)?; + self.postfix_encrypt_inout(nonce, associated_data, buffer) } /// Decrypt the data in `ciphertext`, write resulting ciphertext to `buffer`, verify @@ -286,14 +270,8 @@ pub trait Aead { ciphertext: &[u8], buffer: &'out mut [u8], ) -> Result<&'out mut [u8]> { - let tag_len = Self::TagSize::USIZE; - let pt_len = ciphertext.len().checked_sub(tag_len).ok_or(Error)?; - let pt_dst = buffer.get_mut(..pt_len).ok_or(Error)?; - let (ct, tag) = ciphertext.split_at(pt_len); - let tag = tag.try_into().expect("tag has correct length"); - let buf = InOutBuf::new(ct, pt_dst).expect("buffers have the same length"); - self.detached_decrypt_inout(nonce, associated_data, buf, tag)?; - Ok(pt_dst) + let buffer = InOutBuf::new(ciphertext, buffer).map_err(|_| Error)?; + self.postfix_decrypt_inout(nonce, associated_data, buffer) } /// Encrypt the data in `buffer`, and append the authentication tag to it. From 0809a57d7eed51b5dbf59a0cb0e0b56f75a67173 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, 20 Nov 2024 20:57:54 +0300 Subject: [PATCH 8/9] Fix compilation error --- aead/src/buffer.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aead/src/buffer.rs b/aead/src/buffer.rs index 8e983aea4..396d2db7e 100644 --- a/aead/src/buffer.rs +++ b/aead/src/buffer.rs @@ -1,4 +1,4 @@ -use crate::{Error, Result}; +use crate::Result; #[cfg(feature = "alloc")] use alloc::vec::Vec; @@ -65,7 +65,7 @@ impl Buffer for ArrayVec { fn resize(&mut self, len: usize) -> Result<()> { if let Some(ext_len) = len.checked_sub(self.len()) { let buf = &[0u8; N][..ext_len]; - self.try_extend_from_slice(buf).map_err(|_| Error) + self.try_extend_from_slice(buf).map_err(|_| crate::Error) } else { self.truncate(len); Ok(()) @@ -73,7 +73,7 @@ impl Buffer for ArrayVec { } fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error) + ArrayVec::try_extend_from_slice(self, other).map_err(|_| crate::Error) } fn truncate(&mut self, len: usize) { @@ -84,11 +84,11 @@ impl Buffer for ArrayVec { #[cfg(feature = "heapless")] impl Buffer for heapless::Vec { fn resize(&mut self, len: usize) -> Result<()> { - heapless::Vec::resize(self, len, 0).map_err(|_| Error) + heapless::Vec::resize(self, len, 0).map_err(|_| crate::Error) } fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - heapless::Vec::extend_from_slice(self, other).map_err(|_| Error) + heapless::Vec::extend_from_slice(self, other).map_err(|_| crate::Error) } fn truncate(&mut self, len: usize) { From eb1c7d77552be85902014b595bfa1a8bf864fa2f 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, 20 Nov 2024 21:09:35 +0300 Subject: [PATCH 9/9] use inout v0.2.0-rc.3 --- Cargo.lock | 16 ++++------------ aead/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac9117c1c..ae4080815 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ "bytes", "crypto-common 0.2.0-rc.1", "heapless", - "inout 0.2.0-rc.2 (git+https://github.com/RustCrypto/utils)", + "inout 0.2.0-rc.3", ] [[package]] @@ -223,7 +223,7 @@ version = "0.5.0-pre.7" dependencies = [ "blobby", "crypto-common 0.2.0-rc.1", - "inout 0.2.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)", + "inout 0.2.0-rc.3", "zeroize", ] @@ -728,22 +728,14 @@ dependencies = [ [[package]] name = "inout" -version = "0.2.0-rc.2" +version = "0.2.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14db49369b2c3f15deb5806de446e05c7f07a2d778b54b278c994fcd1d686f31" +checksum = "de49db00f5add6dad75a57946b75de0f26287a6fc95f4f277d48419200422beb" dependencies = [ "block-padding 0.4.0-rc.2", "hybrid-array", ] -[[package]] -name = "inout" -version = "0.2.0-rc.2" -source = "git+https://github.com/RustCrypto/utils#794835232842098d2003acb5b12f8218e5a1fe01" -dependencies = [ - "hybrid-array", -] - [[package]] name = "itoa" version = "1.0.11" diff --git a/aead/Cargo.toml b/aead/Cargo.toml index c1315371f..aa29d9a3f 100644 --- a/aead/Cargo.toml +++ b/aead/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.81" [dependencies] crypto-common = "0.2.0-rc.0" -inout = { version = "0.2.0-rc.1", git = "https://github.com/RustCrypto/utils" } +inout = "0.2.0-rc.3" # optional dependencies arrayvec = { version = "0.7", optional = true, default-features = false }