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}