//! AES-GCM: [Authenticated Encryption and Associated Data (AEAD)][1] cipher //! based on AES in [Galois/Counter Mode][2]. //! //! ## Performance Notes //! //! By default this crate will use software implementations of both AES and //! the POLYVAL universal hash function. //! //! When targeting modern x86/x86_64 CPUs, use the following `RUSTFLAGS` to //! take advantage of high performance AES-NI and CLMUL CPU intrinsics: //! //! ```text //! RUSTFLAGS="-Ctarget-cpu=sandybridge -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3" //! ``` //! //! ## Security Notes //! //! This crate has received one [security audit by NCC Group][3], with no significant //! findings. We would like to thank [MobileCoin][4] for funding the audit. //! //! All implementations contained in the crate are designed to execute in constant //! time, either by relying on hardware intrinsics (i.e. AES-NI and CLMUL on //! x86/x86_64), or using a portable implementation which is only constant time //! on processors which implement constant-time multiplication. //! //! It is not suitable for use on processors with a variable-time multiplication //! operation (e.g. short circuit on multiply-by-zero / multiply-by-one, such as //! certain 32-bit PowerPC CPUs and some non-ARM microcontrollers). //! //! # Usage //! //! Simple usage (allocating, no associated data): //! //! ``` //! use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm` //! use aes_gcm::aead::{Aead, NewAead}; //! //! let key = Key::from_slice(b"an example very very secret key."); //! let cipher = Aes256Gcm::new(key); //! //! let nonce = Nonce::from_slice(b"unique nonce"); // 96-bits; unique per message //! //! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref()) //! .expect("encryption failure!"); // NOTE: handle this error to avoid panics! //! //! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref()) //! .expect("decryption failure!"); // NOTE: handle this error to avoid panics! //! //! assert_eq!(&plaintext, b"plaintext message"); //! ``` //! //! ## In-place Usage (eliminates `alloc` requirement) //! //! This crate has an optional `alloc` feature which can be disabled in e.g. //! microcontroller environments that don't have a heap. //! //! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`] //! methods accept any type that impls the [`aead::Buffer`] trait which //! contains the plaintext for encryption or ciphertext for decryption. //! //! Note that if you enable the `heapless` feature of this crate, //! you will receive an impl of [`aead::Buffer`] for `heapless::Vec` //! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]), //! which can then be passed as the `buffer` parameter to the in-place encrypt //! and decrypt methods: //! //! ``` //! # #[cfg(feature = "heapless")] //! # { //! use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm` //! use aes_gcm::aead::{AeadInPlace, NewAead}; //! use aes_gcm::aead::heapless::Vec; //! //! let key = Key::from_slice(b"an example very very secret key."); //! let cipher = Aes256Gcm::new(key); //! //! let nonce = Nonce::from_slice(b"unique nonce"); // 96-bits; unique per message //! //! let mut buffer: Vec = Vec::new(); // Buffer needs 16-bytes overhead for GCM tag //! buffer.extend_from_slice(b"plaintext message"); //! //! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext //! cipher.encrypt_in_place(nonce, b"", &mut buffer).expect("encryption failure!"); //! //! // `buffer` now contains the message ciphertext //! assert_ne!(&buffer, b"plaintext message"); //! //! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext //! cipher.decrypt_in_place(nonce, b"", &mut buffer).expect("decryption failure!"); //! assert_eq!(&buffer, b"plaintext message"); //! # } //! ``` //! //! [1]: https://en.wikipedia.org/wiki/Authenticated_encryption //! [2]: https://en.wikipedia.org/wiki/Galois/Counter_Mode //! [3]: https://research.nccgroup.com/2020/02/26/public-report-rustcrypto-aes-gcm-and-chacha20poly1305-implementation-review/ //! [4]: https://www.mobilecoin.com/ #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" )] #![deny(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] pub use aead::{self, AeadCore, AeadInPlace, Error, NewAead}; #[cfg(feature = "aes")] pub use aes; use cipher::{ consts::{U0, U16}, generic_array::{typenum::Unsigned, ArrayLength, GenericArray}, Block, BlockCipher, BlockCipherKey, BlockEncrypt, FromBlockCipher, NewBlockCipher, StreamCipher, StreamCipherSeek, }; use core::marker::PhantomData; use ctr::Ctr32BE; use ghash::{ universal_hash::{NewUniversalHash, UniversalHash}, GHash, }; #[cfg(feature = "zeroize")] use zeroize::Zeroize; #[cfg(feature = "aes")] use aes::{cipher::consts::U12, Aes128, Aes256}; /// Maximum length of associated data pub const A_MAX: u64 = 1 << 36; /// Maximum length of plaintext pub const P_MAX: u64 = 1 << 36; /// Maximum length of ciphertext pub const C_MAX: u64 = (1 << 36) + 16; /// AES-GCM keys pub type Key = GenericArray; /// AES-GCM nonces pub type Nonce = GenericArray; /// AES-GCM tags pub type Tag = GenericArray; /// AES-GCM with a 128-bit key and 96-bit nonce #[cfg(feature = "aes")] #[cfg_attr(docsrs, doc(cfg(feature = "aes")))] pub type Aes128Gcm = AesGcm; /// AES-GCM with a 256-bit key and 96-bit nonce #[cfg(feature = "aes")] #[cfg_attr(docsrs, doc(cfg(feature = "aes")))] pub type Aes256Gcm = AesGcm; /// AES-GCM: generic over an underlying AES implementation and nonce size. /// /// This type is generic to support substituting alternative AES implementations /// (e.g. embedded hardware implementations) /// /// It is NOT intended to be instantiated with any block cipher besides AES! /// Doing so runs the risk of unintended cryptographic properties! /// /// The `N` generic parameter can be used to instantiate AES-GCM with other /// nonce sizes, however it's recommended to use it with `typenum::U12`, /// the default of 96-bits. /// /// If in doubt, use the built-in [`Aes128Gcm`] and [`Aes256Gcm`] type aliases. #[derive(Clone)] pub struct AesGcm where Aes: BlockCipher + BlockEncrypt, Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { /// Encryption cipher cipher: Aes, /// GHASH authenticator ghash: GHash, /// Length of the nonce nonce_size: PhantomData, } impl NewAead for AesGcm where Aes: NewBlockCipher + BlockCipher + BlockEncrypt, Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { type KeySize = Aes::KeySize; fn new(key: &BlockCipherKey) -> Self { Aes::new(key).into() } } impl From for AesGcm where Aes: NewBlockCipher + BlockCipher + BlockEncrypt, Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { fn from(cipher: Aes) -> Self { let mut ghash_key = ghash::Key::default(); cipher.encrypt_block(&mut ghash_key); let ghash = GHash::new(&ghash_key); #[cfg(feature = "zeroize")] ghash_key.zeroize(); Self { cipher, ghash, nonce_size: PhantomData, } } } impl AeadCore for AesGcm where Aes: NewBlockCipher + BlockCipher + BlockEncrypt, Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { type NonceSize = NonceSize; type TagSize = U16; type CiphertextOverhead = U0; } impl AeadInPlace for AesGcm where Aes: NewBlockCipher + BlockCipher + BlockEncrypt, Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { fn encrypt_in_place_detached( &self, nonce: &Nonce, associated_data: &[u8], buffer: &mut [u8], ) -> Result { if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX { return Err(Error); } // TODO(tarcieri): interleave encryption with GHASH // See: let mut ctr = self.init_ctr(nonce); ctr.seek(Aes::BlockSize::to_usize()); ctr.apply_keystream(buffer); let mut tag = self.compute_tag(associated_data, buffer); ctr.seek(0); ctr.apply_keystream(tag.as_mut_slice()); Ok(tag) } fn decrypt_in_place_detached( &self, nonce: &Nonce, associated_data: &[u8], buffer: &mut [u8], tag: &Tag, ) -> Result<(), Error> { if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX { return Err(Error); } // TODO(tarcieri): interleave encryption with GHASH // See: let mut expected_tag = self.compute_tag(associated_data, buffer); let mut ctr = self.init_ctr(nonce); ctr.apply_keystream(expected_tag.as_mut_slice()); use subtle::ConstantTimeEq; if expected_tag.ct_eq(&tag).unwrap_u8() == 1 { ctr.apply_keystream(buffer); Ok(()) } else { Err(Error) } } } impl AesGcm where Aes: NewBlockCipher + BlockCipher + BlockEncrypt, Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { /// Initialize counter mode. /// /// See algorithm described in Section 7.2 of NIST SP800-38D: /// /// /// > Define a block, J0, as follows: /// > If len(IV)=96, then J0 = IV || 0{31} || 1. /// > If len(IV) ≠ 96, then let s = 128 ⎡len(IV)/128⎤-len(IV), and /// > J0=GHASH(IV||0s+64||[len(IV)]64). fn init_ctr(&self, nonce: &Nonce) -> Ctr32BE<&Aes> { let j0 = if NonceSize::to_usize() == 12 { let mut block = ghash::Block::default(); block[..12].copy_from_slice(nonce); block[15] = 1; block } else { let mut ghash = self.ghash.clone(); ghash.update_padded(nonce); let mut block = ghash::Block::default(); let nonce_bits = (NonceSize::to_usize() as u64) * 8; block[8..].copy_from_slice(&nonce_bits.to_be_bytes()); ghash.update(&block); ghash.finalize().into_bytes() }; Ctr32BE::from_block_cipher(&self.cipher, &j0) } /// Authenticate the given plaintext and associated data using GHASH fn compute_tag(&self, associated_data: &[u8], buffer: &[u8]) -> Tag { let mut ghash = self.ghash.clone(); ghash.update_padded(associated_data); ghash.update_padded(buffer); let associated_data_bits = (associated_data.len() as u64) * 8; let buffer_bits = (buffer.len() as u64) * 8; let mut block = ghash::Block::default(); block[..8].copy_from_slice(&associated_data_bits.to_be_bytes()); block[8..].copy_from_slice(&buffer_bits.to_be_bytes()); ghash.update(&block); ghash.finalize().into_bytes() } }