Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aes-gcm-0.10.3/src/lib.rs
Line
Count
Source
1
#![no_std]
2
#![cfg_attr(docsrs, feature(doc_cfg))]
3
#![doc = include_str!("../README.md")]
4
#![doc(
5
    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6
    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7
)]
8
#![deny(unsafe_code)]
9
#![warn(missing_docs, rust_2018_idioms)]
10
11
//! # Usage
12
//!
13
//! Simple usage (allocating, no associated data):
14
//!
15
//! ```
16
//! use aes_gcm::{
17
//!     aead::{Aead, AeadCore, KeyInit, OsRng},
18
//!     Aes256Gcm, Nonce, Key // Or `Aes128Gcm`
19
//! };
20
//!
21
//! # fn gen_key() -> Result<(), core::array::TryFromSliceError> {
22
//! // The encryption key can be generated randomly:
23
//! # #[cfg(all(feature = "getrandom", feature = "std"))] {
24
//! let key = Aes256Gcm::generate_key(OsRng);
25
//! # }
26
//!
27
//! // Transformed from a byte array:
28
//! let key: &[u8; 32] = &[42; 32];
29
//! let key: &Key<Aes256Gcm> = key.into();
30
//!
31
//! // Note that you can get byte array from slice using the `TryInto` trait:
32
//! let key: &[u8] = &[42; 32];
33
//! let key: [u8; 32] = key.try_into()?;
34
//! # Ok(()) }
35
//!
36
//! # fn main() -> Result<(), aes_gcm::Error> {
37
//! // Alternatively, the key can be transformed directly from a byte slice
38
//! // (panicks on length mismatch):
39
//! # let key: &[u8] = &[42; 32];
40
//! let key = Key::<Aes256Gcm>::from_slice(key);
41
//!
42
//! let cipher = Aes256Gcm::new(&key);
43
//! let nonce = Aes256Gcm::generate_nonce(&mut OsRng); // 96-bits; unique per message
44
//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?;
45
//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
46
//! assert_eq!(&plaintext, b"plaintext message");
47
//! # Ok(())
48
//! # }
49
//! ```
50
//!
51
//! ## In-place Usage (eliminates `alloc` requirement)
52
//!
53
//! This crate has an optional `alloc` feature which can be disabled in e.g.
54
//! microcontroller environments that don't have a heap.
55
//!
56
//! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`]
57
//! methods accept any type that impls the [`aead::Buffer`] trait which
58
//! contains the plaintext for encryption or ciphertext for decryption.
59
//!
60
//! Note that if you enable the `heapless` feature of this crate,
61
//! you will receive an impl of [`aead::Buffer`] for `heapless::Vec`
62
//! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]),
63
//! which can then be passed as the `buffer` parameter to the in-place encrypt
64
//! and decrypt methods:
65
//!
66
#![cfg_attr(
67
    all(feature = "getrandom", feature = "heapless", feature = "std"),
68
    doc = "```"
69
)]
70
#![cfg_attr(
71
    not(all(feature = "getrandom", feature = "heapless", feature = "std")),
72
    doc = "```ignore"
