Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aes-gcm-0.9.4/src/lib.rs
Line
Count
Source
1
//! AES-GCM: [Authenticated Encryption and Associated Data (AEAD)][1] cipher
2
//! based on AES in [Galois/Counter Mode][2].
3
//!
4
//! ## Performance Notes
5
//!
6
//! By default this crate will use software implementations of both AES and
7
//! the POLYVAL universal hash function.
8
//!
9
//! When targeting modern x86/x86_64 CPUs, use the following `RUSTFLAGS` to
10
//! take advantage of high performance AES-NI and CLMUL CPU intrinsics:
11
//!
12
//! ```text
13
//! RUSTFLAGS="-Ctarget-cpu=sandybridge -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"
14
//! ```
15
//!
16
//! ## Security Notes
17
//!
18
//! This crate has received one [security audit by NCC Group][3], with no significant
19
//! findings. We would like to thank [MobileCoin][4] for funding the audit.
20
//!
21
//! All implementations contained in the crate are designed to execute in constant
22
//! time, either by relying on hardware intrinsics (i.e. AES-NI and CLMUL on
23
//! x86/x86_64), or using a portable implementation which is only constant time
24
//! on processors which implement constant-time multiplication.
25
//!
26
//! It is not suitable for use on processors with a variable-time multiplication
27
//! operation (e.g. short circuit on multiply-by-zero / multiply-by-one, such as
28
//! certain 32-bit PowerPC CPUs and some non-ARM microcontrollers).
29
//!
30
//! # Usage
31
//!
32
//! Simple usage (allocating, no associated data):
33
//!
34
//! ```
35
//! use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm`
36
//! use aes_gcm::aead::{Aead, NewAead};
37
//!
38
//! let key = Key::from_slice(b"an example very very secret key.");
39
//! let cipher = Aes256Gcm::new(key);
40
//!
41
//! let nonce = Nonce::from_slice(b"unique nonce"); // 96-bits; unique per message
42
//!
43
//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())
44
//!     .expect("encryption failure!"); // NOTE: handle this error to avoid panics!
45
//!
46
//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())
47
//!     .expect("decryption failure!"); // NOTE: handle this error to avoid panics!
48
//!
49
//! assert_eq!(&plaintext, b"plaintext message");
50
//! ```
51
//!
52
//! ## In-place Usage (eliminates `alloc` requirement)
53
//!
54
//! This crate has an optional `alloc` feature which can be disabled in e.g.
55
//! microcontroller environments that don't have a heap.
56
//!
57
//! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`]
58
//! methods accept any type that impls the [`aead::Buffer`] trait which
59
//! contains the plaintext for encryption or ciphertext for decryption.
60
//!
61
//! Note that if you enable the `heapless` feature of this crate,
62
//! you will receive an impl of [`aead::Buffer`] for `heapless::Vec`
63
//! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]),
64
//! which can then be passed as the `buffer` parameter to the in-place encrypt
65
//! and decrypt methods:
66
//!
67
//! ```
68
//! # #[cfg(feature = "heapless")]
69
//! # {
70
//! use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm`
71
//! use aes_gcm::aead::{AeadInPlace, NewAead};
72
//! use aes_gcm::aead::heapless::Vec;
73
//!
74
//! let key = Key::from_slice(b"an example very very secret key.");
75
//! let cipher = Aes256Gcm::new(key);
76
//!
77
//! let nonce = Nonce::from_slice(b"unique nonce"); // 96-bits; unique per message
78
//!
79
//! let mut buffer: Vec<u8, 128> = Vec::new(); // Buffer needs 16-bytes overhead for GCM tag
80
//! buffer.extend_from_slice(b"plaintext message");
81
//!
82
//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
83
//! cipher.encrypt_in_place(nonce, b"", &mut buffer).expect("encryption failure!");
84
//!
85
//! // `buffer` now contains the message ciphertext
86
//! assert_ne!(&buffer, b"plaintext message");
87
//!
88
//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
89
//! cipher.decrypt_in_place(nonce, b"", &mut buffer).expect("decryption failure!");
90
//! assert_eq!(&buffer, b"plaintext message");
91
//! # }
92
//! ```
93
//!
94
//! [1]: https://en.wikipedia.org/wiki/Authenticated_encryption
95
//! [2]: https://en.wikipedia.org/wiki/Galois/Counter_Mode
96
//! [3]: https://research.nccgroup.com/2020/02/26/public-report-rustcrypto-aes-gcm-and-chacha20poly1305-implementation-review/
97
//! [4]: https://www.mobilecoin.com/
98
99
#![no_std]
100
#![cfg_attr(docsrs, feature(doc_cfg))]
101
#![doc(
102
    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
103
    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
