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