Skip to content

Commit

Permalink
aead: extract STREAM implementation into aead-stream
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Sep 23, 2024
1 parent 7dfeb99 commit 6c3b850
Show file tree
Hide file tree
Showing 4 changed files with 4 additions and 236 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion aead/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ std = ["alloc", "crypto-common/std"]
dev = ["blobby"]
getrandom = ["crypto-common/getrandom"]
rand_core = ["crypto-common/rand_core"]
stream = []

[package.metadata.docs.rs]
all-features = true
Expand Down
1 change: 0 additions & 1 deletion aead/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ extern crate std;
#[cfg(feature = "dev")]
pub mod dev;

#[cfg(feature = "stream")]
pub mod stream;

pub use crypto_common::{
Expand Down
236 changes: 3 additions & 233 deletions aead/src/stream.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,14 @@
//! Streaming AEAD support.
//!
//! Implementation of the STREAM online authenticated encryption construction
//! as described in the paper
//! [Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance][1].
//! See the [`aead-stream`] crate for a generic implementation of the STREAM construction.
//!
//! ## About
//!
//! The STREAM construction supports encrypting/decrypting sequences of AEAD
//! message segments, which is useful in cases where the overall message is too
//! large to fit in a single buffer and needs to be processed incrementally.
//!
//! STREAM defends against reordering and truncation attacks which are common
//! in naive schemes which attempt to provide these properties, and is proven
//! to meet the security definition of "nonce-based online authenticated
//! encryption" (nOAE) as given in the aforementioned paper.
//!
//! ## Diagram
//!
//! ![STREAM Diagram](https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/img/AEADs/rogaway-stream.svg)
//!
//! Legend:
//!
//! - 𝐄k: AEAD encryption under key `k`
//! - 𝐌: message
//! - 𝐍: nonce
//! - 𝐀: additional associated data
//! - 𝐂: ciphertext
//! - 𝜏: MAC tag
//!
//! [1]: https://eprint.iacr.org/2015/189.pdf
//! [`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::{
typenum::{Unsigned, U4, U5},
Array, ArraySize,
};
use crypto_common::array::{typenum::Unsigned, Array, ArraySize};

#[cfg(feature = "alloc")]
use {crate::Payload, alloc::vec::Vec};
Expand All @@ -50,22 +21,6 @@ pub type Nonce<A, S> = Array<u8, NonceSize<A, S>>;
pub type NonceSize<A, S> =
<<A as AeadCore>::NonceSize as Sub<<S as StreamPrimitive<A>>::NonceOverhead>>::Output;

/// STREAM encryptor instantiated with [`StreamBE32`] as the underlying
/// STREAM primitive.
pub type EncryptorBE32<A> = Encryptor<A, StreamBE32<A>>;

/// STREAM decryptor instantiated with [`StreamBE32`] as the underlying
/// STREAM primitive.
pub type DecryptorBE32<A> = Decryptor<A, StreamBE32<A>>;

/// STREAM encryptor instantiated with [`StreamLE31`] as the underlying
/// STREAM primitive.
pub type EncryptorLE31<A> = Encryptor<A, StreamLE31<A>>;

/// STREAM decryptor instantiated with [`StreamLE31`] as the underlying
/// STREAM primitive.
pub type DecryptorLE31<A> = Decryptor<A, StreamLE31<A>>;

/// Create a new STREAM from the provided AEAD.
pub trait NewStream<A>: StreamPrimitive<A>
where
Expand Down Expand Up @@ -354,188 +309,3 @@ impl_stream_object!(
"decrypt",
"𝒟 STREAM decryptor"
);

/// The original "Rogaway-flavored" STREAM as described in the paper
/// [Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance][1].
///
/// Uses a 32-bit big endian counter and 1-byte "last block" flag stored as
/// the last 5-bytes of the AEAD nonce.
///
/// [1]: https://eprint.iacr.org/2015/189.pdf
#[derive(Debug)]
pub struct StreamBE32<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U5>,
<<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArraySize,
{
/// Underlying AEAD cipher
aead: A,

/// Nonce (sans STREAM overhead)
nonce: Nonce<A, Self>,
}

