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#![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)]
74pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
105
106#[cfg(feature = "aes")]
107pub use aes;
108
109use cipher::{
110 consts::{U0, U16},
111 generic_array::{ArrayLength, GenericArray},
112 BlockCipher, BlockEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore,
113};
114use core::marker::PhantomData;
115use ghash::{universal_hash::UniversalHash, GHash};
116
117#[cfg(feature = "zeroize")]
118use zeroize::Zeroize;
119
120#[cfg(feature = "aes")]
121use aes::{cipher::consts::U12, Aes128, Aes256};
122
123pub const A_MAX: u64 = 1 << 36;
125
126pub const P_MAX: u64 = 1 << 36;
128
129pub const C_MAX: u64 = (1 << 36) + 16;
131
132pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
134
135pub type Tag<TagSize = U16> = GenericArray<u8, TagSize>;
137
138pub trait TagSize: private::SealedTagSize {}
142
143impl<T: private::SealedTagSize> TagSize for T {}
144
145mod private {
146 use aead::generic_array::ArrayLength;
147 use cipher::{consts, Unsigned};
148
149 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#[cfg(feature = "aes")]
161#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
162pub type Aes128Gcm = AesGcm<Aes128, U12>;
163
164#[cfg(feature = "aes")]
166#[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
167pub type Aes256Gcm = AesGcm<Aes256, U12>;
168
169type Block = GenericArray<u8, U16>;
171
172type Ctr32BE<Aes> = ctr::CtrCore<Aes, ctr::flavors::Ctr32BE>;
174
175#[derive(Clone)]
193pub struct AesGcm<Aes, NonceSize, TagSize = U16>
194where
195 TagSize: self::TagSize,
196{
197 cipher: Aes,
199
200 ghash: GHash,
202
203 nonce_size: PhantomData<NonceSize>,
205
206 tag_size: PhantomData<TagSize>,
208}
209
210impl<Aes, NonceSize, TagSize> KeySizeUser for AesGcm<Aes, NonceSize, TagSize>
211where
212 Aes: KeySizeUser,
213 TagSize: self::TagSize,
214{
215 type KeySize = Aes::KeySize;
216}
217
218impl<Aes, NonceSize, TagSize> KeyInit for AesGcm<Aes, NonceSize, TagSize>
219where
220 Aes: BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit,
221 TagSize: self::TagSize,
222{
223 fn new(key: &Key<Self>) -> Self {
224 Aes::new(key).into()
225 }
226}
227
228impl<Aes, NonceSize, TagSize> From<Aes> for AesGcm<Aes, NonceSize, TagSize>
229where
230 Aes: BlockSizeUser<BlockSize = U16> + BlockEncrypt,
231 TagSize: self::TagSize,
232{
233 fn from(cipher: Aes) -> Self {
234 let mut ghash_key = ghash::Key::default();
235 cipher.encrypt_block(&mut ghash_key);
236
237 let ghash = GHash::new(&ghash_key);
238
239 #[cfg(feature = "zeroize")]
240 ghash_key.zeroize();
241
242 Self {
243 cipher,
244 ghash,
245 nonce_size: PhantomData,
246 tag_size: PhantomData,
247 }
248 }
249}
250
251impl<Aes, NonceSize, TagSize> AeadCore for AesGcm<Aes, NonceSize, TagSize>
252where
253 NonceSize: ArrayLength<u8>,
254 TagSize: self::TagSize,
255{
256 type NonceSize = NonceSize;
257 type TagSize = TagSize;
258 type CiphertextOverhead = U0;
259}
260
261impl<Aes, NonceSize, TagSize> AeadInPlace for AesGcm<Aes, NonceSize, TagSize>
262where
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 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 fn decrypt_in_place_detached(
288 &self,
289 nonce: &Nonce<NonceSize>,
290 associated_data: &[u8],
291 buffer: &mut [u8],
292 tag: &Tag<TagSize>,
293 ) -> Result<(), Error> {
294 if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
295 return Err(Error);
296 }
297
298 let (ctr, mask) = self.init_ctr(nonce);
299
300 let expected_tag = self.compute_tag(mask, associated_data, buffer);
303 ctr.apply_keystream_partial(buffer.into());
304
305 use subtle::ConstantTimeEq;
306 if expected_tag[..TagSize::to_usize()].ct_eq(tag).into() {
307 Ok(())
308 } else {
309 Err(Error)
310 }
311 }
312}
313
314impl<Aes, NonceSize, TagSize> AesGcm<Aes, NonceSize, TagSize>
315where
316 Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt,
317 NonceSize: ArrayLength<u8>,
318 TagSize: self::TagSize,
319{
320 fn init_ctr(&self, nonce: &Nonce<NonceSize>) -> (Ctr32BE<&Aes>, Block) {
330 let j0 = if NonceSize::to_usize() == 12 {
331 let mut block = ghash::Block::default();
332 block[..12].copy_from_slice(nonce);
333 block[15] = 1;
334 block
335 } else {
336 let mut ghash = self.ghash.clone();
337 ghash.update_padded(nonce);
338
339 let mut block = ghash::Block::default();
340 let nonce_bits = (NonceSize::to_usize() as u64) * 8;
341 block[8..].copy_from_slice(&nonce_bits.to_be_bytes());
342 ghash.update(&[block]);
343 ghash.finalize()
344 };
345
346 let mut ctr = Ctr32BE::inner_iv_init(&self.cipher, &j0);
347 let mut tag_mask = Block::default();
348 ctr.write_keystream_block(&mut tag_mask);
349 (ctr, tag_mask)
350 }
351
352 fn compute_tag(&self, mask: Block, associated_data: &[u8], buffer: &[u8]) -> Tag {
354 let mut ghash = self.ghash.clone();
355 ghash.update_padded(associated_data);
356 ghash.update_padded(buffer);
357
358 let associated_data_bits = (associated_data.len() as u64) * 8;
359 let buffer_bits = (buffer.len() as u64) * 8;
360
361 let mut block = ghash::Block::default();
362 block[..8].copy_from_slice(&associated_data_bits.to_be_bytes());
363 block[8..].copy_from_slice(&buffer_bits.to_be_bytes());
364 ghash.update(&[block]);
365
366 let mut tag = ghash.finalize();
367 for (a, b) in tag.as_mut_slice().iter_mut().zip(mask.as_slice()) {
368 *a ^= *b;
369 }
370
371 tag
372 }
373}