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);