Skip to content

Commit

Permalink
WIP: eax: Separate internal online impl for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Xanewok committed Sep 17, 2020
1 parent 0d287bc commit 4375553
Showing 1 changed file with 212 additions and 71 deletions.
283 changes: 212 additions & 71 deletions eax/src/online.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
//! // Now decrypt it, using the same key and nonce
//! let mut cipher = Eax::<Aes256, Decrypt>::with_key_and_nonce(key, nonce);
//! cipher.update_assoc(&assoc[..]);
//! cipher.decrypt(&mut buffer[..5]);
//! cipher.decrypt(&mut buffer[5..10]);
//! cipher.decrypt(&mut buffer[10..]);
//! cipher.decrypt_unauthenticated(&mut buffer[..5]);
//! cipher.decrypt_unauthenticated(&mut buffer[5..10]);
//! cipher.decrypt_unauthenticated(&mut buffer[10..]);
//! let res = cipher.finish(&tag);
//!
//! assert_eq!(res, Ok(()));
Expand All @@ -48,7 +48,7 @@
//! // Decrypting the ciphertext with tampered associated data should fail
//! let mut cipher = Eax::<Aes256, Decrypt>::with_key_and_nonce(key, nonce);
//! cipher.update_assoc(b"tampered");
//! cipher.decrypt(&mut cloned);
//! cipher.decrypt_unauthenticated(&mut cloned);
//! let res = cipher.finish(&tag);
//!
//! assert_eq!(res, Err(Error));
Expand Down Expand Up @@ -121,9 +121,9 @@ impl CipherOp for Decrypt {}
/// // Now decrypt it, using the same key and nonce
/// let mut cipher = Eax::<Aes256, Decrypt>::with_key_and_nonce(key, nonce);
/// cipher.update_assoc(&assoc[..]);
/// cipher.decrypt(&mut buffer[..5]);
/// cipher.decrypt(&mut buffer[5..10]);
/// cipher.decrypt(&mut buffer[10..]);
/// cipher.decrypt_unauthenticated(&mut buffer[..5]);
/// cipher.decrypt_unauthenticated(&mut buffer[5..10]);
/// cipher.decrypt_unauthenticated(&mut buffer[10..]);
/// let res = cipher.finish(&tag);
///
/// assert_eq!(res, Ok(()));
Expand All @@ -133,7 +133,7 @@ impl CipherOp for Decrypt {}
/// let mut cipher = Eax::<Aes256, Decrypt>::with_key_and_nonce(key, nonce);
///
/// cipher.update_assoc(b"tampered");
/// cipher.decrypt(&mut cloned);
/// cipher.decrypt_unauthenticated(&mut cloned);
/// let res = cipher.finish(&tag);
///
/// assert_eq!(res, Err(Error));
Expand All @@ -149,10 +149,7 @@ where
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
Op: CipherOp,
{
nonce: Nonce<Cipher::BlockSize>,
data: Cmac<Cipher>,
message: Cmac<Cipher>,
ctr: ctr::Ctr128<Cipher>,
imp: EaxImpl<Cipher>,
/// Denotes whether this stream is used for encryption or decryption.
marker: PhantomData<Op>,
}
Expand All @@ -166,6 +163,107 @@ where
/// Creates a stateful EAX instance that is capable of processing both
/// the associated data and the plaintext in an "on-line" fashion.
pub fn with_key_and_nonce(key: &Key<Cipher>, nonce: &Nonce<Cipher::BlockSize>) -> Self {
let imp = EaxImpl::<Cipher>::with_key_and_nonce(key, nonce);

Self {
imp,
marker: PhantomData,
}
}

/// Process the associated data (AD).
#[inline]
pub fn update_assoc(&mut self, aad: &[u8]) {
self.imp.update_assoc(aad);
}