73
)]
74
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
75
//! use aes_gcm::{
76
//!     aead::{AeadCore, AeadInPlace, KeyInit, OsRng, heapless::Vec},
77
//!     Aes256Gcm, Nonce, // Or `Aes128Gcm`
78
//! };
79
//!
80
//! let key = Aes256Gcm::generate_key(&mut OsRng);
81
//! let cipher = Aes256Gcm::new(&key);
82
//! let nonce = Aes256Gcm::generate_nonce(&mut OsRng); // 96-bits; unique per message
83
//!
84
//! let mut buffer: Vec<u8, 128> = Vec::new(); // Note: buffer needs 16-bytes overhead for auth tag
85
//! buffer.extend_from_slice(b"plaintext message");
86
//!
87
//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
88
//! cipher.encrypt_in_place(&nonce, b"", &mut buffer)?;
89
//!
90
//! // `buffer` now contains the message ciphertext
91
//! assert_ne!(&buffer, b"plaintext message");
92
//!
93
//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
94
//! cipher.decrypt_in_place(&nonce, b"", &mut buffer)?;
95
//! assert_eq!(&buffer, b"plaintext message");
96
//! # Ok(())
97
//! # }
98
//! ```
99
//!
100
//! Similarly, enabling the `arrayvec` feature of this crate will provide an impl of
101
//! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as
102
//! [`aead::arrayvec::ArrayVec`]).
103
104
pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
105
106
#[cfg(feature = "aes")]
107
pub use aes;
108
109
use cipher::{
110
    consts::{U0, U16},
111
    generic_array::{ArrayLength, GenericArray},
112
    BlockCipher, BlockEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore,
113
};
114
use core::marker::PhantomData;
115
use ghash::{universal_hash::UniversalHash, GHash};
116
117
#[cfg(feature = "zeroize")]
118
use zeroize::Zeroize;
119
120
#[cfg(feature = "aes")]
121
use aes::{cipher::consts::U12, Aes128, Aes256};
122
123
/// Maximum length of associated data.
124
pub const A_MAX: u64 = 1 << 36;
125
126
/// Maximum length of plaintext.
127
pub const P_MAX: u64 = 1 << 36;
128
129
/// Maximum length of ciphertext.
130
pub const C_MAX: u64 = (1 << 36) + 16;
131
132
/// AES-GCM nonces.
133
pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
134
135
/// AES-GCM tags.
136
pub type Tag<TagSize = U16> = GenericArray<u8, TagSize>;
137
138
/// Trait implemented for valid tag sizes, i.e.
139
/// [`U12`][consts::U12], [`U13`][consts::U13], [`U14`][consts::U14],
140
/// [`U15`][consts::U15] and [`U16`][consts::U16].
141
pub trait TagSize: private::SealedTagSize {}
142
143
impl<T: private::SealedTagSize> TagSize for T {}
144
145
mod private {
146
    use aead::generic_array::ArrayLength;
147
    use cipher::{consts, Unsigned};
148
149
    // Sealed traits stop other crates from implementing any traits that use it.
150
    pub trait SealedTagSize: ArrayLength<u8> + Unsigned {}
151
152
    impl SealedTagSize for consts::U12 {}
153
    impl SealedTagSize for consts::U13 {}
154
    impl SealedTagSize for consts::U14 {}
155
    impl SealedTagSize for consts::U15 {}
156
    impl SealedTagSize for consts::U16 {}
157
}
158
159
/// AES-GCM with a 128-bit key and 96-bit nonce.
160
#[cfg(feature = "aes")]
161
#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
162
pub type Aes128Gcm = AesGcm<Aes128, U12>;
163
164
/// AES-GCM with a 256-bit key and 96-bit nonce.
165
#[cfg(feature = "aes")]
166
#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
167
pub type Aes256Gcm = AesGcm<Aes256, U12>;
168
169
/// AES block.
170
type Block = GenericArray<u8, U16>;
171
172
/// Counter mode with a 32-bit big endian counter.
173
type Ctr32BE<Aes> = ctr::CtrCore<Aes, ctr::flavors::Ctr32BE>;
174
175
/// AES-GCM: generic over an underlying AES implementation and nonce size.
176
///
177
/// This type is generic to support substituting alternative AES implementations
178
/// (e.g. embedded hardware implementations)
179
///
180
/// It is NOT intended to be instantiated with any block cipher besides AES!
181
/// Doing so runs the risk of unintended cryptographic properties!
182
///
183
/// The `NonceSize` generic parameter can be used to instantiate AES-GCM with other
184
/// nonce sizes, however it's recommended to use it with `typenum::U12`,
185
/// the default of 96-bits.
186
///
187
/// The `TagSize` generic parameter can be used to instantiate AES-GCM with other
188
/// authorization tag sizes, however it's recommended to use it with `typenum::U16`,
189
/// the default of 128-bits.
190
///
191
/// If in doubt, use the built-in [`Aes128Gcm`] and [`Aes256Gcm`] type aliases.
192
#[derive(Clone)]
193
pub struct AesGcm<Aes, NonceSize, TagSize = U16>
194
where
195
    TagSize: self::TagSize,
