Skip to main content

aes/
lib.rs

1//! Pure Rust implementation of the [Advanced Encryption Standard][AES]
2//! (AES, a.k.a. Rijndael).
3//!
4//! # ⚠️ Security Warning: Hazmat!
5//!
6//! This crate implements only the low-level block cipher function, and is intended
7//! for use for implementing higher-level constructions *only*. It is NOT
8//! intended for direct use in applications.
9//!
10//! USE AT YOUR OWN RISK!
11//!
12//! # Supported backends
13//! This crate provides multiple backends including a portable pure Rust
14//! backend as well as ones based on CPU intrinsics.
15//!
16//! By default, it performs runtime detection of CPU intrinsics and uses them
17//! if they are available.
18//!
19//! ## "soft" portable backend
20//! As a baseline implementation, this crate provides a constant-time pure Rust
21//! implementation based on [fixslicing], a more advanced form of bitslicing
22//! implemented entirely in terms of bitwise arithmetic with no use of any
23//! lookup tables or data-dependent branches.
24//!
25//! Enabling the `aes_compact` configuration flag will reduce the code size of this
26//! backend at the cost of decreased performance (using a modified form of
27//! the fixslicing technique called "semi-fixslicing").
28//!
29//! ## ARMv8 intrinsics (Rust 1.61+)
30//! On `aarch64` targets including `aarch64-apple-darwin` (Apple M1) and Linux
31//! targets such as `aarch64-unknown-linux-gnu` and `aarch64-unknown-linux-musl`,
32//! support for using AES intrinsics provided by the ARMv8 Cryptography Extensions.
33//!
34//! On Linux and macOS, support for ARMv8 AES intrinsics is autodetected at
35//! runtime. On other platforms the `aes` target feature must be enabled via
36//! RUSTFLAGS.
37//!
38//! ## `x86`/`x86_64` intrinsics (AES-NI and VAES)
39//! By default this crate uses runtime detection on `i686`/`x86_64` targets
40//! in order to determine if AES-NI and VAES are available, and if they are
41//! not, it will fallback to using a constant-time software implementation.
42//!
43//! Passing `RUSTFLAGS=-Ctarget-feature=+aes,+ssse3` explicitly at
44//! compile-time will override runtime detection and ensure that AES-NI is
45//! used or passing `RUSTFLAGS=-Ctarget-feature=+aes,+avx512f,+ssse3,+vaes`
46//! will ensure that AESNI and VAES are always used.
47//!
48//! Note: Enabling VAES256 or VAES512 still requires specifying `--cfg
49//! aes_backend = "avx256"` or `--cfg aes_backend = "avx512"` explicitly.
50//!
51//! Programs built in this manner will crash with an illegal instruction on
52//! CPUs which do not have AES-NI and VAES enabled.
53//!
54//! Note: runtime detection is not possible on SGX targets. Please use the
55//! aforementioned `RUSTFLAGS` to leverage AES-NI and VAES on these targets.
56//!
57//! # Examples
58//! ```
59//! use aes::Aes128;
60//! use aes::cipher::{Array, BlockCipherEncrypt, BlockCipherDecrypt, KeyInit};
61//!
62//! let key = Array::from([0u8; 16]);
63//! let mut block = Array::from([42u8; 16]);
64//!
65//! // Initialize cipher
66//! let cipher = Aes128::new(&key);
67//!
68//! let block_copy = block;
69//!
70//! // Encrypt block in-place
71//! cipher.encrypt_block(&mut block);
72//!
73//! // And decrypt it back
74//! cipher.decrypt_block(&mut block);
75//! assert_eq!(block, block_copy);
76//!
77//! // Implementation supports parallel block processing. Number of blocks
78//! // processed in parallel depends in general on hardware capabilities.
79//! // This is achieved by instruction-level parallelism (ILP) on a single
80//! // CPU core, which is different from multi-threaded parallelism.
81//! let mut blocks = [block; 100];
82//! cipher.encrypt_blocks(&mut blocks);
83//!
84//! for block in blocks.iter_mut() {
85//!     cipher.decrypt_block(block);
86//!     assert_eq!(block, &block_copy);
87//! }
88//!
89//! // `decrypt_blocks` also supports parallel block processing.
90//! cipher.decrypt_blocks(&mut blocks);
91//!
92//! for block in blocks.iter_mut() {
93//!     cipher.encrypt_block(block);
94//!     assert_eq!(block, &block_copy);
95//! }
96//! ```
97//!
98//! For implementation of block cipher modes of operation see
99//! [`block-modes`] repository.
100//!
101//! # Configuration Flags
102//!
103//! You can modify crate using the following configuration flags:
104//!
105//! - `aes_backend`: explicitly select one of the following backends:
106//!   - `soft`: force software backend
107//!   - `avx256`: force AVX2 backend
108//!   - `avx512`: force AVX-512 backend
109//! - `aes_backend_soft`: modify software backend:
110//!   - `compact`: use compact implementation (less performant, but results in a smaller binary)
111//!
112//! It can be enabled using `RUSTFLAGS` environment variable
113//! (e.g. `RUSTFLAGS='--cfg aes_backend="soft"'`) or by modifying `.cargo/config`.
114//!
115//! [AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
116//! [fixslicing]: https://eprint.iacr.org/2020/1123.pdf
117//! [AES-NI]: https://en.wikipedia.org/wiki/AES_instruction_set
118//! [`block-modes`]: https://github.com/RustCrypto/block-modes/
119
120#![no_std]
121#![doc(
122    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
123    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg"
124)]
125#![cfg_attr(docsrs, feature(doc_cfg))]
126#![warn(missing_docs, rust_2018_idioms)]
127
128pub use cipher;
129
130#[cfg(feature = "hazmat")]
131pub mod hazmat;
132
133mod backends;
134
135use cipher::{
136    AlgorithmName, BlockCipherDecClosure, BlockCipherDecrypt, BlockCipherEncClosure,
137    BlockCipherEncrypt, BlockSizeUser, Key, KeyInit, KeySizeUser,
138    array::Array,
139    consts::{U16, U24, U32},
140};
141use core::fmt;
142use cpubits::cfg_if;
143
144/// 128-bit AES block
145pub type Block = Array<u8, U16>;
146
147// Define token used for target feature detection
148cfg_if! {
149    if #[cfg(aes_backend = "soft")] {
150        type Token = ();
151    } else if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
152        cpufeatures::new!(features_aes, "aes");
153        #[cfg(any(aes_backend = "avx256", aes_backend = "avx512"))]
154        cpufeatures::new!(features_vaes256, "vaes");
155        #[cfg(aes_backend = "avx512")]
156        cpufeatures::new!(features_vaes512, "avx512f", "vaes");
157
158        #[derive(Clone, Copy)]
159        struct Token {
160            aes: features_aes::InitToken,
161            #[cfg(any(aes_backend = "avx256", aes_backend = "avx512"))]
162            vaes256: features_vaes256::InitToken,
163            #[cfg(aes_backend = "avx512")]
164            vaes512: features_vaes512::InitToken,
165        }
166
167        impl Default for Token {
168            fn default() -> Self {
169                Token {
170                    aes: features_aes::InitToken::init(),
171                    #[cfg(any(aes_backend = "avx256", aes_backend = "avx512"))]
172                    vaes256: features_vaes256::InitToken::init(),
173                    #[cfg(aes_backend = "avx512")]
174                    vaes512: features_vaes512::InitToken::init(),
175                }
176            }
177        }
178
179    } else if #[cfg(target_arch = "aarch64")] {
180        cpufeatures::new!(features_aes, "aes");
181
182        #[derive(Clone, Copy)]
183        struct Token {
184            aes: features_aes::InitToken,
185        }
186
187        impl Default for Token {
188            fn default() -> Self {
189                Token {
190                    aes: features_aes::InitToken::init(),
191                }
192            }
193        }
194    } else {
195        type Token = ();
196    }
197}
198
199/// Returns `true` if this crate can use AES hardware acceleration on the current machine.
200///
201/// This is a runtime check performed on the machine where the code is executed.
202///
203/// ```
204/// if aes::hardware_accelerated() {
205///     println!("AES hardware acceleration is available");
206/// } else {
207///     println!("WARNING: using software fallback for AES");
208/// }
209/// ```
210pub fn hardware_accelerated() -> bool {
211    cfg_if! {
212        if #[cfg(aes_backend = "soft")] {
213            false
214        } else if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
215            features_aes::get()
216        } else if #[cfg(target_arch = "aarch64")] {
217            features_aes::get()
218        } else {
219            false
220        }
221    }
222}
223
224macro_rules! impl_key_init {
225    ($name:ident, $soft_name:ident, $key_size:ty, $inner:path) => {
226        impl KeySizeUser for $name {
227            type KeySize = $key_size;
228        }
229
230        impl KeyInit for $name {
231            #[inline]
232            fn new(key: &Key<Self>) -> Self {
233                type Inner = $inner;
234                let token = Token::default();
235                let key = &key.0;
236
237                #[cfg(not(aes_backend = "soft"))]
238                cfg_if! {
239                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
240                        if token.aes.get() {
241                            // SAFETY: we confirmed that the required target features are available
242                            let aes = unsafe { backends::x86_aes::$name::new(key) };
243                            let inner = Inner { aes };
244                            return Self { inner, token };
245                        }
246                    } else if #[cfg(target_arch = "aarch64")] {
247                        if token.aes.get() {
248                            // SAFETY: we confirmed that the required target features are available
249                            let aes = unsafe { backends::aarch64_aes::$name::new(key) };
250                            let inner = Inner { aes };
251                            return Self { inner, token };
252                        }
253                    }
254                }
255
256                let soft = backends::soft::$soft_name::new(key);
257                let inner = Inner { soft };
258                Self { inner, token }
259            }
260        }
261    };
262}
263
264macro_rules! impl_encrypt {
265    ($ty_name:ident, $name:ident) => {
266        impl BlockCipherEncrypt for $ty_name {
267            #[inline]
268            fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = U16>) {
269                #[cfg(not(aes_backend = "soft"))]
270                cfg_if! {
271                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
272                        #[cfg(aes_backend = "avx512")]
273                        if self.token.vaes512.get() {
274                            // SAFETY: we access correct union variant
275                            let enc_rk = unsafe { &self.inner.aes.enc_rk };
276                            // SAFETY: we confirmed that the required target features are available
277                            unsafe { backends::x86_vaes512::$name::encrypt(enc_rk, f) };
278                            return;
279                        }
280
281                        #[cfg(any(aes_backend = "avx256", aes_backend = "avx512"))]
282                        if self.token.vaes256.get() {
283                            // SAFETY: we access correct union variant
284                            let enc_rk = unsafe { &self.inner.aes.enc_rk };
285                            // SAFETY: we confirmed that the required target features are available
286                            unsafe { backends::x86_vaes256::$name::encrypt(enc_rk, f) };
287                            return;
288                        }
289
290                        if self.token.aes.get() {
291                            // SAFETY: we access correct union variant
292                            let aes = unsafe { &self.inner.aes };
293                            // SAFETY: we confirmed that the required target features are available
294                            unsafe { aes.encrypt(f) };
295                            return;
296                        }
297                    } else if #[cfg(target_arch = "aarch64")] {
298                        if self.token.aes.get() {
299                            // SAFETY: we access correct union variant
300                            let aes = unsafe { &self.inner.aes };
301                            // SAFETY: we confirmed that the required target features are available
302                            unsafe { aes.encrypt(f) };
303                            return;
304                        }
305                    }
306                }
307
308                // SAFETY: we access correct union variant
309                let backend = unsafe { &self.inner.soft };
310                f.call(backend);
311            }
312        }
313    };
314}
315
316macro_rules! impl_decrypt {
317    ($name:ident, $alg_name:ident) => {
318        impl BlockCipherDecrypt for $name {
319            #[inline]
320            fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = U16>) {
321                #[cfg(not(aes_backend = "soft"))]
322                cfg_if! {
323                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
324                        #[cfg(aes_backend = "avx512")]
325                        if self.token.vaes512.get() {
326                            // SAFETY: we access correct union variant
327                            let dec_rk = unsafe { &self.inner.aes.dec_rk };
328                            // SAFETY: we confirmed that the required target features are available
329                            unsafe { backends::x86_vaes512::$alg_name::decrypt(dec_rk, f) };
330                            return;
331                        }
332
333                        #[cfg(any(aes_backend = "avx256", aes_backend = "avx512"))]
334                        if self.token.vaes256.get() {
335                            // SAFETY: we access correct union variant
336                            let dec_rk = unsafe { &self.inner.aes.dec_rk };
337                            // SAFETY: we confirmed that the required target features are available
338                            unsafe { backends::x86_vaes256::$alg_name::decrypt(dec_rk, f) };
339                            return;
340                        }
341
342                        if self.token.aes.get() {
343                            // SAFETY: we access correct union variant
344                            let backend = unsafe { &self.inner.aes };
345                            // SAFETY: we confirmed that the required target features are available
346                            unsafe { backend.decrypt(f) };
347                            return;
348                        }
349                    } else if #[cfg(target_arch = "aarch64")] {
350                        if self.token.aes.get() {
351                            // SAFETY: we access correct union variant
352                            let backend = unsafe { &self.inner.aes };
353                            // SAFETY: we confirmed that the required target features are available
354                            unsafe { backend.decrypt(f) };
355                            return;
356                        }
357                    }
358                }
359
360                // SAFETY: we access correct union variant
361                let backend = unsafe { &self.inner.soft };
362                f.call(backend);
363            }
364        }
365    };
366}
367
368macro_rules! impl_from_enc {
369    ($name:ident, $name_enc:ident, $inner:path, $into_fn:ident) => {
370        impl From<&$name_enc> for $name {
371            #[inline]
372            fn from(enc: &$name_enc) -> $name {
373                type Inner = $inner;
374
375                let token = enc.token;
376
377                #[cfg(not(aes_backend = "soft"))]
378                cfg_if! {
379                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
380                        if token.aes.get() {
381                            // SAFETY: we access correct union variant
382                            let aes_enc = unsafe { &enc.inner.aes };
383                            // SAFETY: we confirmed that the required target features are available
384                            let aes = unsafe { aes_enc.$into_fn() };
385                            let inner = Inner { aes };
386                            return Self { inner, token };
387                        }
388                    } else if #[cfg(target_arch = "aarch64")] {
389                        if token.aes.get() {
390                            // SAFETY: we access correct union variant
391                            let aes_enc = unsafe { &enc.inner.aes };
392                            // SAFETY: we confirmed that the required target features are available
393                            let aes = unsafe { aes_enc.$into_fn() };
394                            let inner = Inner { aes };
395                            return Self { inner, token };
396                        }
397                    }
398                }
399
400                // SAFETY: we access correct union variant
401                let soft = unsafe { enc.inner.soft };
402                let inner = Inner { soft };
403                Self { inner, token }
404            }
405        }
406
407        impl From<$name_enc> for $name {
408            #[inline]
409            fn from(enc: $name_enc) -> $name {
410                Self::from(&enc)
411            }
412        }
413    };
414}
415
416macro_rules! common_impls {
417    ($name:ident) => {
418        impl BlockSizeUser for $name {
419            type BlockSize = U16;
420        }
421
422        impl fmt::Debug for $name {
423            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
424                f.write_str(concat!(stringify!($name), " { .. }"))
425            }
426        }
427
428        impl AlgorithmName for $name {
429            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
430                f.write_str(stringify!($name))
431            }
432        }
433
434        impl Drop for $name {
435            #[inline]
436            fn drop(&mut self) {
437                #[cfg(feature = "zeroize")]
438                unsafe {
439                    zeroize::zeroize_flat_type(self);
440                }
441            }
442        }
443
444        #[cfg(feature = "zeroize")]
445        impl zeroize::ZeroizeOnDrop for $name {}
446    };
447}
448
449macro_rules! define_aes_impl {
450    (
451        name = $name:ident,
452        name_enc = $name_enc:ident,
453        name_dec = $name_dec:ident,
454        module = $module:tt,
455        key_size = $key_size:ident,
456        doc = $doc:expr,
457    ) => {
458        mod $module {
459            use crate::backends;
460
461            #[derive(Copy, Clone)]
462            pub(super) union Inner {
463                #[cfg(all(
464                    any(target_arch = "x86_64", target_arch = "x86"),
465                    not(aes_backend = "soft"),
466                ))]
467                pub(super) aes: backends::x86_aes::$name,
468                #[cfg(all(target_arch = "aarch64", not(aes_backend = "soft")))]
469                pub(super) aes: backends::aarch64_aes::$name,
470                pub(super) soft: backends::soft::$name,
471            }
472
473            #[derive(Copy, Clone)]
474            pub(super) union InnerEnc {
475                #[cfg(all(
476                    any(target_arch = "x86_64", target_arch = "x86"),
477                    not(aes_backend = "soft"),
478                ))]
479                pub(super) aes: backends::x86_aes::$name_enc,
480                #[cfg(all(target_arch = "aarch64", not(aes_backend = "soft")))]
481                pub(super) aes: backends::aarch64_aes::$name_enc,
482                pub(super) soft: backends::soft::$name,
483            }
484
485            #[derive(Copy, Clone)]
486            pub(super) union InnerDec {
487                #[cfg(all(
488                    any(target_arch = "x86_64", target_arch = "x86"),
489                    not(aes_backend = "soft"),
490                ))]
491                pub(super) aes: backends::x86_aes::$name_dec,
492                #[cfg(all(target_arch = "aarch64", not(aes_backend = "soft")))]
493                pub(super) aes: backends::aarch64_aes::$name_dec,
494                pub(super) soft: backends::soft::$name,
495            }
496        }
497
498        #[doc=$doc]
499        #[doc = "block cipher"]
500        #[derive(Clone)]
501        pub struct $name {
502            inner: $module::Inner,
503            #[allow(dead_code, reason = "this field is not used on software-only targets")]
504            token: Token,
505        }
506
507        common_impls!($name);
508        impl_key_init!($name, $name, $key_size, $module::Inner);
509        impl_encrypt!($name, $name);
510        impl_decrypt!($name, $name);
511        impl_from_enc!($name, $name_enc, $module::Inner, as_encdec);
512
513        #[doc=$doc]
514        #[doc = "block cipher (encrypt-only)"]
515        #[derive(Clone)]
516        pub struct $name_enc {
517            inner: $module::InnerEnc,
518            #[allow(dead_code, reason = "this field is not used on software-only targets")]
519            token: Token,
520        }
521
522        common_impls!($name_enc);
523        impl_key_init!($name_enc, $name, $key_size, $module::InnerEnc);
524        impl_encrypt!($name_enc, $name);
525
526        #[doc=$doc]
527        #[doc = "block cipher (decrypt-only)"]
528        #[derive(Clone)]
529        pub struct $name_dec {
530            inner: $module::InnerDec,
531            #[allow(dead_code, reason = "this field is not used on software-only targets")]
532            token: Token,
533        }
534
535        common_impls!($name_dec);
536        impl_key_init!($name_dec, $name, $key_size, $module::InnerDec);
537        impl_decrypt!($name_dec, $name);
538        impl_from_enc!($name_dec, $name_enc, $module::InnerDec, as_dec);
539    };
540}
541
542define_aes_impl!(
543    name = Aes128,
544    name_enc = Aes128Enc,
545    name_dec = Aes128Dec,
546    module = aes128,
547    key_size = U16,
548    doc = "AES-128",
549);
550define_aes_impl!(
551    name = Aes192,
552    name_enc = Aes192Enc,
553    name_dec = Aes192Dec,
554    module = aes192,
555    key_size = U24,
556    doc = "AES-192",
557);
558define_aes_impl!(
559    name = Aes256,
560    name_enc = Aes256Enc,
561    name_dec = Aes256Dec,
562    module = aes256,
563    key_size = U32,
564    doc = "AES-256",
565);