use core::fmt;
use rand_core::{
TryCryptoRng,
TryRng,
};
#[cfg(feature = "custom-entropy")]
use crate::custom_entropy::{
generate_custom_entropy,
has_custom_entropy_source,
};
use crate::kt128_expander::Kt128Expander;
use crate::{
Error,
Result,
};
#[derive(Debug)]
pub struct NoStdRng {
reseed_counter: u32,
bytes_generated: usize,
reseed_interval: usize,
deterministic_rng: Option<Kt128Expander>,
}
impl NoStdRng {
pub fn new() -> Result<Self> {
#[cfg(feature = "getrandom")]
{
let mut test_bytes = [0u8; 1];
getrandom::fill(&mut test_bytes).map_err(|_| Error::EntropySourceUnavailable {
source: "getrandom",
context: Some("initialization test failed"),
})?;
Ok(Self {
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: 1024 * 1024, deterministic_rng: None,
})
}
#[cfg(not(feature = "getrandom"))]
{
Err(Error::FeatureNotAvailable {
feature: "no_std RNG",
required_features: &["getrandom"],
})
}
}
#[must_use]
pub fn new_deterministic(seed: [u8; 32]) -> Self {
Self {
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: 0, deterministic_rng: Some(Kt128Expander::from_det_seed_32(seed)),
}
}
#[must_use]
pub fn new_deterministic_from_u64(seed: u64) -> Self {
Self {
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: 0,
deterministic_rng: Some(Kt128Expander::from_det_u64(seed)),
}
}
#[must_use]
pub fn is_deterministic(&self) -> bool {
self.reseed_interval == 0
}
#[must_use]
pub fn bytes_generated(&self) -> usize {
self.bytes_generated
}
#[must_use]
pub fn reseed_counter(&self) -> u32 {
self.reseed_counter
}
#[must_use]
pub fn reseed_interval(&self) -> usize {
self.reseed_interval
}
}
impl TryRng for NoStdRng {
type Error = Error;
fn try_next_u32(&mut self) -> core::result::Result<u32, Self::Error> {
let mut bytes = [0u8; 4];
self.try_fill_bytes(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn try_next_u64(&mut self) -> core::result::Result<u64, Self::Error> {
let mut bytes = [0u8; 8];
self.try_fill_bytes(&mut bytes)?;
Ok(u64::from_le_bytes(bytes))
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> core::result::Result<(), Self::Error> {
if dest.is_empty() {
return Ok(());
}
if !self.is_deterministic() && self.bytes_generated >= self.reseed_interval {
self.reseed_counter = self.reseed_counter.wrapping_add(1);
self.bytes_generated = 0;
}
if let Some(ref mut expander) = self.deterministic_rng {
expander.fill_bytes(dest);
} else {
#[cfg(feature = "custom-entropy")]
{
if has_custom_entropy_source() {
if let Err(e) = generate_custom_entropy(dest) {
#[cfg(feature = "getrandom")]
{
getrandom::fill(dest).map_err(|_| Error::EntropySourceUnavailable {
source: "getrandom",
context: Some(
"custom entropy source failed and getrandom fallback also failed",
),
})?;
let _ = e;
}
#[cfg(not(feature = "getrandom"))]
{
let _ = e;
return Err(Error::EntropySourceUnavailable {
source: "custom-entropy",
context: Some(
"custom entropy source failed and the 'getrandom' feature is \
not enabled to fall back to",
),
});
}
}
} else {
#[cfg(feature = "getrandom")]
{
getrandom::fill(dest).map_err(|_| Error::EntropySourceUnavailable {
source: "getrandom",
context: Some("no custom entropy source registered"),
})?;
}
#[cfg(not(feature = "getrandom"))]
{
return Err(Error::FeatureNotAvailable {
feature: "no_std RNG entropy source",
required_features: &["custom-entropy source", "getrandom"],
});
}
}
}
#[cfg(not(feature = "custom-entropy"))]
{
#[cfg(feature = "getrandom")]
{
getrandom::fill(dest).map_err(|_| Error::EntropySourceUnavailable {
source: "getrandom",
context: None,
})?;
}
#[cfg(not(feature = "getrandom"))]
{
return Err(Error::FeatureNotAvailable {
feature: "no_std RNG entropy source",
required_features: &["getrandom"],
});
}
}
}
self.bytes_generated += dest.len();
Ok(())
}
}
impl TryCryptoRng for NoStdRng {}
impl NoStdRng {
#[must_use]
pub fn next_u32(&mut self) -> u32 {
self.try_next_u32()
.unwrap_or_else(|e| panic!("NoStdRng::next_u32 failed: {e}"))
}
#[must_use]
pub fn next_u64(&mut self) -> u64 {
self.try_next_u64()
.unwrap_or_else(|e| panic!("NoStdRng::next_u64 failed: {e}"))
}
pub fn fill_bytes(&mut self, dst: &mut [u8]) {
self.try_fill_bytes(dst)
.unwrap_or_else(|e| panic!("NoStdRng::fill_bytes failed: {e}"));
}
}
impl fmt::Display for NoStdRng {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"NoStdRng(deterministic: {}, bytes_generated: {}, reseed_counter: {})",
self.is_deterministic(),
self.bytes_generated,
self.reseed_counter
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "custom-entropy"))]
#[test]
fn test_try_fill_bytes_returns_err_not_panic_when_no_entropy_source() {
let mut rng = NoStdRng {
reseed_counter: 0,
bytes_generated: 0,
reseed_interval: 1024 * 1024,
deterministic_rng: None,
};
let mut dest = [0u8; 8];
let result = rng.try_fill_bytes(&mut dest);
assert!(
result.is_err(),
"try_fill_bytes must return Err when no entropy source is available, not panic"
);
}
#[cfg(feature = "getrandom")]
#[test]
fn test_no_std_rng_creation() {
let rng = NoStdRng::new();
assert!(rng.is_ok());
}
#[test]
fn test_deterministic_rng_creation() {
let seed = [1u8; 32];
let rng = NoStdRng::new_deterministic(seed);
assert!(rng.is_deterministic());
}
#[cfg(feature = "getrandom")]
#[test]
fn test_rng_bytes_generation() {
let mut rng = NoStdRng::new().unwrap();
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
let all_zeros = bytes.iter().all(|&b| b == 0);
assert!(!all_zeros);
}
#[cfg(feature = "getrandom")]
#[test]
fn test_rng_reseed_counter() {
let mut rng = NoStdRng::new().unwrap();
let initial_counter = rng.reseed_counter();
let initial_bytes = rng.bytes_generated();
let mut bytes = [0u8; 1024];
rng.fill_bytes(&mut bytes);
assert!(rng.bytes_generated() > initial_bytes);
if rng.is_deterministic() {
assert_eq!(rng.reseed_counter(), initial_counter);
} else {
assert!(rng.reseed_counter() >= initial_counter);
}
}
#[test]
fn test_deterministic_rng_consistency() {
let seed = [42u8; 32];
let mut rng1 = NoStdRng::new_deterministic(seed);
let mut rng2 = NoStdRng::new_deterministic(seed);
let mut bytes1 = [0u8; 32];
let mut bytes2 = [0u8; 32];
rng1.fill_bytes(&mut bytes1);
rng2.fill_bytes(&mut bytes2);
assert_eq!(bytes1, bytes2);
}
#[test]
fn test_deterministic_seeds_differ_in_final_byte_yield_different_streams() {
let seed_a = [0u8; 32];
let mut seed_b = [0u8; 32];
seed_b[31] = 1;
let mut rng_a = NoStdRng::new_deterministic(seed_a);
let mut rng_b = NoStdRng::new_deterministic(seed_b);
let mut out_a = [0u8; 64];
let mut out_b = [0u8; 64];
rng_a.fill_bytes(&mut out_a);
rng_b.fill_bytes(&mut out_b);
assert_ne!(
out_a, out_b,
"KT128 streams from different 32-byte keys must diverge immediately"
);
}
}