196
{
197
    /// Encryption cipher.
198
    cipher: Aes,
199
200
    /// GHASH authenticator.
201
    ghash: GHash,
202
203
    /// Length of the nonce.
204
    nonce_size: PhantomData<NonceSize>,
205
206
    /// Length of the tag.
207
    tag_size: PhantomData<TagSize>,
208
}
209
210
impl<Aes, NonceSize, TagSize> KeySizeUser for AesGcm<Aes, NonceSize, TagSize>
211
where
212
    Aes: KeySizeUser,
213
    TagSize: self::TagSize,
214
{
215
    type KeySize = Aes::KeySize;
216
}
217
218
impl<Aes, NonceSize, TagSize> KeyInit for AesGcm<Aes, NonceSize, TagSize>
219
where
220
    Aes: BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit,
221
    TagSize: self::TagSize,
222
{
223
1.48k
    fn new(key: &Key<Self>) -> Self {
224
1.48k
        Aes::new(key).into()
225
1.48k
    }
226
}
227
228
impl<Aes, NonceSize, TagSize> From<Aes> for AesGcm<Aes, NonceSize, TagSize>
229
where
230
    Aes: BlockSizeUser<BlockSize = U16> + BlockEncrypt,
231
    TagSize: self::TagSize,
232
{
233
1.48k
    fn from(cipher: Aes) -> Self {
234
1.48k
        let mut ghash_key = ghash::Key::default();
235
1.48k
        cipher.encrypt_block(&mut ghash_key);
236
237
1.48k
        let ghash = GHash::new(&ghash_key);
238
239
        #[cfg(feature = "zeroize")]
240
        ghash_key.zeroize();
241
242
1.48k
        Self {
243
1.48k
            cipher,
244
1.48k
            ghash,
245
1.48k
            nonce_size: PhantomData,
246
1.48k
            tag_size: PhantomData,
247
1.48k
        }
248
1.48k
    }
249
}
250
251
impl<Aes, NonceSize, TagSize> AeadCore for AesGcm<Aes, NonceSize, TagSize>
252
where
253
    NonceSize: ArrayLength<u8>,
254
    TagSize: self::TagSize,
255
{
256
    type NonceSize = NonceSize;
257
    type TagSize = TagSize;
258
    type CiphertextOverhead = U0;
259
}
260
261
impl<Aes, NonceSize, TagSize> AeadInPlace for AesGcm<Aes, NonceSize, TagSize>
262
where
263
    Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
264
    NonceSize: ArrayLength<u8>,
265
    TagSize: self::TagSize,
266
{
267
    fn encrypt_in_place_detached(
268
        &self,
269
        nonce: &Nonce<NonceSize>,
270
        associated_data: &[u8],
271
        buffer: &mut [u8],
272
    ) -> Result<Tag<TagSize>, Error> {
273
        if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
274
            return Err(Error);
275
        }
276
277
        let (ctr, mask) = self.init_ctr(nonce);
278
279
        // TODO(tarcieri): interleave encryption with GHASH
280
        // See: <https://github.com/RustCrypto/AEADs/issues/74>
281
        ctr.apply_keystream_partial(buffer.into());
282
283
        let full_tag = self.compute_tag(mask, associated_data, buffer);
284
        Ok(Tag::clone_from_slice(&full_tag[..TagSize::to_usize()]))
285
    }
286
287
1.21k
    fn decrypt_in_place_detached(
288
1.21k
        &self,
289
1.21k
        nonce: &Nonce<NonceSize>,
290
1.21k
        associated_data: &[u8],
291
1.21k
        buffer: &mut [u8],
292
1.21k
        tag: &Tag<TagSize>,
293
1.21k
    ) -> Result<(), Error> {
294
1.21k
        if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
295
0
            return Err(Error);
296
1.21k
        }
