eax/lib.rs
1//! EAX: [Authenticated Encryption and Associated Data (AEAD)][1] cipher
2//! based on AES in counter mode.
3//!
4//! # Usage
5//!
6//! Simple usage (allocating, no associated data):
7//!
8#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
9#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
10//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
11//! use aes::Aes256;
12//! use eax::{
13//! aead::{Aead, KeyInit, OsRng, generic_array::GenericArray},
14//! Eax, Nonce
15//! };
16//!
17//! pub type Aes256Eax = Eax<Aes256>;
18//!
19//! let key = Aes256Eax::generate_key(&mut OsRng);
20//! let cipher = Aes256Eax::new(&key);
21//! let nonce = GenericArray::from_slice(b"my unique nonces"); // 128-bits; unique per message
22//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())?;
23//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())?;
24//! assert_eq!(&plaintext, b"plaintext message");
25//! # Ok(())
26//! # }
27//! ```
28//!
29//! ## In-place Usage (eliminates `alloc` requirement)
30//!
31//! This crate has an optional `alloc` feature which can be disabled in e.g.
32//! microcontroller environments that don't have a heap.
33//!
34//! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`]
35//! methods accept any type that impls the [`aead::Buffer`] trait which
36//! contains the plaintext for encryption or ciphertext for decryption.
37//!
38//! Note that if you enable the `heapless` feature of this crate,
39//! you will receive an impl of [`aead::Buffer`] for `heapless::Vec`
40//! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]),
41//! which can then be passed as the `buffer` parameter to the in-place encrypt
42//! and decrypt methods:
43//!
44//! ```
45//! # #[cfg(feature = "heapless")]
46//! # {
47//! use aes::Aes256;
48//! use eax::Eax;
49//! use eax::aead::{AeadInPlace, KeyInit, generic_array::GenericArray};
50//! use eax::aead::heapless::Vec;
51//!
52//! let key = GenericArray::from_slice(b"an example very very secret key.");
53//! let cipher = Eax::<Aes256>::new(key);
54//!
55//! let nonce = GenericArray::from_slice(b"my unique nonces"); // 128-bits; unique per message
56//!
57//! let mut buffer: Vec<u8, 128> = Vec::new();
58//! buffer.extend_from_slice(b"plaintext message");
59//!
60//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
61//! cipher.encrypt_in_place(nonce, b"", &mut buffer).expect("encryption failure!");
62//!
63//! // `buffer` now contains the message ciphertext
64//! assert_ne!(&buffer, b"plaintext message");
65//!
66//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
67//! cipher.decrypt_in_place(nonce, b"", &mut buffer).expect("decryption failure!");
68//! assert_eq!(&buffer, b"plaintext message");
69//! # }
70//! ```
71//!
72//! ## Custom Tag Length
73//!
74//! The tag for eax is usually 16 bytes long but it can be shortened if needed.
75//! The second generic argument of `Eax` can be set to the tag length:
76//!
77//! ```
78//! # #[cfg(feature = "heapless")]
79//! # {
80//! use aes::Aes256;
81//! use eax::Eax;
82//! use eax::aead::{AeadInPlace, KeyInit, generic_array::GenericArray};
83//! use eax::aead::heapless::Vec;
84//! use eax::aead::consts::{U8, U128};
85//!
86//! let key = GenericArray::from_slice(b"an example very very secret key.");
87//! let cipher = Eax::<Aes256, U8>::new(key);
88//!
89//! let nonce = GenericArray::from_slice(b"my unique nonces"); // 128-bits; unique per message
90//!
91//! let mut buffer: Vec<u8, 128> = Vec::new();
92//! buffer.extend_from_slice(b"plaintext message");
93//!
94//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
95//! let tag = cipher.encrypt_in_place_detached(nonce, b"", &mut buffer).expect("encryption failure!");
96//!
97//! // The tag has only 8 bytes, compared to the usual 16 bytes
98//! assert_eq!(tag.len(), 8);
99//!
100//! // `buffer` now contains the message ciphertext
101//! assert_ne!(&buffer, b"plaintext message");
102//!
103//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
104//! cipher.decrypt_in_place_detached(nonce, b"", &mut buffer, &tag).expect("decryption failure!");
105//! assert_eq!(&buffer, b"plaintext message");
106//! # }
107//! ```
108//!
109//! [1]: https://en.wikipedia.org/wiki/Authenticated_encryption
110
111#![no_std]
112#![cfg_attr(docsrs, feature(doc_cfg))]
113#![doc(
114 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
115 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
116)]
117#![deny(unsafe_code)]
118#![warn(missing_docs, rust_2018_idioms)]
119
120pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
121pub use cipher;
122
123use cipher::{
124 consts::{U0, U16},
125 generic_array::{functional::FunctionalSequence, GenericArray},
126 BlockCipher, BlockEncrypt, InnerIvInit, StreamCipherCore,
127};
128use cmac::{digest::Output, Cmac, Mac};
129use core::marker::PhantomData;
130
131mod traits;
132
133use traits::TagSize;
134
135// TODO Max values?
136/// Maximum length of associated data
137pub const A_MAX: u64 = 1 << 36;
138
139/// Maximum length of plaintext
140pub const P_MAX: u64 = 1 << 36;
141
142/// Maximum length of ciphertext
143pub const C_MAX: u64 = (1 << 36) + 16;
144
145/// EAX nonces
146pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
147
148/// EAX tags
149pub type Tag<TagSize> = GenericArray<u8, TagSize>;
150
151pub mod online;
152
153/// Counter mode with a 128-bit big endian counter.
154type Ctr128BE<C> = ctr::CtrCore<C, ctr::flavors::Ctr128BE>;
155
156/// EAX: generic over an underlying block cipher implementation.
157///
158/// This type is generic to support substituting alternative cipher
159/// implementations.
160///
161/// If in doubt, use the built-in [`Aes128Eax`] and [`Aes256Eax`] type aliases.
162///
163/// Type parameters:
164/// - `Cipher`: block cipher.
165/// - `M`: size of MAC tag, valid values: up to `U16`.
166#[derive(Clone)]
167pub struct Eax<Cipher, M = U16>
168where
169 Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
170 M: TagSize,
171{
172 /// Encryption key
173 key: Key<Cipher>,
174 _tag_size: PhantomData<M>,
175}
176
177impl<Cipher, M> KeySizeUser for Eax<Cipher, M>
178where
179 Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
180 M: TagSize,
181{
182 type KeySize = Cipher::KeySize;
183}
184
185impl<Cipher, M> KeyInit for Eax<Cipher, M>
186where
187 Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
188 M: TagSize,
189{
190 fn new(key: &Key<Cipher>) -> Self {
191 Self {
192 key: key.clone(),
193 _tag_size: PhantomData,
194 }
195 }
196}
197
198impl<Cipher, M> AeadCore for Eax<Cipher, M>
199where
200 Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
201 M: TagSize,
202{
203 type NonceSize = Cipher::BlockSize;
204 type TagSize = M;
205 type CiphertextOverhead = U0;
206}
207
208impl<Cipher, M> AeadInPlace for Eax<Cipher, M>
209where
210 Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
211 M: TagSize,
212{
213 fn encrypt_in_place_detached(
214 &self,
215 nonce: &Nonce<Self::NonceSize>,
216 associated_data: &[u8],
217 buffer: &mut [u8],
218 ) -> Result<Tag<M>, Error> {
219 if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
220 return Err(Error);
221 }
222
223 // https://crypto.stackexchange.com/questions/26948/eax-cipher-mode-with-nonce-equal-header
224 // has an explanation of eax.
225
226 // l = block cipher size = 128 (for AES-128) = 16 byte
227 // 1. n ← OMAC(0 || Nonce)
228 // (the 0 means the number zero in l bits)
229 let n = Self::cmac_with_iv(&self.key, 0, nonce);
230
231 // 2. h ← OMAC(1 || associated data)
232 let h = Self::cmac_with_iv(&self.key, 1, associated_data);
233
234 // 3. enc ← CTR(M) using n as iv
235 Ctr128BE::<Cipher>::inner_iv_init(Cipher::new(&self.key), &n)
236 .apply_keystream_partial(buffer.into());
237
238 // 4. c ← OMAC(2 || enc)
239 let c = Self::cmac_with_iv(&self.key, 2, buffer);
240
241 // 5. tag ← n ^ h ^ c
242 // (^ means xor)
243 let full_tag = n.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
244 let tag = Tag::<M>::clone_from_slice(&full_tag[..M::to_usize()]);
245 Ok(tag)
246 }
247
248 fn decrypt_in_place_detached(
249 &self,
250 nonce: &Nonce<Self::NonceSize>,
251 associated_data: &[u8],
252 buffer: &mut [u8],
253 tag: &Tag<M>,
254 ) -> Result<(), Error> {
255 if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
256 return Err(Error);
257 }
258
259 // 1. n ← OMAC(0 || Nonce)
260 let n = Self::cmac_with_iv(&self.key, 0, nonce);
261
262 // 2. h ← OMAC(1 || associated data)
263 let h = Self::cmac_with_iv(&self.key, 1, associated_data);
264
265 // 4. c ← OMAC(2 || enc)
266 let c = Self::cmac_with_iv(&self.key, 2, buffer);
267
268 // 5. tag ← n ^ h ^ c
269 // (^ means xor)
270 let expected_tag = n.zip(h, |a, b| a ^ b).zip(c, |a, b| a ^ b);
271
272 let expected_tag = &expected_tag[..tag.len()];
273
274 // Constant-time MAC comparison
275 use subtle::ConstantTimeEq;
276 if expected_tag.ct_eq(tag).into() {
277 // Decrypt
278 Ctr128BE::<Cipher>::inner_iv_init(Cipher::new(&self.key), &n)
279 .apply_keystream_partial(buffer.into());
280
281 Ok(())
282 } else {
283 Err(Error)
284 }
285 }
286}
287
288impl<Cipher, M> Eax<Cipher, M>
289where
290 Cipher: BlockCipher<BlockSize = U16> + BlockEncrypt + Clone + KeyInit,
291 M: TagSize,
292{
293 /// CMAC/OMAC1
294 ///
295 /// To avoid constructing new buffers on the heap, an iv encoded into 16
296 /// bytes is prepended inside this function.
297 fn cmac_with_iv(
298 key: &GenericArray<u8, Cipher::KeySize>,
299 iv: u8,
300 data: &[u8],
301 ) -> Output<Cmac<Cipher>> {
302 let mut mac = <Cmac<Cipher> as Mac>::new(key);
303 mac.update(&[0; 15]);
304 mac.update(&[iv]);
305 mac.update(data);
306
307 mac.finalize().into_bytes()
308 }
309}