aes_gcm_siv/lib.rs
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#![warn(missing_docs, rust_2018_idioms)]
9
10//! # Usage
11//!
12//! Simple usage (allocating, no associated data):
13//!
14#![cfg_attr(feature = "getrandom", doc = "```")]
15#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
16//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
17//! // NOTE: requires the `getrandom` feature is enabled
18//!
19//! use aes_gcm_siv::{
20//! aead::{Aead, AeadCore, Generate, Key, KeyInit},
21//! Aes256GcmSiv, Nonce // Or `Aes128GcmSiv`
22//! };
23//!
24//! let key = Key::<Aes256GcmSiv>::generate();
25//! let cipher = Aes256GcmSiv::new(&key);
26//!
27//! let nonce = Nonce::generate(); // MUST be unique per message
28//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?;
29//!
30//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
31//! assert_eq!(&plaintext, b"plaintext message");
32//! # Ok(())
33//! # }
34//! ```
35//!
36//! ## In-place Usage (eliminates `alloc` requirement)
37//!
38//! This crate has an optional `alloc` feature which can be disabled in e.g.
39//! microcontroller environments that don't have a heap.
40//!
41//! The [`AeadInOut::encrypt_in_place`] and [`AeadInOut::decrypt_in_place`]
42//! methods accept any type that impls the [`aead::Buffer`] trait which
43//! contains the plaintext for encryption or ciphertext for decryption.
44//!
45//! Enabling the `arrayvec` feature of this crate will provide an impl of
46//! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as
47//! [`aead::arrayvec::ArrayVec`]), and enabling the `bytes` feature of this crate will
48//! provide an impl of [`aead::Buffer`] for `bytes::BytesMut` (re-exported from the
49//! [`aead`] crate as [`aead::bytes::BytesMut`]).
50//!
51//! It can then be passed as the `buffer` parameter to the in-place encrypt
52//! and decrypt methods:
53//!
54#![cfg_attr(all(feature = "getrandom", feature = "arrayvec"), doc = "```")]
55#![cfg_attr(
56 not(all(feature = "getrandom", feature = "arrayvec")),
57 doc = "```ignore"
58)]
59//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
60//! // NOTE: requires the `arrayvec` and `getrandom` features are enabled
61//!
62//! use aes_gcm_siv::{
63//! aead::{AeadInOut, AeadCore, Buffer, Generate, Key, KeyInit, arrayvec::ArrayVec},
64//! Aes256GcmSiv, Nonce, // Or `Aes128GcmSiv`
65//! };
66//!
67//! let key = Key::<Aes256GcmSiv>::generate();
68//! let cipher = Aes256GcmSiv::new(&key);
69//!
70//! let nonce = Nonce::generate(); // 96-bits; unique per message
71//! let mut buffer: ArrayVec<u8, 128> = ArrayVec::new(); // Note: buffer needs 16-bytes overhead for auth tag
72//! buffer.extend_from_slice(b"plaintext message");
73//!
74//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
75//! cipher.encrypt_in_place(&nonce, b"", &mut buffer)?;
76//!
77//! // `buffer` now contains the message ciphertext
78//! assert_ne!(buffer.as_ref(), b"plaintext message");
79//!
80//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
81//! cipher.decrypt_in_place(&nonce, b"", &mut buffer)?;
82//! assert_eq!(buffer.as_ref(), b"plaintext message");
83//! # Ok(())
84//! # }
85//! ```
86
87pub use aead::{self, AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser};
88
89#[cfg(feature = "aes")]
90pub use aes;
91
92use aead::{TagPosition, inout::InOutBuf};
93use cipher::{
94 BlockCipherEncrypt, BlockSizeUser, InnerIvInit, StreamCipherCore,
95 array::Array,
96 consts::{U12, U16},
97};
98use polyval::{Polyval, universal_hash::UniversalHash};
99
100/// AES is optional to allow swapping in hardware-specific backends.
101#[cfg(feature = "aes")]
102use aes::{Aes128, Aes256};
103
104/// Maximum length of associated data (from RFC8452 § 6).
105pub const A_MAX: u64 = 1 << 36;
106
107/// Maximum length of plaintext (from RFC8452 § 6).
108pub const P_MAX: u64 = 1 << 36;
109
110/// Maximum length of ciphertext (from RFC8452 § 6).
111pub const C_MAX: u64 = (1 << 36) + 16;
112
113/// AES-GCM-SIV nonces.
114pub type Nonce = Array<u8, U12>;
115
116/// AES-GCM-SIV tags.
117pub type Tag = Array<u8, U16>;
118
119/// AES-GCM-SIV with a 128-bit key.
120#[cfg(feature = "aes")]
121pub type Aes128GcmSiv = AesGcmSiv<Aes128>;
122
123/// AES-GCM-SIV with a 256-bit key.
124#[cfg(feature = "aes")]
125pub type Aes256GcmSiv = AesGcmSiv<Aes256>;
126
127/// Counter mode with a 32-bit little endian counter.
128type Ctr32LE<Aes> = ctr::CtrCore<Aes, ctr::flavors::Ctr32LE>;
129
130/// AES-GCM-SIV: Misuse-Resistant Authenticated Encryption Cipher (RFC 8452).
131#[derive(Clone)]
132pub struct AesGcmSiv<Aes> {
133 /// Key generating key used to derive AES-GCM-SIV subkeys.
134 key_generating_key: Aes,
135}
136
137impl<Aes> KeySizeUser for AesGcmSiv<Aes>
138where
139 Aes: KeySizeUser,
140{
141 type KeySize = Aes::KeySize;
142}
143
144impl<Aes> KeyInit for AesGcmSiv<Aes>
145where
146 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit,
147{
148 fn new(key_bytes: &Key<Self>) -> Self {
149 Self {
150 key_generating_key: Aes::new(key_bytes),
151 }
152 }
153}
154
155impl<Aes> From<Aes> for AesGcmSiv<Aes>
156where
157 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
158{
159 fn from(key_generating_key: Aes) -> Self {
160 Self { key_generating_key }
161 }
162}
163
164impl<Aes> AeadCore for AesGcmSiv<Aes>
165where
166 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit,
167{
168 type NonceSize = U12;
169 type TagSize = U16;
170 const TAG_POSITION: TagPosition = TagPosition::Postfix;
171}
172
173impl<Aes> AeadInOut for AesGcmSiv<Aes>
174where
175 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit,
176{
177 fn encrypt_inout_detached(
178 &self,
179 nonce: &Nonce,
180 associated_data: &[u8],
181 buffer: InOutBuf<'_, '_, u8>,
182 ) -> Result<Tag, Error> {
183 Cipher::<Aes>::new(&self.key_generating_key, nonce)
184 .encrypt_inout_detached(associated_data, buffer)
185 }
186
187 fn decrypt_inout_detached(
188 &self,
189 nonce: &Nonce,
190 associated_data: &[u8],
191 buffer: InOutBuf<'_, '_, u8>,
192 tag: &Tag,
193 ) -> Result<(), Error> {
194 Cipher::<Aes>::new(&self.key_generating_key, nonce).decrypt_inout_detached(
195 associated_data,
196 buffer,
197 tag,
198 )
199 }
200}
201
202/// AES-GCM-SIV: Misuse-Resistant Authenticated Encryption Cipher (RFC8452).
203struct Cipher<Aes>
204where
205 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
206{
207 /// Encryption cipher.
208 enc_cipher: Aes,
209
210 /// POLYVAL universal hash.
211 polyval: Polyval,
212
213 /// Nonce.
214 nonce: Nonce,
215}
216
217impl<Aes> Cipher<Aes>
218where
219 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit,
220{
221 /// Initialize AES-GCM-SIV, deriving per-nonce message-authentication and
222 /// message-encryption keys.
223 pub(crate) fn new(key_generating_key: &Aes, nonce: &Nonce) -> Self {
224 let mut mac_key = polyval::Key::default();
225 let mut enc_key = Array::default();
226 let mut block = cipher::Block::<Aes>::default();
227 let mut counter = 0u32;
228
229 // Derive subkeys from the master key-generating-key in counter mode.
230 //
231 // From RFC8452 § 4: <https://tools.ietf.org/html/rfc8452#section-4>
232 //
233 // > The message-authentication key is 128 bit, and the message-encryption
234 // > key is either 128 (for AES-128) or 256 bit (for AES-256).
235 // >
236 // > These keys are generated by encrypting a series of plaintext blocks
237 // > that contain a 32-bit, little-endian counter followed by the nonce,
238 // > and then discarding the second half of the resulting ciphertext. In
239 // > the AES-128 case, 128 + 128 = 256 bits of key material need to be
240 // > generated, and, since encrypting each block yields 64 bits after
241 // > discarding half, four blocks need to be encrypted. The counter
242 // > values for these blocks are 0, 1, 2, and 3. For AES-256, six blocks
243 // > are needed in total, with counter values 0 through 5 (inclusive).
244 for derived_key in &mut [mac_key.as_mut_slice(), enc_key.as_mut_slice()] {
245 for chunk in derived_key.chunks_mut(8) {
246 block[..4].copy_from_slice(&counter.to_le_bytes());
247 block[4..].copy_from_slice(nonce.as_slice());
248
249 key_generating_key.encrypt_block(&mut block);
250 chunk.copy_from_slice(&block.as_slice()[..8]);
251
252 counter += 1;
253 }
254 }
255
256 let result = Self {
257 enc_cipher: Aes::new(&enc_key),
258 polyval: Polyval::new(&mac_key),
259 nonce: *nonce,
260 };
261
262 // Zeroize all intermediate buffers
263 // TODO(tarcieri): use `Zeroizing` when const generics land
264 #[cfg(feature = "zeroize")]
265 {
266 use zeroize::Zeroize;
267 mac_key.as_mut_slice().zeroize();
268 enc_key.as_mut_slice().zeroize();
269 block.as_mut_slice().zeroize();
270 }
271
272 result
273 }
274
275 /// Encrypt the given message in-place, returning the authentication tag.
276 pub(crate) fn encrypt_inout_detached(
277 mut self,
278 associated_data: &[u8],
279 buffer: InOutBuf<'_, '_, u8>,
280 ) -> Result<Tag, Error> {
281 if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
282 return Err(Error);
283 }
284
285 self.polyval.update_padded(associated_data);
286 self.polyval.update_padded(buffer.get_in());
287
288 let tag = self.finish_tag(associated_data.len(), buffer.len());
289 init_ctr(&self.enc_cipher, &tag).apply_keystream_partial(buffer);
290
291 Ok(tag)
292 }
293
294 /// Decrypt the given message, first authenticating ciphertext integrity
295 /// and returning an error if it's been tampered with.
296 pub(crate) fn decrypt_inout_detached(
297 mut self,
298 associated_data: &[u8],
299 mut buffer: InOutBuf<'_, '_, u8>,
300 tag: &Tag,
301 ) -> Result<(), Error> {
302 if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
303 return Err(Error);
304 }
305
306 self.polyval.update_padded(associated_data);
307
308 // TODO(tarcieri): interleave decryption and authentication
309 init_ctr(&self.enc_cipher, tag).apply_keystream_partial(buffer.reborrow());
310 self.polyval.update_padded(buffer.get_out());
311
312 let expected_tag = self.finish_tag(associated_data.len(), buffer.len());
313
314 use subtle::ConstantTimeEq;
315 if expected_tag.ct_eq(tag).into() {
316 Ok(())
317 } else {
318 // On MAC verify failure, re-encrypt the plaintext buffer to
319 // prevent accidental exposure.
320 init_ctr(&self.enc_cipher, tag).apply_keystream_partial(buffer);
321 Err(Error)
322 }
323 }
324
325 /// Finish computing POLYVAL tag for AAD and buffer of the given length.
326 fn finish_tag(&mut self, associated_data_len: usize, buffer_len: usize) -> Tag {
327 let associated_data_bits = (associated_data_len as u64) * 8;
328 let buffer_bits = (buffer_len as u64) * 8;
329
330 let mut block = polyval::Block::default();
331 block[..8].copy_from_slice(&associated_data_bits.to_le_bytes());
332 block[8..].copy_from_slice(&buffer_bits.to_le_bytes());
333 self.polyval.update(&[block]);
334
335 let mut tag = self.polyval.finalize_reset();
336
337 // XOR the nonce into the resulting tag
338 for (i, byte) in tag[..12].iter_mut().enumerate() {
339 *byte ^= self.nonce[i];
340 }
341
342 // Clear the highest bit
343 tag[15] &= 0x7f;
344
345 self.enc_cipher.encrypt_block(&mut tag);
346 tag
347 }
348}
349
350/// Initialize counter mode.
351///
352/// From RFC8452 § 4: <https://tools.ietf.org/html/rfc8452#section-4>
353///
354/// > The initial counter block is the tag with the most significant bit
355/// > of the last byte set to one.
356#[inline]
357fn init_ctr<Aes>(cipher: Aes, nonce: &cipher::Block<Aes>) -> Ctr32LE<Aes>
358where
359 Aes: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
360{
361 let mut counter_block = *nonce;
362 counter_block[15] |= 0x80;
363 Ctr32LE::inner_iv_init(cipher, &counter_block)
364}