use crate::helpers::{AlgoHandle, Handle};
use crate::Error;
use core::convert::TryFrom;
use core::fmt;
use core::ptr;
use winapi::shared::bcrypt::*;
use winapi::shared::ntdef::ULONG;
#[derive(Clone, Copy, PartialOrd, PartialEq)]
pub enum RandomAlgorithmId {
Rng,
DualECRng,
Fips186DsaRng,
}
impl<'a> TryFrom<&'a str> for RandomAlgorithmId {
type Error = &'a str;
fn try_from(value: &'a str) -> Result<RandomAlgorithmId, Self::Error> {
match value {
BCRYPT_RNG_ALGORITHM => Ok(RandomAlgorithmId::Rng),
BCRYPT_RNG_DUAL_EC_ALGORITHM => Ok(RandomAlgorithmId::DualECRng),
BCRYPT_RNG_FIPS186_DSA_ALGORITHM => Ok(RandomAlgorithmId::Fips186DsaRng),
_ => Err(value),
}
}
}
impl From<RandomAlgorithmId> for &'static str {
fn from(val: RandomAlgorithmId) -> Self {
match val {
RandomAlgorithmId::Rng => BCRYPT_RNG_ALGORITHM,
RandomAlgorithmId::DualECRng => BCRYPT_RNG_DUAL_EC_ALGORITHM,
RandomAlgorithmId::Fips186DsaRng => BCRYPT_RNG_FIPS186_DSA_ALGORITHM,
}
}
}
impl fmt::Display for RandomAlgorithmId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Into::<&'static str>::into(*self))
}
}
pub struct RandomNumberGenerator {
handle: RandomAlgoHandle,
}
impl RandomNumberGenerator {
pub fn open(id: RandomAlgorithmId) -> crate::Result<RandomNumberGenerator> {
let handle = RandomAlgoHandle::open(id)?;
Ok(Self { handle })
}
pub fn system_preferred() -> RandomNumberGenerator {
let handle = RandomAlgoHandle::SystemPreferred;
Self { handle }
}
pub fn gen_random(&self, buffer: &mut [u8]) -> crate::Result<()> {
self.gen_random_with_opts(buffer, self.handle.flags())
}
pub fn gen_random_with_entropy_in_buffer(&self, buffer: &mut [u8]) -> crate::Result<()> {
self.gen_random_with_opts(
buffer,
self.handle.flags() | BCRYPT_RNG_USE_ENTROPY_IN_BUFFER,
)
}
fn gen_random_with_opts(&self, buffer: &mut [u8], opts: ULONG) -> crate::Result<()> {
let handle = self.handle.handle();
Error::check(unsafe {
BCryptGenRandom(handle, buffer.as_mut_ptr(), buffer.len() as ULONG, opts)
})
}
}
#[cfg(feature = "rand")]
impl rand_core::CryptoRng for RandomNumberGenerator {}
#[cfg(feature = "rand")]
impl rand_core::RngCore for RandomNumberGenerator {
fn next_u32(&mut self) -> u32 {
rand_core::impls::next_u32_via_fill(self)
}
fn next_u64(&mut self) -> u64 {
rand_core::impls::next_u64_via_fill(self)
}
fn fill_bytes(&mut self, dst: &mut [u8]) {
self.try_fill_bytes(dst).unwrap()
}
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), rand_core::Error> {
self.gen_random(dst)
.map_err(|e| From::<core::num::NonZeroU32>::from(e.into()))
}
}
enum RandomAlgoHandle {
SystemPreferred,
Specified(AlgoHandle),
}
impl RandomAlgoHandle {
fn open(id: RandomAlgorithmId) -> crate::Result<Self> {
Ok(Self::Specified(AlgoHandle::open(id.into())?))
}
fn handle(&self) -> BCRYPT_ALG_HANDLE {
match self {
Self::SystemPreferred => ptr::null_mut(),
Self::Specified(handle) => handle.as_ptr(),
}
}
fn flags(&self) -> ULONG {
match self {
Self::SystemPreferred => BCRYPT_USE_SYSTEM_PREFERRED_RNG,
Self::Specified(_) => 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_rng(rng: RandomNumberGenerator) {
let empty = vec![0; 32];
let mut buf = empty.clone();
rng.gen_random(&mut buf).expect("RNG to succeed");
assert_ne!(&buf, &empty);
let mut buf2 = buf.clone();
rng.gen_random_with_entropy_in_buffer(&mut buf2)
.expect("RNG to succeeed");
assert_ne!(&buf2, &empty);
assert_ne!(&buf2, &buf);
}
#[test]
fn system_preferred() {
let rng = RandomNumberGenerator::system_preferred();
test_rng(rng);
}
#[test]
fn rng() {
let rng = RandomNumberGenerator::open(RandomAlgorithmId::Rng).unwrap();
test_rng(rng);
}
#[test]
fn dualecrng() {
let rng = RandomNumberGenerator::open(RandomAlgorithmId::DualECRng).unwrap();
test_rng(rng);
}
#[test]
fn fips186dsarng() {
let rng = RandomNumberGenerator::open(RandomAlgorithmId::Fips186DsaRng).unwrap();
test_rng(rng);
}
}