#![cfg(random)]
use crate::sys;
use core::mem::size_of_val;
use num_traits::PrimInt;
pub struct RNG {
pub(crate) wc_rng: *mut sys::WC_RNG,
}
unsafe impl Send for RNG {}
pub(crate) enum RngHandle {
Owned(RNG),
#[cfg(feature = "alloc")]
Shared(alloc::rc::Rc<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 wc_rng: *mut sys::WC_RNG = core::ptr::null_mut();
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_new_ex(&mut wc_rng, core::ptr::null_mut(), 0, heap, dev_id)
};
if rc == 0 {
Ok(RNG {wc_rng})
} else {
Err(rc)
}
}
pub fn new_with_nonce<T: PrimInt>(nonce: &mut [T]) -> Result<Self, i32> {
RNG::new_with_nonce_ex(nonce, None, None)
}
pub fn new_with_nonce_ex<T: PrimInt>(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 = crate::buffer_len_to_u32(size_of_val(nonce))?;
let mut wc_rng: *mut sys::WC_RNG = core::ptr::null_mut();
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_new_ex(&mut wc_rng, ptr, size, heap, dev_id)
};
if rc == 0 {
Ok(RNG {wc_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 = crate::buffer_len_to_u32(nonce.len())?;
}
let seed_a_size = crate::buffer_len_to_u32(seed_a.len())?;
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 = crate::buffer_len_to_u32(seed_b.len())?;
}
let output_size = crate::buffer_len_to_u32(output.len())?;
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 = crate::buffer_len_to_u32(seed.len())?;
let rc = unsafe { sys::wc_RNG_TestSeed(seed.as_ptr(), seed_size) };
if rc != 0 {
return Err(rc);
}
Ok(())
}
pub fn generate_byte(&self) -> Result<u8, i32> {
let mut b: u8 = 0;
let rc = unsafe { sys::wc_RNG_GenerateByte(self.wc_rng, &mut b) };
if rc == 0 {
Ok(b)
} else {
Err(rc)
}
}
pub fn generate_block<T: PrimInt>(&self, buf: &mut [T]) -> Result<(), i32> {
let ptr = buf.as_mut_ptr() as *mut u8;
let size = crate::buffer_len_to_u32(size_of_val(buf))?;
let rc = unsafe { sys::wc_RNG_GenerateBlock(self.wc_rng, ptr, size) };
if rc == 0 {
Ok(())
} else {
Err(rc)
}
}
#[cfg(random_hashdrbg)]
pub fn reseed(&self, seed: &[u8]) -> Result<(), i32> {
let seed_size = crate::buffer_len_to_u32(seed.len())?;
let rc = unsafe {
sys::wc_RNG_DRBG_Reseed(self.wc_rng, seed.as_ptr(), seed_size)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
}
#[cfg(feature = "rand_core")]
impl rand_core::TryRng for RNG {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
rand_core::utils::next_word_via_fill(self)
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
rand_core::utils::next_word_via_fill(self)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.generate_block(dest).expect("RNG failure");
Ok(())
}
}
#[cfg(feature = "rand_core")]
impl rand_core::TryCryptoRng for RNG {}
impl Drop for RNG {
fn drop(&mut self) {
unsafe { sys::wc_rng_free(self.wc_rng); }
}
}