ntrust_native/
rng.rs

1//! Implementation of a pseudo-random number generator
2//! based on AES256 in CTR mode.
3//!
4//! The implementation follows the design discussed in this blogpost:
5//! <https://lukas-prokop.at/articles/2021-12-31-nists-rng-in-rust>
6
7use aes::BlockEncrypt;
8use aes::NewBlockCipher;
9use std::error;
10use std::fmt;
11
12/// Trait requiring primitives to generate pseudo-random numbers.
13/// `AesState` is an object implementing this trait.
14pub trait RNGState {
15    /// Fill the buffer `x` with pseudo-random bytes resulting from the
16    /// RNG run updating the RNG state
17    fn randombytes(&mut self, x: &mut [u8]) -> Result<(), Box<dyn error::Error>>;
18    /// Initialize/reset the RNG state based on the seed provided as `entropy_input`
19    fn randombytes_init(&mut self, entropy_input: [u8; 48]);
20}
21
22/// AesState is a struct storing data of a pseudo-random number generator.
23/// Using `randombytes_init`, it can be initialized once. Using `randombytes`,
24/// one can successively fetch new pseudo-random numbers.
25#[derive(Clone, Debug, PartialEq)]
26pub struct AesState {
27    pub key: [u8; 32],
28    pub v: [u8; 16],
29    pub reseed_counter: i32,
30}
31
32impl AesState {
33    /// Returns a fresh RNG state
34    pub fn new() -> AesState {
35        AesState {
36            key: [0; 32],
37            v: [0; 16],
38            reseed_counter: 0,
39        }
40    }
41
42    /// This runs AES256 in ECB mode. Here `key` is a 256-bit AES key,
43    /// `ctr` is a 128-bit plaintext value and `buffer` is a 128-bit
44    /// ciphertext value.
45    fn aes256_ecb(key: &[u8; 32], ctr: &[u8; 16], buffer: &mut [u8; 16]) {
46        let cipher = aes::Aes256::new(key.into());
47        buffer.copy_from_slice(ctr);
48        cipher.encrypt_block(buffer.into());
49    }
50
51    /// Update `key` and `v` with `provided_data` by running one round of AES in counter mode
52    fn aes256_ctr_update(
53        provided_data: &mut Option<[u8; 48]>,
54        key: &mut [u8; 32],
55        v: &mut [u8; 16],
56    ) {
57        let mut temp = [[0u8; 16]; 3];
58
59        for tmp in &mut temp[0..3] {
60            let count = u128::from_be_bytes(*v);
61            v.copy_from_slice(&(count + 1).to_be_bytes());
62
63            Self::aes256_ecb(key, v, tmp);
64        }
65
66        if let Some(d) = provided_data {
67            for j in 0..3 {
68                for i in 0..16 {
69                    temp[j][i] ^= d[16 * j + i];
70                }
71            }
72        }
73
74        key[0..16].copy_from_slice(&temp[0]);
75        key[16..32].copy_from_slice(&temp[1]);
76        v.copy_from_slice(&temp[2]);
77    }
78}
79
80impl RNGState for AesState {
81    /// Fill the buffer `x` with pseudo-random bytes resulting from the
82    /// AES run in counter mode updating the object state
83    fn randombytes(&mut self, x: &mut [u8]) -> Result<(), Box<dyn error::Error>> {
84        for chunk in x.chunks_mut(16) {
85            let count = u128::from_be_bytes(self.v);
86            self.v.copy_from_slice(&(count + 1).to_be_bytes());
87
88            let mut block = [0u8; 16];
89            Self::aes256_ecb(&self.key, &self.v, &mut block);
90
91            (*chunk).copy_from_slice(&block[..chunk.len()]);
92        }
93
94        Self::aes256_ctr_update(&mut None, &mut self.key, &mut self.v);
95        self.reseed_counter += 1;
96
97        Ok(())
98    }
99
100    /// Initialize/reset the state based on the seed provided as `entropy_input`
101    fn randombytes_init(&mut self, entropy_input: [u8; 48]) {
102        self.key = [0u8; 32];
103        self.v = [0u8; 16];
104        self.reseed_counter = 1i32;
105
106        Self::aes256_ctr_update(&mut Some(entropy_input), &mut self.key, &mut self.v);
107        self.reseed_counter = 1;
108    }
109}
110
111impl Default for AesState {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117impl Eq for AesState {}
118
119impl fmt::Display for AesState {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        writeln!(f, "AesState {{")?;
122        writeln!(f, "  key = {:?}", self.key)?;
123        writeln!(f, "  v   = {:?}", self.v)?;
124        writeln!(f, "  reseed_counter = {}", self.reseed_counter)?;
125        writeln!(f, "}}")
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_rng() -> Result<(), Box<dyn error::Error>> {
135        let mut data = [0u8; 256];
136        let mut entropy_input = [0u8; 48];
137        let mut personalization_string = [0u8; 48];
138        let mut rng_state = AesState::new();
139
140        for i in 0..48 {
141            entropy_input[i] = i as u8;
142            personalization_string[i] = 0 as u8;
143        }
144
145        rng_state.randombytes_init(entropy_input);
146
147        rng_state.randombytes(&mut data)?;
148        let ref1 = [
149            0x06u8, 0x15, 0x50, 0x23, 0x4D, 0x15, 0x8C, 0x5E, 0xC9, 0x55, 0x95, 0xFE, 0x04, 0xEF,
150            0x7A, 0x25, 0x76, 0x7F, 0x2E, 0x24, 0xCC, 0x2B, 0xC4, 0x79, 0xD0, 0x9D, 0x86, 0xDC,
151            0x9A, 0xBC, 0xFD, 0xE7, 0x05, 0x6A, 0x8C, 0x26, 0x6F, 0x9E, 0xF9, 0x7E, 0xD0, 0x85,
152            0x41, 0xDB, 0xD2, 0xE1, 0xFF, 0xA1, 0x98, 0x10, 0xF5, 0x39, 0x2D, 0x07, 0x62, 0x76,
153            0xEF, 0x41, 0x27, 0x7C, 0x3A, 0xB6, 0xE9, 0x4A, 0x4E, 0x3B, 0x7D, 0xCC, 0x10, 0x4A,
154            0x05, 0xBB, 0x08, 0x9D, 0x33, 0x8B, 0xF5, 0x5C, 0x72, 0xCA, 0xB3, 0x75, 0x38, 0x9A,
155            0x94, 0xBB, 0x92, 0x0B, 0xD5, 0xD6, 0xDC, 0x9E, 0x7F, 0x2E, 0xC6, 0xFD, 0xE0, 0x28,
156            0xB6, 0xF5, 0x72, 0x4B, 0xB0, 0x39, 0xF3, 0x65, 0x2A, 0xD9, 0x8D, 0xF8, 0xCE, 0x6C,
157            0x97, 0x01, 0x32, 0x10, 0xB8, 0x4B, 0xBE, 0x81, 0x38, 0x8C, 0x3D, 0x14, 0x1D, 0x61,
158            0x95, 0x7C, 0x73, 0xBC, 0xDC, 0x5E, 0x5C, 0xD9, 0x25, 0x25, 0xF4, 0x6A, 0x2B, 0x75,
159            0x7B, 0x03, 0xCA, 0xB5, 0xC3, 0x37, 0x00, 0x4A, 0x2D, 0xA3, 0x53, 0x24, 0xA3, 0x25,
160            0x71, 0x35, 0x64, 0xDA, 0xE2, 0x8F, 0x57, 0xAC, 0xC6, 0xDB, 0xE3, 0x2A, 0x07, 0x26,
161            0x19, 0x0B, 0xAA, 0x6B, 0x8A, 0x0A, 0x25, 0x5A, 0xA1, 0xAD, 0x01, 0xE8, 0xDD, 0x56,
162            0x9A, 0xA3, 0x6D, 0x09, 0x62, 0x56, 0xC4, 0x20, 0x71, 0x8A, 0x69, 0xD4, 0x6D, 0x8D,
163            0xB1, 0xC6, 0xDD, 0x40, 0x60, 0x6A, 0x0B, 0xE3, 0xC2, 0x35, 0xBE, 0xFE, 0x62, 0x3A,
164            0x90, 0x59, 0x3F, 0x82, 0xD6, 0xA8, 0xF9, 0xF9, 0x24, 0xE4, 0x4E, 0x36, 0xBE, 0x87,
165            0xF7, 0xD2, 0x6B, 0x84, 0x45, 0x96, 0x6F, 0x9E, 0xE3, 0x29, 0xC4, 0x26, 0xC1, 0x25,
166            0x21, 0xE8, 0x5F, 0x6F, 0xD4, 0xEC, 0xD5, 0xD5, 0x66, 0xBA, 0x0A, 0x34, 0x87, 0x12,
167            0x5D, 0x79, 0xCC, 0x64,
168        ];
169        assert_eq!(data, ref1);
170
171        rng_state.randombytes(&mut data)?;
172        let ref2 = [
173            0xC1u8, 0x7E, 0x03, 0x40, 0x61, 0xED, 0x5E, 0xA8, 0x17, 0xC4, 0x1D, 0x61, 0x63, 0x62,
174            0x81, 0xE8, 0x16, 0xF8, 0x17, 0xDC, 0xF7, 0x53, 0xA9, 0x1D, 0x97, 0xC0, 0x18, 0xFF,
175            0x82, 0xFB, 0xC9, 0xB1, 0x72, 0x8F, 0xC6, 0x6A, 0xF1, 0x14, 0xB5, 0x79, 0x78, 0xFB,
176            0x60, 0x82, 0xB7, 0x0D, 0x28, 0x51, 0x40, 0xB2, 0x67, 0x25, 0xAA, 0x5F, 0x7B, 0xB4,
177            0x40, 0x98, 0x20, 0xF6, 0x7E, 0x2D, 0x65, 0x6E, 0xDA, 0xCA, 0x30, 0xB5, 0xBB, 0x12,
178            0xEB, 0x52, 0x49, 0xCC, 0x38, 0x09, 0xB1, 0x88, 0xCF, 0x0C, 0xC9, 0x5B, 0x5A, 0xE0,
179            0xEF, 0xE8, 0xFC, 0x58, 0x87, 0x15, 0x2C, 0xB6, 0x60, 0x1B, 0x4C, 0xCF, 0x9F, 0xC4,
180            0x11, 0x89, 0x4F, 0xA0, 0xC0, 0x26, 0x4E, 0xB5, 0x1A, 0x48, 0x1D, 0x4D, 0x70, 0x74,
181            0xFD, 0xF0, 0x65, 0x05, 0x30, 0x30, 0xC8, 0xA9, 0x2B, 0xFC, 0xDD, 0x06, 0xBF, 0x18,
182            0xC8, 0x48, 0x9C, 0x38, 0xD0, 0x37, 0x84, 0xFD, 0x63, 0x00, 0x18, 0x30, 0xE5, 0xA3,
183            0x85, 0xA4, 0xA3, 0x78, 0x66, 0x69, 0x3F, 0x5B, 0xDA, 0xB8, 0xA8, 0xA2, 0x5B, 0x51,
184            0x9D, 0xDB, 0xF2, 0xD2, 0x82, 0x68, 0x60, 0x1D, 0x95, 0xBE, 0xED, 0x64, 0x7E, 0x43,
185            0x04, 0x84, 0xA2, 0x27, 0xC0, 0x23, 0xB0, 0x29, 0x7A, 0x28, 0x2F, 0x06, 0xC9, 0x13,
186            0x76, 0x43, 0x3B, 0xDE, 0x5E, 0xC3, 0xAB, 0xBA, 0x8C, 0x06, 0xB8, 0x30, 0xC2, 0x64,
187            0x52, 0xEA, 0x2F, 0xA7, 0xED, 0xEA, 0x8D, 0xCF, 0xE2, 0x0E, 0xAF, 0xCF, 0x89, 0x80,
188            0xB3, 0xD5, 0xAE, 0xCE, 0xF8, 0x9D, 0xD8, 0x61, 0xAC, 0xEC, 0x1F, 0x5F, 0x7C, 0xD2,
189            0xAE, 0x6B, 0x3C, 0xDE, 0x3C, 0x1D, 0x80, 0xA2, 0x83, 0x0D, 0xD0, 0xB9, 0xE8, 0x46,
190            0x8A, 0xFA, 0xD1, 0x61, 0x98, 0x10, 0x74, 0xBE, 0xB3, 0x3D, 0xF1, 0xCD, 0xFF, 0x9A,
191            0x52, 0x14, 0xF9, 0xF0,
192        ];
193        assert_eq!(data, ref2);
194
195        Ok(())
196    }
197}