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