diff --git a/Cargo.lock b/Cargo.lock index 136d93a91..ae4080815 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.3", ] [[package]] @@ -222,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.3", "zeroize", ] @@ -727,9 +728,9 @@ 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", diff --git a/aead/Cargo.toml b/aead/Cargo.toml index 9cfaa2ff9..aa29d9a3f 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.3" # 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/buffer.rs b/aead/src/buffer.rs new file mode 100644 index 000000000..396d2db7e --- /dev/null +++ b/aead/src/buffer.rs @@ -0,0 +1,97 @@ +use crate::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(|_| crate::Error) + } else { + self.truncate(len); + Ok(()) + } + } + + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { + ArrayVec::try_extend_from_slice(self, other).map_err(|_| crate::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(|_| crate::Error) + } + + fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { + heapless::Vec::extend_from_slice(self, other).map_err(|_| crate::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 2a3560c21..92154440b 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -8,7 +8,7 @@ #![forbid(unsafe_code)] #![warn( clippy::unwrap_used, - missing_docs, + // missing_docs, rust_2018_idioms, missing_debug_implementations )] @@ -19,38 +19,36 @@ extern crate alloc; #[cfg(feature = "dev")] pub mod dev; -pub mod stream; +mod buffer; +pub use buffer::Buffer; 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 +66,246 @@ 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; - /// Generate a random nonce for this AEAD algorithm. + /// Encrypt the [`InOutBuf`] data, returning the authentication tag. + fn detached_encrypt_inout( + &self, + nonce: &Nonce, + associated_data: &[u8], + buffer: InOutBuf<'_, '_, u8>, + ) -> Result>; + + /// 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) = 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; + let tag = self.detached_encrypt_inout(nonce, associated_data, msg)?; + tag_dst.copy_from_slice(&tag); + let res = &mut buffer.into_out()[..res_len]; + Ok(res) + } + + /// 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(buf.into_out()) + } + + /// 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 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 + /// 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]> { + self.postfix_decrypt_inout(nonce, associated_data, buffer.into()) + } + + /// 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 buffer = InOutBufReserved::from_slices(plaintext, buffer).map_err(|_| Error)?; + self.postfix_encrypt_inout(nonce, associated_data, buffer) } - /// 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 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. + /// + /// `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 +329,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,264 +358,79 @@ 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>( - &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(()) - }}; -} - -/// 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( + #[cfg(feature = "alloc")] + #[inline] + fn decrypt_to_vec<'msg, 'aad>( &self, nonce: &Nonce, - associated_data: &[u8], - buffer: &mut dyn Buffer, - ) -> Result<()> { - let tag = self.encrypt_in_place_detached(nonce, associated_data, buffer.as_mut())?; - buffer.extend_from_slice(tag.as_slice())?; - Ok(()) + ct_payload: impl Into>, + ) -> Result> { + let mut buf = Vec::new(); + self.decrypt_to_buffer(nonce, ct_payload, &mut buf)?; + Ok(buf) } - /// 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 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<()> { - impl_decrypt_in_place!(self, nonce, associated_data, buffer) + 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(()) } - /// 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( + #[inline] + fn decrypt_to_buffer<'msg, 'aad, B: Buffer + ?Sized>( &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. - /// - /// 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, + ct_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: 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(()) } - /// 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. /// - /// 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) - } - - /// 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) - } -} - -#[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) - } -} - -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) + /// 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) } - 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) + /// Generate a random nonce for this AEAD algorithm using the specified [`CryptoRngCore`]. + /// + /// 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) } } @@ -473,7 +442,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,98 +456,8 @@ 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"" } } } - -/// 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: 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() - } - - /// 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 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 len(&self) -> usize { - BytesMut::len(self) - } - - fn is_empty(&self) -> bool { - BytesMut::is_empty(self) - } - - 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::ArrayVec { - fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> { - arrayvec::ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error) - } - - fn truncate(&mut self, len: usize) { - arrayvec::ArrayVec::truncate(self, len); - } -} - -#[cfg(feature = "heapless")] -impl Buffer for heapless::Vec { - 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); - } -} - -#[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 deleted file mode 100644 index 5ee90c5f8..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::{AeadCore, AeadInPlace, 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: AeadInPlace, - 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: AeadInPlace, - 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: AeadInPlace, - 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: AeadInPlace, - 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" -);