#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::print_stdout,
clippy::print_stderr
)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "alloc")]
use lib_q_random::{
new_deterministic_rng,
new_secure_rng,
};
#[cfg(not(feature = "alloc"))]
use lib_q_random::{
new_deterministic_rng_no_std,
new_secure_rng_no_std,
};
use rand_core::Rng;
fn pad_label(s: &[u8]) -> [u8; 32] {
assert!(s.len() <= 32);
let mut out = [0u8; 32];
out[..s.len()].copy_from_slice(s);
out
}
#[test]
fn test_secure_rng_never_produces_zeros() {
#[cfg(feature = "alloc")]
{
let mut rng = new_secure_rng().expect("Failed to create secure RNG");
for _ in 0..100 {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
assert_ne!(bytes, [0u8; 32], "Secure RNG produced all zeros!");
let non_zero_count = bytes.iter().filter(|&&b| b != 0).count();
assert!(non_zero_count > 0, "Secure RNG produced no non-zero bytes!");
}
}
#[cfg(not(feature = "alloc"))]
{
let mut rng = new_secure_rng_no_std().expect("Failed to create secure RNG");
for _ in 0..100 {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
assert_ne!(bytes, [0u8; 32], "Secure RNG produced all zeros!");
let non_zero_count = bytes.iter().filter(|&&b| b != 0).count();
assert!(non_zero_count > 0, "Secure RNG produced no non-zero bytes!");
}
}
}
#[test]
fn test_secure_rng_produces_different_values() {
#[cfg(feature = "alloc")]
{
let mut rng = new_secure_rng().expect("Failed to create secure RNG");
let mut prev_bytes = [0u8; 32];
rng.fill_bytes(&mut prev_bytes);
for _ in 0..50 {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
assert_ne!(
bytes, prev_bytes,
"Secure RNG produced identical values on successive calls!"
);
prev_bytes = bytes;
}
}
#[cfg(not(feature = "alloc"))]
{
let mut rng = new_secure_rng_no_std().expect("Failed to create secure RNG");
let mut prev_bytes = [0u8; 32];
rng.fill_bytes(&mut prev_bytes);
for _ in 0..50 {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
assert_ne!(
bytes, prev_bytes,
"Secure RNG produced identical values on successive calls!"
);
prev_bytes = bytes;
}
}
}
#[test]
fn test_different_rng_instances_produce_different_values() {
#[cfg(feature = "alloc")]
{
let mut rng1 = new_secure_rng().expect("Failed to create secure RNG 1");
let mut rng2 = new_secure_rng().expect("Failed to create secure RNG 2");
let mut bytes1 = [0u8; 32];
let mut bytes2 = [0u8; 32];
rng1.fill_bytes(&mut bytes1);
rng2.fill_bytes(&mut bytes2);
assert_ne!(
bytes1, bytes2,
"Different RNG instances produced identical values!"
);
}
#[cfg(not(feature = "alloc"))]
{
let mut rng1 = new_secure_rng_no_std().expect("Failed to create secure RNG 1");
let mut rng2 = new_secure_rng_no_std().expect("Failed to create secure RNG 2");
let mut bytes1 = [0u8; 32];
let mut bytes2 = [0u8; 32];
rng1.fill_bytes(&mut bytes1);
rng2.fill_bytes(&mut bytes2);
assert_ne!(
bytes1, bytes2,
"Different RNG instances produced identical values!"
);
}
}
#[test]
fn test_deterministic_rng_consistency() {
let seed = pad_label(b"test seed for deterministic RNG");
#[cfg(feature = "alloc")]
{
let mut rng1 = new_deterministic_rng(seed);
let mut rng2 = new_deterministic_rng(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,
"Deterministic RNGs with same seed produced different values!"
);
}
#[cfg(not(feature = "alloc"))]
{
let mut rng1 = new_deterministic_rng_no_std(seed);
let mut rng2 = new_deterministic_rng_no_std(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,
"Deterministic RNGs with same seed produced different values!"
);
}
}
#[test]
fn test_deterministic_rng_different_seeds() {
let seed1 = pad_label(b"seed 1");
let seed2 = pad_label(b"seed 2");
#[cfg(feature = "alloc")]
{
let mut rng1 = new_deterministic_rng(seed1);
let mut rng2 = new_deterministic_rng(seed2);
let mut bytes1 = [0u8; 32];
let mut bytes2 = [0u8; 32];
rng1.fill_bytes(&mut bytes1);
rng2.fill_bytes(&mut bytes2);
assert_ne!(
bytes1, bytes2,
"Deterministic RNGs with different seeds produced identical values!"
);
}
#[cfg(not(feature = "alloc"))]
{
let mut rng1 = new_deterministic_rng_no_std(seed1);
let mut rng2 = new_deterministic_rng_no_std(seed2);
let mut bytes1 = [0u8; 32];
let mut bytes2 = [0u8; 32];
rng1.fill_bytes(&mut bytes1);
rng2.fill_bytes(&mut bytes2);
assert_ne!(
bytes1, bytes2,
"Deterministic RNGs with different seeds produced identical values!"
);
}
}
#[test]
#[cfg(feature = "alloc")]
fn test_secure_rng_entropy_quality() {
let mut rng = new_secure_rng().expect("Failed to create secure RNG");
let mut samples = Vec::new();
for _ in 0..1000 {
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes);
samples.extend_from_slice(&bytes);
}
test_byte_distribution(&samples);
test_autocorrelation(&samples);
test_runs_test(&samples);
}
#[cfg(feature = "alloc")]
fn test_byte_distribution(data: &[u8]) {
let n = data.len();
assert!(
n >= 256 * 10,
"need enough samples for byte histogram (got {})",
n
);
let mut byte_counts = [0u32; 256];
for &byte in data {
byte_counts[byte as usize] += 1;
}
let expected = n as f64 / 256.0;
let mut chi_sq = 0.0;
for &count in &byte_counts {
let o = f64::from(count);
let diff = o - expected;
chi_sq += diff * diff / expected;
}
const DF: f64 = 255.0;
let sd = (2.0 * DF).sqrt();
const Z: f64 = 4.42;
let chi_sq_upper = DF + Z * sd;
assert!(
chi_sq <= chi_sq_upper,
"χ² uniformity statistic {:.2} exceeds {:.1} (possible non-uniform bytes; n={})",
chi_sq,
chi_sq_upper,
n
);
}
#[cfg(feature = "alloc")]
fn test_autocorrelation(data: &[u8]) {
let n_pairs = data.len().saturating_sub(1);
if n_pairs < 2 {
return;
}
let n = n_pairs as f64;
let mut sum_x = 0.0;
let mut sum_y = 0.0;
let mut sum_xx = 0.0;
let mut sum_yy = 0.0;
let mut sum_xy = 0.0;
for i in 0..n_pairs {
let x = data[i] as f64;
let y = data[i + 1] as f64;
sum_x += x;
sum_y += y;
sum_xx += x * x;
sum_yy += y * y;
sum_xy += x * y;
}
let mean_x = sum_x / n;
let mean_y = sum_y / n;
let cov = sum_xy / n - mean_x * mean_y;
let var_x = sum_xx / n - mean_x * mean_x;
let var_y = sum_yy / n - mean_y * mean_y;
if var_x <= 1e-12 || var_y <= 1e-12 {
return;
}
let r = cov / (var_x.sqrt() * var_y.sqrt());
let threshold = 8.0 / n.sqrt();
assert!(
r.abs() < threshold,
"Lag-1 correlation |r|={} exceeds bound {:.4} (~8/√n, n={} pairs)",
r,
threshold,
n_pairs
);
}
#[cfg(feature = "alloc")]
fn test_runs_test(data: &[u8]) {
if data.is_empty() {
return;
}
let mut runs = 1;
let mut current_byte = data[0];
for &byte in data.iter().skip(1) {
if byte != current_byte {
runs += 1;
current_byte = byte;
}
}
let expected_runs = data.len() / 2; let tolerance = ((expected_runs as f64 * 0.5) as usize).max(50);
assert!(
runs >= expected_runs - tolerance,
"Too few runs: {} (expected ~{})",
runs,
expected_runs
);
}
#[test]
fn test_secure_failure_when_entropy_unavailable() {
#[cfg(feature = "alloc")]
{
let result = new_secure_rng();
assert!(
result.is_ok(),
"RNG creation should not panic even if entropy is limited"
);
}
#[cfg(not(feature = "alloc"))]
{
let result = new_secure_rng_no_std();
assert!(
result.is_ok(),
"RNG creation should not panic even if entropy is limited"
);
}
}
#[test]
fn test_rng_edge_cases() {
#[cfg(feature = "alloc")]
{
let mut rng = new_secure_rng().expect("Failed to create secure RNG");
let mut empty = [];
rng.fill_bytes(&mut empty);
let mut single = [0u8; 1];
rng.fill_bytes(&mut single);
let mut large = vec![0u8; 1024];
rng.fill_bytes(&mut large);
let non_zero_count = large.iter().filter(|&&b| b != 0).count();
assert!(
non_zero_count > 0,
"Large buffer should have non-zero bytes"
);
}
#[cfg(not(feature = "alloc"))]
{
let mut rng = new_secure_rng_no_std().expect("Failed to create secure RNG");
let mut empty = [];
rng.fill_bytes(&mut empty);
let mut single = [0u8; 1];
rng.fill_bytes(&mut single);
let mut large = [0u8; 1024];
rng.fill_bytes(&mut large);
let non_zero_count = large.iter().filter(|&&b| b != 0).count();
assert!(
non_zero_count > 0,
"Large buffer should have non-zero bytes"
);
}
}
#[test]
fn test_rng_security_across_reseeding() {
#[cfg(feature = "alloc")]
{
let mut rng = new_secure_rng().expect("Failed to create secure RNG");
let mut before_bytes = [0u8; 32];
rng.fill_bytes(&mut before_bytes);
for _ in 0..100 {
let mut temp = [0u8; 1024];
rng.fill_bytes(&mut temp);
}
let mut after_bytes = [0u8; 32];
rng.fill_bytes(&mut after_bytes);
assert_ne!(
before_bytes, [0u8; 32],
"Data before reseed should not be all zeros"
);
assert_ne!(
after_bytes, [0u8; 32],
"Data after reseed should not be all zeros"
);
assert_ne!(
before_bytes, after_bytes,
"Data before and after reseed should be different"
);
}
#[cfg(not(feature = "alloc"))]
{
let mut rng = new_secure_rng_no_std().expect("Failed to create secure RNG");
let mut before_bytes = [0u8; 32];
rng.fill_bytes(&mut before_bytes);
for _ in 0..100 {
let mut temp = [0u8; 1024];
rng.fill_bytes(&mut temp);
}
let mut after_bytes = [0u8; 32];
rng.fill_bytes(&mut after_bytes);
assert_ne!(
before_bytes, [0u8; 32],
"Data before reseed should not be all zeros"
);
assert_ne!(
after_bytes, [0u8; 32],
"Data after reseed should not be all zeros"
);
assert_ne!(
before_bytes, after_bytes,
"Data before and after reseed should be different"
);
}
}