Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions eax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,29 @@
//! assert_eq!(buffer.as_ref(), b"plaintext message");
//! # }
//! ```
//!
//! ## Custom Nonce Length
//!
//! EAX accepts nonces of any length. The nonce is 16 bytes long by default;
//! the third generic argument of `Eax` sets another length:
//!
//! ```
//! # #[cfg(feature = "alloc")]
//! # {
//! use aes::Aes128;
//! use eax::Eax;
//! use eax::aead::{Aead, KeyInit, array::Array};
//! use eax::aead::consts::{U12, U16};
//!
//! let key = Array::from_slice(b"very secret key.");
//! let cipher = Eax::<Aes128, U16, U12>::new(key);
//!
//! let nonce = Array::from_slice(b"unique nonce"); // 96-bits; unique per message
//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref()).unwrap();
//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref()).unwrap();
//! assert_eq!(&plaintext, b"plaintext message");
//! # }
//! ```

mod traits;

Expand All @@ -133,7 +156,9 @@ pub use cipher;

use aead::{TagPosition, inout::InOutBuf};
use cipher::{
BlockCipherEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore, array::Array, consts::U16,
BlockCipherEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore,
array::{Array, ArraySize},
consts::U16,
};
use cmac::{Cmac, Mac, digest::Output};
use core::{fmt, marker::PhantomData};
Expand Down Expand Up @@ -167,52 +192,60 @@ type Ctr128BE<C> = ctr::CtrCore<C, ctr::flavors::Ctr128BE>;
/// ## Type parameters
/// - `Cipher`: block cipher.
/// - `M`: size of MAC tag, valid values: up to `U16`.
/// - `NonceSize`: size of nonce, any length (default `U16`).
#[derive(Clone)]
pub struct Eax<Cipher, M = U16>
pub struct Eax<Cipher, M = U16, NonceSize = U16>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
/// Encryption key
key: Key<Cipher>,
_tag_size: PhantomData<M>,
_nonce_size: PhantomData<NonceSize>,
}

impl<Cipher, M> KeySizeUser for Eax<Cipher, M>
impl<Cipher, M, NonceSize> KeySizeUser for Eax<Cipher, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
type KeySize = Cipher::KeySize;
}

impl<Cipher, M> KeyInit for Eax<Cipher, M>
impl<Cipher, M, NonceSize> KeyInit for Eax<Cipher, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
fn new(key: &Key<Cipher>) -> Self {
Self {
key: key.clone(),
_tag_size: PhantomData,
_nonce_size: PhantomData,
}
}
}

impl<Cipher, M> AeadCore for Eax<Cipher, M>
impl<Cipher, M, NonceSize> AeadCore for Eax<Cipher, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
type NonceSize = Cipher::BlockSize;
type NonceSize = NonceSize;
type TagSize = M;
const TAG_POSITION: TagPosition = TagPosition::Postfix;
}

impl<Cipher, M> AeadInOut for Eax<Cipher, M>
impl<Cipher, M, NonceSize> AeadInOut for Eax<Cipher, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
fn encrypt_inout_detached(
&self,
Expand Down Expand Up @@ -285,20 +318,22 @@ where
}
}

impl<Cipher, M> fmt::Debug for Eax<Cipher, M>
impl<Cipher, M, NonceSize> fmt::Debug for Eax<Cipher, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Eax").finish_non_exhaustive()
}
}

