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; // Or `Aes128Gcm`
36 //! use aes_gcm::aead::{Aead, NewAead, generic_array::GenericArray};
37 //!
38 //! let key = GenericArray::from_slice(b"an example very very secret key.");
39 //! let cipher = Aes256Gcm::new(key);
40 //!
41 //! let nonce = GenericArray::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; // Or `Aes128Gcm`
71 //! use aes_gcm::aead::{AeadInPlace, NewAead, generic_array::GenericArray};
72 //! use aes_gcm::aead::heapless::{Vec, consts::U128};
73 //!
74 //! let key = GenericArray::from_slice(b"an example very very secret key.");
75 //! let cipher = Aes256Gcm::new(key);
76 //!
77 //! let nonce = GenericArray::from_slice(b"unique nonce"); // 96-bits; unique per message
78 //!
79 //! let mut buffer: Vec<u8, U128> = Vec::new();
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, AeadInPlace, Error, NewAead};
109 
110 #[cfg(feature = "aes")]
111 pub use aes;
112 
113 use cipher::{
114     block::{Block, BlockCipher, Key, NewBlockCipher},
115     consts::{U0, U16},
116     generic_array::{ArrayLength, GenericArray},
117     stream::{FromBlockCipher, SyncStreamCipher},
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 tags
142 pub type Tag = GenericArray<u8, U16>;
143 
144 /// AES-GCM with a 128-bit key and 96-bit nonce
145 #[cfg(feature = "aes")]
146 #[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
147 pub type Aes128Gcm = AesGcm<Aes128, U12>;
148 
149 /// AES-GCM with a 256-bit key and 96-bit nonce
150 #[cfg(feature = "aes")]
151 #[cfg_attr(docsrs, doc(cfg(feature = "aes")))]
152 pub type Aes256Gcm = AesGcm<Aes256, U12>;
153 
154 /// AES-GCM: generic over an underlying AES implementation and nonce size.
155 ///
156 /// This type is generic to support substituting alternative AES implementations
157 /// (e.g. embedded hardware implementations)
158 ///
159 /// It is NOT intended to be instantiated with any block cipher besides AES!
160 /// Doing so runs the risk of unintended cryptographic properties!
161 ///
162 /// The `N` generic parameter can be used to instantiate AES-GCM with other
163 /// nonce sizes, however it's recommended to use it with `typenum::U12`,
164 /// the default of 96-bits.
165 ///
166 /// If in doubt, use the built-in [`Aes128Gcm`] and [`Aes256Gcm`] type aliases.
167 #[derive(Clone)]
168 pub struct AesGcm<Aes, NonceSize>
169 where
170     Aes: BlockCipher<BlockSize = U16>,
171     Aes::ParBlocks: ArrayLength<Block<Aes>>,
172     NonceSize: ArrayLength<u8>,
173 {
174     /// Encryption cipher
175     cipher: Aes,
176 
177     /// GHASH authenticator
178     ghash: GHash,
179 
180     /// Length of the nonce
181     nonce_size: PhantomData<NonceSize>,
182 }
183 
184 impl<Aes, NonceSize> NewAead for AesGcm<Aes, NonceSize>
185 where
186     Aes: BlockCipher<BlockSize = U16> + NewBlockCipher,
187     Aes::ParBlocks: ArrayLength<Block<Aes>>,
188     NonceSize: ArrayLength<u8>,
189 {
190     type KeySize = Aes::KeySize;
191 
new(key: &Key<Aes>) -> Self192     fn new(key: &Key<Aes>) -> Self {
193         Aes::new(key).into()
194     }
195 }
196 
197 impl<Aes, NonceSize> From<Aes> for AesGcm<Aes, NonceSize>
198 where
199     Aes: BlockCipher<BlockSize = U16> + NewBlockCipher,
200     Aes::ParBlocks: ArrayLength<Block<Aes>>,
201     NonceSize: ArrayLength<u8>,
202 {
from(cipher: Aes) -> Self203     fn from(cipher: Aes) -> Self {
204         let mut ghash_key = GenericArray::default();
205         cipher.encrypt_block(&mut ghash_key);
206 
207         let ghash = GHash::new(&ghash_key);
208 
209         #[cfg(feature = "zeroize")]
210         ghash_key.zeroize();
211 
212         Self {
213             cipher,
214             ghash,
215             nonce_size: PhantomData,
216         }
217     }
218 }
219 
220 impl<Aes, NonceSize> AeadInPlace for AesGcm<Aes, NonceSize>
221 where
222     Aes: BlockCipher<BlockSize = U16> + NewBlockCipher,
223     Aes::ParBlocks: ArrayLength<Block<Aes>>,
224     NonceSize: ArrayLength<u8>,
225 {
226     type NonceSize = NonceSize;
227     type TagSize = U16;
228     type CiphertextOverhead = U0;
229 
encrypt_in_place_detached( &self, nonce: &GenericArray<u8, NonceSize>, associated_data: &[u8], buffer: &mut [u8], ) -> Result<Tag, Error>230     fn encrypt_in_place_detached(
231         &self,
232         nonce: &GenericArray<u8, NonceSize>,
233         associated_data: &[u8],
234         buffer: &mut [u8],
235     ) -> Result<Tag, Error> {
236         if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
237             return Err(Error);
238         }
239 
240         // TODO(tarcieri): interleave encryption with GHASH
241         // See: <https://github.com/RustCrypto/AEADs/issues/74>
242         let mut ctr = self.init_ctr(nonce);
243         ctr.seek_ctr(1);
244         ctr.apply_keystream(buffer);
245 
246         let mut tag = self.compute_tag(associated_data, buffer);
247         ctr.seek_ctr(0);
248         ctr.apply_keystream(tag.as_mut_slice());
249 
250         Ok(tag)
251     }
252 
decrypt_in_place_detached( &self, nonce: &GenericArray<u8, NonceSize>, associated_data: &[u8], buffer: &mut [u8], tag: &Tag, ) -> Result<(), Error>253     fn decrypt_in_place_detached(
254         &self,
255         nonce: &GenericArray<u8, NonceSize>,
256         associated_data: &[u8],
257         buffer: &mut [u8],
258         tag: &Tag,
259     ) -> Result<(), Error> {
260         if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
261             return Err(Error);
262         }
263 
264         // TODO(tarcieri): interleave encryption with GHASH
265         // See: <https://github.com/RustCrypto/AEADs/issues/74>
266         let mut expected_tag = self.compute_tag(associated_data, buffer);
267         let mut ctr = self.init_ctr(nonce);
268         ctr.apply_keystream(expected_tag.as_mut_slice());
269 
270         use subtle::ConstantTimeEq;
271         if expected_tag.ct_eq(&tag).unwrap_u8() == 1 {
272             ctr.apply_keystream(buffer);
273             Ok(())
274         } else {
275             Err(Error)
276         }
277     }
278 }
279 
280 impl<Aes, NonceSize> AesGcm<Aes, NonceSize>
281 where
282     Aes: BlockCipher<BlockSize = U16> + NewBlockCipher,
283     Aes::ParBlocks: ArrayLength<Block<Aes>>,
284     NonceSize: ArrayLength<u8>,
285 {
286     /// Initialize counter mode.
287     ///
288     /// See algorithm described in Section 7.2 of NIST SP800-38D:
289     /// <https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf>
290     ///
291     /// > Define a block, J0, as follows:
292     /// > If len(IV)=96, then J0 = IV || 0{31} || 1.
293     /// > If len(IV) ≠ 96, then let s = 128 ⎡len(IV)/128⎤-len(IV), and
294     /// >     J0=GHASH(IV||0s+64||[len(IV)]64).
init_ctr(&self, nonce: &GenericArray<u8, NonceSize>) -> Ctr32BE<&Aes>295     fn init_ctr(&self, nonce: &GenericArray<u8, NonceSize>) -> Ctr32BE<&Aes> {
296         let j0 = if NonceSize::to_usize() == 12 {
297             let mut block = GenericArray::default();
298             block[..12].copy_from_slice(nonce);
299             block[15] = 1;
300             block
301         } else {
302             let mut ghash = self.ghash.clone();
303             ghash.update_padded(nonce);
304 
305             let mut block = GenericArray::default();
306             let nonce_bits = (NonceSize::to_usize() as u64) * 8;
307             block[8..].copy_from_slice(&nonce_bits.to_be_bytes());
308             ghash.update(&block);
309 
310             ghash.finalize().into_bytes()
311         };
312 
313         Ctr32BE::from_block_cipher(&self.cipher, &j0)
314     }
315 
316     /// Authenticate the given plaintext and associated data using GHASH
compute_tag(&self, associated_data: &[u8], buffer: &[u8]) -> Tag317     fn compute_tag(&self, associated_data: &[u8], buffer: &[u8]) -> Tag {
318         let mut ghash = self.ghash.clone();
319         ghash.update_padded(associated_data);
320         ghash.update_padded(buffer);
321 
322         let associated_data_bits = (associated_data.len() as u64) * 8;
323         let buffer_bits = (buffer.len() as u64) * 8;
324 
325         let mut block = GenericArray::default();
326         block[..8].copy_from_slice(&associated_data_bits.to_be_bytes());
327         block[8..].copy_from_slice(&buffer_bits.to_be_bytes());
328         ghash.update(&block);
329 
330         ghash.finalize().into_bytes()
331     }
332 }
333