/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aes-gcm-0.10.3/src/lib.rs
Line | Count | Source |
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 | | //! # Usage |
12 | | //! |
13 | | //! Simple usage (allocating, no associated data): |
14 | | //! |
15 | | //! ``` |
16 | | //! use aes_gcm::{ |
17 | | //! aead::{Aead, AeadCore, KeyInit, OsRng}, |
18 | | //! Aes256Gcm, Nonce, Key // Or `Aes128Gcm` |
19 | | //! }; |
20 | | //! |
21 | | //! # fn gen_key() -> Result<(), core::array::TryFromSliceError> { |
22 | | //! // The encryption key can be generated randomly: |
23 | | //! # #[cfg(all(feature = "getrandom", feature = "std"))] { |
24 | | //! let key = Aes256Gcm::generate_key(OsRng); |
25 | | //! # } |
26 | | //! |
27 | | //! // Transformed from a byte array: |
28 | | //! let key: &[u8; 32] = &[42; 32]; |
29 | | //! let key: &Key<Aes256Gcm> = key.into(); |
30 | | //! |
31 | | //! // Note that you can get byte array from slice using the `TryInto` trait: |
32 | | //! let key: &[u8] = &[42; 32]; |
33 | | //! let key: [u8; 32] = key.try_into()?; |
34 | | //! # Ok(()) } |
35 | | //! |
36 | | //! # fn main() -> Result<(), aes_gcm::Error> { |
37 | | //! // Alternatively, the key can be transformed directly from a byte slice |
38 | | //! // (panicks on length mismatch): |
39 | | //! # let key: &[u8] = &[42; 32]; |
40 | | //! let key = Key::<Aes256Gcm>::from_slice(key); |
41 | | //! |
42 | | //! let cipher = Aes256Gcm::new(&key); |
43 | | //! let nonce = Aes256Gcm::generate_nonce(&mut OsRng); // 96-bits; unique per message |
44 | | //! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?; |
45 | | //! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?; |
46 | | //! assert_eq!(&plaintext, b"plaintext message"); |
47 | | //! # Ok(()) |
48 | | //! # } |
49 | | //! ``` |
50 | | //! |
51 | | //! ## In-place Usage (eliminates `alloc` requirement) |
52 | | //! |
53 | | //! This crate has an optional `alloc` feature which can be disabled in e.g. |
54 | | //! microcontroller environments that don't have a heap. |
55 | | //! |
56 | | //! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`] |
57 | | //! methods accept any type that impls the [`aead::Buffer`] trait which |
58 | | //! contains the plaintext for encryption or ciphertext for decryption. |
59 | | //! |
60 | | //! Note that if you enable the `heapless` feature of this crate, |
61 | | //! you will receive an impl of [`aead::Buffer`] for `heapless::Vec` |
62 | | //! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]), |
63 | | //! which can then be passed as the `buffer` parameter to the in-place encrypt |
64 | | //! and decrypt methods: |
65 | | //! |
66 | | #![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 | | )] |
74 | | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
75 | | //! use aes_gcm::{ |
76 | | //! aead::{AeadCore, AeadInPlace, KeyInit, OsRng, heapless::Vec}, |
77 | | //! Aes256Gcm, Nonce, // Or `Aes128Gcm` |
78 | | //! }; |
79 | | //! |
80 | | //! let key = Aes256Gcm::generate_key(&mut OsRng); |
81 | | //! let cipher = Aes256Gcm::new(&key); |
82 | | //! let nonce = Aes256Gcm::generate_nonce(&mut OsRng); // 96-bits; unique per message |
83 | | //! |
84 | | //! let mut buffer: Vec<u8, 128> = Vec::new(); // Note: buffer needs 16-bytes overhead for auth tag |
85 | | //! buffer.extend_from_slice(b"plaintext message"); |
86 | | //! |
87 | | //! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext |
88 | | //! cipher.encrypt_in_place(&nonce, b"", &mut buffer)?; |
89 | | //! |
90 | | //! // `buffer` now contains the message ciphertext |
91 | | //! assert_ne!(&buffer, b"plaintext message"); |
92 | | //! |
93 | | //! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext |
94 | | //! cipher.decrypt_in_place(&nonce, b"", &mut buffer)?; |
95 | | //! assert_eq!(&buffer, b"plaintext message"); |
96 | | //! # Ok(()) |
97 | | //! # } |
98 | | //! ``` |
99 | | //! |
100 | | //! Similarly, enabling the `arrayvec` feature of this crate will provide an impl of |
101 | | //! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as |
102 | | //! [`aead::arrayvec::ArrayVec`]). |
103 | | |
104 | | pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser}; |
105 | | |
106 | | #[cfg(feature = "aes")] |
107 | | pub use aes; |
108 | | |
109 | | use cipher::{ |
110 | | consts::{U0, U16}, |
111 | | generic_array::{ArrayLength, GenericArray}, |
112 | | BlockCipher, BlockEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore, |
113 | | }; |
114 | | use core::marker::PhantomData; |
115 | | use ghash::{universal_hash::UniversalHash, GHash}; |
116 | | |
117 | | #[cfg(feature = "zeroize")] |
118 | | use zeroize::Zeroize; |
119 | | |
120 | | #[cfg(feature = "aes")] |
121 | | use aes::{cipher::consts::U12, Aes128, Aes256}; |
122 | | |
123 | | /// Maximum length of associated data. |
124 | | pub const A_MAX: u64 = 1 << 36; |
125 | | |
126 | | /// Maximum length of plaintext. |
127 | | pub const P_MAX: u64 = 1 << 36; |
128 | | |
129 | | /// Maximum length of ciphertext. |
130 | | pub const C_MAX: u64 = (1 << 36) + 16; |
131 | | |
132 | | /// AES-GCM nonces. |
133 | | pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>; |
134 | | |
135 | | /// AES-GCM tags. |
136 | | pub type Tag<TagSize = U16> = GenericArray<u8, TagSize>; |
137 | | |
138 | | /// Trait implemented for valid tag sizes, i.e. |
139 | | /// [`U12`][consts::U12], [`U13`][consts::U13], [`U14`][consts::U14], |
140 | | /// [`U15`][consts::U15] and [`U16`][consts::U16]. |
141 | | pub trait TagSize: private::SealedTagSize {} |
142 | | |
143 | | impl<T: private::SealedTagSize> TagSize for T {} |
144 | | |
145 | | mod private { |
146 | | use aead::generic_array::ArrayLength; |
147 | | use cipher::{consts, Unsigned}; |
148 | | |
149 | | // Sealed traits stop other crates from implementing any traits that use it. |
150 | | 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 | | /// AES-GCM with a 128-bit key and 96-bit nonce. |
160 | | #[cfg(feature = "aes")] |
161 | | #[cfg_attr(docsrs, doc(cfg(feature = "aes")))] |
162 | | pub type Aes128Gcm = AesGcm<Aes128, U12>; |
163 | | |
164 | | /// AES-GCM with a 256-bit key and 96-bit nonce. |
165 | | #[cfg(feature = "aes")] |
166 | | #[cfg_attr(docsrs, doc(cfg(feature = "aes")))] |
167 | | pub type Aes256Gcm = AesGcm<Aes256, U12>; |
168 | | |
169 | | /// AES block. |
170 | | type Block = GenericArray<u8, U16>; |
171 | | |
172 | | /// Counter mode with a 32-bit big endian counter. |
173 | | type Ctr32BE<Aes> = ctr::CtrCore<Aes, ctr::flavors::Ctr32BE>; |
174 | | |
175 | | /// AES-GCM: generic over an underlying AES implementation and nonce size. |
176 | | /// |
177 | | /// This type is generic to support substituting alternative AES implementations |
178 | | /// (e.g. embedded hardware implementations) |
179 | | /// |
180 | | /// It is NOT intended to be instantiated with any block cipher besides AES! |
181 | | /// Doing so runs the risk of unintended cryptographic properties! |
182 | | /// |
183 | | /// The `NonceSize` generic parameter can be used to instantiate AES-GCM with other |
184 | | /// nonce sizes, however it's recommended to use it with `typenum::U12`, |
185 | | /// the default of 96-bits. |
186 | | /// |
187 | | /// The `TagSize` generic parameter can be used to instantiate AES-GCM with other |
188 | | /// authorization tag sizes, however it's recommended to use it with `typenum::U16`, |
189 | | /// the default of 128-bits. |
190 | | /// |
191 | | /// If in doubt, use the built-in [`Aes128Gcm`] and [`Aes256Gcm`] type aliases. |
192 | | #[derive(Clone)] |
193 | | pub struct AesGcm<Aes, NonceSize, TagSize = U16> |
194 | | where |
195 | | TagSize: self::TagSize, |
196 | | { |
197 | | /// Encryption cipher. |
198 | | cipher: Aes, |
199 | | |
200 | | /// GHASH authenticator. |
201 | | ghash: GHash, |
202 | | |
203 | | /// Length of the nonce. |
204 | | nonce_size: PhantomData<NonceSize>, |
205 | | |
206 | | /// Length of the tag. |
207 | | tag_size: PhantomData<TagSize>, |
208 | | } |
209 | | |
210 | | impl<Aes, NonceSize, TagSize> KeySizeUser for AesGcm<Aes, NonceSize, TagSize> |
211 | | where |
212 | | Aes: KeySizeUser, |
213 | | TagSize: self::TagSize, |
214 | | { |
215 | | type KeySize = Aes::KeySize; |
216 | | } |
217 | | |
218 | | impl<Aes, NonceSize, TagSize> KeyInit for AesGcm<Aes, NonceSize, TagSize> |
219 | | where |
220 | | Aes: BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit, |
221 | | TagSize: self::TagSize, |
222 | | { |
223 | 1.48k | fn new(key: &Key<Self>) -> Self { |
224 | 1.48k | Aes::new(key).into() |
225 | 1.48k | } |
226 | | } |
227 | | |
228 | | impl<Aes, NonceSize, TagSize> From<Aes> for AesGcm<Aes, NonceSize, TagSize> |
229 | | where |
230 | | Aes: BlockSizeUser<BlockSize = U16> + BlockEncrypt, |
231 | | TagSize: self::TagSize, |
232 | | { |
233 | 1.48k | fn from(cipher: Aes) -> Self { |
234 | 1.48k | let mut ghash_key = ghash::Key::default(); |
235 | 1.48k | cipher.encrypt_block(&mut ghash_key); |
236 | | |
237 | 1.48k | let ghash = GHash::new(&ghash_key); |
238 | | |
239 | | #[cfg(feature = "zeroize")] |
240 | | ghash_key.zeroize(); |
241 | | |
242 | 1.48k | Self { |
243 | 1.48k | cipher, |
244 | 1.48k | ghash, |
245 | 1.48k | nonce_size: PhantomData, |
246 | 1.48k | tag_size: PhantomData, |
247 | 1.48k | } |
248 | 1.48k | } |
249 | | } |
250 | | |
251 | | impl<Aes, NonceSize, TagSize> AeadCore for AesGcm<Aes, NonceSize, TagSize> |
252 | | where |
253 | | NonceSize: ArrayLength<u8>, |
254 | | TagSize: self::TagSize, |
255 | | { |
256 | | type NonceSize = NonceSize; |
257 | | type TagSize = TagSize; |
258 | | type CiphertextOverhead = U0; |
259 | | } |
260 | | |
261 | | impl<Aes, NonceSize, TagSize> AeadInPlace for AesGcm<Aes, NonceSize, TagSize> |
262 | | where |
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 | | // TODO(tarcieri): interleave encryption with GHASH |
280 | | // See: <https://github.com/RustCrypto/AEADs/issues/74> |
281 | | 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 | 1.21k | fn decrypt_in_place_detached( |
288 | 1.21k | &self, |
289 | 1.21k | nonce: &Nonce<NonceSize>, |
290 | 1.21k | associated_data: &[u8], |
291 | 1.21k | buffer: &mut [u8], |
292 | 1.21k | tag: &Tag<TagSize>, |
293 | 1.21k | ) -> Result<(), Error> { |
294 | 1.21k | if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX { |
295 | 0 | return Err(Error); |
296 | 1.21k | } |
297 | | |
298 | 1.21k | let (ctr, mask) = self.init_ctr(nonce); |
299 | | |
300 | | // TODO(tarcieri): interleave encryption with GHASH |
301 | | // See: <https://github.com/RustCrypto/AEADs/issues/74> |
302 | 1.21k | let expected_tag = self.compute_tag(mask, associated_data, buffer); |
303 | | |
304 | | use subtle::ConstantTimeEq; |
305 | 1.21k | if expected_tag[..TagSize::to_usize()].ct_eq(tag).into() { |
306 | 476 | ctr.apply_keystream_partial(buffer.into()); |
307 | 476 | Ok(()) |
308 | | } else { |
309 | 740 | Err(Error) |
310 | | } |
311 | 1.21k | } |
312 | | } |
313 | | |
314 | | impl<Aes, NonceSize, TagSize> AesGcm<Aes, NonceSize, TagSize> |
315 | | where |
316 | | Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt, |
317 | | NonceSize: ArrayLength<u8>, |
318 | | TagSize: self::TagSize, |
319 | | { |
320 | | /// Initialize counter mode. |
321 | | /// |
322 | | /// See algorithm described in Section 7.2 of NIST SP800-38D: |
323 | | /// <https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf> |
324 | | /// |
325 | | /// > Define a block, J0, as follows: |
326 | | /// > If len(IV)=96, then J0 = IV || 0{31} || 1. |
327 | | /// > If len(IV) ≠ 96, then let s = 128 ⎡len(IV)/128⎤-len(IV), and |
328 | | /// > J0=GHASH(IV||0s+64||[len(IV)]64). |
329 | 1.21k | fn init_ctr(&self, nonce: &Nonce<NonceSize>) -> (Ctr32BE<&Aes>, Block) { |
330 | 1.21k | let j0 = if NonceSize::to_usize() == 12 { |
331 | 1.21k | let mut block = ghash::Block::default(); |
332 | 1.21k | block[..12].copy_from_slice(nonce); |
333 | 1.21k | block[15] = 1; |
334 | 1.21k | block |
335 | | } else { |
336 | 0 | let mut ghash = self.ghash.clone(); |
337 | 0 | ghash.update_padded(nonce); |
338 | | |
339 | 0 | let mut block = ghash::Block::default(); |
340 | 0 | let nonce_bits = (NonceSize::to_usize() as u64) * 8; |
341 | 0 | block[8..].copy_from_slice(&nonce_bits.to_be_bytes()); |
342 | 0 | ghash.update(&[block]); |
343 | 0 | ghash.finalize() |
344 | | }; |
345 | | |
346 | 1.21k | let mut ctr = Ctr32BE::inner_iv_init(&self.cipher, &j0); |
347 | 1.21k | let mut tag_mask = Block::default(); |
348 | 1.21k | ctr.write_keystream_block(&mut tag_mask); |
349 | 1.21k | (ctr, tag_mask) |
350 | 1.21k | } |
351 | | |
352 | | /// Authenticate the given plaintext and associated data using GHASH. |
353 | 1.21k | fn compute_tag(&self, mask: Block, associated_data: &[u8], buffer: &[u8]) -> Tag { |
354 | 1.21k | let mut ghash = self.ghash.clone(); |
355 | 1.21k | ghash.update_padded(associated_data); |
356 | 1.21k | ghash.update_padded(buffer); |
357 | | |
358 | 1.21k | let associated_data_bits = (associated_data.len() as u64) * 8; |
359 | 1.21k | let buffer_bits = (buffer.len() as u64) * 8; |
360 | | |
361 | 1.21k | let mut block = ghash::Block::default(); |
362 | 1.21k | block[..8].copy_from_slice(&associated_data_bits.to_be_bytes()); |
363 | 1.21k | block[8..].copy_from_slice(&buffer_bits.to_be_bytes()); |
364 | 1.21k | ghash.update(&[block]); |
365 | | |
366 | 1.21k | let mut tag = ghash.finalize(); |
367 | 19.4k | for (a, b) in tag.as_mut_slice().iter_mut().zip(mask.as_slice()) { |
368 | 19.4k | *a ^= *b; |
369 | 19.4k | } |
370 | | |
371 | 1.21k | tag |
372 | 1.21k | } |
373 | | } |