rand_aes/
lib.rs

1//! # AES based pseudo-random number generator
2//!
3//! This crate implements a pseudo-random number generator (PRNG) using the AES block cipher in
4//! counter (CTR) mode. It provides four variants:
5//!
6//!  1. [`Aes128Ctr64`]: Utilizes AES-128 encryption with a 64-bit counter.
7//!  2. [`Aes128Ctr128`]: Utilizes AES-128 encryption with a 128-bit counter.
8//!  3. [`Aes256Ctr64`]: Utilizes AES-256 encryption with a 64-bit counter.
9//!  4. [`Aes256Ctr128`]: Utilizes AES-256 encryption with a 128-bit counter.
10//!
11//! Common functionality is provided using the [`Random`] trait or the optionally provided
12//! [`rand_core::RngCore`] and [`rand_core::SeedableRng`] traits.
13//!
14//! ## Optimal Performance
15//!
16//! We provide runtime detection for the hardware accelerated AES instruction set for all supported
17//! platforms. Should the executing CPU not support hardware accelerated AES, a software fallback
18//! is provided. But we highly recommend to enable the specific target feature on compile time,
19//! since the AES instruction sets is available on modern desktop CPU for at least 10 years.
20//! Enabling the target feature enables the compiler to more aggressively inline and provides
21//! much better performance. The runtime detection is not supported in `no_std`.
22//!
23//! Use the following target features for optimal performance:
24//!
25//! - aarch64: `aes` (using the cryptographic extension)
26//! - x86: `sse2` and `aes` (using AES-NI)
27//! - x86_64: `aes` (using AES-NI)
28//!
29//! There is experimental support for the RISC-V vector crypto extension. Please read the README.md
30//! for more information how to use it.
31//!
32//! ## Security Note
33//!
34//! While based on well-established cryptographic primitives, this PRNG is not intended for
35//! cryptographic key generation or other sensitive cryptographic operations, simply because safe,
36//! automatic re-seeding is not provided. We tested its statistical qualities by running versions
37//! with reduced rounds against `practrand` and `TESTu01`'s Big Crush. A version with just 3 rounds
38//! of AES encryption rounds passes the `practrand` tests with at least 16 TB. `TESTu01`'s Big Crush
39//! requires at least 5 rounds to be successfully cleared. AES-128 uses 10 rounds, whereas
40//! AES-256 uses 14 rounds.
41//!
42//! ## Parallel Stream Generation
43//!
44//! The 128-bit counter PRNG support efficient parallel stream generation through the [`Jump`] trait.
45//! The provided functions allow you to create multiple independent streams of random numbers, which
46//! is particularly useful for parallel or distributed computations. The API is designed to easily
47//! create new random number generators for child threads / tasks from a base instance.
48//!
49//! ### Jump Function
50//!
51//! The [`Jump::jump()`] function advances the PRNG counter by 2^64 steps. It can be used to create
52//! up to 2^64 non-overlapping subsequences.
53//!
54//! ### Long Jump Function
55//!
56//! The [`Jump::long_jump()`] function advances the PRNG counter by 2^96 steps. This allows for even
57//! larger separations between subsequences, useful for creating up to 2^32 independent streams.
58//!
59//! These functions are particularly useful in scenarios requiring multiple independent PRNG streams,
60//! such as parallel Monte Carlo simulations or distributed computing tasks.
61
62#![cfg_attr(docsrs, feature(doc_cfg))]
63#![cfg_attr(feature = "verification", allow(unused))]
64#![cfg_attr(not(feature = "std"), no_std)]
65
66pub mod seeds;
67
68#[cfg(all(feature = "tls", not(feature = "verification")))]
69#[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
70pub mod tls;
71
72mod traits;
73
74mod backend;
75
76#[cfg(all(
77    feature = "force_runtime_detection",
78    not(any(
79        all(target_arch = "riscv64", feature = "experimental_riscv"),
80        target_arch = "aarch64",
81        target_arch = "x86_64",
82        target_arch = "x86",
83    ))
84))]
85compile_error!("The hardware AES implementation is not available for this platform. Remove the `force_runtime_detection` feature");
86
87#[cfg(all(feature = "force_runtime_detection", not(feature = "std")))]
88compile_error!("The runtime detection is not supported in `no_std`");
89
90#[cfg(all(
91    feature = "std",
92    any(
93        target_arch = "x86_64",
94        target_arch = "x86",
95        target_arch = "aarch64",
96        all(target_arch = "riscv64", feature = "experimental_riscv"),
97    ),
98    any(
99        not(any(
100            all(
101                any(target_arch = "x86_64", target_arch = "x86"),
102                target_feature = "sse2",
103                target_feature = "aes",
104            ),
105            all(
106                target_arch = "aarch64",
107                target_feature = "neon",
108                target_feature = "aes",
109            ),
110            all(target_arch = "riscv64", feature = "experimental_riscv"),
111        )),
112        feature = "force_runtime_detection",
113    ),
114))]
115pub(crate) mod runtime;
116
117#[cfg(all(
118    feature = "std",
119    any(
120        target_arch = "x86_64",
121        target_arch = "x86",
122        target_arch = "aarch64",
123        all(target_arch = "riscv64", feature = "experimental_riscv"),
124    ),
125    any(
126        not(any(
127            all(
128                any(target_arch = "x86_64", target_arch = "x86"),
129                target_feature = "sse2",
130                target_feature = "aes",
131            ),
132            all(
133                target_arch = "aarch64",
134                target_feature = "neon",
135                target_feature = "aes",
136            ),
137            all(target_arch = "riscv64", feature = "experimental_riscv"),
138        )),
139        feature = "force_runtime_detection",
140    ),
141))]
142pub use runtime::{Aes128Ctr128, Aes128Ctr64, Aes256Ctr128, Aes256Ctr64};
143
144#[cfg(all(
145    target_arch = "aarch64",
146    target_feature = "neon",
147    target_feature = "aes",
148    not(feature = "force_runtime_detection"),
149    not(feature = "force_software"),
150    not(feature = "verification"),
151))]
152pub use backend::aarch64::{Aes128Ctr128, Aes128Ctr64, Aes256Ctr128, Aes256Ctr64};
153
154#[cfg(all(
155    target_arch = "riscv64",
156    feature = "experimental_riscv",
157    not(feature = "force_runtime_detection"),
158    not(feature = "force_software"),
159    not(feature = "verification"),
160))]
161pub use backend::riscv64::{Aes128Ctr128, Aes128Ctr64, Aes256Ctr128, Aes256Ctr64};
162
163#[cfg(all(
164    any(target_arch = "x86_64", target_arch = "x86"),
165    target_feature = "sse2",
166    target_feature = "aes",
167    not(feature = "force_runtime_detection"),
168    not(feature = "force_software"),
169    not(feature = "verification"),
170))]
171pub use backend::x86::{Aes128Ctr128, Aes128Ctr64, Aes256Ctr128, Aes256Ctr64};
172
173#[cfg(all(
174    any(
175        not(any(
176            target_arch = "aarch64",
177            all(target_arch = "riscv64", feature = "experimental_riscv"),
178            any(target_arch = "x86_64", target_arch = "x86"),
179        )),
180        feature = "force_software",
181    ),
182    not(feature = "force_runtime_detection"),
183    not(feature = "verification"),
184))]
185pub use backend::soft::{Aes128Ctr128, Aes128Ctr64, Aes256Ctr128, Aes256Ctr64};
186
187#[cfg(not(feature = "verification"))]
188mod implementation;
189
190#[cfg(feature = "verification")]
191#[doc(hidden)]
192pub mod verification;
193
194pub use traits::{Jump, Random};
195
196#[allow(unused)]
197pub(crate) mod constants {
198    pub(crate) const AES_RCON: [u32; 10] =
199        [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
200    pub(crate) const AES_BLOCK_WORDS: usize = 4;
201    pub(crate) const AES_WORD_SIZE: usize = 4;
202    pub(crate) const AES_BLOCK_SIZE: usize = AES_WORD_SIZE * AES_BLOCK_WORDS;
203    pub(crate) const AES128_KEY_SIZE: usize = 16;
204    pub(crate) const AES256_KEY_SIZE: usize = 32;
205    pub(crate) const AES128_KEY_COUNT: usize = 11;
206    pub(crate) const AES256_KEY_COUNT: usize = 15;
207}
208
209#[cfg(feature = "getrandom")]
210pub(crate) fn secure_bytes<const N: usize>() -> [u8; N] {
211    let mut bytes = [0u8; N];
212    getrandom::fill(&mut bytes).expect("Can't get random bytes from OS");
213    bytes
214}
215
216#[cfg(all(test, not(feature = "verification")))]
217#[allow(unused)]
218mod tests {
219    use super::*;
220    use crate::constants::{
221        AES128_KEY_COUNT, AES128_KEY_SIZE, AES256_KEY_COUNT, AES256_KEY_SIZE, AES_BLOCK_SIZE,
222    };
223    use hex_literal::hex;
224
225    // From NIST FIPS 197
226    const TV_AES128_KEY: [u8; AES128_KEY_SIZE] = hex!("000102030405060708090a0b0c0d0e0f");
227    const TV_AES128_IV: [u8; AES_BLOCK_SIZE] = hex!("00112233445566778899aabbccddeeff");
228    const TV_AES128_ROUND_KEYS: [[u8; AES_BLOCK_SIZE]; AES128_KEY_COUNT] = [
229        hex!("000102030405060708090a0b0c0d0e0f"),
230        hex!("d6aa74fdd2af72fadaa678f1d6ab76fe"),
231        hex!("b692cf0b643dbdf1be9bc5006830b3fe"),
232        hex!("b6ff744ed2c2c9bf6c590cbf0469bf41"),
233        hex!("47f7f7bc95353e03f96c32bcfd058dfd"),
234        hex!("3caaa3e8a99f9deb50f3af57adf622aa"),
235        hex!("5e390f7df7a69296a7553dc10aa31f6b"),
236        hex!("14f9701ae35fe28c440adf4d4ea9c026"),
237        hex!("47438735a41c65b9e016baf4aebf7ad2"),
238        hex!("549932d1f08557681093ed9cbe2c974e"),
239        hex!("13111d7fe3944a17f307a78b4d2b30c5"),
240    ];
241    const TV_AES128_NEXT_0: [u8; AES_BLOCK_SIZE] = hex!("69c4e0d86a7b0430d8cdb78070b4c55a");
242    const TV_AES128_NEXT_1: [u8; AES_BLOCK_SIZE] = hex!("a556156c72876577f67f95a9d9e640a7");
243
244    // From NIST FIPS 197
245    const TV_AES256_KEY: [u8; AES256_KEY_SIZE] =
246        hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
247    const TV_AES256_IV: [u8; AES_BLOCK_SIZE] = hex!("00112233445566778899aabbccddeeff");
248    const TV_AES256_ROUND_KEYS: [[u8; AES_BLOCK_SIZE]; AES256_KEY_COUNT] = [
249        hex!("000102030405060708090a0b0c0d0e0f"),
250        hex!("101112131415161718191a1b1c1d1e1f"),
251        hex!("a573c29fa176c498a97fce93a572c09c"),
252        hex!("1651a8cd0244beda1a5da4c10640bade"),
253        hex!("ae87dff00ff11b68a68ed5fb03fc1567"),
254        hex!("6de1f1486fa54f9275f8eb5373b8518d"),
255        hex!("c656827fc9a799176f294cec6cd5598b"),
256        hex!("3de23a75524775e727bf9eb45407cf39"),
257        hex!("0bdc905fc27b0948ad5245a4c1871c2f"),
258        hex!("45f5a66017b2d387300d4d33640a820a"),
259        hex!("7ccff71cbeb4fe5413e6bbf0d261a7df"),
260        hex!("f01afafee7a82979d7a5644ab3afe640"),
261        hex!("2541fe719bf500258813bbd55a721c0a"),
262        hex!("4e5a6699a9f24fe07e572baacdf8cdea"),
263        hex!("24fc79ccbf0979e9371ac23c6d68de36"),
264    ];
265    const TV_AES256_NEXT_0: [u8; AES_BLOCK_SIZE] = hex!("8ea2b7ca516745bfeafc49904b496089");
266    const TV_AES256_NEXT_1: [u8; AES_BLOCK_SIZE] = hex!("81ae7d5e4138bf730d2a8871fec2cd0c");
267
268    pub(crate) fn aes128_key_expansion_test<F>(expansion: F)
269    where
270        F: FnOnce([u8; AES128_KEY_SIZE]) -> [[u8; AES_BLOCK_SIZE]; AES128_KEY_COUNT],
271    {
272        let expanded = expansion(TV_AES128_KEY);
273
274        for (exp, act) in TV_AES128_ROUND_KEYS.iter().zip(expanded.iter()) {
275            assert_eq!(exp, act);
276        }
277    }
278
279    pub(crate) fn aes256_key_expansion_test<F>(expansion: F)
280    where
281        F: FnOnce([u8; AES256_KEY_SIZE]) -> [[u8; AES_BLOCK_SIZE]; AES256_KEY_COUNT],
282    {
283        let expanded = expansion(TV_AES256_KEY);
284
285        for (exp, act) in TV_AES256_ROUND_KEYS.iter().zip(expanded.iter()) {
286            assert_eq!(exp, act);
287        }
288    }
289
290    #[test]
291    fn test_aes128_64_ctr() {
292        let mut ctr = [0u8; 8];
293        let mut nonce = [0u8; 8];
294        ctr.copy_from_slice(&TV_AES128_IV[0..8]);
295        nonce.copy_from_slice(&TV_AES128_IV[8..16]);
296
297        let prng = unsafe { Aes128Ctr64::from_seed_impl(TV_AES128_KEY, nonce, ctr) };
298
299        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES128_NEXT_0);
300        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES128_NEXT_1);
301    }
302
303    #[test]
304    fn test_aes128_128_ctr() {
305        let prng = unsafe { Aes128Ctr128::from_seed_impl(TV_AES128_KEY, TV_AES128_IV) };
306
307        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES128_NEXT_0);
308        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES128_NEXT_1);
309    }
310
311    #[test]
312    fn test_aes256_64_ctr() {
313        let mut ctr = [0u8; 8];
314        let mut nonce = [0u8; 8];
315        ctr.copy_from_slice(&TV_AES256_IV[0..8]);
316        nonce.copy_from_slice(&TV_AES256_IV[8..16]);
317
318        let prng = unsafe { Aes256Ctr64::from_seed_impl(TV_AES256_KEY, nonce, ctr) };
319
320        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES256_NEXT_0);
321        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES256_NEXT_1);
322    }
323
324    #[test]
325    fn test_aes256_128_ctr() {
326        let prng = unsafe { Aes256Ctr128::from_seed_impl(TV_AES256_KEY, TV_AES256_IV) };
327
328        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES256_NEXT_0);
329        assert_eq!(unsafe { prng.next_impl().to_le_bytes() }, TV_AES256_NEXT_1);
330    }
331}