#![allow(clippy::struct_excessive_bools, clippy::doc_markdown)]
#[cfg(feature = "alloc")]
use alloc::{
boxed::Box,
collections::BTreeMap,
string::String,
};
use core::fmt;
use rand_core::{
CryptoRng,
Rng,
};
use crate::Result;
pub trait SecureRng: Rng + CryptoRng + Send + Sync {
fn fill_bytes_secure(&mut self, dest: &mut [u8]) -> Result<()> {
self.fill_bytes(dest);
Ok(())
}
fn next_u32_secure(&mut self) -> Result<u32> {
Ok(self.next_u32())
}
fn next_u64_secure(&mut self) -> Result<u64> {
Ok(self.next_u64())
}
fn initialize(&mut self, entropy: &[u8]) -> Result<()> {
let _ = entropy;
Ok(())
}
fn is_secure(&self) -> bool {
true
}
fn entropy_quality(&self) -> f64 {
1.0
}
fn security_level(&self) -> SecurityLevel {
if self.is_secure() {
SecurityLevel::CryptographicallySecure
} else {
SecurityLevel::Deterministic
}
}
fn reseed(&mut self) -> Result<()> {
Ok(())
}
fn state_size(&self) -> usize {
0
}
fn reseed_interval(&self) -> Option<usize> {
None
}
}
pub trait EntropySource: Send + Sync {
fn get_entropy(&mut self, dest: &mut [u8]) -> Result<()>;
fn is_available(&self) -> bool {
true
}
fn quality(&self) -> f64 {
1.0
}
fn name(&self) -> &'static str {
"Unknown"
}
fn source_type(&self) -> EntropySourceType {
EntropySourceType::User }
fn max_entropy_per_call(&self) -> Option<usize> {
None
}
fn requires_initialization(&self) -> bool {
false
}
fn initialize(&mut self, config: &EntropyConfig) -> Result<()> {
let _ = config;
Ok(())
}
}
pub trait RngProvider: Send + Sync {
#[cfg(feature = "alloc")]
fn create_rng(&self, config: &RngConfig) -> Result<Box<dyn SecureRng>>;
fn name(&self) -> &'static str;
fn capabilities(&self) -> ProviderCapabilities;
fn supports_config(&self, config: &RngConfig) -> bool {
let _ = config;
true
}
fn priority(&self) -> u32 {
0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SecurityLevel {
Deterministic,
CryptographicallySecure,
Hardware,
Software,
}
impl fmt::Display for SecurityLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Deterministic => write!(f, "Deterministic"),
Self::CryptographicallySecure => write!(f, "Cryptographically Secure"),
Self::Hardware => write!(f, "Hardware"),
Self::Software => write!(f, "Software"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EntropySourceType {
OperatingSystem,
Hardware,
User,
Deterministic,
}
impl fmt::Display for EntropySourceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OperatingSystem => write!(f, "Operating System"),
Self::Hardware => write!(f, "Hardware"),
Self::User => write!(f, "User"),
Self::Deterministic => write!(f, "Deterministic"),
}
}
}
pub struct RngConfig {
pub security_level: SecurityLevel,
#[cfg(feature = "alloc")]
pub entropy_source: Option<Box<dyn EntropySource>>,
pub reseed_interval: Option<usize>,
#[cfg(feature = "alloc")]
pub parameters: BTreeMap<String, String>,
}
impl Default for RngConfig {
fn default() -> Self {
Self {
security_level: SecurityLevel::CryptographicallySecure,
#[cfg(feature = "alloc")]
entropy_source: None,
reseed_interval: None,
#[cfg(feature = "alloc")]
parameters: BTreeMap::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct EntropyConfig {
pub min_quality: f64,
pub max_per_call: Option<usize>,
#[cfg(feature = "alloc")]
pub parameters: BTreeMap<String, String>,
}
impl Default for EntropyConfig {
fn default() -> Self {
Self {
min_quality: 0.8,
max_per_call: None,
#[cfg(feature = "alloc")]
parameters: BTreeMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProviderCapabilities {
pub secure: bool,
pub deterministic: bool,
pub hardware: bool,
pub reseeding: bool,
pub custom_entropy: bool,
pub no_std: bool,
pub wasm: bool,
}
impl Default for ProviderCapabilities {
fn default() -> Self {
Self {
secure: true,
deterministic: false,
hardware: false,
reseeding: false,
custom_entropy: false,
no_std: true,
wasm: false,
}
}
}
#[cfg(test)]
mod tests {
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::format;
use super::*;
#[test]
fn test_security_level_ordering() {
assert!(SecurityLevel::CryptographicallySecure > SecurityLevel::Deterministic);
assert!(SecurityLevel::Hardware > SecurityLevel::CryptographicallySecure);
}
#[test]
#[cfg(any(feature = "std", feature = "alloc"))]
fn test_entropy_source_type_display() {
assert_eq!(
format!("{}", EntropySourceType::OperatingSystem),
"Operating System"
);
assert_eq!(format!("{}", EntropySourceType::Hardware), "Hardware");
}
#[test]
fn test_rng_config_default() {
let config = RngConfig::default();
assert_eq!(
config.security_level,
SecurityLevel::CryptographicallySecure
);
assert!(config.reseed_interval.is_none());
}
#[test]
fn test_entropy_config_default() {
let config = EntropyConfig::default();
#[allow(clippy::float_cmp)]
{
assert_eq!(config.min_quality, 0.8);
}
assert!(config.max_per_call.is_none());
}
#[test]
fn test_provider_capabilities_default() {
let caps = ProviderCapabilities::default();
assert!(caps.secure);
assert!(!caps.deterministic);
assert!(!caps.hardware);
assert!(!caps.reseeding);
assert!(!caps.custom_entropy);
assert!(caps.no_std);
assert!(!caps.wasm);
}
}