Skip to content

Commit 0a28591

Browse files
authored
eax: Allow variable tag length (#231)
Implemented analogous to ccm by passing a second generic argument to Eax.
1 parent 1e121c8 commit 0a28591

3 files changed

Lines changed: 117 additions & 31 deletions

File tree

eax/src/lib.rs

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,42 @@
6767
//! # }
6868
//! ```
6969
//!
70+
//! ## Custom Tag Length
71+
//!
72+
//! The tag for eax is usually 16 bytes long but it can be shortened if needed.
73+
//! The second generic argument of `Eax` can be set to the tag length:
74+
//!
75+
//! ```
76+
//! # #[cfg(feature = "heapless")]
77+
//! # {
78+
//! use aes::Aes256;
79+
//! use eax::Eax;
80+
//! use eax::aead::{AeadInPlace, NewAead, generic_array::GenericArray};
81+
//! use eax::aead::heapless::{Vec, consts::U8, consts::U128};
82+
//!
83+
//! let key = GenericArray::from_slice(b"an example very very secret key.");
84+
//! let cipher = Eax::<Aes256, U8>::new(key);
85+
//!
86+
//! let nonce = GenericArray::from_slice(b"my unique nonces"); // 128-bits; unique per message
87+
//!
88+
//! let mut buffer: Vec<u8, U128> = Vec::new();
89+
//! buffer.extend_from_slice(b"plaintext message");
90+
//!
91+
//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
92+
//! let tag = cipher.encrypt_in_place_detached(nonce, b"", &mut buffer).expect("encryption failure!");
93+
//!
94+
//! // The tag has only 8 bytes, compared to the usual 16 bytes
95+
//! assert_eq!(tag.len(), 8);
96+
//!
97+
//! // `buffer` now contains the message ciphertext
98+
//! assert_ne!(&buffer, b"plaintext message");
99+
//!
100+
//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
101+
//! cipher.decrypt_in_place_detached(nonce, b"", &mut buffer, &tag).expect("decryption failure!");
102+
//! assert_eq!(&buffer, b"plaintext message");
103+
//! # }
104+
//! ```
105+
//!
70106
//! [1]: https://en.wikipedia.org/wiki/Authenticated_encryption
71107
72108
#![no_std]
@@ -88,6 +124,11 @@ use cipher::{
88124
stream::{FromBlockCipher, SyncStreamCipher},
89125
};
90126
use cmac::{crypto_mac::NewMac, Cmac, Mac};
127+
use core::marker::PhantomData;
128+
129+
mod traits;
130+
131+
use traits::TagSize;
91132

92133
// TODO Max values?
93134
/// Maximum length of associated data
@@ -100,7 +141,7 @@ pub const P_MAX: u64 = 1 << 36;
100141
pub const C_MAX: u64 = (1 << 36) + 16;
101142

102143
/// EAX tags
103-
pub type Tag = GenericArray<u8, U16>;
144+
pub type Tag = aead::Tag<U16>;
104145

105146
pub mod online;
106147

@@ -110,43 +151,54 @@ pub mod online;
110151
/// implementations.
111152
///
112153
/// If in doubt, use the built-in [`Aes128Eax`] and [`Aes256Eax`] type aliases.
154+
///
155+
/// Type parameters:
156+
/// - `Cipher`: block cipher.
157+
/// - `M`: size of MAC tag, valid values: up to `U16`.
113158
#[derive(Clone)]
114-
pub struct Eax<Cipher>
159+
pub struct Eax<Cipher, M = U16>
115160
where
116161
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
117162
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
163+
M: TagSize,
118164
{
119165
/// Encryption key
120166
key: Key<Cipher>,
167+
_tag_size: PhantomData<M>,
121168
}
122169

123-
impl<Cipher> NewAead for Eax<Cipher>
170+
impl<Cipher, M> NewAead for Eax<Cipher, M>
124171
where
125172
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
126173
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
174+
M: TagSize,
127175
{
128176
type KeySize = Cipher::KeySize;
129177

130178
fn new(key: &Key<Cipher>) -> Self {
131-
Self { key: key.clone() }
179+
Self {
180+
key: key.clone(),
181+
_tag_size: Default::default(),
182+
}
132183
}
133184
}
134185

135-
impl<Cipher> AeadInPlace for Eax<Cipher>
186+
impl<Cipher, M> AeadInPlace for Eax<Cipher, M>
136187
where
137188
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
138189
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
190+
M: TagSize,
139191
{
140192
type NonceSize = Cipher::BlockSize;
141-
type TagSize = <Cmac<Cipher> as Mac>::OutputSize;
193+
type TagSize = M;
142194
type CiphertextOverhead = U0;
143195

144196
fn encrypt_in_place_detached(
145197
&self,
146198
nonce: &Nonce<Self::NonceSize>,
147199
associated_data: &[u8],
148200
buffer: &mut [u8],
149-
) -> Result<Tag, Error> {
201+
) -> Result<aead::Tag<M>, Error> {
150202
if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
151203
return Err(Error);
152204
}
@@ -171,15 +223,17 @@ where
171223

172224
// 5. tag ← n ^ h ^ c
173225
// (^ means xor)
174-
Ok(n.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b))
226+
let full_tag = n.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
227+
let tag = aead::Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()]);
228+
Ok(tag)
175229
}
176230

