ccm/
lib.rs

1//! Counter with CBC-MAC ([CCM]): [Authenticated Encryption and Associated Data (AEAD)][1]
2//! algorithm generic over block ciphers with block size equal to 128 bits as specified in
3//! [RFC 3610].
4//!
5//! # Usage
6//!
7//! Simple usage (allocating, no associated data):
8//!
9#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
10#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
11//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! use aes::Aes256;
13//! use ccm::{
14//!     aead::{Aead, KeyInit, OsRng, generic_array::GenericArray},
15//!     consts::{U10, U13},
16//!     Ccm,
17//! };
18//!
19//! // AES-256-CCM type with tag and nonce size equal to 10 and 13 bytes respectively
20//! pub type Aes256Ccm = Ccm<Aes256, U10, U13>;
21//!
22//! let key = Aes256Ccm::generate_key(&mut OsRng);
23//! let cipher = Aes256Ccm::new(&key);
24//! let nonce = GenericArray::from_slice(b"unique nonce."); // 13-bytes; unique per message
25//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())?;
26//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())?;
27//! assert_eq!(&plaintext, b"plaintext message");
28//! # Ok(())
29//! # }
30//! ```
31//!
32//! This crate implements traits from the [`aead`] crate and is capable to perfrom
33//! encryption and decryption in-place wihout relying on `alloc`.
34//!
35//! [RFC 3610]: https://tools.ietf.org/html/rfc3610
36//! [CCM]: https://en.wikipedia.org/wiki/CCM_mode
37//! [aead]: https://docs.rs/aead
38//! [1]: https://en.wikipedia.org/wiki/Authenticated_encryption
39
40#![no_std]
41#![doc(
42    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
43    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
44)]
45#![deny(unsafe_code)]
46#![warn(missing_docs, rust_2018_idioms)]
47
48pub use aead::{self, consts, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
49
50use aead::{
51    consts::{U0, U16},
52    generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
53};
54use cipher::{
55    Block, BlockCipher, BlockEncrypt, BlockSizeUser, InnerIvInit, StreamCipher, StreamCipherSeek,
56};
57use core::marker::PhantomData;
58use ctr::{Ctr32BE, Ctr64BE, CtrCore};
59use subtle::ConstantTimeEq;
60
61mod private;
62
63/// CCM nonces
64pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
65
66/// CCM tags
67pub type Tag<TagSize> = GenericArray<u8, TagSize>;
68
69/// Trait implemented for valid tag sizes, i.e.
70/// [`U4`][consts::U4], [`U6`][consts::U6], [`U8`][consts::U8],
71/// [`U10`][consts::U10], [`U12`][consts::U12], [`U14`][consts::U14], and
72/// [`U16`][consts::U16].
73pub trait TagSize: private::SealedTag {}
74
75impl<T: private::SealedTag> TagSize for T {}
76
77/// Trait implemented for valid nonce sizes, i.e.
78/// [`U7`][consts::U7], [`U8`][consts::U8], [`U9`][consts::U9],
79/// [`U10`][consts::U10], [`U11`][consts::U11], [`U12`][consts::U12], and
80/// [`U13`][consts::U13].
81pub trait NonceSize: private::SealedNonce {}
82
83impl<T: private::SealedNonce> NonceSize for T {}
84
85/// CCM instance generic over an underlying block cipher.
86///
87/// Type parameters:
88/// - `C`: block cipher.
89/// - `M`: size of MAC tag in bytes, valid values:
90/// [`U4`][consts::U4], [`U6`][consts::U6], [`U8`][consts::U8],
91/// [`U10`][consts::U10], [`U12`][consts::U12], [`U14`][consts::U14],
92/// [`U12`][consts::U12].
93/// - `N`: size of nonce, valid values:
94/// [`U7`][consts::U7], [`U8`][consts::U8], [`U9`][consts::U9],
95/// [`U10`][consts::U10], [`U11`][consts::U11], [`U12`][consts::U12],
96/// [`U13`][consts::U13].
97#[derive(Clone)]
98pub struct Ccm<C, M, N>
99where
100    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
101    M: ArrayLength<u8> + TagSize,
102    N: ArrayLength<u8> + NonceSize,
103{
104    cipher: C,
105    _pd: PhantomData<(M, N)>,
106}
107
108impl<C, M, N> Ccm<C, M, N>
109where
110    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
111    M: ArrayLength<u8> + TagSize,
112    N: ArrayLength<u8> + NonceSize,
113{
114    fn extend_nonce(nonce: &Nonce<N>) -> Block<C> {
115        let mut ext_nonce = Block::<C>::default();
116        ext_nonce[0] = N::get_l() - 1;
117        ext_nonce[1..][..nonce.len()].copy_from_slice(nonce);
118        ext_nonce
119    }
120
121    fn calc_mac(
122        &self,
123        nonce: &Nonce<N>,
124        adata: &[u8],
125        buffer: &[u8],
126    ) -> Result<Tag<C::BlockSize>, Error> {
127        let is_ad = !adata.is_empty();
128        let l = N::get_l();
129        let flags = 64 * (is_ad as u8) + 8 * M::get_m_tick() + (l - 1);
130
131        if buffer.len() > N::get_max_len() {
132            return Err(Error);
133        }
134
135        let mut b0 = Block::<C>::default();
136        b0[0] = flags;
137        let n = 1 + N::to_usize();
138        b0[1..n].copy_from_slice(nonce);
139
140        let cb = b0.len() - n;
141        // the max len check makes certain that we discard only
142        // zero bytes from `b`
143        if cb > 4 {
144            let b = (buffer.len() as u64).to_be_bytes();
145            b0[n..].copy_from_slice(&b[b.len() - cb..]);
146        } else {
147            let b = (buffer.len() as u32).to_be_bytes();
148            b0[n..].copy_from_slice(&b[b.len() - cb..]);
149        }
150
151        let mut mac = CbcMac::from_cipher(&self.cipher);
152        mac.block_update(&b0);
153
154        if !adata.is_empty() {
155            let alen = adata.len();
156            let (n, mut b) = fill_aad_header(alen);
157            if b.len() - n >= alen {
158                b[n..][..alen].copy_from_slice(adata);
159                mac.block_update(&b);
160            } else {
161                let (l, r) = adata.split_at(b.len() - n);
162                b[n..].copy_from_slice(l);
163                mac.block_update(&b);
164                mac.update(r);
165            }
166        }
167
168        mac.update(buffer);
169
170        Ok(mac.finalize())
171    }
172}
173
174impl<C, M, N> From<C> for Ccm<C, M, N>
175where
176    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
177    M: ArrayLength<u8> + TagSize,
178    N: ArrayLength<u8> + NonceSize,
179{
180    fn from(cipher: C) -> Self {
181        Self {
182            cipher,
183            _pd: PhantomData,
184        }
185    }
186}
187
188impl<C, M, N> KeySizeUser for Ccm<C, M, N>
189where
190    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit,
191    M: ArrayLength<u8> + TagSize,
192    N: ArrayLength<u8> + NonceSize,
193{
194    type KeySize = C::KeySize;
195}
196
197impl<C, M, N> KeyInit for Ccm<C, M, N>
198where
199    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit,
200    M: ArrayLength<u8> + TagSize,
201    N: ArrayLength<u8> + NonceSize,
202{
203    fn new(key: &Key<Self>) -> Self {
204        Self::from(C::new(key))
205    }
206}
207
208impl<C, M, N> AeadCore for Ccm<C, M, N>
209where
210    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
211    M: ArrayLength<u8> + TagSize,
212    N: ArrayLength<u8> + NonceSize,
213{
214    type NonceSize = N;
215    type TagSize = M;
216    type CiphertextOverhead = U0;
217}
218
219impl<C, M, N> AeadInPlace for Ccm<C, M, N>
220where
221    C: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
222    M: ArrayLength<u8> + TagSize,
223    N: ArrayLength<u8> + NonceSize,
224{
225    fn encrypt_in_place_detached(
226        &self,
227        nonce: &Nonce<N>,
228        adata: &[u8],
229        buffer: &mut [u8],
230    ) -> Result<Tag<Self::TagSize>, Error> {
231        let mut full_tag = self.calc_mac(nonce, adata, buffer)?;
232
233        let ext_nonce = Self::extend_nonce(nonce);
234        // number of bytes left for counter (max 8)
235        let cb = C::BlockSize::USIZE - N::USIZE - 1;
236
237        if cb > 4 {
238            let mut ctr = Ctr64BE::from_core(CtrCore::inner_iv_init(&self.cipher, &ext_nonce));
239            ctr.apply_keystream(&mut full_tag);
240            ctr.apply_keystream(buffer);
241        } else {
242            let mut ctr = Ctr32BE::from_core(CtrCore::inner_iv_init(&self.cipher, &ext_nonce));
243            ctr.apply_keystream(&mut full_tag);
244            ctr.apply_keystream(buffer);
245        }
246
247        Ok(Tag::clone_from_slice(&full_tag[..M::to_usize()]))
248    }
249
250    fn decrypt_in_place_detached(
251        &self,
252        nonce: &Nonce<N>,
253        adata: &[u8],
254        buffer: &mut [u8],
255        tag: &Tag<Self::TagSize>,
256    ) -> Result<(), Error> {
257        let ext_nonce = Self::extend_nonce(nonce);
258        // number of bytes left for counter (max 8)
259        let cb = C::BlockSize::USIZE - N::USIZE - 1;
260
261        if cb > 4 {
262            let mut ctr = Ctr64BE::from_core(CtrCore::inner_iv_init(&self.cipher, &ext_nonce));
263            ctr.seek(C::BlockSize::USIZE);
264            ctr.apply_keystream(buffer);
265        } else {
266            let mut ctr = Ctr32BE::from_core(CtrCore::inner_iv_init(&self.cipher, &ext_nonce));
267            ctr.seek(C::BlockSize::USIZE);
268            ctr.apply_keystream(buffer);
269        }
270
271        let mut full_tag = self.calc_mac(nonce, adata, buffer)?;
272
273        if cb > 4 {
274            let mut ctr = Ctr64BE::from_core(CtrCore::inner_iv_init(&self.cipher, &ext_nonce));
275            ctr.apply_keystream(&mut full_tag);
276        } else {
277            let mut ctr = Ctr32BE::from_core(CtrCore::inner_iv_init(&self.cipher, &ext_nonce));
278            ctr.apply_keystream(&mut full_tag);
279        }
280
281        if full_tag[..tag.len()].ct_eq(tag).into() {
282            Ok(())
283        } else {
284            buffer.iter_mut().for_each(|v| *v = 0);
285            Err(Error)
286        }
287    }
288}
289
290struct CbcMac<'a, C: BlockCipher + BlockEncrypt> {
291    cipher: &'a C,
292    state: Block<C>,
293}
294
295impl<'a, C> CbcMac<'a, C>
296where
297    C: BlockCipher + BlockEncrypt,
298{
299    fn from_cipher(cipher: &'a C) -> Self {
300        Self {
301            cipher,
302            state: Default::default(),
303        }
304    }
305
306    fn update(&mut self, data: &[u8]) {
307        let mut chunks = data.chunks_exact(C::BlockSize::USIZE);
308        for chunk in &mut chunks {
309            self.block_update(Block::<C>::from_slice(chunk));
310        }
311        let rem = chunks.remainder();
312        if !rem.is_empty() {
313            let mut bn = Block::<C>::default();
314            bn[..rem.len()].copy_from_slice(rem);
315            self.block_update(&bn);
316        }
317    }
318
319    fn block_update(&mut self, block: &Block<C>) {
320        self.state
321            .iter_mut()
322            .zip(block.iter())
323            .for_each(|(a, b)| *a ^= b);
324        self.cipher.encrypt_block(&mut self.state);
325    }
326
327    fn finalize(self) -> Block<C> {
328        self.state
329    }
330}
331
332fn fill_aad_header(adata_len: usize) -> (usize, GenericArray<u8, U16>) {
333    debug_assert_ne!(adata_len, 0);
334
335    let mut b = GenericArray::<u8, U16>::default();
336    let n = if adata_len < 0xFF00 {
337        b[..2].copy_from_slice(&(adata_len as u16).to_be_bytes());
338        2
339    } else if adata_len <= core::u32::MAX as usize {
340        b[0] = 0xFF;
341        b[1] = 0xFE;
342        b[2..6].copy_from_slice(&(adata_len as u32).to_be_bytes());
343        6
344    } else {
345        b[0] = 0xFF;
346        b[1] = 0xFF;
347        b[2..10].copy_from_slice(&(adata_len as u64).to_be_bytes());
348        10
349    };
350    (n, b)
351}
352
353#[cfg(test)]
354mod tests {
355    #[test]
356    fn fill_aad_header_test() {
357        use super::fill_aad_header;
358        use hex_literal::hex;
359
360        let (n, b) = fill_aad_header(0x0123);
361        assert_eq!(n, 2);
362        assert_eq!(b[..], hex!("01230000000000000000000000000000")[..]);
363
364        let (n, b) = fill_aad_header(0xFF00);
365        assert_eq!(n, 6);
366        assert_eq!(b[..], hex!("FFFE0000FF0000000000000000000000")[..]);
367
368        let (n, b) = fill_aad_header(0x01234567);
369        assert_eq!(n, 6);
370        assert_eq!(b[..], hex!("FFFE0123456700000000000000000000")[..]);
371
372        #[cfg(target_pointer_width = "64")]
373        {
374            let (n, b) = fill_aad_header(0x0123456789ABCDEF);
375            assert_eq!(n, 10);
376            assert_eq!(b[..], hex!("FFFF0123456789ABCDEF000000000000")[..]);
377        }
378    }
379}