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
use crate::error::{Error, Result};
/// Provides a way to extract random values from WolfSSL.
pub struct Random(wolfssl_sys::WC_RNG);
impl Random {
/// Initializes a [`Random`] object.
pub fn new() -> Result<Random> {
crate::wolf_init()?;
let mut rng = std::mem::MaybeUninit::<wolfssl_sys::WC_RNG>::uninit();
// SAFETY:
// [`wc_InitRng()`][0] ([also][1]) is documented to receive [`WC_RNG`] as input to initialise seed and key cipher.
// The corresponding memory is deallocated, when dropping this structure
//
// [0]: https://www.wolfssl.com/doxygen/group__Random.html#ga1a87307fac65d3c2a47ffb743020f83c
// [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Random.html#function-wc_initrng
match unsafe { wolfssl_sys::wc_InitRng(rng.as_mut_ptr()) } {
0 => {
// SAFETY:
// Based on the documentation from [`wc_InitRng()`][0], `rng` will be initialised successfully
// if the return code is `0`. So can safely call `assume_init` here
//
// [0]: https://www.wolfssl.com/doxygen/group__Random.html#ga1a87307fac65d3c2a47ffb743020f83c
let rng = unsafe { rng.assume_init() };
Ok(Random(rng))
}
e => Err(Error::fatal(e)),
}
}
/// Generates a random collection of bytes. Advances the internal state of the RNG.
pub fn random_bytes<const N: usize>(&mut self) -> Result<[u8; N]> {
let num_bytes: u32 = N.try_into().expect("failed to convert usize to u32");
let mut buf = [0u8; N];
// SAFETY:
// The following invariants required by [`wc_RNG_GenerateBlock()`][0] is satisfied:
// - first argument `&rng` is mutable reference to properly initialised random number generator
// - second argment has valid mutable buffer with proper size corresponding to the size `sz`
//
// [0]: https://www.wolfssl.com/doxygen/group__Random.html#ga9a289fb3f58f4a5f7e15c2b5a1b0d7c6
match unsafe {
wolfssl_sys::wc_RNG_GenerateBlock(&mut self.0 as *mut _, buf.as_mut_ptr(), num_bytes)
} {
0 => Ok(buf),
e => Err(Error::fatal(e)),
}
}
/// Generates a random u64. Advances the internal state of the RNG.
pub fn random_u64(&mut self) -> Result<u64> {
let buf: [u8; 8] = self.random_bytes()?;
Ok(u64::from_ne_bytes(buf))
}
}
impl Drop for Random {
fn drop(&mut self) {
// SAFETY:
// [`wc_FreeRng()`][0] receives properly initialised random number generator [`WC_RNG`] as input to securely free drgb.
// The pointer used as argument is created by `wc_InitRng` api call in new() constructor.
//
// [0]: https://www.wolfssl.com/doxygen/group__Random.html#ga72ffd8b507b3a895af8a6e9996caba86
let res = unsafe { wolfssl_sys::wc_FreeRng(&mut self.0 as *mut _) };
if res != 0 {
log::warn!("Unusual free result: {}", Error::fatal(res));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn random_roundtrip() {
let mut rng = Random::new().unwrap();
for _ in 0..10 {
let _ = rng.random_u64().unwrap();
}
drop(rng)
}
#[test]
fn random_bytes_roundtrip() {
let mut rng = Random::new().unwrap();
for _ in 0..10 {
let _ = rng.random_bytes::<32>().unwrap();
let _: [u8; 16] = rng.random_bytes().unwrap();
}
drop(rng)
}
}