/// Derives the tag from the encrypted/decrypted message so far.
///
/// If the encryption/decryption operation is finished, [`finish`] method
/// *must* be called instead.
///
///[`finish`]: #method.finish
#[inline]
pub fn tag_clone(&self) -> Tag {
self.imp.tag_clone()
}
}

impl<Cipher> Eax<Cipher, Encrypt>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
/// Applies encryption to the plaintext.
#[inline]
pub fn encrypt(&mut self, msg: &mut [u8]) {
self.imp.encrypt(msg)
}

/// Finishes the encryption stream, returning the derived tag.
///
/// This *must* be called after the stream encryption is finished.
#[must_use = "tag must be saved to later verify decrypted data"]
#[inline]
pub fn finish(self) -> Tag {
self.imp.tag()
}
}

impl<Cipher> Eax<Cipher, Decrypt>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
/// Applies decryption to the ciphertext **without** verifying the
/// authenticity of decrypted message.
///
/// To correctly verify the authenticity, use the [`finish`] associated
/// function.
///
/// [`finish`]: #method.finish
#[inline]
pub fn decrypt_unauthenticated(&mut self, msg: &mut [u8]) {
self.imp.decrypt(msg)
}

/// Finishes the decryption stream, verifying whether the associated and
/// decrypted data stream has not been tampered with.
///
/// This *must* be called after the stream decryption is finished.
#[must_use = "decrypted data stream must be verified for authenticity"]
pub fn finish(self, expected: &Tag) -> Result<(), Error> {
self.imp.verify_ct(expected)
}
}

/// Implementation of the raw EAX operations.
///
/// Main reason behind extracting the logic to a single, separate type is to
/// facilitate testing of the internal logic.
#[doc(hidden)]
struct EaxImpl<Cipher>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
nonce: Nonce<Cipher::BlockSize>,
data: Cmac<Cipher>,
message: Cmac<Cipher>,
ctr: ctr::Ctr128<Cipher>,
// HACK: Needed for the test harness due to AEAD trait online/offline interface mismatch
#[cfg(test)]
key: Key<Cipher>,
}

impl<Cipher> EaxImpl<Cipher>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
/// Creates a stateful EAX instance that is capable of processing both
/// the associated data and the plaintext in an "on-line" fashion.
fn with_key_and_nonce(key: &Key<Cipher>, nonce: &Nonce<Cipher::BlockSize>) -> Self {
let prepend_cmac = |key, init_val, data| {
let mut cmac = Cmac::<Cipher>::new(key);
cmac.update(&[0; 15]);
Expand All @@ -191,102 +289,145 @@ where

let cipher = ctr::Ctr128::<Cipher>::from_block_cipher(Cipher::new(&key), &n);

Eax {
Self {
nonce: n,
data: h,
message: c,
ctr: cipher,
marker: PhantomData,
#[cfg(test)]
key: key.clone(),
}
}
}

impl<Cipher, Op> Eax<Cipher, Op>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
Op: CipherOp,
{
/// Process the associated data (AD).
#[inline]
pub fn update_assoc(&mut self, aad: &[u8]) {
self.data.update(aad);
}

/// Applies encryption to the plaintext.
#[inline]
fn encrypt(&mut self, msg: &mut [u8]) {
self.ctr.apply_keystream(msg);
self.message.update(msg);
}

/// Applies decryption to the ciphertext.
#[inline]
fn decrypt(&mut self, msg: &mut [u8]) {
self.message.update(msg);
self.ctr.apply_keystream(msg);
}

/// Derives the tag from the encrypted/decrypted message so far.
///
/// NOTE: This has to be called when the value is consumed.
#[inline]
fn finish_inner(self) -> Tag {
fn tag(self) -> Tag {
let h = self.data.finalize().into_bytes();
let c = self.message.finalize().into_bytes();

self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b)
}

/// Derives the tag from the encrypted/decrypted message so far.
///
/// If the encryption/decryption operation is finished, [`finish`] method
/// *must* be called instead.
///
///[`finish`]: #method.finish
#[inline]
pub fn tag_clone(&self) -> Tag {
fn tag_clone(&self) -> Tag {
let h = self.data.clone().finalize().into_bytes();
let c = self.message.clone().finalize().into_bytes();

self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b)
}
}

impl<Cipher> Eax<Cipher, Encrypt>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
/// Applies encryption to the plaintext.
#[inline]
pub fn encrypt(&mut self, msg: &mut [u8]) {
self.ctr.apply_keystream(msg);
self.message.update(msg);
}

/// Finishes the encryption stream, returning the derived tag.
///
/// This *must* be called after the stream encryption is finished.
#[must_use = "tag must be saved to later verify decrypted data"]
#[inline]
pub fn finish(self) -> Tag {
self.finish_inner()
}
}

