#![cfg(random)]
use crate::sys;
use core::mem::{size_of_val, MaybeUninit};
pub struct RNG {
pub(crate) wc_rng: sys::WC_RNG,
}
impl RNG {
pub fn new() -> Result<Self, i32> {
RNG::new_ex(None, None)
}
pub fn new_ex(heap: Option<*mut core::ffi::c_void>, dev_id: Option<i32>) -> Result<Self, i32> {
#[cfg(fips)]
{
let rc = unsafe { sys::wc_SetSeed_Cb_fips(Some(sys::wc_GenerateSeed)) };
if rc != 0 {
return Err(rc);
}
}
let mut rng: MaybeUninit<RNG> = MaybeUninit::uninit();
let heap = match heap {
Some(heap) => heap,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(dev_id) => dev_id,
None => sys::INVALID_DEVID,
};
let rc = unsafe { sys::wc_InitRng_ex(&mut (*rng.as_mut_ptr()).wc_rng, heap, dev_id) };
if rc == 0 {
let rng = unsafe { rng.assume_init() };
Ok(rng)
} else {
Err(rc)
}
}
pub fn new_with_nonce<T>(nonce: &mut [T]) -> Result<Self, i32> {
RNG::new_with_nonce_ex(nonce, None, None)
}
pub fn new_with_nonce_ex<T>(
nonce: &mut [T],
heap: Option<*mut core::ffi::c_void>,
dev_id: Option<i32>,
) -> Result<Self, i32> {
#[cfg(fips)]
{
let rc = unsafe { sys::wc_SetSeed_Cb_fips(Some(sys::wc_GenerateSeed)) };
if rc != 0 {
return Err(rc);
}
}
let ptr = nonce.as_mut_ptr() as *mut u8;
let size: u32 = size_of_val(nonce) as u32;
let mut rng: MaybeUninit<RNG> = MaybeUninit::uninit();
let heap = match heap {
Some(heap) => heap,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(dev_id) => dev_id,
None => sys::INVALID_DEVID,
};
let rc = unsafe {
sys::wc_InitRngNonce_ex(&mut (*rng.as_mut_ptr()).wc_rng, ptr, size, heap, dev_id)
};
if rc == 0 {
let rng = unsafe { rng.assume_init() };
Ok(rng)
} else {
Err(rc)
}
}
#[cfg(random_hashdrbg)]
pub fn health_test(
nonce: Option<&[u8]>,
seed_a: &[u8],
seed_b: Option<&[u8]>,
output: &mut [u8],
) -> Result<(), i32> {
Self::health_test_ex(nonce, seed_a, seed_b, output, None, None)
}
#[cfg(random_hashdrbg)]
pub fn health_test_ex(
nonce: Option<&[u8]>,
seed_a: &[u8],
seed_b: Option<&[u8]>,
output: &mut [u8],
heap: Option<*mut core::ffi::c_void>,
dev_id: Option<i32>,
) -> Result<(), i32> {
let mut nonce_ptr = core::ptr::null();
let mut nonce_size = 0u32;
if let Some(nonce) = nonce {
nonce_ptr = nonce.as_ptr();
nonce_size = nonce.len() as u32;
}
let seed_a_size = seed_a.len() as u32;
let mut seed_b_ptr = core::ptr::null();
let mut seed_b_size = 0u32;
if let Some(seed_b) = seed_b {
seed_b_ptr = seed_b.as_ptr();
seed_b_size = seed_b.len() as u32;
}
let output_size = output.len() as u32;
let heap = match heap {
Some(heap) => heap,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(dev_id) => dev_id,
None => sys::INVALID_DEVID,
};
let rc = unsafe {
sys::wc_RNG_HealthTest_ex(
if seed_b_size > 0 { 1 } else { 0 },
nonce_ptr,
nonce_size,
seed_a.as_ptr(),
seed_a_size,
seed_b_ptr,
seed_b_size,
output.as_mut_ptr(),
output_size,
heap,
dev_id,
)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
#[cfg(random_hashdrbg)]
pub fn test_seed(seed: &[u8]) -> Result<(), i32> {
let seed_size = seed.len() as u32;
let rc = unsafe { sys::wc_RNG_TestSeed(seed.as_ptr(), seed_size) };
if rc != 0 {
return Err(rc);
}
Ok(())
}
pub fn generate_byte(&mut self) -> Result<u8, i32> {
let mut b: u8 = 0;
let rc = unsafe { sys::wc_RNG_GenerateByte(&mut self.wc_rng, &mut b) };
if rc == 0 {
Ok(b)
} else {
Err(rc)
}
}
pub fn generate_block<T>(&mut self, buf: &mut [T]) -> Result<(), i32> {
debug_assert!(
core::mem::size_of::<T>() > 0 || buf.is_empty(),
"generate_block called with non-empty slice of zero-sized type"
);
let ptr = buf.as_mut_ptr() as *mut u8;
let size: u32 = size_of_val(buf) as u32;
let rc = unsafe { sys::wc_RNG_GenerateBlock(&mut self.wc_rng, ptr, size) };
if rc == 0 {
Ok(())
} else {
Err(rc)
}
}
#[cfg(random_hashdrbg)]
pub fn reseed(&mut self, seed: &[u8]) -> Result<(), i32> {
let seed_size = seed.len() as u32;
let rc = unsafe { sys::wc_RNG_DRBG_Reseed(&mut self.wc_rng, seed.as_ptr(), seed_size) };
if rc != 0 {
return Err(rc);
}
Ok(())
}
}
impl Drop for RNG {
fn drop(&mut self) {
unsafe {
sys::wc_FreeRng(&mut self.wc_rng);
}
}
}