use std::time::{SystemTime, UNIX_EPOCH};
use std::fmt::Write as FmtWrite;
#[cfg(target_family = "unix")]
use std::fs::File;
#[cfg(target_family = "unix")]
use std::io::Read;
#[cfg(target_os = "windows")]
mod windows_rng {
use std::ffi::c_void;
use std::os::raw::{c_uchar, c_ulong};
type NTSTATUS = std::os::raw::c_long;
const STATUS_SUCCESS: NTSTATUS = 0;
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: c_ulong = 0x00000002;
#[link(name = "bcrypt")]
extern "system" {
fn BCryptGenRandom(
hAlgorithm: *mut c_void,
pbBuffer: *mut c_uchar,
cbBuffer: c_ulong,
dwFlags: c_ulong,
) -> NTSTATUS;
}
pub fn get_random_bytes(buffer: &mut [u8]) -> Result<(), String> {
if buffer.len() > c_ulong::MAX as usize {
return Err(format!(
"Buffer length {} exceeds maximum {} for BCryptGenRandom",
buffer.len(),
c_ulong::MAX
));
}
let nt_status = unsafe {
BCryptGenRandom(
std::ptr::null_mut(),
buffer.as_mut_ptr(),
buffer.len() as c_ulong,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
if nt_status == STATUS_SUCCESS {
Ok(())
} else {
Err(format!(
"BCryptGenRandom failed with NTSTATUS: {:#010X}",
nt_status
))
}
}
}
fn try_get_os_random_u64_seed() -> Option<u64> {
let mut seed_bytes = [0u8; 8];
#[cfg(target_family = "unix")]
{
if let Ok(mut f) = File::open("/dev/urandom") {
if f.read_exact(&mut seed_bytes).is_ok() {
return Some(u64::from_le_bytes(seed_bytes));
} else {
eprintln!("Warning: Failed to read 8 bytes from /dev/urandom for seed.");
}
} else {
eprintln!("Warning: Failed to open /dev/urandom for seed.");
}
}
#[cfg(target_os = "windows")]
{
if self::windows_rng::get_random_bytes(&mut seed_bytes).is_ok() {
return Some(u64::from_le_bytes(seed_bytes));
} else {
eprintln!("Warning: BCryptGenRandom failed to provide 8 bytes for seed.");
}
}
#[cfg(not(any(target_family = "unix", target_os = "windows")))]
eprintln!("Warning: No OS-specific RNG available for seeding on this target platform.");
None }
struct XorShift64 {
state: u64,
}
impl XorShift64 {
fn new(seed: u64) -> Self {
let initial_state = if seed == 0 { 0xBAD5EED0BAD5EED0u64 } else { seed };
XorShift64 { state: initial_state }
}
fn next(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
#[allow(dead_code)]
fn next_bytes_8(&mut self) -> [u8; 8] { self.next().to_le_bytes()
}
#[allow(dead_code)] pub fn rand_range(&mut self, min: i64, max: i64) -> i64 {
if min > max {
panic!("min ({}) cannot be greater than max ({}) in XorShift64::rand_range", min, max);
}
if min == max { return min; }
if min == i64::MIN && max == i64::MAX { return self.next() as i64; }
let num_values_i128 = (max as i128) - (min as i128) + 1;
debug_assert!(num_values_i128 > 0 && num_values_i128 <= u64::MAX as i128);
let num_values = num_values_i128 as u64;
let rand_u64 = self.next();
let value_in_sub_range = rand_u64 % num_values;
min + (value_in_sub_range as i64)
}
}
static mut GLOBAL_RNG_STATE: u64 = 0;
pub struct Rand {
rng: XorShift64,
}
impl Rand {
pub fn init() {
let seed = match try_get_os_random_u64_seed() {
Some(s) => {
s
}
None => {
eprintln!("Warning: OS-provided seed failed or unavailable. Falling back to time-based seed for global RNG. This is NOT cryptographically secure.");
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0xBAD5EED0BAD5EED0u64, |d| d.as_nanos() as u64) }
};
let rand_instance = Rand::new(seed);
unsafe { GLOBAL_RNG_STATE = rand_instance.rng.state; }
}
pub fn new(seed: u64) -> Rand {
Rand { rng: XorShift64::new(seed) }
}
pub fn get(&self) -> u64 { self.rng.state }
#[allow(dead_code)]
pub fn build(state: u64) -> Rand { Rand { rng: XorShift64 { state } } }
pub fn rand(&mut self) -> u32 { self.rng.next() as u32 }
pub fn gen_i64(&mut self) -> i64 { self.rng.next() as i64 }
pub fn shuffle<T>(&mut self, a: &mut [T]) {
if a.is_empty() { return; }
let mut i = a.len() - 1;
while i > 0 {
let j = (self.rand() as usize) % (i + 1);
a.swap(i, j);
i -= 1;
}
}
pub fn rand_range(&mut self, a: i32, b: i32) -> i32 {
self.rng.rand_range(a as i64, b as i64) as i32
}
pub fn rand_range_i64(&mut self, min: i64, max: i64) -> i64 {
self.rng.rand_range(min, max)
}
pub fn rand_float(&mut self) -> f64 {
(self.rng.next() as f64) / (u64::MAX as f64 + 1.0) }
}
#[allow(dead_code)]
pub fn rand() -> u32 {
unsafe {
let mut rand_instance = Rand::build(GLOBAL_RNG_STATE);
let x = rand_instance.rand();
GLOBAL_RNG_STATE = rand_instance.get();
x
}
}
#[allow(dead_code)]
pub fn rand_i64() -> i64 {
unsafe {
let mut rand_instance = Rand::build(GLOBAL_RNG_STATE);
let x = rand_instance.gen_i64();
GLOBAL_RNG_STATE = rand_instance.get();
x
}
}
#[allow(dead_code)]
pub fn shuffle<T>(a: &mut [T]) {
unsafe {
let mut rand_instance = Rand::build(GLOBAL_RNG_STATE);
rand_instance.shuffle(a);
GLOBAL_RNG_STATE = rand_instance.get();
}
}
#[allow(dead_code)]
pub fn rand_range(a: i32, b: i32) -> i32 {
unsafe {
let mut rand_instance = Rand::build(GLOBAL_RNG_STATE);
let result = rand_instance.rand_range(a, b);
GLOBAL_RNG_STATE = rand_instance.get();
result
}
}
#[allow(dead_code)]
pub fn rand_range_i64(min: i64, max: i64) -> i64 {
unsafe {
let mut rand_instance = Rand::build(GLOBAL_RNG_STATE);
let result = rand_instance.rand_range_i64(min, max);
GLOBAL_RNG_STATE = rand_instance.get();
result
}
}
#[allow(dead_code)]
pub fn rand_float() -> f64 {
unsafe {
let mut rand_instance = Rand::build(GLOBAL_RNG_STATE);
let result = rand_instance.rand_float();
GLOBAL_RNG_STATE = rand_instance.get();
result
}
}
pub fn fill_bytes(buffer: &mut [u8]) {
if buffer.is_empty() {
return;
}
let mut random_bytes_sourced_securely = false;
#[cfg(target_family = "unix")]
{
if !buffer.is_empty() { if let Ok(mut f) = File::open("/dev/urandom") {
if f.read_exact(buffer).is_ok() {
random_bytes_sourced_securely = true;
} else {
eprintln!(
"fill_bytes Warning: Failed to read {} bytes from /dev/urandom. Will use PRNG fallback.",
buffer.len()
);
}
} else {
eprintln!("fill_bytes Warning: Failed to open /dev/urandom. Will use PRNG fallback.");
}
}
}
#[cfg(target_os = "windows")]
{
if !random_bytes_sourced_securely && !buffer.is_empty() {
match self::windows_rng::get_random_bytes(buffer) {
Ok(()) => {
random_bytes_sourced_securely = true;
}
Err(e) => {
eprintln!(
"fill_bytes Warning: Windows BCryptGenRandom failed for {} bytes (Error: {}). Will use PRNG fallback.",
buffer.len(), e
);
}
}
}
}
if !random_bytes_sourced_securely {
eprintln!(
"fill_bytes Warning: OS-specific RNG failed or unavailable for {} bytes. \
Falling back to XorShift64 PRNG. This fallback is NOT cryptographically secure if itself seeded by time.",
buffer.len()
);
let seed = match try_get_os_random_u64_seed() {
Some(s) => {
s
}
None => {
eprintln!("fill_bytes Warning: OS-provided seed failed for fallback PRNG. Falling back to time-based seed. This is NOT cryptographically secure.");
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0xBAD5EED0BAD5EED0u64, |d| d.as_nanos() as u64)
}
};
let mut fallback_rng = XorShift64::new(seed);
let mut i = 0;
while i < buffer.len() {
let rand_u64 = fallback_rng.next();
let rand_bytes_arr = rand_u64.to_le_bytes(); let remaining_in_buffer = buffer.len() - i;
let copy_len = std::cmp::min(rand_bytes_arr.len(), remaining_in_buffer);
buffer[i..i + copy_len].copy_from_slice(&rand_bytes_arr[0..copy_len]);
i += copy_len;
}
}
}
pub fn generate_v4_uuid() -> Result<String, std::io::Error> {
let mut bytes = [0u8; 16];
fill_bytes(&mut bytes);
bytes[6] = (bytes[6] & 0x0F) | 0x40; bytes[8] = (bytes[8] & 0x3F) | 0x80;
let mut uuid_str = String::with_capacity(36);
for (i, byte_val) in bytes.iter().enumerate() {
if i == 4 || i == 6 || i == 8 || i == 10 {
if FmtWrite::write_char(&mut uuid_str, '-').is_err() { return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to write hyphen"));
}
}
if write!(uuid_str, "{:02x}", byte_val).is_err() {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to write byte"));
}
}
Ok(uuid_str)
}
pub fn uuid() -> Result<String, std::io::Error> {
generate_v4_uuid()
}