1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! AES256-CTR-DRBG using BearSSL AES for exact KAT compatibility
#[cfg(feature = "bearssl-aes")]
use alloc::format;
#[cfg(feature = "bearssl-aes")]
use alloc::string::String;
#[cfg(feature = "bearssl-aes")]
use rand_core::{
TryCryptoRng,
TryRng,
};
#[cfg(feature = "bearssl-aes")]
use crate::bearssl_aes_pure::Aes256CtxPure;
/// AES256-CTR-DRBG using BearSSL AES primitive
#[cfg(feature = "bearssl-aes")]
pub struct BearSslAes256CtrDrbg {
key: [u8; 32],
v: [u8; 16],
reseed_counter: u32,
}
#[cfg(feature = "bearssl-aes")]
impl BearSslAes256CtrDrbg {
pub fn instantiate(entropy_input: &[u8; 48]) -> Self {
let mut key = [0u8; 32];
let mut v = [0u8; 16];
// Initialize Key and V to zeros (NIST SP 800-90A requirement)
// The entropy_input will be used as provided_data in the update
// This matches the reference implementation's randombytes_init:
// memset(DRBG_ctx.Key, 0x00, 32);
// memset(DRBG_ctx.V, 0x00, 16);
// AES256_CTR_DRBG_Update(seed_material, DRBG_ctx.Key, DRBG_ctx.V);
// Call ctr_drbg_update with entropy_input as provided_data
Self::ctr_drbg_update(Some(entropy_input), &mut key, &mut v);
Self {
key,
v,
reseed_counter: 1,
}
}
fn ctr_drbg_update(provided_data: Option<&[u8]>, key: &mut [u8; 32], v: &mut [u8; 16]) {
let mut temp = [0u8; 48];
// Generate 48 bytes using AES-256-ECB (3 blocks of 16 bytes)
// This matches the reference implementation's AES256_CTR_DRBG_Update
// Note: Reference creates a new AES context for each encryption call
for i in 0..3 {
Self::increment_counter(v);
// Create new AES context for each encryption (matches reference: AES256_ECB creates new context each time)
let aes_ctx = Aes256CtxPure::new(key);
let encrypted = aes_ctx.encrypt_block(v);
temp[i * 16..(i + 1) * 16].copy_from_slice(&encrypted);
}
// XOR with provided_data if present (matches reference: if (provided_data != NULL))
if let Some(data) = provided_data {
// Ensure we don't exceed temp bounds (should be 48 bytes)
let len = core::cmp::min(48, data.len());
for i in 0..len {
temp[i] ^= data[i];
}
}
// Update Key and V from temp (matches reference: memcpy(Key, temp, 32); memcpy(V, temp+32, 16))
key.copy_from_slice(&temp[..32]);
v.copy_from_slice(&temp[32..48]);
}
pub fn increment_counter(v: &mut [u8; 16]) {
// Match reference implementation exactly:
// for (int j=15; j>=0; j--) {
// if ( V[j] == 0xff )
// V[j] = 0x00;
// else {
// V[j]++;
// break;
// }
// }
for i in (0..16).rev() {
if v[i] == 0xFF {
v[i] = 0x00;
} else {
v[i] += 1;
break;
}
}
}
}
#[cfg(feature = "bearssl-aes")]
impl TryRng for BearSslAes256CtrDrbg {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut bytes = [0u8; 4];
self.try_fill_bytes(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut bytes = [0u8; 8];
self.try_fill_bytes(&mut bytes)?;
Ok(u64::from_le_bytes(bytes))
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
#[cfg(all(feature = "debug-drbg-state", feature = "std"))]
{
println!("=== fill_bytes START ===");
println!("Requested {} bytes", dest.len());
println!(
"Initial state: Key: {:02x?} V: {:02x?} Reseed: {}",
&self.key[..],
&self.v[..],
self.reseed_counter
);
}
#[cfg(all(feature = "debug-drbg-state-defmt", not(feature = "std")))]
{
defmt::info!("=== fill_bytes START ===");
defmt::info!("Requested {} bytes", dest.len());
defmt::info!(
"Initial state: Key: {:02x} V: {:02x} Reseed: {}",
&self.key[..],
&self.v[..],
self.reseed_counter
);
}
// Generate output bytes (matches reference: randombytes function)
// Note: Reference creates a new AES context for each encryption call
let mut offset = 0;
while offset < dest.len() {
Self::increment_counter(&mut self.v);
// Create new AES context for each encryption (matches reference: AES256_ECB creates new context each time)
let aes_ctx = Aes256CtxPure::new(&self.key);
let block = aes_ctx.encrypt_block(&self.v);
let to_copy = core::cmp::min(16, dest.len() - offset);
dest[offset..offset + to_copy].copy_from_slice(&block[..to_copy]);
offset += to_copy;
}
Self::ctr_drbg_update(None, &mut self.key, &mut self.v);
self.reseed_counter += 1;
#[cfg(all(feature = "debug-drbg-state", feature = "std"))]
{
println!(
"State after update: Key: {:02x?} V: {:02x?} Reseed: {}",
&self.key[..],
&self.v[..],
self.reseed_counter
);
println!("=== fill_bytes END ===");
}
#[cfg(all(feature = "debug-drbg-state-defmt", not(feature = "std")))]
{
defmt::info!(
"State after update: Key: {:02x} V: {:02x} Reseed: {}",
&self.key[..],
&self.v[..],
self.reseed_counter
);
defmt::info!("=== fill_bytes END ===");
}
Ok(())
}
}
#[cfg(feature = "bearssl-aes")]
impl TryCryptoRng for BearSslAes256CtrDrbg {}
#[cfg(feature = "bearssl-aes")]
impl BearSslAes256CtrDrbg {
pub fn debug_state(&self) -> String {
format!(
"Key: {:02x?}\nV: {:02x?}\nReseed: {}",
&self.key[..],
&self.v[..],
self.reseed_counter
)
}
}