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
9#![cfg_attr(feature = "getrandom", doc = "```")]
14#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
15#![cfg_attr(all(feature = "getrandom", feature = "arrayvec"), doc = "```")]
54#![cfg_attr(
55 not(all(feature = "getrandom", feature = "arrayvec")),
56 doc = "```ignore"
57)]
58pub use aead::{self, AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser};
86
87#[cfg(feature = "aes")]
88pub use aes;
89
90use aead::{TagPosition, inout::InOutBuf};
91
92use cipher::{
93 BlockCipherEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore,
94 array::{Array, ArraySize},
95 consts::U16,
96};
97use core::{fmt, marker::PhantomData};
98use ghash::{GHash, universal_hash::UniversalHash};
99
100#[cfg(feature = "zeroize")]
101use zeroize::Zeroize;
102
103#[cfg(feature = "aes")]
104use aes::{Aes128, Aes256, cipher::consts::U12};
105
106pub const A_MAX: u64 = (1 << 61) - 1;
108
109pub const P_MAX: u64 = (1 << 36) - 32;
111
112pub type Nonce<NonceSize> = Array<u8, NonceSize>;
114
115pub type Tag<TagSize = U16> = Array<u8, TagSize>;
117
118pub trait TagSize: private::SealedTagSize {}
124
125impl<T: private::SealedTagSize> TagSize for T {}
126
127mod private {
128 use cipher::{array::ArraySize, consts, typenum::Unsigned};
129
130 pub trait SealedTagSize: ArraySize + Unsigned {}
132
133 #[cfg(feature = "hazmat")]
134 impl SealedTagSize for consts::U4 {}
135 #[cfg(feature = "hazmat")]
136 impl SealedTagSize for consts::U8 {}
137
138 impl SealedTagSize for consts::U12 {}
139 impl SealedTagSize for consts::U13 {}
140 impl SealedTagSize for consts::U14 {}
141 impl SealedTagSize for consts::U15 {}
142 impl SealedTagSize for consts::U16 {}
143}
144
145#[cfg(feature = "aes")]
147#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
148pub type Aes128Gcm = AesGcm<Aes128, U12>;
149
150#[cfg(feature = "aes")]
152#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
153pub type Aes256Gcm = AesGcm<Aes256, U12>;
154
155type Block = Array<u8, U16>;
157
158type Ctr32BE<Aes> = ctr::CtrCore<Aes, ctr::flavors::Ctr32BE>;
160
161#[derive(Clone)]
187pub struct AesGcm<Aes, NonceSize, TagSize = U16>
188where
189 TagSize: crate::TagSize,
190{
191 cipher: Aes,
193
194 ghash: GHash,
196
197 nonce_size: PhantomData<NonceSize>,
199
200 tag_size: PhantomData<TagSize>,
202}
203
204impl<Aes, NonceSize, TagSize> KeySizeUser for AesGcm<Aes, NonceSize, TagSize>
205where
206 Aes: KeySizeUser,
207 TagSize: crate::TagSize,
208{
209 type KeySize = Aes::KeySize;
210}
211
212impl<Aes, NonceSize, TagSize> KeyInit for AesGcm<Aes, NonceSize, TagSize>
213where
214 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit,
215 TagSize: crate::TagSize,
216{
217 fn new(key: &Key<Self>) -> Self {
218 Aes::new(key).into()
219 }
220}
221
222impl<Aes, NonceSize, TagSize> From<Aes> for AesGcm<Aes, NonceSize, TagSize>
223where
224 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
225 TagSize: crate::TagSize,
226{
227 fn from(cipher: Aes) -> Self {
228 let mut ghash_key = ghash::Key::default();
229 cipher.encrypt_block(&mut ghash_key);
230
231 let ghash = GHash::new(&ghash_key);
232
233 #[cfg(feature = "zeroize")]
234 ghash_key.zeroize();
235
236 Self {
237 cipher,
238 ghash,
239 nonce_size: PhantomData,
240 tag_size: PhantomData,
241 }
242 }
243}
244
245impl<Aes, NonceSize, TagSize> AeadCore for AesGcm<Aes, NonceSize, TagSize>
246where
247 NonceSize: ArraySize,
248 TagSize: crate::TagSize,
249{
250 type NonceSize = NonceSize;
251 type TagSize = TagSize;
252 const TAG_POSITION: TagPosition = TagPosition::Postfix;
253}
254
255impl<Aes, NonceSize, TagSize> AeadInOut for AesGcm<Aes, NonceSize, TagSize>
256where
257 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
258 NonceSize: ArraySize,
259 TagSize: crate::TagSize,
260{
261 fn encrypt_inout_detached(
262 &self,
263 nonce: &Nonce<NonceSize>,
264 associated_data: &[u8],
265 mut buffer: InOutBuf<'_, '_, u8>,
266 ) -> Result<Tag<TagSize>, Error> {
267 if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
268 return Err(Error);
269 }
270
271 let (ctr, mask) = self.init_ctr(nonce);
272
273 ctr.apply_keystream_partial(buffer.reborrow());
276
277 let full_tag = self.compute_tag(mask, associated_data, buffer.get_out());
278 full_tag[..TagSize::to_usize()]
279 .try_into()
280 .map_err(|_| Error)
281 }
282
283 fn decrypt_inout_detached(
284 &self,
285 nonce: &Nonce<NonceSize>,
286 associated_data: &[u8],
287 buffer: InOutBuf<'_, '_, u8>,
288 tag: &Tag<TagSize>,
289 ) -> Result<(), Error> {
290 if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
291 return Err(Error);
292 }
293
294 let (ctr, mask) = self.init_ctr(nonce);
295
296 let expected_tag = self.compute_tag(mask, associated_data, buffer.get_in());
299
300 use subtle::ConstantTimeEq;
301 if expected_tag[..TagSize::to_usize()].ct_eq(tag).into() {
302 ctr.apply_keystream_partial(buffer);
303 Ok(())
304 } else {
305 Err(Error)
306 }
307 }
308}
309
310impl<Aes, NonceSize, TagSize> AesGcm<Aes, NonceSize, TagSize>
311where
312 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
313 NonceSize: ArraySize,
314 TagSize: crate::TagSize,
315{
316 fn init_ctr(&self, nonce: &Nonce<NonceSize>) -> (Ctr32BE<&Aes>, Block) {
326 let j0 = if NonceSize::to_usize() == 12 {
327 let mut block = ghash::Block::default();
328 block[..12].copy_from_slice(nonce);
329 block[15] = 1;
330 block
331 } else {
332 let mut ghash = self.ghash.clone();
333 ghash.update_padded(nonce);
334
335 let mut block = ghash::Block::default();
336 let nonce_bits = (NonceSize::to_usize() as u64) * 8;
337 block[8..].copy_from_slice(&nonce_bits.to_be_bytes());
338 ghash.update(&[block]);
339 ghash.finalize()
340 };
341
342 let mut ctr = Ctr32BE::inner_iv_init(&self.cipher, &j0);
343 let mut tag_mask = Block::default();
344 ctr.write_keystream_block(&mut tag_mask);
345 (ctr, tag_mask)
346 }
347
348 fn compute_tag(&self, mask: Block, associated_data: &[u8], buffer: &[u8]) -> Tag {
350 let mut ghash = self.ghash.clone();
351 ghash.update_padded(associated_data);
352 ghash.update_padded(buffer);
353
354 let associated_data_bits = (associated_data.len() as u64) * 8;
355 let buffer_bits = (buffer.len() as u64) * 8;
356
357 let mut block = ghash::Block::default();
358 block[..8].copy_from_slice(&associated_data_bits.to_be_bytes());
359 block[8..].copy_from_slice(&buffer_bits.to_be_bytes());
360 ghash.update(&[block]);
361
362 let mut tag = ghash.finalize();
363 for (a, b) in tag.as_mut_slice().iter_mut().zip(mask.as_slice()) {
364 *a ^= *b;
365 }
366
367 tag
368 }
369}
370
371impl<Aes, NonceSize, TagSize> fmt::Debug for AesGcm<Aes, NonceSize, TagSize>
372where
373 TagSize: crate::TagSize,
374{
375 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
376 f.debug_struct("AesGcm").finish_non_exhaustive()
377 }
378}