From ad69263abac66575ecb800f3063830b34ad0c31e Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Tue, 21 Jan 2025 22:10:50 +0300 Subject: [PATCH] aead-stream: move the remaining items from `aead` (#647) This PR follows removal of the items from `aead` in https://github.com/RustCrypto/traits/pull/1713 In a future PR I plan to refactor `aead-stream` further, so it's convenient to have all code in one place. --- aead-stream/Cargo.toml | 2 +- aead-stream/src/lib.rs | 309 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 301 insertions(+), 10 deletions(-) diff --git a/aead-stream/Cargo.toml b/aead-stream/Cargo.toml index 65338967..f58ca44c 100644 --- a/aead-stream/Cargo.toml +++ b/aead-stream/Cargo.toml @@ -13,7 +13,7 @@ categories = ["cryptography", "no-std"] rust-version = "1.81" [dependencies] -aead = { version = "=0.6.0-rc.0", default-features = false, features = ["stream"] } +aead = { version = "=0.6.0-rc.0", default-features = false } [features] alloc = ["aead/alloc"] diff --git a/aead-stream/src/lib.rs b/aead-stream/src/lib.rs index 67a8aa01..1d776031 100644 --- a/aead-stream/src/lib.rs +++ b/aead-stream/src/lib.rs @@ -5,19 +5,21 @@ #[cfg(feature = "alloc")] extern crate alloc; -use aead::array::{ - typenum::{Unsigned, U4, U5}, - Array, ArraySize, +use aead::{ + array::{ + typenum::{Unsigned, U4, U5}, + Array, ArraySize, + }, + AeadCore, AeadInPlace, Buffer, Error, Result, }; -use aead::{AeadCore, AeadInPlace, Buffer, Error, Result}; -use core::ops::Sub; +use core::ops::{AddAssign, Sub}; pub use aead; -pub use aead::{ - stream::{Decryptor, Encryptor, NewStream, StreamPrimitive}, - Key, KeyInit, -}; +pub use aead::{Key, KeyInit}; + +#[cfg(feature = "alloc")] +use {aead::Payload, alloc::vec::Vec}; /// Nonce as used by a given AEAD construction and STREAM primitive. pub type Nonce = Array>; @@ -27,6 +29,295 @@ pub type Nonce = Array>; 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" +); + /// STREAM encryptor instantiated with [`StreamBE32`] as the underlying /// STREAM primitive. pub type EncryptorBE32 = Encryptor>;