impl<Cipher> Eax<Cipher, Decrypt>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
/// Applies decryption to the ciphertext.
#[inline]
pub fn decrypt(&mut self, msg: &mut [u8]) {
self.message.update(msg);
self.ctr.apply_keystream(msg);
}

/// Finishes the decryption stream, verifying whether the associated and
/// decrypted data stream has not been tampered with.
///
/// This *must* be called after the stream decryption is finished.
#[must_use = "decrypted data stream must be verified for authenticity"]
pub fn finish(self, expected: &Tag) -> Result<(), Error> {
// Check mac using secure comparison
fn verify_ct(self, expected: &Tag) -> Result<(), Error> {
// Check MAC using secure comparison
use subtle::ConstantTimeEq;

let resulting_tag = &self.finish_inner()[..expected.len()];
let resulting_tag = &self.tag()[..expected.len()];
if resulting_tag.ct_eq(expected).unwrap_u8() == 1 {
Ok(())
} else {
Err(Error)
}
}
}


// Because the current AEAD test harness expects the types to implement both
// `NewAead` and `AeadMutInPlace` traits, do so here so that we can test the
// internal logic used by the public interface for the online EAX variant.
// These are not publicly implemented in general, because the traits are
// designed for offline usage and are somewhat wasteful when used in online mode.
#[cfg(test)]
mod test_impl {
use super::*;
use aead::AeadMutInPlace;

impl<Cipher> NewAead for EaxImpl<Cipher>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
type KeySize = Cipher::KeySize;

fn new(key: &Key<Cipher>) -> Self {
// HACK: The nonce will be initialized by the appropriate
// decrypt/encrypt functions from `AeadMutInPlace` implementation.
// This is currently done so because that trait only implements
// offline operations and thus need to re-initialize the `EaxImpl`
// instance.
let nonce = GenericArray::default();

Self::with_key_and_nonce(key, &nonce)
}
}

impl<Cipher> AeadMutInPlace for super::EaxImpl<Cipher>
where
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
{
type NonceSize = Cipher::BlockSize;
type TagSize = <Cmac<Cipher> as Mac>::OutputSize;
type CiphertextOverhead = U0;

fn encrypt_in_place_detached(
&mut self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<Tag, Error> {
// HACK: Reinitialize the instance
*self = Self::with_key_and_nonce(&self.key.clone(), nonce);

self.update_assoc(associated_data);
self.encrypt(buffer);

Ok(self.tag_clone())
}

fn decrypt_in_place_detached(
&mut self,
nonce: &Nonce<Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
expected_tag: &Tag,
) -> Result<(), Error> {
// HACK: Reinitialize the instance
*self = Self::with_key_and_nonce(&self.key.clone(), nonce);

self.update_assoc(associated_data);
self.decrypt(buffer);

let tag = self.tag_clone();

// Check mac using secure comparison
use subtle::ConstantTimeEq;
if expected_tag.ct_eq(&tag).unwrap_u8() == 1 {
Ok(())
} else {
Err(Error)
}
}
}
}

0 comments on commit 4375553

Please sign in to comment.