1#[cfg(feature = "alloc")]
13use alloc::boxed::Box;
14
15use core::{marker::PhantomData, ops::DerefMut};
16
17use core::ffi;
18use embedded_mbedtls_sys::{
19 mbedtls_ctr_drbg_context, mbedtls_ctr_drbg_init, mbedtls_ctr_drbg_seed,
20 MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG, MBEDTLS_ERR_ENTROPY_SOURCE_FAILED,
21};
22use rand_core::{CryptoRng, RngCore};
23
24use crate::error::Error;
25
26pub(crate) unsafe extern "C" fn rng_try_fill_bytes_callback_fn<RNG: RngCore>(
29 entropy_context: *mut ffi::c_void,
30 buf: *mut ffi::c_uchar,
31 buf_len: usize,
32) -> ffi::c_int {
33 let rng: &mut RNG = &mut *(entropy_context as *mut RNG);
34 let bytes = core::slice::from_raw_parts_mut(buf, buf_len);
35 if rng.try_fill_bytes(bytes).is_err() {
36 return embedded_mbedtls_sys::MBEDTLS_ERR_SSL_NO_RNG;
37 }
38 0
39}
40
41pub struct CtrDrbg<'a, RNG: RngCore + CryptoRng, D: DerefMut<Target = RNG>> {
58 context: mbedtls_ctr_drbg_context,
59 entropy_source: D,
60 _custom: PhantomData<&'a [u8]>,
61}
62
63impl<'a, RNG: RngCore + CryptoRng> CtrDrbg<'a, RNG, &'a mut RNG> {
64 pub fn new(
85 entropy_source: &'a mut RNG,
86 personalization_string: Option<&'a [u8]>,
87 ) -> Result<Self, Error> {
88 Self::new_generic(entropy_source, personalization_string)
89 }
90}
91
92#[cfg(feature = "alloc")]
93impl<'a, RNG: RngCore + CryptoRng> CtrDrbg<'a, RNG, Box<RNG>> {
94 pub fn new_with_heap_rng(
113 entropy_source: RNG,
114 personalization_string: Option<&'a [u8]>,
115 ) -> Result<Self, Error> {
116 Self::new_generic(Box::new(entropy_source), personalization_string)
117 }
118}
119
120impl<'a, RNG: RngCore + CryptoRng, D: DerefMut<Target = RNG>> CtrDrbg<'a, RNG, D> {
121 fn new_generic(
128 entropy_source: D,
129 personalization_string: Option<&'a [u8]>,
130 ) -> Result<Self, Error> {
131 let context = mbedtls_ctr_drbg_context::default();
132 let mut this = Self {
133 context,
134 entropy_source,
135 _custom: PhantomData,
136 };
137 unsafe { mbedtls_ctr_drbg_init(&mut this.context) };
138 if let Some(custom) = personalization_string {
139 let ret = unsafe {
140 mbedtls_ctr_drbg_seed(
141 &mut this.context,
142 Some(rng_try_fill_bytes_callback_fn::<RNG>),
143 this.entropy_source.deref_mut() as *mut RNG as *mut ffi::c_void,
144 custom.as_ptr(),
145 custom.len(),
146 )
147 };
148 if ret != 0 {
149 return Err(ret.into());
150 }
151 } else {
152 let ret = unsafe {
153 mbedtls_ctr_drbg_seed(
154 &mut this.context,
155 Some(rng_try_fill_bytes_callback_fn::<RNG>),
156 this.entropy_source.deref_mut() as *mut RNG as *mut ffi::c_void,
157 core::ptr::null(),
158 0,
159 )
160 };
161 if ret != 0 {
162 return Err(ret.into());
163 }
164 }
165
166 Ok(this)
167 }
168}
169
170impl<R: RngCore + CryptoRng, D: DerefMut<Target = R>> Drop for CtrDrbg<'_, R, D> {
171 fn drop(&mut self) {
172 unsafe {
173 embedded_mbedtls_sys::mbedtls_ctr_drbg_free(&mut self.context);
174 }
175 }
176}
177
178impl<RNG: RngCore + CryptoRng, D: DerefMut<Target = RNG>> CryptoRng for CtrDrbg<'_, RNG, D> {}
179
180impl<RNG: RngCore + CryptoRng, D: DerefMut<Target = RNG>> RngCore for CtrDrbg<'_, RNG, D> {
181 fn next_u32(&mut self) -> u32 {
182 rand_core::impls::next_u32_via_fill(self)
183 }
184
185 fn next_u64(&mut self) -> u64 {
186 rand_core::impls::next_u64_via_fill(self)
187 }
188
189 fn fill_bytes(&mut self, dest: &mut [u8]) {
190 if dest.len() > embedded_mbedtls_sys::MBEDTLS_CTR_DRBG_MAX_REQUEST as usize {
191 log::error!("Failed to generate random data: Request too big!");
192 panic!("Failed to generate random data: Request too big!");
193 }
194 if self.try_fill_bytes(dest).is_err() {
195 panic!("Failed to generate random data: Entropy source failed!");
196 }
197 }
198
199 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
200 let ret = unsafe {
201 embedded_mbedtls_sys::mbedtls_ctr_drbg_random(
202 &mut self.context as *mut mbedtls_ctr_drbg_context as *mut ffi::c_void,
203 dest.as_mut_ptr(),
204 dest.len(),
205 )
206 };
207
208 if ret == MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG {
209 log::error!("Failed to generate random data: Request to too big!");
210 use core::num::NonZeroU32;
211 use rand_core::Error;
212 return Err(Error::from(unsafe {
213 NonZeroU32::new_unchecked(Error::CUSTOM_START)
214 }));
215 }
216 if ret == MBEDTLS_ERR_ENTROPY_SOURCE_FAILED {
217 log::error!("Failed to generate random data: Entropy source failed!");
218 use core::num::NonZeroU32;
219 use rand_core::Error;
220 return Err(Error::from(unsafe {
221 NonZeroU32::new_unchecked(Error::CUSTOM_START + 1)
222 }));
223 }
224 if ret < 0 {
225 log::error!("Failed to generate random data: mbedtls error {ret}");
226 use core::num::NonZeroU32;
227 use rand_core::Error;
228 return Err(Error::from(unsafe {
229 NonZeroU32::new_unchecked(Error::CUSTOM_START + 2)
230 }));
231 }
232 Ok(())
233 }
234}
235
236#[cfg(test)]
237mod test {
238 use rand_core::RngCore;
239
240 use super::CtrDrbg;
241
242 #[test]
243 fn stack_entropy_drbg() {
244 let mut entropy_source = rand::thread_rng();
245 let mut ctr_drbg = CtrDrbg::new(&mut entropy_source, None).unwrap();
246
247 let _random = ctr_drbg.next_u32();
248 }
249
250 #[cfg(feature = "alloc")]
251 #[test]
252 fn boxed_entropy_drbg() {
253 let mut ctr_drbg = CtrDrbg::new_with_heap_rng(Box::new(rand::thread_rng()), None).unwrap();
254
255 let _random = ctr_drbg.next_u32();
256 }
257}