104
)]
105
#![deny(unsafe_code)]
106
#![warn(missing_docs, rust_2018_idioms)]
107
108
pub use aead::{self, AeadCore, AeadInPlace, Error, NewAead};
109
110
#[cfg(feature = "aes")]
111
pub use aes;
112
113
use cipher::{
114
    consts::{U0, U16},
115
    generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
116
    Block, BlockCipher, BlockCipherKey, BlockEncrypt, FromBlockCipher, NewBlockCipher,
117
    StreamCipher, StreamCipherSeek,
118
};
119
use core::marker::PhantomData;
120
use ctr::Ctr32BE;
121
use ghash::{
122
    universal_hash::{NewUniversalHash, UniversalHash},
123
    GHash,
124
};
125
126
#[cfg(feature = "zeroize")]
127
use zeroize::Zeroize;
128
129
#[cfg(feature = "aes")]
130
use aes::{cipher::consts::U12, Aes128, Aes256};
131
132
/// Maximum length of associated data
133
pub const A_MAX: u64 = 1 << 36;
134
135
/// Maximum length of plaintext
136
pub const P_MAX: u64 = 1 << 36;
137
138
/// Maximum length of ciphertext
139
pub const C_MAX: u64 = (1 << 36) + 16;
140
141
/// AES-GCM keys
142
pub type Key<KeySize> = GenericArray<u8, KeySize>;
143
144
/// AES-GCM nonces
145
pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
146
147
/// AES-GCM tags
148
pub type Tag = GenericArray<u8, U16>;
149
150
/// AES-GCM with a 128-bit key and 96-bit nonce
151
#[cfg(feature = "aes")]
152
#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
153
pub type Aes128Gcm = AesGcm<Aes128, U12>;
154
155
/// AES-GCM with a 256-bit key and 96-bit nonce
156
#[cfg(feature = "aes")]
157
#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
158
pub type Aes256Gcm = AesGcm<Aes256, U12>;
159
160
/// AES-GCM: generic over an underlying AES implementation and nonce size.
161
///
162
/// This type is generic to support substituting alternative AES implementations
163
/// (e.g. embedded hardware implementations)
164
///
165
/// It is NOT intended to be instantiated with any block cipher besides AES!
166
/// Doing so runs the risk of unintended cryptographic properties!
167
///
168
/// The `N` generic parameter can be used to instantiate AES-GCM with other
169
/// nonce sizes, however it's recommended to use it with `typenum::U12`,
170
/// the default of 96-bits.
171
///
172
/// If in doubt, use the built-in [`Aes128Gcm`] and [`Aes256Gcm`] type aliases.
173
#[derive(Clone)]
174
pub struct AesGcm<Aes, NonceSize>
175
where
176
    Aes: BlockCipher<BlockSize = U16> + BlockEncrypt,
177
    Aes::ParBlocks: ArrayLength<Block<Aes>>,
178
    NonceSize: ArrayLength<u8>,
179
{
180
    /// Encryption cipher
181
    cipher: Aes,
182
183
    /// GHASH authenticator
184
    ghash: GHash,
185
186
    /// Length of the nonce
187
    nonce_size: PhantomData<NonceSize>,
188
}
189
190
impl<Aes, NonceSize> NewAead for AesGcm<Aes, NonceSize>
191
where
192
    Aes: NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
193
    Aes::ParBlocks: ArrayLength<Block<Aes>>,
194
    NonceSize: ArrayLength<u8>,
195
{
196
    type KeySize = Aes::KeySize;
197
198
1.35k
    fn new(key: &BlockCipherKey<Aes>) -> Self {
199
1.35k
        Aes::new(key).into()
200
1.35k
    }
201
}
202
203
impl<Aes, NonceSize> From<Aes> for AesGcm<Aes, NonceSize>
204
where
205
    Aes: NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
206
    Aes::ParBlocks: ArrayLength<Block<Aes>>,
207
    NonceSize: ArrayLength<u8>,
208
{
209
1.35k
    fn from(cipher: Aes) -> Self {
210
1.35k
        let mut ghash_key = ghash::Key::default();
211
1.35k
        cipher.encrypt_block(&mut ghash_key);
212
213
1.35k
        let ghash = GHash::new(&ghash_key);
214
215
        #[cfg(feature = "zeroize")]
216
        ghash_key.zeroize();
217
218
1.35k
        Self {
219
1.35k
            cipher,
220
1.35k
            ghash,
221
1.35k
            nonce_size: PhantomData,
222
1.35k
        }
223
1.35k
    }
224
}
225
226
impl<Aes, NonceSize> AeadCore for AesGcm<Aes, NonceSize>
227
where
228
    Aes: NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
229
    Aes::ParBlocks: ArrayLength<Block<Aes>>,
230
    NonceSize: ArrayLength<u8>,
231
{
232
    type NonceSize = NonceSize;
233
    type TagSize = U16;
234
    type CiphertextOverhead = U0;
235
}
236
237
impl<Aes, NonceSize> AeadInPlace for AesGcm<Aes, NonceSize>
238
where
239
    Aes: NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
