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