From c60d78a9552a9d047326f90061aab445da72929d Mon Sep 17 00:00:00 2001 From: Hansaem Wi Date: Tue, 3 Nov 2026 13:35:16 +0900 Subject: [PATCH] eax: support nonces of any length EAX takes a nonce of any length, but `Eax` fixed it to the 16-byte block size. Add a `NonceSize` type parameter (default `U16`, so existing code is unchanged) to `Eax` and `online::Eax`, as `AesGcm` does. Test with the Wycheproof `aes_eax_test.json` groups whose nonce is not 16 bytes (0 to 257 bytes, 18 vectors per key size), through both the one-shot and the online API. Closes #239 Assisted-by: Claude Code:claude-opus-5-5 --- eax/src/lib.rs | 53 ++++++++++--- eax/src/online.rs | 24 ++++-- eax/tests/data/wycheproof-128_nonce-len.blb | Bin 0 -> 2099 bytes eax/tests/data/wycheproof-192_nonce-len.blb | Bin 0 -> 2243 bytes eax/tests/data/wycheproof-256_nonce-len.blb | Bin 0 -> 2387 bytes eax/tests/wycheproof.rs | 79 +++++++++++++++++++- 6 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 eax/tests/data/wycheproof-128_nonce-len.blb create mode 100644 eax/tests/data/wycheproof-192_nonce-len.blb create mode 100644 eax/tests/data/wycheproof-256_nonce-len.blb diff --git a/eax/src/lib.rs b/eax/src/lib.rs index c58794f7..2d9e8aad 100644 --- a/eax/src/lib.rs +++ b/eax/src/lib.rs @@ -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::::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; @@ -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}; @@ -167,52 +192,60 @@ type Ctr128BE = ctr::CtrCore; /// ## 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 +pub struct Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { /// Encryption key key: Key, _tag_size: PhantomData, + _nonce_size: PhantomData, } -impl KeySizeUser for Eax +impl KeySizeUser for Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { type KeySize = Cipher::KeySize; } -impl KeyInit for Eax +impl KeyInit for Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { fn new(key: &Key) -> Self { Self { key: key.clone(), _tag_size: PhantomData, + _nonce_size: PhantomData, } } } -impl AeadCore for Eax +impl AeadCore for Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { - type NonceSize = Cipher::BlockSize; + type NonceSize = NonceSize; type TagSize = M; const TAG_POSITION: TagPosition = TagPosition::Postfix; } -impl AeadInOut for Eax +impl AeadInOut for Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { fn encrypt_inout_detached( &self, @@ -285,20 +318,22 @@ where } } -impl fmt::Debug for Eax +impl fmt::Debug for Eax where Cipher: BlockSizeUser + 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 Eax +impl Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { /// CMAC/OMAC1 /// diff --git a/eax/src/online.rs b/eax/src/online.rs index b139aa08..b78213e3 100644 --- a/eax/src/online.rs +++ b/eax/src/online.rs @@ -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; @@ -150,31 +151,35 @@ impl CipherOp for Decrypt {} /// [`Eax`]: ../struct.Eax.html /// [`Decrypt`]: struct.Decrypt.html /// [`finish`]: #method.finish -pub struct Eax +pub struct Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, Op: CipherOp, M: TagSize, + NonceSize: ArraySize, { imp: EaxImpl, /// Denotes whether this stream is used for encryption or decryption. marker: PhantomData, + _nonce_size: PhantomData, } -impl Eax +impl Eax where Cipher: BlockSizeUser + 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, nonce: &Nonce) -> Self { + pub fn with_key_and_nonce(key: &Key, nonce: &Nonce) -> Self { let imp = EaxImpl::::with_key_and_nonce(key, nonce); Self { imp, marker: PhantomData, + _nonce_size: PhantomData, } } @@ -196,10 +201,11 @@ where } } -impl Eax +impl Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { /// Applies encryption to the plaintext. #[inline] @@ -217,10 +223,11 @@ where } } -impl Eax +impl Eax where Cipher: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, M: TagSize, + NonceSize: ArraySize, { /// Applies decryption to the ciphertext **without** verifying the /// authenticity of decrypted message. @@ -267,11 +274,12 @@ where } } -impl fmt::Debug for Eax +impl fmt::Debug for Eax where Cipher: BlockSizeUser + 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() @@ -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, nonce: &Nonce) -> Self { + fn with_key_and_nonce(key: &Key, nonce: &[u8]) -> Self { let prepend_cmac = |key, init_val, data| { let mut cmac = as KeyInit>::new(key); cmac.update(&[0; 15]); diff --git a/eax/tests/data/wycheproof-128_nonce-len.blb b/eax/tests/data/wycheproof-128_nonce-len.blb new file mode 100644 index 0000000000000000000000000000000000000000..0e86d0d8ebb16c02c592950eae302ec0765b7c1e GIT binary patch literal 2099 zcmV-32+a3d03g2H`&t1Wyo6v6%PDLXuMHSOid1@(>WJLrY;1)903enJmweY%YaRaI z40EGeQI{Y%PNWvBJTFnFZhe9#G;K~8vkVAFC#@V=D#9HS00K24nDy6_2~y&9zU;Y} z*aX5s@F2?L9NG0%ChG%&LHtC@!Q~jW;0E5+QxOhr#bqe~5YY*V^2IKctuTMmhl3ca z4=bw}PyOX1R#hpfz7ZP?Eo~tG-$E!h9-;UaSW**fG=TRQ4i;we++{C!2>Xu>03ao? zE-0)H_Uh0gYC9r`$N)g;C}J%hZh4J{Wv+89Odek|t4C*7|D-GrY$s!pq;9MrsO}N6 zZG7v07wVzpxH>HM82h{g{cZwVag*y;_5d)zj|g6Bo(}V4Vt~OO&#mym=P4Qg!G5n$ z@6ws1J0;GH!yAC*NS^u5b5`u^sT;?=JTv?Y%_VCJiZtxVupUyTAQn}>W-or`g<)3# z{Wp2mC>TPu6NvWt1bQl&MRfo^g?le-)(FYYpd@T`4600_oL?M$am$i(@Yu+^Ww#1m zHEYV8-P4}^IWlZq^&04#Q1EsvI@q%ooFpBiUrBq*mb6n8%gR4^znR`rSgCtE>*K^8b(e?z~!G@ zaGlIl>J?_h!=d+i$7$-%L4Fr6UgoGQb?GfpTN^rT&KK9ah85>Ywk|XuF!024#)els zFH*51Q}>#X!s_b_uj*2cG7+w4~Hg8slCs9Y!`2c_b*#h3` zv30-*GMY2Ltj$~wv!sgDFK&7;@)!%hO zn3c`Rx0v<;fG?9tzWF#8K80|Y8Vw@mA@;Z`Db;W<~j&Gi;VBKrn%$(v+GWH*e6{U1)x*bjCf*gPG2 zm&899BnhOdRtdqe2zy_yvH&2+@Oz=wRaB|HKsgLB#ZsI=(R30M1irGMOv~h2KMDx2MqGW(+>%1O6YRP9Uw3#watqdbeR6tZ9B7`@qDCU|& z8S>WE1<9llfB^bF^J&j1>EMVtJT-suV9VQ&*?WGXu^;2#c068^i)&wQhhkgz$ck1K zM0DQ$wjXh^FMGfim{;|dOOEkkx+MT0+y#U7GqK^$W^63aw%iC$KvB<7>Me25@6)N> z!wD~&i!S4ZqZ@H=GF>}a_I|4@yC5N_>z@X{r^~b_CRU$t>g$03a}rpzYzInYv(Nup zDnS~Uq-q_{-ZJ14Xv$SWl@f{Zh$Ld!z5EY|e#e)#CXCr|hRcM0Gbb6Lj)DFw#Xf@A zc$XU-8}0jTG5LS#)BAsY4`ZLShkv1w>N&r^ZAVIiVH@h{h@ZSa9EGT^{pvf&I>od~ znSN8bG`%U^iaz*r03b)KqYs>;8bAv!SmDo+j&s`RGaU}J&Fna)0{PZr z&O#+>LwW2aL*aZ@D&8vfa#f;;bdicp{%=|UAkzhy_GkzY@5xS~4Gag813-aR_lPyY d7KA|J5pjxePByveItLjc;OQL+i_1ZDO~`B~>V*IR literal 0 HcmV?d00001 diff --git a/eax/tests/data/wycheproof-192_nonce-len.blb b/eax/tests/data/wycheproof-192_nonce-len.blb new file mode 100644 index 0000000000000000000000000000000000000000..4cf6d00f6e57d8db87fcba9dbda770e6e1e664af GIT binary patch literal 2243 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 Rc1ONRtl)5SI_$}+&_SSvM4JEr literal 0 HcmV?d00001 diff --git a/eax/tests/data/wycheproof-256_nonce-len.blb b/eax/tests/data/wycheproof-256_nonce-len.blb new file mode 100644 index 0000000000000000000000000000000000000000..583b5979f6447aabeee11230b8a1bb2a179c9c7a GIT binary patch literal 2387 zcmV-Z39R;706>7cF_Opw&X!)Ian=6^Jn-~8YAUsMvIs(M5kQ;?xv&^brN52+SW?02 z5;vUy03b~%I{B#nO29PnOB6Fq7Op_dR#hi634I(VC0Rr;Zhh+2e0k@!?waU7$egF5WX`A_bM!E?J!_CDP$r0PzMT; z$08ZPxvg<(%-Im&P7^>`)av_9;Lpd?+nK2KOBL6%p9;*4rk7)|8Nq3vhi6I{FUuP^ zsh)ATM?kHc03cMGHd^@;^RR&v9LsBhRfs^u25u%=l#f8*UN`zc58fML9pZ5)m0VQq z*QD0cK6vgx0urd+PzUXYvp*VekhT#|o(RtUmIv~}cD>Kr#m=1980N99^8Hw-b}VJ{ z8vrm6@$C7=1%zf@n&t1}dFS^xe(Gr!(yJe;KrXc$Mw+PWo@`X8G@Gtaq7M!@ClR}$6)Zwe1GZ-!aTo-t zz5`H-UokftZ_)rhuv3|mhN6N`Ld=XHI49H)<$F<&rrN%g;OLyLiI;0YZv3f%CXtG@WY~;r z>;mYSi!0?0Kag+vt^Lu3^K<7`7)rft{DZv)u^#%OrvQGt?0LY3)E;0h2@ifiNlk0> z`k~xy@T-c5>RmO|CbCGvt%w6r;v@DK4L8h|RpA6#<($2390@Md9dgXauONUP#XuC@ z`a}ABSP4V&oyMrY;HOkUd-`?{USHTt981WcYhQI2aOlVzDTpa$jbKtaZQTP6#{v6wKB%#Zj^CB8bVY^4z>s?mPzh>D|wd@uq zhoHZODt)n5-z7km=ziK{(A+Ifg%B{q;j(dxBJVuc;ZcG$7qAV6Hsuq8Xo)_@H=F>3RrX*U%ZX%L|DqP#)VE`R2S z@q9q4R4M&G7mGNB^2U~zUWkbS0!3S0oc^6(v;UCt| zfCE2)uEd1w&_s-0e88f903dp(k)FGi4>V~C%a~;tRLejbD1?^z2?J|2^I%Ip%`}r@ z>q%7%zUafxh%hXIW{{#l2|lqSLsNd}=^P(%TZSeB_}cl@;n&u35~3cW3WiEN!Lm6~_x)C3%vS&92 z8n`#~P zQ}pEy*do4nT&@iwp{2xA03h>fP>v{Phw2cGz!N~;15rR@;tda0O^1~) z%8HHv(v}u8%-Mn_WFt&`c~rVsS_scT^M>Sa>}e(BG*1H*e+2u>ph_pMkBqfas}J?a zWsTg@g95OObBE8)Ux_1ExfWB;$1^a_=e-K8GxH~F0bT=MV`&fa1||3%-~P_I`B{X5LRO^COyq*2;3G{D9?4N+ z_z!FXh1G55ii{CW_$!w}>9XQrIcKC!!P&iJb&TqXP1)A_ii9DBI?!c#;q8x0*o2SSQ>_g89D5OL_57OD(ZO5E={kde_Gj2--BtPp`{?3d6-uYA_1^V2Vezb-jql6_AY1!_L$biU5djztSodUl(Lho*m!cBEgYw3QJFoc=BA-0Y7*Z8k)GP%jzHLeG FZFe&gjOhRX literal 0 HcmV?d00001 diff --git a/eax/tests/wycheproof.rs b/eax/tests/wycheproof.rs index 31ea4356..29a6681d 100644 --- a/eax/tests/wycheproof.rs +++ b/eax/tests/wycheproof.rs @@ -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); aead::new_fail_test!(aes128_fail, "wycheproof-128_fail", Eax); aead::new_pass_test!(aes192_pass, "wycheproof-192_pass", Eax); aead::new_fail_test!(aes192_fail, "wycheproof-192_fail", Eax); aead::new_pass_test!(aes256_pass, "wycheproof-256_pass", Eax); aead::new_fail_test!(aes256_fail, "wycheproof-256_fail", Eax); + +/// Checks a vector with both `Eax` and `online::Eax` using an `N`-byte nonce. +fn nonce_len_test(i: usize, tv: &TestVector) +where + C: BlockSizeUser + BlockCipherEncrypt + Clone + KeyInit, + N: ArraySize, +{ + if let Err(reason) = pass_test::>(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 = tag.try_into().expect("wrong tag size"); + + let mut buf = tv.plaintext.to_vec(); + let mut cipher = EaxOnline::::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::::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);