240
    Aes::ParBlocks: ArrayLength<Block<Aes>>,
241
    NonceSize: ArrayLength<u8>,
242
{
243
    fn encrypt_in_place_detached(
244
        &self,
245
        nonce: &Nonce<Self::NonceSize>,
246
        associated_data: &[u8],
247
        buffer: &mut [u8],
248
    ) -> Result<Tag, Error> {
249
        if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
250
            return Err(Error);
251
        }
252
253
        // TODO(tarcieri): interleave encryption with GHASH
254
        // See: <https://github.com/RustCrypto/AEADs/issues/74>
255
        let mut ctr = self.init_ctr(nonce);
256
        ctr.seek(Aes::BlockSize::to_usize());
257
        ctr.apply_keystream(buffer);
258
259
        let mut tag = self.compute_tag(associated_data, buffer);
260
        ctr.seek(0);
261
        ctr.apply_keystream(tag.as_mut_slice());
262
263
        Ok(tag)
264
    }
265
266
2.87k
    fn decrypt_in_place_detached(
267
2.87k
        &self,
268
2.87k
        nonce: &Nonce<Self::NonceSize>,
269
2.87k
        associated_data: &[u8],
270
2.87k
        buffer: &mut [u8],
271
2.87k
        tag: &Tag,
272
2.87k
    ) -> Result<(), Error> {
273
2.87k
        if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
274
0
            return Err(Error);
275
2.87k
        }
276
277
        // TODO(tarcieri): interleave encryption with GHASH
278
        // See: <https://github.com/RustCrypto/AEADs/issues/74>
279
2.87k
        let mut expected_tag = self.compute_tag(associated_data, buffer);
280
2.87k
        let mut ctr = self.init_ctr(nonce);
281
2.87k
        ctr.apply_keystream(expected_tag.as_mut_slice());
282
283
        use subtle::ConstantTimeEq;
284
2.87k
        if expected_tag.ct_eq(&tag).unwrap_u8() == 1 {
285
60
            ctr.apply_keystream(buffer);
286
60
            Ok(())
287
        } else {
288
2.81k
            Err(Error)
289
        }
290
2.87k
    }
291
}
292
293
impl<Aes, NonceSize> AesGcm<Aes, NonceSize>
294
where
295
    Aes: NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
296
    Aes::ParBlocks: ArrayLength<Block<Aes>>,
297
    NonceSize: ArrayLength<u8>,
298
{
299
    /// Initialize counter mode.
300
    ///
301
    /// See algorithm described in Section 7.2 of NIST SP800-38D:
302
    /// <https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf>
303
    ///
304
    /// > Define a block, J0, as follows:
305
    /// > If len(IV)=96, then J0 = IV || 0{31} || 1.
306
    /// > If len(IV) ≠ 96, then let s = 128 ⎡len(IV)/128⎤-len(IV), and
307
    /// >     J0=GHASH(IV||0s+64||[len(IV)]64).
308
2.87k
    fn init_ctr(&self, nonce: &Nonce<NonceSize>) -> Ctr32BE<&Aes> {
309
2.87k
        let j0 = if NonceSize::to_usize() == 12 {
310
2.87k
            let mut block = ghash::Block::default();
311
2.87k
            block[..12].copy_from_slice(nonce);
312
2.87k
            block[15] = 1;
313
2.87k
            block
314
        } else {
315
0
            let mut ghash = self.ghash.clone();
316
0
            ghash.update_padded(nonce);
317
318
0
            let mut block = ghash::Block::default();
319
0
            let nonce_bits = (NonceSize::to_usize() as u64) * 8;
320
0
            block[8..].copy_from_slice(&nonce_bits.to_be_bytes());
321
0
            ghash.update(&block);
322
0
            ghash.finalize().into_bytes()
323
        };
324
325
2.87k
        Ctr32BE::from_block_cipher(&self.cipher, &j0)
326
2.87k
    }
327
328
    /// Authenticate the given plaintext and associated data using GHASH
329
2.87k
    fn compute_tag(&self, associated_data: &[u8], buffer: &[u8]) -> Tag {
330
2.87k
        let mut ghash = self.ghash.clone();
331
2.87k
        ghash.update_padded(associated_data);
332
2.87k
        ghash.update_padded(buffer);
333
334
2.87k
        let associated_data_bits = (associated_data.len() as u64) * 8;
335
2.87k
        let buffer_bits = (buffer.len() as u64) * 8;
336
337
2.87k
        let mut block = ghash::Block::default();
338
2.87k
        block[..8].copy_from_slice(&associated_data_bits.to_be_bytes());
339
2.87k
        block[8..].copy_from_slice(&buffer_bits.to_be_bytes());
340
2.87k
        ghash.update(&block);
341
2.87k
        ghash.finalize().into_bytes()
342
2.87k
    }
343
}