impl<Cipher, M> Eax<Cipher, M>
impl<Cipher, M, NonceSize> Eax<Cipher, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
/// CMAC/OMAC1
///
Expand Down
24 changes: 16 additions & 8 deletions eax/src/online.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
use crate::{Cmac, Error, Nonce, Tag, TagSize};
use aead::consts::U16;
use cipher::{
BlockCipherEncrypt, BlockSizeUser, Key, KeyInit, KeyIvInit, StreamCipher, array::Array,
BlockCipherEncrypt, BlockSizeUser, Key, KeyInit, KeyIvInit, StreamCipher,
array::{Array, ArraySize},
typenum::Unsigned,
};
use cmac::Mac;
Expand Down Expand Up @@ -150,31 +151,35 @@ impl CipherOp for Decrypt {}
/// [`Eax`]: ../struct.Eax.html
/// [`Decrypt`]: struct.Decrypt.html
/// [`finish`]: #method.finish
pub struct Eax<Cipher, Op, M = U16>
pub struct Eax<Cipher, Op, M = U16, NonceSize = U16>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
Op: CipherOp,
M: TagSize,
NonceSize: ArraySize,
{
imp: EaxImpl<Cipher, M>,
/// Denotes whether this stream is used for encryption or decryption.
marker: PhantomData<Op>,
_nonce_size: PhantomData<NonceSize>,
}

impl<Cipher, Op, M> Eax<Cipher, Op, M>
impl<Cipher, Op, M, NonceSize> Eax<Cipher, Op, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
Op: CipherOp,
M: TagSize,
NonceSize: ArraySize,
{
/// 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 {
pub fn with_key_and_nonce(key: &Key<Cipher>, nonce: &Nonce<NonceSize>) -> Self {
let imp = EaxImpl::<Cipher, M>::with_key_and_nonce(key, nonce);

Self {
imp,
marker: PhantomData,
_nonce_size: PhantomData,
}
}

Expand All @@ -196,10 +201,11 @@ where
}
}

impl<Cipher, M> Eax<Cipher, Encrypt, M>
impl<Cipher, M, NonceSize> Eax<Cipher, Encrypt, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
/// Applies encryption to the plaintext.
#[inline]
Expand All @@ -217,10 +223,11 @@ where
}
}

impl<Cipher, M> Eax<Cipher, Decrypt, M>
impl<Cipher, M, NonceSize> Eax<Cipher, Decrypt, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
M: TagSize,
NonceSize: ArraySize,
{
/// Applies decryption to the ciphertext **without** verifying the
/// authenticity of decrypted message.
Expand Down Expand Up @@ -267,11 +274,12 @@ where
}
}