297
298
1.21k
        let (ctr, mask) = self.init_ctr(nonce);
299
300
        // TODO(tarcieri): interleave encryption with GHASH
301
        // See: <https://github.com/RustCrypto/AEADs/issues/74>
302
1.21k
        let expected_tag = self.compute_tag(mask, associated_data, buffer);
303
304
        use subtle::ConstantTimeEq;
305
1.21k
        if expected_tag[..TagSize::to_usize()].ct_eq(tag).into() {
306
476
            ctr.apply_keystream_partial(buffer.into());
307
476
            Ok(())
308
        } else {
309
740
            Err(Error)
310
        }
311
1.21k
    }
312
}
313
314
impl<Aes, NonceSize, TagSize> AesGcm<Aes, NonceSize, TagSize>
315
where
316
    Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
317
    NonceSize: ArrayLength<u8>,
318
    TagSize: self::TagSize,
319
{
320
    /// Initialize counter mode.
321
    ///
322
    /// See algorithm described in Section 7.2 of NIST SP800-38D:
323
    /// <https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf>
324
    ///
325
    /// > Define a block, J0, as follows:
326
    /// > If len(IV)=96, then J0 = IV || 0{31} || 1.
327
    /// > If len(IV) ≠ 96, then let s = 128 ⎡len(IV)/128⎤-len(IV), and
328
    /// >     J0=GHASH(IV||0s+64||[len(IV)]64).
329
1.21k
    fn init_ctr(&self, nonce: &Nonce<NonceSize>) -> (Ctr32BE<&Aes>, Block) {
330
1.21k
        let j0 = if NonceSize::to_usize() == 12 {
331
1.21k
            let mut block = ghash::Block::default();
332
1.21k
            block[..12].copy_from_slice(nonce);
333
1.21k
            block[15] = 1;
334
1.21k
            block
335
        } else {
336
0
            let mut ghash = self.ghash.clone();
337
0
            ghash.update_padded(nonce);
338
339
0
            let mut block = ghash::Block::default();
340
0
            let nonce_bits = (NonceSize::to_usize() as u64) * 8;
341
0
            block[8..].copy_from_slice(&nonce_bits.to_be_bytes());
342
0
            ghash.update(&[block]);
343
0
            ghash.finalize()
344
        };
345
346
1.21k
        let mut ctr = Ctr32BE::inner_iv_init(&self.cipher, &j0);
347
1.21k
        let mut tag_mask = Block::default();
348
1.21k
        ctr.write_keystream_block(&mut tag_mask);
349
1.21k
        (ctr, tag_mask)
350
1.21k
    }
351
352
    /// Authenticate the given plaintext and associated data using GHASH.
353
1.21k
    fn compute_tag(&self, mask: Block, associated_data: &[u8], buffer: &[u8]) -> Tag {
354
1.21k
        let mut ghash = self.ghash.clone();
355
1.21k
        ghash.update_padded(associated_data);
356
1.21k
        ghash.update_padded(buffer);
357
358
1.21k
        let associated_data_bits = (associated_data.len() as u64) * 8;
359
1.21k
        let buffer_bits = (buffer.len() as u64) * 8;
360
361
1.21k
        let mut block = ghash::Block::default();
362
1.21k
        block[..8].copy_from_slice(&associated_data_bits.to_be_bytes());
363
1.21k
        block[8..].copy_from_slice(&buffer_bits.to_be_bytes());
364
1.21k
        ghash.update(&[block]);
365
366
1.21k
        let mut tag = ghash.finalize();
367
19.4k
        for (a, b) in tag.as_mut_slice().iter_mut().zip(mask.as_slice()) {
368
19.4k
            *a ^= *b;
369
19.4k
        }
370
371
1.21k
        tag
372
1.21k
    }
373
}