Skip to main content

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