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        cpufeatures::new!(features_vaes256, "vaes");
154        cpufeatures::new!(features_vaes512, "avx512f", "vaes");
155
156        #[derive(Clone, Copy)]
157        struct Token {
158            aes: features_aes::InitToken,
159            vaes256: features_vaes256::InitToken,
160            vaes512: features_vaes512::InitToken,
161        }
162
163        impl Default for Token {
164            fn default() -> Self {
165                Token {
166                    aes: features_aes::InitToken::init(),
167                    vaes256: features_vaes256::InitToken::init(),
168                    vaes512: features_vaes512::InitToken::init(),
169                }
170            }
171        }
172
173    } else if #[cfg(all(target_arch = "aarch64", not(miri)))] {
174        cpufeatures::new!(features_aes, "aes");
175
176        #[derive(Clone, Copy)]
177        struct Token {
178            aes: features_aes::InitToken,
179        }
180
181        impl Default for Token {
182            fn default() -> Self {
183                Token {
184                    aes: features_aes::InitToken::init(),
185                }
186            }
187        }
188    } else {
189        type Token = ();
190    }
191}
192
193/// Returns `true` if this crate can use AES hardware acceleration on the current machine.
194///
195/// This is a runtime check performed on the machine where the code is executed.
196///
197/// ```
198/// if aes::hardware_accelerated() {
199///     println!("AES hardware acceleration is available");
200/// } else {
201///     println!("WARNING: using software fallback for AES");
202/// }
203/// ```
204pub fn hardware_accelerated() -> bool {
205    cfg_if! {
206        if #[cfg(aes_backend = "soft")] {
207            false
208        } else if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
209            features_aes::get()
210        } else if #[cfg(all(target_arch = "aarch64", not(miri)))] {
211            features_aes::get()
212        } else {
213            false
214        }
215    }
216}
217
218macro_rules! impl_key_init {
219    ($name:ident, $soft_name:ident, $key_size:ty, $inner:path) => {
220        impl KeySizeUser for $name {
221            type KeySize = $key_size;
222        }
223
224        impl KeyInit for $name {
225            #[inline]
226            fn new(key: &Key<Self>) -> Self {
227                type Inner = $inner;
228                let token = Token::default();
229                let key = &key.0;
230
231                #[cfg(not(aes_backend = "soft"))]
232                cfg_if! {
233                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
234                        if token.aes.get() {
235                            // SAFETY: we confirmed that the required target features are available
236                            let aes = unsafe { backends::x86_aes::$name::new(key) };
237                            let inner = Inner { aes };
238                            return Self { inner, token };
239                        }
240                    } else if #[cfg(all(target_arch = "aarch64", not(miri)))] {
241                        if token.aes.get() {
242                            // SAFETY: we confirmed that the required target features are available
243                            let aes = unsafe { backends::aarch64_aes::$name::new(key) };
244                            let inner = Inner { aes };
245                            return Self { inner, token };
246                        }
247                    }
248                }
249
250                let soft = backends::soft::$soft_name::new(key);
251                let inner = Inner { soft };
252                Self { inner, token }
253            }
254        }
255    };
256}
257
258macro_rules! impl_encrypt {
259    ($ty_name:ident, $name:ident) => {
260        impl BlockCipherEncrypt for $ty_name {
261            #[inline]
262            fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = U16>) {
263                #[cfg(not(aes_backend = "soft"))]
264                cfg_if! {
265                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
266                        if self.token.vaes512.get() {
267                            // SAFETY: we access correct union variant
268                            let enc_rk = unsafe { &self.inner.aes.enc_rk };
269                            // SAFETY: we confirmed that the required target features are available
270                            unsafe { backends::x86_vaes512::$name::encrypt(enc_rk, f) };
271                            return;
272                        }
273
274                        if self.token.vaes256.get() {
275                            // SAFETY: we access correct union variant
276                            let enc_rk = unsafe { &self.inner.aes.enc_rk };
277                            // SAFETY: we confirmed that the required target features are available
278                            unsafe { backends::x86_vaes256::$name::encrypt(enc_rk, f) };
279                            return;
280                        }
281
282                        if self.token.aes.get() {
283                            // SAFETY: we access correct union variant
284                            let aes = unsafe { &self.inner.aes };
285                            // SAFETY: we confirmed that the required target features are available
286                            unsafe { aes.encrypt(f) };
287                            return;
288                        }
289                    } else if #[cfg(all(target_arch = "aarch64", not(miri)))] {
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                    }
298                }
299
300                // SAFETY: we access correct union variant
301                let backend = unsafe { &self.inner.soft };
302                f.call(backend);
303            }
304        }
305    };
306}
307
308macro_rules! impl_decrypt {
309    ($name:ident, $alg_name:ident) => {
310        impl BlockCipherDecrypt for $name {
311            #[inline]
312            fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = U16>) {
313                #[cfg(not(aes_backend = "soft"))]
314                cfg_if! {
315                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
316                        if self.token.vaes512.get() {
317                            // SAFETY: we access correct union variant
318                            let dec_rk = unsafe { &self.inner.aes.dec_rk };
319                            // SAFETY: we confirmed that the required target features are available
320                            unsafe { backends::x86_vaes512::$alg_name::decrypt(dec_rk, f) };
321                            return;
322                        }
323
324                        if self.token.vaes256.get() {
325                            // SAFETY: we access correct union variant
326                            let dec_rk = unsafe { &self.inner.aes.dec_rk };
327                            // SAFETY: we confirmed that the required target features are available
328                            unsafe { backends::x86_vaes256::$alg_name::decrypt(dec_rk, f) };
329                            return;
330                        }
331
332                        if self.token.aes.get() {
333                            // SAFETY: we access correct union variant
334                            let backend = unsafe { &self.inner.aes };
335                            // SAFETY: we confirmed that the required target features are available
336                            unsafe { backend.decrypt(f) };
337                            return;
338                        }
339                    } else if #[cfg(all(target_arch = "aarch64", not(miri)))] {
340                        if self.token.aes.get() {
341                            // SAFETY: we access correct union variant
342                            let backend = unsafe { &self.inner.aes };
343                            // SAFETY: we confirmed that the required target features are available
344                            unsafe { backend.decrypt(f) };
345                            return;
346                        }
347                    }
348                }
349
350                // SAFETY: we access correct union variant
351                let backend = unsafe { &self.inner.soft };
352                f.call(backend);
353            }
354        }
355    };
356}
357
358macro_rules! impl_from_enc {
359    ($name:ident, $name_enc:ident, $inner:path, $into_fn:ident) => {
360        impl From<&$name_enc> for $name {
361            #[inline]
362            fn from(enc: &$name_enc) -> $name {
363                type Inner = $inner;
364
365                let token = enc.token;
366
367                #[cfg(not(aes_backend = "soft"))]
368                cfg_if! {
369                    if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
370                        if token.aes.get() {
371                            // SAFETY: we access correct union variant
372                            let aes_enc = unsafe { &enc.inner.aes };
373                            // SAFETY: we confirmed that the required target features are available
374                            let aes = unsafe { aes_enc.$into_fn() };
375                            let inner = Inner { aes };
376                            return Self { inner, token };
377                        }
378                    } else if #[cfg(all(target_arch = "aarch64", not(miri)))] {
379                        if token.aes.get() {
380                            // SAFETY: we access correct union variant
381                            let aes_enc = unsafe { &enc.inner.aes };
382                            // SAFETY: we confirmed that the required target features are available
383                            let aes = unsafe { aes_enc.$into_fn() };
384                            let inner = Inner { aes };
385                            return Self { inner, token };
386                        }
387                    }
388                }
389
390                // SAFETY: we access correct union variant
391                let soft = unsafe { enc.inner.soft };
392                let inner = Inner { soft };
393                Self { inner, token }
394            }
395        }
396
397        impl From<$name_enc> for $name {
398            #[inline]
399            fn from(enc: $name_enc) -> $name {
400                Self::from(&enc)
401            }
402        }
403    };
404}
405
406macro_rules! common_impls {
407    ($name:ident) => {
408        impl BlockSizeUser for $name {
409            type BlockSize = U16;
410        }
411
412        impl fmt::Debug for $name {
413            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
414                f.write_str(concat!(stringify!($name), " { .. }"))
415            }
416        }
417
418        impl AlgorithmName for $name {
419            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
420                f.write_str(stringify!($name))
421            }
422        }
423
424        impl Drop for $name {
425            #[inline]
426            fn drop(&mut self) {
427                #[cfg(feature = "zeroize")]
428                unsafe {
429                    zeroize::zeroize_flat_type(self);
430                }
431            }
432        }
433
434        #[cfg(feature = "zeroize")]
435        impl zeroize::ZeroizeOnDrop for $name {}
436    };
437}
438
439macro_rules! define_aes_impl {
440    (
441        name = $name:ident,
442        name_enc = $name_enc:ident,
443        name_dec = $name_dec:ident,
444        module = $module:tt,
445        key_size = $key_size:ident,
446        doc = $doc:expr,
447    ) => {
448        mod $module {
449            use crate::backends;
450
451            #[derive(Copy, Clone)]
452            pub(super) union Inner {
453                #[cfg(all(
454                    any(target_arch = "x86_64", target_arch = "x86"),
455                    not(aes_backend = "soft"),
456                ))]
457                pub(super) aes: backends::x86_aes::$name,
458                #[cfg(all(target_arch = "aarch64", not(miri), not(aes_backend = "soft")))]
459                pub(super) aes: backends::aarch64_aes::$name,
460                pub(super) soft: backends::soft::$name,
461            }
462
463            #[derive(Copy, Clone)]
464            pub(super) union InnerEnc {
465                #[cfg(all(
466                    any(target_arch = "x86_64", target_arch = "x86"),
467                    not(aes_backend = "soft"),
468                ))]
469                pub(super) aes: backends::x86_aes::$name_enc,
470                #[cfg(all(target_arch = "aarch64", not(miri), not(aes_backend = "soft")))]
471                pub(super) aes: backends::aarch64_aes::$name_enc,
472                pub(super) soft: backends::soft::$name,
473            }
474
475            #[derive(Copy, Clone)]
476            pub(super) union InnerDec {
477                #[cfg(all(
478                    any(target_arch = "x86_64", target_arch = "x86"),
479                    not(aes_backend = "soft"),
480                ))]
481                pub(super) aes: backends::x86_aes::$name_dec,
482                #[cfg(all(target_arch = "aarch64", not(miri), not(aes_backend = "soft")))]
483                pub(super) aes: backends::aarch64_aes::$name_dec,
484                pub(super) soft: backends::soft::$name,
485            }
486        }
487
488        #[doc=$doc]
489        #[doc = "block cipher"]
490        #[derive(Clone)]
491        pub struct $name {
492            inner: $module::Inner,
493            #[allow(dead_code, reason = "this field is not used on software-only targets")]
494            token: Token,
495        }
496
497        common_impls!($name);
498        impl_key_init!($name, $name, $key_size, $module::Inner);
499        impl_encrypt!($name, $name);
500        impl_decrypt!($name, $name);
501        impl_from_enc!($name, $name_enc, $module::Inner, as_encdec);
502
503        #[doc=$doc]
504        #[doc = "block cipher (encrypt-only)"]
505        #[derive(Clone)]
506        pub struct $name_enc {
507            inner: $module::InnerEnc,
508            #[allow(dead_code, reason = "this field is not used on software-only targets")]
509            token: Token,
510        }
511
512        common_impls!($name_enc);
513        impl_key_init!($name_enc, $name, $key_size, $module::InnerEnc);
514        impl_encrypt!($name_enc, $name);
515
516        #[doc=$doc]
517        #[doc = "block cipher (decrypt-only)"]
518        #[derive(Clone)]
519        pub struct $name_dec {
520            inner: $module::InnerDec,
521            #[allow(dead_code, reason = "this field is not used on software-only targets")]
522            token: Token,
523        }
524
525        common_impls!($name_dec);
526        impl_key_init!($name_dec, $name, $key_size, $module::InnerDec);
527        impl_decrypt!($name_dec, $name);
528        impl_from_enc!($name_dec, $name_enc, $module::InnerDec, as_dec);
529    };
530}
531
532define_aes_impl!(
533    name = Aes128,
534    name_enc = Aes128Enc,
535    name_dec = Aes128Dec,
536    module = aes128,
537    key_size = U16,
538    doc = "AES-128",
539);
540define_aes_impl!(
541    name = Aes192,
542    name_enc = Aes192Enc,
543    name_dec = Aes192Dec,
544    module = aes192,
545    key_size = U24,
546    doc = "AES-192",
547);
548define_aes_impl!(
549    name = Aes256,
550    name_enc = Aes256Enc,
551    name_dec = Aes256Dec,
552    module = aes256,
553    key_size = U32,
554    doc = "AES-256",
555);