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