use rand::rngs::{OsRng, StdRng};
use rand::{RngCore, SeedableRng, TryRngCore};
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug)]
pub struct NonceError(String);
impl std::fmt::Display for NonceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for NonceError {}
pub struct NonceGenerator {
rng: StdRng,
}
impl NonceGenerator {
pub fn new() -> Result<Self, NonceError> {
let mut seed = [0u8; 32];
let mut rng = OsRng;
rng.try_fill_bytes(&mut seed)
.map_err(|e| NonceError(format!("OsRng unavailable: {e}")))?;
Ok(Self {
rng: StdRng::from_seed(seed),
})
}
pub fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
}
}
pub struct AtomicNonceGenerator {
counter: AtomicU64,
entropy_base: AtomicU64,
}
impl AtomicNonceGenerator {
pub const fn new() -> Self {
Self {
counter: AtomicU64::new(1),
entropy_base: AtomicU64::new(0),
}
}
pub fn init(&self) -> Result<(), NonceError> {
if self.entropy_base.load(Ordering::Relaxed) == 0 {
let mut buf = [0u8; 8];
let mut rng = OsRng;
rng.try_fill_bytes(&mut buf)
.map_err(|e| NonceError(format!("OsRng unavailable: {e}")))?;
let entropy = u64::from_le_bytes(buf);
let _ =
self.entropy_base
.compare_exchange(0, entropy, Ordering::SeqCst, Ordering::Relaxed);
}
Ok(())
}
#[inline]
pub fn next_u64(&self) -> Result<u64, NonceError> {
let base = self.entropy_base.load(Ordering::Relaxed);
let base = if base == 0 {
self.init()?;
self.entropy_base.load(Ordering::Relaxed)
} else {
base
};
let count = self.counter.fetch_add(1, Ordering::Relaxed);
Ok(base.wrapping_add(count))
}
}
impl Default for AtomicNonceGenerator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "nonce_tests.rs"]
mod tests;