Skip to main content

libcrux_aesgcm/
lib.rs

1//! # AES-GCM
2//!
3//! This crate implements AES-GCM-128 and AES-GCM-256. The crate provides
4//! optimized implementations for ARM and x86_64 platforms with support
5//! for AES hardware acceleration, as well as a bit-sliced portable
6//! implementation.
7//!
8//! For general use, we provide a platform-multiplexing API via the
9//! [`AesGcm128Key`] and [`AesGcm256Key`] structs, which selects the most
10//! performant implementation at runtime.
11//!
12//! Usage example:
13//!
14//! ```rust
15//! // Multiplexed owned API
16//! use libcrux_aesgcm::AeadConsts as _;
17//! use libcrux_aesgcm::{AesGcm128, AesGcm128Key, AesGcm128Nonce, AesGcm128Tag, NONCE_LEN, TAG_LEN};
18//!
19//! let k: AesGcm128Key = [0; AesGcm128::KEY_LEN].into();
20//! let nonce: AesGcm128Nonce = [0; NONCE_LEN].into();
21//! let mut tag: AesGcm128Tag = [0; TAG_LEN].into();
22//!
23//! let pt = b"the quick brown fox jumps over the lazy dog";
24//! let mut ct = [0; 43];
25//! let mut pt_out = [0; 43];
26//!
27//! k.encrypt(&mut ct, &mut tag, &nonce, b"", pt).unwrap();
28//! k.decrypt(&mut pt_out, &nonce, b"", &ct, &tag).unwrap();
29//! assert_eq!(pt, &pt_out);
30//! ```
31//!
32//! We also provide access to [lower-level AEAD
33//! APIs](libcrux_traits::aead) for the platform-multiplexing
34//! implementation with the [`AesGcm128`] and [`AesGcm256`] structs.
35//!
36//! Users who want to use a platform-specific implementation directly can
37//! access them in the submodules `aes_gcm_128::{portable, x64, neon}`.
38//!
39//!
40
41#![no_std]
42#![deny(unsafe_code)]
43#[cfg(feature = "std")]
44extern crate std;
45
46mod aes;
47mod ctr;
48mod gf128;
49mod platform;
50
51mod traits_api;
52
53mod aes_gcm;
54
55/// Implementations of AES-GCM 128
56///
57/// This module contains implementations of AES-GCM 128:
58/// - [`AesGcm128`]: A platform-multiplexing implementation, which will at
59/// runtime select the most performant implementation among the following for the given
60/// architecture at runtime.
61#[cfg_attr(
62    feature = "simd256",
63    doc = "- [`aes_gcm_128::x64::X64AesGcm128`]: An implementation optimized for x86_64 AES-NI instruction sets."
64)]
65#[cfg_attr(
66    feature = "simd128",
67    doc = "- [`aes_gcm_128::neon::NeonAesGcm128`]: An implementation optimized for ARM NEON instruction sets."
68)]
69/// - [`aes_gcm_128::portable::PortableAesGcm128`]: A portable, bit-sliced implementation.
70///
71/// See [`EncryptError`],
72/// [`DecryptError`](libcrux_traits::aead::arrayref::DecryptError) and
73/// [`KeyGenError`](libcrux_traits::aead::arrayref::DecryptError) for
74/// errors.
75///
76/// The [`libcrux_traits`](libcrux_traits) crate provides two typed APIs for AEADs:
77///
78/// ## Owned key-centric API
79/// This API operates on owned arrays for keys, nonces and tags:
80/// ```rust
81/// // Using the multiplexed implementation.
82/// use libcrux_aesgcm::AeadConsts as _;
83/// use libcrux_aesgcm::{NONCE_LEN, TAG_LEN, aes_gcm_128::{AesGcm128, Key, Tag, Nonce}};
84///
85/// let k: Key = [0; AesGcm128::KEY_LEN].into();
86/// let nonce: Nonce = [0; NONCE_LEN].into();
87/// let mut tag: Tag = [0; TAG_LEN].into();
88///
89/// let pt = b"the quick brown fox jumps over the lazy dog";
90/// let mut ct = [0; 43];
91/// let mut pt_out = [0; 43];
92///
93/// k.encrypt(&mut ct, &mut tag, &nonce, b"", pt).unwrap();
94/// k.decrypt(&mut pt_out, &nonce, b"", &ct, &tag).unwrap();
95/// assert_eq!(pt, &pt_out);
96/// ```
97///
98/// ## Refs key-centric API
99/// This API operates on array references for keys, nonces and tags:
100/// ```rust
101/// // Using the multiplexed API
102/// use libcrux_aesgcm::{AeadConsts as _, Aead as _};
103/// use libcrux_aesgcm::{NONCE_LEN, TAG_LEN, aes_gcm_128::{AesGcm128}};
104///
105/// let algo = AesGcm128;
106///
107/// let mut tag_bytes = [0; TAG_LEN];
108/// let tag = algo.new_tag_mut(&mut tag_bytes).unwrap();
109///
110/// let key = algo.new_key(&[0; AesGcm128::KEY_LEN]).unwrap();
111/// let nonce = algo.new_nonce(&[0; NONCE_LEN]).unwrap();
112///
113/// let pt = b"the quick brown fox jumps over the lazy dog";
114/// let mut ct = [0; 43];
115/// let mut pt_out = [0; 43];
116///
117/// key.encrypt(&mut ct, tag, nonce, b"", pt).unwrap();
118/// let tag = algo.new_tag(&tag_bytes).unwrap();
119/// key.decrypt(&mut pt_out, nonce, b"", &ct, tag).unwrap();
120/// assert_eq!(pt, &pt_out);
121/// ```
122pub mod aes_gcm_128;
123
124/// Implementations of AES-GCM 256
125///
126/// This module contains implementations of AES-GCM 256:
127/// - [`AesGcm256`]: A platform-multiplexing implementation, which will at
128/// runtime select the most performant implementation among the following for the given
129/// architecture at runtime.
130#[cfg_attr(
131    feature = "simd256",
132    doc = "- [`aes_gcm_256::x64::X64AesGcm256`]: An implementation optimized for x86_64 AES-NI instruction sets."
133)]
134#[cfg_attr(
135    feature = "simd128",
136    doc = "- [`aes_gcm_256::neon::NeonAesGcm256`]: An implementation optimized for ARM NEON instruction sets."
137)]
138/// - [`aes_gcm_256::portable::PortableAesGcm256`]: A portable, bit-sliced implementation.
139///
140/// See [`EncryptError`],
141/// [`DecryptError`](libcrux_traits::aead::arrayref::DecryptError) and
142/// [`KeyGenError`](libcrux_traits::aead::arrayref::DecryptError) for
143/// errors.
144///
145/// The [`libcrux_traits`](libcrux_traits) crate provides two typed APIs for AEADs:
146///
147/// ## Owned key-centric API
148/// This API operates on owned arrays for keys, nonces and tags:
149/// ```rust
150/// // Using the multiplexed implementation.
151/// use libcrux_aesgcm::AeadConsts as _;
152/// use libcrux_aesgcm::{NONCE_LEN, TAG_LEN, aes_gcm_256::{AesGcm256, Key, Tag, Nonce}};
153///
154/// let k: Key = [0; AesGcm256::KEY_LEN].into();
155/// let nonce: Nonce = [0; NONCE_LEN].into();
156/// let mut tag: Tag = [0; TAG_LEN].into();
157///
158/// let pt = b"the quick brown fox jumps over the lazy dog";
159/// let mut ct = [0; 43];
160/// let mut pt_out = [0; 43];
161///
162/// k.encrypt(&mut ct, &mut tag, &nonce, b"", pt).unwrap();
163/// k.decrypt(&mut pt_out, &nonce, b"", &ct, &tag).unwrap();
164/// assert_eq!(pt, &pt_out);
165/// ```
166///
167/// ## Refs key-centric API
168/// This API operates on array references for keys, nonces and tags:
169/// ```rust
170/// // Using the multiplexed API
171/// use libcrux_aesgcm::{AeadConsts as _, Aead as _};
172/// use libcrux_aesgcm::{NONCE_LEN, TAG_LEN, aes_gcm_256::{AesGcm256}};
173///
174/// let algo = AesGcm256;
175///
176/// let mut tag_bytes = [0; TAG_LEN];
177/// let tag = algo.new_tag_mut(&mut tag_bytes).unwrap();
178///
179/// let key = algo.new_key(&[0; AesGcm256::KEY_LEN]).unwrap();
180/// let nonce = algo.new_nonce(&[0; NONCE_LEN]).unwrap();
181///
182/// let pt = b"the quick brown fox jumps over the lazy dog";
183/// let mut ct = [0; 43];
184/// let mut pt_out = [0; 43];
185///
186/// key.encrypt(&mut ct, tag, nonce, b"", pt).unwrap();
187/// let tag = algo.new_tag(&tag_bytes).unwrap();
188/// key.decrypt(&mut pt_out, nonce, b"", &ct, tag).unwrap();
189/// assert_eq!(pt, &pt_out);
190/// ```
191pub mod aes_gcm_256;
192
193/// Trait for an AES State.
194/// Implemented for 128 and 256.
195pub(crate) trait State {
196    fn init(key: &[u8]) -> Self;
197    fn set_nonce(&mut self, nonce: &[u8]);
198    fn encrypt(&mut self, aad: &[u8], plaintext: &[u8], ciphertext: &mut [u8], tag: &mut [u8]);
199    fn decrypt(
200        &mut self,
201        aad: &[u8],
202        ciphertext: &[u8],
203        tag: &[u8],
204        plaintext: &mut [u8],
205    ) -> Result<(), DecryptError>;
206}
207
208pub(crate) mod implementations {
209
210    #[cfg(doc)]
211    use super::{aes_gcm_128, aes_gcm_256};
212
213    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for platform-multiplexed AES-GCM 128.
214    ///
215    /// The implementation used is determined automatically at runtime.
216    /// - `x64`
217    /// - `neon`
218    /// - `portable`
219    ///
220    /// For more information on usage, see [`aes_gcm_128`].
221    #[derive(Clone, Copy, PartialEq, Eq)]
222    pub struct AesGcm128;
223
224    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for portable AES-GCM 128.
225    ///
226    /// For more information on usage, see [`aes_gcm_128`].
227    #[derive(Clone, Copy, PartialEq, Eq)]
228    pub struct PortableAesGcm128;
229
230    #[cfg(feature = "simd128")]
231    #[derive(Clone, Copy, PartialEq, Eq)]
232    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for ARM Neon optimized AES-GCM 128.
233    ///
234    /// Should only be used directly after performing runtime checks for the necessary CPU
235    /// features.
236    ///
237    /// For more information on usage, see [`aes_gcm_128`].
238    pub struct NeonAesGcm128;
239
240    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for x86_64 AES-NI optimized AES-GCM 128.
241    ///
242    /// Should only be used directly after performing runtime checks for the necessary CPU
243    /// features.
244    ///
245    /// For more information on usage, see [`aes_gcm_128`].
246    #[cfg(feature = "simd256")]
247    #[derive(Clone, Copy, PartialEq, Eq)]
248    pub struct X64AesGcm128;
249
250    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for platform-multiplexed AES-GCM 256.
251    ///
252    /// The implementation used is determined automatically at runtime.
253    /// - `x64`
254    /// - `neon`
255    /// - `portable`
256    ///
257    /// For more information on usage, see [`aes_gcm_256`].
258    #[derive(Clone, Copy, PartialEq, Eq)]
259    pub struct AesGcm256;
260
261    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for portable AES-GCM 256.
262    ///
263    /// For more information on usage, see [`aes_gcm_256`].
264    #[derive(Clone, Copy, PartialEq, Eq)]
265    pub struct PortableAesGcm256;
266
267    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for ARM Neon optimized AES-GCM 256.
268    ///
269    /// For more information on usage, see [`aes_gcm_256`].
270    #[cfg(feature = "simd128")]
271    #[derive(Clone, Copy, PartialEq, Eq)]
272    pub struct NeonAesGcm256;
273
274    /// Access to [lower-level AEAD APIs](libcrux_traits::aead) for x86_64 AES-NI optimized AES-GCM 256.
275    ///
276    /// For more information on usage, see [`aes_gcm_256`].
277    #[derive(Clone, Copy, PartialEq, Eq)]
278    #[cfg(feature = "simd256")]
279    pub struct X64AesGcm256;
280}
281// pub use implementations::*;
282
283/// Tag length.
284pub const TAG_LEN: usize = 16;
285
286/// Nonce length.
287pub const NONCE_LEN: usize = 12;
288
289#[doc(inline)]
290pub use aes_gcm_128::KEY_LEN as AESGCM128_KEY_LEN;
291#[doc(inline)]
292pub use aes_gcm_256::KEY_LEN as AESGCM256_KEY_LEN;
293
294pub use libcrux_traits::aead::arrayref::{DecryptError, EncryptError, KeyGenError};
295
296/// Generic AES-GCM encrypt.
297pub(crate) fn encrypt<S: State>(
298    key: &[u8],
299    nonce: &[u8],
300    aad: &[u8],
301    plaintext: &[u8],
302    ciphertext: &mut [u8],
303    tag: &mut [u8],
304) -> Result<(), EncryptError> {
305    debug_assert!(nonce.len() == NONCE_LEN);
306    debug_assert!(tag.len() == TAG_LEN);
307
308    // plaintext length check
309    if plaintext.len() / crate::aes::AES_BLOCK_LEN >= (u32::MAX - 1) as usize {
310        return Err(EncryptError::PlaintextTooLong);
311    }
312
313    // ensure ciphertext and plaintext have same length
314    if ciphertext.len() != plaintext.len() {
315        return Err(EncryptError::WrongCiphertextLength);
316    }
317
318    let mut st = S::init(key);
319    st.set_nonce(nonce);
320    st.encrypt(aad, plaintext, ciphertext, tag);
321
322    Ok(())
323}
324
325/// Generic AES-GCM decrypt.
326pub(crate) fn decrypt<S: State>(
327    key: &[u8],
328    nonce: &[u8],
329    aad: &[u8],
330    ciphertext: &[u8],
331    tag: &[u8],
332    plaintext: &mut [u8],
333) -> Result<(), DecryptError> {
334    debug_assert!(nonce.len() == NONCE_LEN);
335    debug_assert!(tag.len() == TAG_LEN);
336
337    // plaintext length check
338    if plaintext.len() / crate::aes::AES_BLOCK_LEN >= (u32::MAX - 1) as usize {
339        return Err(DecryptError::PlaintextTooLong);
340    }
341
342    // ensure ciphertext and plaintext have same length
343    if ciphertext.len() != plaintext.len() {
344        return Err(DecryptError::WrongPlaintextLength);
345    }
346
347    let mut st = S::init(key);
348    st.set_nonce(nonce);
349    st.decrypt(aad, ciphertext, tag, plaintext)
350}
351
352/// Macro to instantiate the different variants, both 128/256 and platforms.
353macro_rules! pub_crate_mod {
354    ($variant_comment:literal, $mod_name:ident, $state:ty) => {
355        #[doc = $variant_comment]
356        pub mod $mod_name {
357            use crate::$mod_name::KEY_LEN;
358            use crate::{platform, DecryptError, EncryptError};
359
360            type State = $state;
361
362            #[doc = $variant_comment]
363            /// encrypt.
364            pub fn encrypt(
365                key: &[u8],
366                nonce: &[u8],
367                aad: &[u8],
368                plaintext: &[u8],
369                ciphertext: &mut [u8],
370                tag: &mut [u8],
371            ) -> Result<(), EncryptError> {
372                debug_assert!(key.len() == KEY_LEN);
373                crate::encrypt::<State>(key, nonce, aad, plaintext, ciphertext, tag)
374            }
375
376            #[doc = $variant_comment]
377            /// decrypt.
378            pub fn decrypt(
379                key: &[u8],
380                nonce: &[u8],
381                aad: &[u8],
382                ciphertext: &[u8],
383                tag: &[u8],
384                plaintext: &mut [u8],
385            ) -> Result<(), DecryptError> {
386                debug_assert!(key.len() == KEY_LEN);
387                crate::decrypt::<State>(key, nonce, aad, ciphertext, tag, plaintext)
388            }
389        }
390    };
391}
392
393pub(crate) mod portable {
394    pub_crate_mod!(r"AES-GCM 128 ", aes_gcm_128, crate::aes_gcm_128::State<platform::portable::State, platform::portable::FieldElement>);
395    pub_crate_mod!(r"AES-GCM 256 ", aes_gcm_256, crate::aes_gcm_256::State<platform::portable::State, platform::portable::FieldElement>);
396}
397
398#[cfg(feature = "simd128")]
399pub(crate) mod neon {
400    pub_crate_mod!(r"AES-GCM 128 ", aes_gcm_128, crate::aes_gcm_128::State<platform::neon::State, platform::neon::FieldElement>);
401    pub_crate_mod!(r"AES-GCM 256 ", aes_gcm_256, crate::aes_gcm_256::State<platform::neon::State, platform::neon::FieldElement>);
402}
403
404#[cfg(feature = "simd256")]
405pub(crate) mod x64 {
406    // Here we don't use the `pub_crate_mod` macro because we need to add target features
407    // onto the functions.
408    macro_rules! x64_pub_crate_mod {
409        ($variant_comment:literal, $mod_name:ident, $state:ty) => {
410            #[doc = $variant_comment]
411            pub mod $mod_name {
412                use crate::$mod_name::KEY_LEN;
413                use crate::{platform, DecryptError, EncryptError};
414
415                type State = $state;
416
417                #[doc = $variant_comment]
418                /// encrypt.
419                pub fn encrypt(
420                    key: &[u8],
421                    nonce: &[u8],
422                    aad: &[u8],
423                    plaintext: &[u8],
424                    ciphertext: &mut [u8],
425                    tag: &mut [u8],
426                ) -> Result<(), EncryptError> {
427                    debug_assert!(key.len() == KEY_LEN);
428
429                    // due to use of `target_feature`, unsafe is needed here
430                    #[inline]
431                    #[cfg_attr(not(hax), target_feature(enable = "avx2", enable = "aes"))]
432                    #[allow(unsafe_code)]
433                    unsafe fn inner(
434                        key: &[u8],
435                        nonce: &[u8],
436                        aad: &[u8],
437                        plaintext: &[u8],
438                        ciphertext: &mut [u8],
439                        tag: &mut [u8],
440                    ) -> Result<(), EncryptError> {
441                        crate::encrypt::<State>(key, nonce, aad, plaintext, ciphertext, tag)
442                    }
443
444                    #[allow(unsafe_code)]
445                    unsafe {
446                        inner(key, nonce, aad, plaintext, ciphertext, tag)
447                    }
448                }
449
450                #[doc = $variant_comment]
451                /// decrypt.
452                pub fn decrypt(
453                    key: &[u8],
454                    nonce: &[u8],
455                    aad: &[u8],
456                    ciphertext: &[u8],
457                    tag: &[u8],
458                    plaintext: &mut [u8],
459                ) -> Result<(), DecryptError> {
460                    debug_assert!(key.len() == KEY_LEN);
461
462                    // due to use of `target_feature`, unsafe is needed here
463                    #[inline]
464                    #[cfg_attr(not(hax), target_feature(enable = "avx2", enable = "aes"))]
465                    #[allow(unsafe_code)]
466                    unsafe fn inner(
467                        key: &[u8],
468                        nonce: &[u8],
469                        aad: &[u8],
470                        ciphertext: &[u8],
471                        tag: &[u8],
472                        plaintext: &mut [u8],
473                    ) -> Result<(), DecryptError> {
474                        crate::decrypt::<State>(key, nonce, aad, ciphertext, tag, plaintext)
475                    }
476
477                    #[allow(unsafe_code)]
478                    unsafe {
479                        inner(key, nonce, aad, ciphertext, tag, plaintext)
480                    }
481                }
482            }
483        };
484    }
485
486    x64_pub_crate_mod!(r"AES-GCM 128 ", aes_gcm_128, crate::aes_gcm_128::State<platform::x64::State, platform::x64::FieldElement>);
487    x64_pub_crate_mod!(r"AES-GCM 256 ", aes_gcm_256, crate::aes_gcm_256::State<platform::x64::State, platform::x64::FieldElement>);
488}
489
490// traits re-exports
491pub use libcrux_traits::aead::consts::AeadConsts;
492pub use libcrux_traits::aead::typed_refs::Aead;
493
494pub use implementations::{AesGcm128, AesGcm256};
495
496#[doc(inline)]
497pub use aes_gcm_128::Key as AesGcm128Key;
498#[doc(inline)]
499pub use aes_gcm_128::Nonce as AesGcm128Nonce;
500#[doc(inline)]
501pub use aes_gcm_128::Tag as AesGcm128Tag;
502
503#[doc(inline)]
504pub use aes_gcm_256::Key as AesGcm256Key;
505#[doc(inline)]
506pub use aes_gcm_256::Nonce as AesGcm256Nonce;
507#[doc(inline)]
508pub use aes_gcm_256::Tag as AesGcm256Tag;