#![allow(clippy::must_use_candidate)]
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use core::fmt;
#[cfg(feature = "alloc")]
use rand_core::{
TryCryptoRng,
TryRng,
};
#[cfg(feature = "alloc")]
use crate::Result;
#[cfg(feature = "alloc")]
use crate::traits::{
EntropySource,
ProviderCapabilities,
RngConfig,
RngProvider,
SecureRng,
SecurityLevel,
};
#[cfg(feature = "alloc")]
use crate::validation::EntropyValidator;
#[cfg(feature = "alloc")]
pub struct LibQRng {
entropy_source: Box<dyn EntropySource>,
validator: EntropyValidator,
security_level: SecurityLevel,
deterministic: bool,
reseed_counter: u32,
bytes_generated: usize,
reseed_interval: Option<usize>,
}
pub trait FillableBytes: private::Sealed + Copy {}
mod private {
pub trait Sealed {}
}
macro_rules! impl_fillable_bytes {
($($t:ty),* $(,)?) => {
$(
impl private::Sealed for $t {}
impl FillableBytes for $t {}
)*
};
}
impl_fillable_bytes!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
#[cfg(feature = "alloc")]
impl LibQRng {
pub fn new_secure() -> Result<Self> {
let entropy_source = crate::entropy::EntropySourceFactory::create_best_available()?;
let validator = EntropyValidator::with_settings(
64, 8192, 0.3, false, );
Ok(Self {
entropy_source,
validator,
security_level: SecurityLevel::CryptographicallySecure,
deterministic: false,
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: Some(1024 * 1024), })
}
pub fn new_deterministic(seed: [u8; 32]) -> Self {
let entropy_source =
crate::entropy::EntropySourceFactory::create_deterministic_entropy(seed);
let validator = EntropyValidator::with_settings(
32, 1024, 0.1, false, );
Self {
entropy_source,
validator,
security_level: SecurityLevel::Deterministic,
deterministic: true,
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: None, }
}
pub fn new_deterministic_from_u64(seed: u64) -> Self {
let entropy_source =
crate::entropy::EntropySourceFactory::create_deterministic_entropy_from_u64(seed);
let validator = EntropyValidator::with_settings(32, 1024, 0.1, false);
Self {
entropy_source,
validator,
security_level: SecurityLevel::Deterministic,
deterministic: true,
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: None,
}
}
#[cfg(feature = "deterministic-saturnin")]
pub fn new_deterministic_saturnin(seed: [u8; 32]) -> Result<Self> {
let entropy_source = alloc::boxed::Box::new(
crate::saturnin_det::SaturninDeterministicEntropySource::new(seed)?,
);
let validator = EntropyValidator::with_settings(32, 1024, 0.1, false);
Ok(Self {
entropy_source,
validator,
security_level: SecurityLevel::Deterministic,
deterministic: true,
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: None,
})
}
#[cfg(feature = "nist-drbg")]
pub fn new_nist_drbg(entropy_input: [u8; 48]) -> Self {
let entropy_source =
crate::entropy::EntropySourceFactory::create_nist_drbg_entropy(entropy_input);
let validator = EntropyValidator::with_settings(
256, 4096, 0.9, true, );
Self {
entropy_source,
validator,
security_level: SecurityLevel::CryptographicallySecure,
deterministic: true, reseed_counter: 0,
bytes_generated: 0,
reseed_interval: Some(1_000_000), }
}
pub fn new_custom<T: EntropySource + 'static>(entropy_source: T) -> Self {
let entropy_source = Box::new(entropy_source);
let validator = match entropy_source.source_type() {
crate::traits::EntropySourceType::Hardware => {
EntropyValidator::with_settings(64, 8192, 0.4, false)
}
crate::traits::EntropySourceType::OperatingSystem => {
EntropyValidator::with_settings(64, 8192, 0.3, false)
}
_ => EntropyValidator::with_settings(64, 8192, 0.3, false),
};
let security_level = match entropy_source.source_type() {
crate::traits::EntropySourceType::Hardware => SecurityLevel::Hardware,
crate::traits::EntropySourceType::OperatingSystem => {
SecurityLevel::CryptographicallySecure
}
crate::traits::EntropySourceType::Deterministic |
crate::traits::EntropySourceType::User => SecurityLevel::Deterministic,
};
let deterministic = matches!(
entropy_source.source_type(),
crate::traits::EntropySourceType::Deterministic |
crate::traits::EntropySourceType::User
);
Self {
entropy_source,
validator,
security_level,
deterministic,
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: if deterministic {
None
} else {
Some(1024 * 1024)
},
}
}
pub fn with_config(config: &RngConfig) -> Result<Self> {
let entropy_source = if let Some(_source) = &config.entropy_source {
crate::entropy::EntropySourceFactory::create_best_available()?
} else {
crate::entropy::EntropySourceFactory::create_best_available()?
};
let validator = match config.security_level {
SecurityLevel::Hardware => EntropyValidator::with_settings(64, 8192, 0.4, false),
SecurityLevel::CryptographicallySecure => {
EntropyValidator::with_settings(64, 8192, 0.3, false)
}
SecurityLevel::Deterministic => EntropyValidator::with_settings(32, 1024, 0.1, false),
SecurityLevel::Software => EntropyValidator::with_settings(64, 8192, 0.3, false),
};
let deterministic =
entropy_source.source_type() == crate::traits::EntropySourceType::Deterministic;
Ok(Self {
entropy_source,
validator,
security_level: config.security_level,
deterministic,
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: config.reseed_interval,
})
}
pub fn is_deterministic(&self) -> bool {
self.deterministic
}
pub fn security_level(&self) -> SecurityLevel {
self.security_level
}
pub fn entropy_source_name(&self) -> &'static str {
self.entropy_source.name()
}
pub fn entropy_source_type(&self) -> crate::traits::EntropySourceType {
self.entropy_source.source_type()
}
pub fn reseed_counter(&self) -> u32 {
self.reseed_counter
}
pub fn bytes_generated(&self) -> usize {
self.bytes_generated
}
pub fn is_secure(&self) -> bool {
self.security_level == SecurityLevel::CryptographicallySecure
}
pub fn entropy_quality(&self) -> f64 {
match self.security_level {
SecurityLevel::CryptographicallySecure => 1.0,
SecurityLevel::Deterministic => 0.0,
SecurityLevel::Hardware => 0.95,
SecurityLevel::Software => 0.8,
}
}
fn needs_reseed(&self) -> bool {
if let Some(interval) = self.reseed_interval {
self.bytes_generated >= interval
} else {
false
}
}
fn reseed_if_needed(&mut self) -> Result<()> {
if self.needs_reseed() {
self.reseed()?;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl SecureRng for LibQRng {
fn fill_bytes_secure(&mut self, dest: &mut [u8]) -> Result<()> {
self.reseed_if_needed()?;
self.entropy_source.get_entropy(dest)?;
if !self.deterministic && dest.len() >= 64 {
self.validator.validate_entropy(&dest[..64])?;
}
self.bytes_generated += dest.len();
Ok(())
}
fn next_u32_secure(&mut self) -> Result<u32> {
let mut bytes = [0u8; 4];
self.fill_bytes_secure(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn next_u64_secure(&mut self) -> Result<u64> {
let mut bytes = [0u8; 8];
self.fill_bytes_secure(&mut bytes)?;
Ok(u64::from_le_bytes(bytes))
}
fn initialize(&mut self, entropy: &[u8]) -> Result<()> {
if self.deterministic {
let seed: [u8; 32] = entropy.try_into().map_err(|_| {
crate::Error::invalid_configuration(
"deterministic seed",
"exactly 32 bytes",
"slice length is not 32",
)
})?;
let new_source =
crate::entropy::EntropySourceFactory::create_deterministic_entropy(seed);
self.entropy_source = new_source;
self.reseed_counter = 0;
self.bytes_generated = 0;
}
Ok(())
}
fn is_secure(&self) -> bool {
!self.deterministic
}
fn entropy_quality(&self) -> f64 {
self.entropy_source.quality()
}
fn security_level(&self) -> SecurityLevel {
self.security_level
}
fn reseed(&mut self) -> Result<()> {
if self.deterministic {
return Ok(()); }
self.reseed_counter = self.reseed_counter.wrapping_add(1);
self.bytes_generated = 0;
Ok(())
}
fn state_size(&self) -> usize {
64
}
fn reseed_interval(&self) -> Option<usize> {
self.reseed_interval
}
}
#[cfg(feature = "alloc")]
impl TryRng for LibQRng {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> core::result::Result<u32, Self::Error> {
match self.next_u32_secure() {
Ok(value) => Ok(value),
Err(_) => rng_abort(),
}
}
fn try_next_u64(&mut self) -> core::result::Result<u64, Self::Error> {
match self.next_u64_secure() {
Ok(value) => Ok(value),
Err(_) => rng_abort(),
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> core::result::Result<(), Self::Error> {
match self.fill_bytes_secure(dest) {
Ok(()) => Ok(()),
Err(_) => rng_abort(),
}
}
}
#[cfg(feature = "alloc")]
#[inline(never)]
#[allow(clippy::panic)]
fn rng_abort() -> ! {
#[cfg(feature = "std")]
std::process::abort();
#[cfg(not(feature = "std"))]
panic!("CRITICAL SECURITY FAILURE: RNG entropy unavailable");
}
#[cfg(feature = "alloc")]
impl TryCryptoRng for LibQRng {}
#[cfg(feature = "alloc")]
impl LibQRng {