cryprot_core/
aes_rng.rs

1//! RNG based on AES in CTR mode.
2//!
3//! This implementation is based on the implementation given in the
4//! [scuttlebutt](https://github.com/GaloisInc/swanky/blob/4455754abadee07f168079ac45ef33535b0df27d/scuttlebutt/src/rand_aes.rs)
5//! crate. Instead of using an own AES implementation, [`AesRng`](`AesRng`) uses
6//! the [aes](`aes`) crate.
7//!
8//! On platforms wwith hardware accelerated AES instructions, the [`AesRng`] can
9//! generate multiple GiB of random data per second. Make sure to compile with
10//! the `aes` target feature enabled to have optimal performance without runtime
11//! detection of the feature.
12use std::mem;
13
14use aes::{
15    Aes128,
16    cipher::{BlockCipherEncrypt, KeyInit},
17};
18use rand::{RngExt, SeedableRng};
19use rand_core::{
20    TryCryptoRng, TryRng,
21    block::{BlockRng, Generator},
22};
23
24use crate::{AES_PAR_BLOCKS, Block};
25
26// TODO i think softspoken ot has some implementation performance optimizations
27// see sect 7 https://eprint.iacr.org/2022/192.pdf
28
29/// This uses AES in a counter-mode to implement a PRG. TODO: Citation for
30/// why/when this is secure.
31#[derive(Clone, Debug)]
32pub struct AesRng(BlockRng<AesRngCore>);
33
34impl TryRng for AesRng {
35    type Error = core::convert::Infallible;
36
37    #[inline]
38    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
39        Ok(self.0.next_word())
40    }
41
42    #[inline]
43    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
44        Ok(self.0.next_u64_from_u32())
45    }
46
47    #[inline]
48    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
49        let block_size = mem::size_of::<aes::Block>();
50        let block_len = dest.len() / block_size * block_size;
51        let (block_bytes, rest_bytes) = dest.split_at_mut(block_len);
52        // fast path so we don't unnecessarily copy u32 from Generator::generate into
53        // dest
54        let blocks = bytemuck::cast_slice_mut::<_, aes::Block>(block_bytes);
55        for chunk in blocks.chunks_mut(AES_PAR_BLOCKS) {
56            for block in chunk.iter_mut() {
57                *block = aes::cipher::Array(self.0.core.state.to_le_bytes());
58                self.0.core.state += 1;
59            }
60            self.0.core.aes.encrypt_blocks(chunk);
61        }
62        // handle the tail
63        self.0.fill_bytes(rest_bytes);
64        Ok(())
65    }
66}
67
68impl SeedableRng for AesRng {
69    type Seed = Block;
70
71    #[inline]
72    fn from_seed(seed: Self::Seed) -> Self {
73        AesRng(BlockRng::new(AesRngCore::from_seed(seed)))
74    }
75}
76
77impl TryCryptoRng for AesRng {}
78
79impl AesRng {
80    /// Create a new random number generator using a random seed from
81    /// `rand::random`.
82    #[inline]
83    pub fn new() -> Self {
84        let seed = rand::random::<Block>();
85        AesRng::from_seed(seed)
86    }
87
88    /// Create a new RNG using a random seed from this one.
89    #[inline]
90    pub fn fork(&mut self) -> Self {
91        let seed = self.random::<Block>();
92        AesRng::from_seed(seed)
93    }
94}
95
96impl Default for AesRng {
97    #[inline]
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103/// The core of `AesRng`, used with `BlockRng`.
104#[derive(Clone)]
105pub struct AesRngCore {
106    aes: Aes128,
107    state: u128,
108}
109
110impl std::fmt::Debug for AesRngCore {
111    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
112        write!(f, "AesRngCore {{}}")
113    }
114}
115
116impl Generator for AesRngCore {
117    // This is equivalent to `[aes::Block; AES_PAR_BLOCKS]`
118    type Output = [u32; AES_PAR_BLOCKS * (mem::size_of::<aes::Block>() / mem::size_of::<u32>())];
119
120    // Compute `E(state)` AES_PAR_BLOCKS times, where `state` is a counter.
121    #[inline]
122    fn generate(&mut self, results: &mut Self::Output) {
123        let blocks = bytemuck::cast_slice_mut::<_, aes::Block>(results);
124        blocks.iter_mut().for_each(|blk| {
125            // aes::Block is a type alias to Array, but type aliases can't be used as
126            // constructors
127            *blk = aes::cipher::Array(self.state.to_le_bytes());
128            self.state += 1;
129        });
130        self.aes.encrypt_blocks(blocks);
131    }
132}
133
134impl SeedableRng for AesRngCore {
135    type Seed = Block;
136
137    #[inline]
138    fn from_seed(seed: Self::Seed) -> Self {
139        let aes = Aes128::new(&seed.into());
140        AesRngCore {
141            aes,
142            state: Default::default(),
143        }
144    }
145}
146
147impl From<AesRngCore> for AesRng {
148    #[inline]
149    fn from(core: AesRngCore) -> Self {
150        AesRng(BlockRng::new(core))
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_generate() {
160        let mut rng = AesRng::new();
161        let a = rng.random::<[Block; 8]>();
162        let b = rng.random::<[Block; 8]>();
163        assert_ne!(a, b);
164    }
165}