impl<A> NewStream<A> for StreamBE32<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U5>,
<<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArraySize,
{
fn from_aead(aead: A, nonce: &Nonce<A, Self>) -> Self {
Self {
aead,
nonce: nonce.clone(),
}
}
}

impl<A> StreamPrimitive<A> for StreamBE32<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U5>,
<<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArraySize,
{
type NonceOverhead = U5;
type Counter = u32;
const COUNTER_INCR: u32 = 1;
const COUNTER_MAX: u32 = u32::MAX;

fn encrypt_in_place(
&self,
position: u32,
last_block: bool,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let nonce = self.aead_nonce(position, last_block);
self.aead.encrypt_in_place(&nonce, associated_data, buffer)
}

fn decrypt_in_place(
&self,
position: Self::Counter,
last_block: bool,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let nonce = self.aead_nonce(position, last_block);
self.aead.decrypt_in_place(&nonce, associated_data, buffer)
}
}

impl<A> StreamBE32<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U5>,
<<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArraySize,
{
/// Compute the full AEAD nonce including the STREAM counter and last
/// block flag.
fn aead_nonce(&self, position: u32, last_block: bool) -> crate::Nonce<A> {
let mut result = Array::default();

// TODO(tarcieri): use `generic_array::sequence::Concat` (or const generics)
let (prefix, tail) = result.split_at_mut(NonceSize::<A, Self>::to_usize());
prefix.copy_from_slice(&self.nonce);

let (counter, flag) = tail.split_at_mut(4);
counter.copy_from_slice(&position.to_be_bytes());
flag[0] = last_block as u8;

result
}
}

/// STREAM as instantiated with a 31-bit little endian counter and 1-bit
/// "last block" flag stored as the most significant bit of the counter
/// when interpreted as a 32-bit integer.
///
/// The 31-bit + 1-bit value is stored as the last 4 bytes of the AEAD nonce.
#[derive(Debug)]
pub struct StreamLE31<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U4>,
<<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArraySize,
{
/// Underlying AEAD cipher
aead: A,

/// Nonce (sans STREAM overhead)
nonce: Nonce<A, Self>,
}

impl<A> NewStream<A> for StreamLE31<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U4>,
<<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArraySize,
{
fn from_aead(aead: A, nonce: &Nonce<A, Self>) -> Self {
Self {
aead,
nonce: nonce.clone(),
}
}
}

impl<A> StreamPrimitive<A> for StreamLE31<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U4>,
<<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArraySize,
{
type NonceOverhead = U4;
type Counter = u32;
const COUNTER_INCR: u32 = 1;
const COUNTER_MAX: u32 = 0xfff_ffff;

fn encrypt_in_place(
&self,
position: u32,
last_block: bool,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let nonce = self.aead_nonce(position, last_block)?;
self.aead.encrypt_in_place(&nonce, associated_data, buffer)
}

fn decrypt_in_place(
&self,
position: Self::Counter,
last_block: bool,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let nonce = self.aead_nonce(position, last_block)?;
self.aead.decrypt_in_place(&nonce, associated_data, buffer)
}
}

impl<A> StreamLE31<A>
where
A: AeadInPlace,
A::NonceSize: Sub<U4>,
<<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArraySize,
{
/// Compute the full AEAD nonce including the STREAM counter and last
/// block flag.
fn aead_nonce(&self, position: u32, last_block: bool) -> Result<crate::Nonce<A>> {
if position > Self::COUNTER_MAX {
return Err(Error);
}

let mut result = Array::default();

// TODO(tarcieri): use `generic_array::sequence::Concat` (or const generics)
let (prefix, tail) = result.split_at_mut(NonceSize::<A, Self>::to_usize());
prefix.copy_from_slice(&self.nonce);

let position_with_flag = position | ((last_block as u32) << 31);
tail.copy_from_slice(&position_with_flag.to_le_bytes());

Ok(result)
}
}

0 comments on commit 6c3b850

Please sign in to comment.