impl<Cipher, Op, M> fmt::Debug for Eax<Cipher, Op, M>
impl<Cipher, Op, M, NonceSize> fmt::Debug for Eax<Cipher, Op, M, NonceSize>
where
Cipher: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
Op: CipherOp,
M: TagSize,
NonceSize: ArraySize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Eax").finish_non_exhaustive()
Expand Down Expand Up @@ -302,7 +310,7 @@ where
{
/// 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 {
fn with_key_and_nonce(key: &Key<Cipher>, nonce: &[u8]) -> Self {
let prepend_cmac = |key, init_val, data| {
let mut cmac = <Cmac<Cipher> as KeyInit>::new(key);
cmac.update(&[0; 15]);
Expand Down
Binary file added eax/tests/data/wycheproof-128_nonce-len.blb
Binary file not shown.
Binary file added eax/tests/data/wycheproof-192_nonce-len.blb
Binary file not shown.
Binary file added eax/tests/data/wycheproof-256_nonce-len.blb
Binary file not shown.
79 changes: 76 additions & 3 deletions eax/tests/wycheproof.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,86 @@
//! Test vectors from Wycheproof (`aes_eax_test.json`): the groups with a
//! 128-bit nonce and a 128-bit tag, for AES-128, AES-192 and AES-256.
//! Test vectors from Wycheproof (`aes_eax_test.json`) with a 128-bit tag, for
//! AES-128, AES-192 and AES-256.
#![cfg(feature = "alloc")]

use aead::{
array::ArraySize,
consts::{U0, U4, U8, U12, U16, U20, U32, U64, U128, U257},
dev::{TestVector, blobby, pass_test},
};
use aes::{Aes128, Aes192, Aes256};
use eax::Eax;
use eax::{
Eax, KeyInit, Tag,
cipher::{BlockCipherEncrypt, BlockSizeUser},
online::{Decrypt, Eax as EaxOnline, Encrypt},
};

// The groups with a 128-bit nonce.
aead::new_pass_test!(aes128_pass, "wycheproof-128_pass", Eax<Aes128>);
aead::new_fail_test!(aes128_fail, "wycheproof-128_fail", Eax<Aes128>);
aead::new_pass_test!(aes192_pass, "wycheproof-192_pass", Eax<Aes192>);
aead::new_fail_test!(aes192_fail, "wycheproof-192_fail", Eax<Aes192>);
aead::new_pass_test!(aes256_pass, "wycheproof-256_pass", Eax<Aes256>);
aead::new_fail_test!(aes256_fail, "wycheproof-256_fail", Eax<Aes256>);

/// Checks a vector with both `Eax` and `online::Eax` using an `N`-byte nonce.
fn nonce_len_test<C, N>(i: usize, tv: &TestVector)
where
C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + Clone + KeyInit,
N: ArraySize,
{
if let Err(reason) = pass_test::<Eax<C, U16, N>>(tv) {
panic!("\nFailed test #{i}\nreason:\t{reason:?}\ntest vector:\t{tv:?}\n");
}

let key = tv.key.try_into().expect("wrong key size");
let nonce = tv.nonce.try_into().expect("wrong nonce size");
let (ct, tag) = tv.ciphertext.split_at(tv.plaintext.len());
let tag: &Tag<U16> = tag.try_into().expect("wrong tag size");

let mut buf = tv.plaintext.to_vec();
let mut cipher = EaxOnline::<C, Encrypt, U16, N>::with_key_and_nonce(key, nonce);
cipher.update_assoc(tv.aad);
cipher.encrypt(&mut buf);
assert_eq!(buf, ct, "online encryption, test #{i}");
assert_eq!(&cipher.finish(), tag, "online tag, test #{i}");

let mut cipher = EaxOnline::<C, Decrypt, U16, N>::with_key_and_nonce(key, nonce);
cipher.update_assoc(tv.aad);
cipher.decrypt_unauthenticated_hazmat(&mut buf);
assert_eq!(buf, tv.plaintext, "online decryption, test #{i}");
assert!(cipher.finish(tag).is_ok(), "online tag check, test #{i}");
}

/// The groups with a nonce that is not 128 bits long (0 to 257 bytes).
macro_rules! new_nonce_len_test {
($name:ident, $test_name:expr, $cipher:ty) => {
#[test]
fn $name() {
blobby::parse_into_structs!(
include_bytes!(concat!("data/", $test_name, ".blb"));
static TEST_VECTORS: &[
TestVector { key, nonce, aad, plaintext, ciphertext }
];
);

for (i, tv) in TEST_VECTORS.iter().enumerate() {
match tv.nonce.len() {
0 => nonce_len_test::<$cipher, U0>(i, tv),
4 => nonce_len_test::<$cipher, U4>(i, tv),
8 => nonce_len_test::<$cipher, U8>(i, tv),
12 => nonce_len_test::<$cipher, U12>(i, tv),
20 => nonce_len_test::<$cipher, U20>(i, tv),
32 => nonce_len_test::<$cipher, U32>(i, tv),
64 => nonce_len_test::<$cipher, U64>(i, tv),
128 => nonce_len_test::<$cipher, U128>(i, tv),
257 => nonce_len_test::<$cipher, U257>(i, tv),
n => panic!("no test for a {n}-byte nonce"),
}
}
}
};
}

new_nonce_len_test!(aes128_nonce_len, "wycheproof-128_nonce-len", Aes128);
new_nonce_len_test!(aes192_nonce_len, "wycheproof-192_nonce-len", Aes192);
new_nonce_len_test!(aes256_nonce_len, "wycheproof-256_nonce-len", Aes256);
Loading