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,