177231
fn decrypt_in_place_detached(
178232
&self,
179233
nonce: &Nonce<Self::NonceSize>,
180234
associated_data: &[u8],
181235
buffer: &mut [u8],
182-
tag: &Tag,
236+
tag: &aead::Tag<M>,
183237
) -> Result<(), Error> {
184238
if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
185239
return Err(Error);
@@ -213,10 +267,11 @@ where
213267
}
214268
}
215269

216-
impl<Cipher> Eax<Cipher>
270+
impl<Cipher, M> Eax<Cipher, M>
217271
where
218272
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
219273
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
274+
M: TagSize,
220275
{
221276
/// CMAC/OMAC1
222277
///

eax/src/online.rs

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
6060
use crate::*;
6161

62+
use aead::Tag;
6263
use core::marker::PhantomData;
6364

6465
pub use Eax as EaxOnline;
@@ -141,27 +142,29 @@ impl CipherOp for Decrypt {}
141142
/// [`Eax`]: ../struct.Eax.html
142143
/// [`Decrypt`]: struct.Decrypt.html
143144
/// [`finish`]: #method.finish
144-
pub struct Eax<Cipher, Op>
145+
pub struct Eax<Cipher, Op, M = U16>
145146
where
146147
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
147148
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
148149
Op: CipherOp,
150+
M: TagSize,
149151
{
150-
imp: EaxImpl<Cipher>,
152+
imp: EaxImpl<Cipher, M>,
151153
/// Denotes whether this stream is used for encryption or decryption.
152154
marker: PhantomData<Op>,
153155
}
154156

155-
impl<Cipher, Op> Eax<Cipher, Op>
157+
impl<Cipher, Op, M> Eax<Cipher, Op, M>
156158
where
157159
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
158160
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
159161
Op: CipherOp,
162+
M: TagSize,
160163
{
161164
/// Creates a stateful EAX instance that is capable of processing both
162165
/// the associated data and the plaintext in an "on-line" fashion.
163166
pub fn with_key_and_nonce(key: &Key<Cipher>, nonce: &Nonce<Cipher::BlockSize>) -> Self {
164-
let imp = EaxImpl::<Cipher>::with_key_and_nonce(key, nonce);
167+
let imp = EaxImpl::<Cipher, M>::with_key_and_nonce(key, nonce);
165168

166169
Self {
167170
imp,
@@ -182,15 +185,16 @@ where
182185
///
183186
///[`finish`]: #method.finish
184187
#[inline]
185-
pub fn tag_clone(&self) -> Tag {
188+
pub fn tag_clone(&self) -> Tag<M> {
186189
self.imp.tag_clone()
187190
}
188191
}
189192

190-
impl<Cipher> Eax<Cipher, Encrypt>
193+
impl<Cipher, M> Eax<Cipher, Encrypt, M>
191194
where
192195
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
193196
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
197+
M: TagSize,
194198
{
195199
/// Applies encryption to the plaintext.
196200
#[inline]
@@ -203,15 +207,16 @@ where
203207
/// This *must* be called after the stream encryption is finished.
204208
#[must_use = "tag must be saved to later verify decrypted data"]
205209
#[inline]
206-
pub fn finish(self) -> Tag {
210+
pub fn finish(self) -> Tag<M> {
207211
self.imp.tag()
208212
}
209213
}
210214

211-
impl<Cipher> Eax<Cipher, Decrypt>
215+
impl<Cipher, M> Eax<Cipher, Decrypt, M>
212216
where
213217
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
214218
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
219+
M: TagSize,
215220
{
216221
/// Applies decryption to the ciphertext **without** verifying the
217222
/// authenticity of decrypted message.
@@ -246,7 +251,7 @@ where
246251
///
247252
/// This *must* be called after the stream decryption is finished.
248253
#[must_use = "decrypted data stream must be verified for authenticity"]
249-
pub fn finish(self, expected: &Tag) -> Result<(), Error> {
254+
pub fn finish(self, expected: &Tag<M>) -> Result<(), Error> {
250255
self.imp.verify_ct(expected)
251256
}
252257
}
@@ -256,10 +261,11 @@ where
256261
/// Main reason behind extracting the logic to a single, separate type is to
257262
/// facilitate testing of the internal logic.
258263
#[doc(hidden)]
259-
struct EaxImpl<Cipher>
264+
struct EaxImpl<Cipher, M>
260265
where
261266
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
262267
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
268+
M: TagSize,
263269
{
264270
nonce: Nonce<Cipher::BlockSize>,
265271
data: Cmac<Cipher>,
@@ -268,12 +274,14 @@ where
268274
// HACK: Needed for the test harness due to AEAD trait online/offline interface mismatch
269275
#[cfg(test)]
270276
key: Key<Cipher>,
277+
_tag_size: PhantomData<M>,
271278
}
272279

273-
impl<Cipher> EaxImpl<Cipher>
280+
impl<Cipher, M> EaxImpl<Cipher, M>
274281
where
275282
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
276283
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
284+
M: TagSize,
277285
{
278286
/// Creates a stateful EAX instance that is capable of processing both
279287
/// the associated data and the plaintext in an "on-line" fashion.
@@ -310,6 +318,7 @@ where
310318
ctr: cipher,
311319
#[cfg(test)]
312320
key: key.clone(),
321+
_tag_size: Default::default(),
313322
}
314323
}
315324

@@ -335,25 +344,27 @@ where
335344

336345
/// Derives the tag from the encrypted/decrypted message so far.
337346
#[inline]
338-
fn tag(self) -> Tag {
347+
fn tag(self) -> Tag<M> {
339348
let h = self.data.finalize().into_bytes();
340349
let c = self.message.finalize().into_bytes();
341350

342-
self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b)
351+
let full_tag = self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
352+
Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()])
343353
}
344354

345355
/// Derives the tag from the encrypted/decrypted message so far.
346356
#[inline]
347-
fn tag_clone(&self) -> Tag {
357+
fn tag_clone(&self) -> Tag<M> {
348358
let h = self.data.clone().finalize().into_bytes();
349359
let c = self.message.clone().finalize().into_bytes();
350360

351-
self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b)
361+
let full_tag = self.nonce.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
362+
Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()])
352363
}
353364

354365
/// Finishes the decryption stream, verifying whether the associated and
355366
/// decrypted data stream has not been tampered with.
356-
fn verify_ct(self, expected: &Tag) -> Result<(), Error> {
367+
fn verify_ct(self, expected: &Tag<M>) -> Result<(), Error> {
357368
// Check MAC using secure comparison
358369
use subtle::ConstantTimeEq;
359370

@@ -376,10 +387,11 @@ mod test_impl {
376387
use super::*;
377388
use aead::AeadMutInPlace;
378389

379-
impl<Cipher> NewAead for EaxImpl<Cipher>
390+
impl<Cipher, M> NewAead for EaxImpl<Cipher, M>
380391
where
381392
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
382393
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
394+
M: TagSize,
383395
{
384396
type KeySize = Cipher::KeySize;
385397

@@ -395,21 +407,22 @@ mod test_impl {
395407
}
396408
}
397409

398-
impl<Cipher> AeadMutInPlace for super::EaxImpl<Cipher>
410+
impl<Cipher, M> AeadMutInPlace for super::EaxImpl<Cipher, M>
399411
where
400412
Cipher: BlockCipher<BlockSize = U16> + NewBlockCipher + Clone,
401413
Cipher::ParBlocks: ArrayLength<Block<Cipher>>,
414+
M: TagSize,
402415
{
403416
type NonceSize = Cipher::BlockSize;
404-
type TagSize = <Cmac<Cipher> as Mac>::OutputSize;
417+
type TagSize = M;
405418
type CiphertextOverhead = U0;
406419

407420
fn encrypt_in_place_detached(
408421
&mut self,
409422
nonce: &Nonce<Self::NonceSize>,
410423
associated_data: &[u8],
411424
buffer: &mut [u8],
412-
) -> Result<Tag, Error> {
425+
) -> Result<Tag<M>, Error> {
413426
// HACK: Reinitialize the instance
414427
*self = Self::with_key_and_nonce(&self.key.clone(), nonce);
415428

@@ -424,7 +437,7 @@ mod test_impl {
424437
nonce: &Nonce<Self::NonceSize>,
425438
associated_data: &[u8],
426439
buffer: &mut [u8],
427-
expected_tag: &Tag,
440+
expected_tag: &Tag<M>,
428441
) -> Result<(), Error> {
429442
// HACK: Reinitialize the instance
430443
*self = Self::with_key_and_nonce(&self.key.clone(), nonce);

eax/src/traits.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use aead::consts::{U16, U4};
2+
use aead::generic_array::typenum::type_operators::{IsGreaterOrEqual, IsLessOrEqual};
3+
use aead::generic_array::typenum::Unsigned;
4+
use aead::generic_array::ArrayLength;
5+
6+
mod private {
7+
// Sealed traits stop other crates from implementing any traits that use it.
8+
pub trait SealedTag {}
9+
10+
impl<T> SealedTag for T where
11+
T: super::IsGreaterOrEqual<super::U4> + super::IsLessOrEqual<super::U16>
12+
{
13+
}
14+
}
15+
16+
pub trait TagSize: ArrayLength<u8> + Unsigned + private::SealedTag {}
17+
18+
impl<T> TagSize for T where T: ArrayLength<u8> + IsGreaterOrEqual<U4> + IsLessOrEqual<U16> {}

0 commit comments

Comments
 (0)