abstracted Rng

This commit is contained in:
2026-04-25 08:19:29 -04:00
parent 3734306830
commit dcc9100d1b
+41 -17
View File
@@ -1,4 +1,5 @@
use rand::Rng;
use rand::RngCore;
use rand::rngs::OsRng;
use rand::SeedableRng;
use rand::rngs::StdRng;
@@ -147,7 +148,7 @@ impl Default for PasswordConfig {
}
}
fn random_ascii(rng: &mut impl Rng, char_set: &[u8]) -> u8 {
fn random_ascii(rng: &mut MyRng, char_set: &[u8]) -> u8 {
// NOT IDEAL FOR CRYPTO/password generation
// let range_max = 122; // 125 - 33
// let n = rng.next_u32() % range_max; // slightly modulo biased
@@ -156,13 +157,13 @@ fn random_ascii(rng: &mut impl Rng, char_set: &[u8]) -> u8 {
// todo: validate this fact
// this function under the hood creates a zone that divides evenly
// into the range so we don't encounter modulo bias
*char_set.choose(rng).expect("char_set should not be empty")
*char_set.choose(rng.as_rng()).expect("char_set should not be empty")
}
// returns error if generated password doesn't contain the necessary chars
// to satisfy the policy
// let the caller decide what to do
fn generate_password(config: &PasswordConfig, rng: &mut impl Rng) -> Result<String,String> {
fn generate_password(config: &PasswordConfig, rng: &mut MyRng) -> Result<String,String> {
if config.len < 8 {
return Err(String::from("Password length not long enough"));
}
@@ -209,7 +210,7 @@ fn generate_password(config: &PasswordConfig, rng: &mut impl Rng) -> Result<Stri
// we can take a slice of "words" because we don't care about:
// capacity, mutation (push/pop), allocation strategy
fn generate_passphrase(config: &PassphraseConfig,
word_list: &[&str], rng: &mut impl Rng) -> Result<String,String> {
word_list: &[&str], rng: &mut MyRng) -> Result<String,String> {
if config.phrases < 2 {
return Err("Phrase count too low".to_string());
}
@@ -237,8 +238,8 @@ fn generate_passphrase(config: &PassphraseConfig,
Ok(res)
}
fn random_word<'a>(word_list: &[&'a str], rng: &mut impl Rng) -> &'a str {
word_list[rng.gen_range(0..word_list.len())]
fn random_word<'a>(word_list: &[&'a str], rng: &mut MyRng) -> &'a str {
word_list[rng.as_rng().gen_range(0..word_list.len())]
}
fn get_word_list() -> &'static [&'static str] {
@@ -255,20 +256,43 @@ fn get_word_list() -> &'static [&'static str] {
Wordlist::get_list(&Wordlist::EffLong)
}
enum Determinism {
Predictable,
#[allow(dead_code)]
Random,
}
enum MyRng { Std(StdRng), Os(OsRng) }
impl MyRng {
// todo: understand RngCore
fn as_rng(&mut self) -> &mut dyn RngCore {
match self {
MyRng::Std(r) => r,
MyRng::Os(r) => r,
}
}
}
fn get_rng(d: &Determinism) -> MyRng {
match d {
// when we are looking for Reproducability we can use the same seed value
// to create our Rng. Because both StdRng and OsRng implement Rng
// we make our methods slightly more generic and this works just fine
// comment out when we need random version!
Determinism::Predictable => MyRng::Std(StdRng::seed_from_u64(42)),
// we definitely want to use the OS for randomness
// doesn't require seeding because it is backed by CSPRNG, backed by OS
// the OS maintains it's own entropy from mouse movement, hardware noise,etc
// we pass it through the functions to avoid a potential syscall
Determinism::Random => MyRng::Os(OsRng),
}
}
fn main() {
let word_list = get_word_list();
// we definitely want to use the OS for randomness
// doesn't require seeding because it is backed by CSPRNG, backed by OS
// the OS maintains it's own entropy from mouse movement, hardware noise,etc
// we pass it through the functions to avoid a potential syscall
let mut _rng = OsRng;
// when we are looking for Reproducability we can use the same seed value
// to create our Rng. Because both StdRng and OsRng implement Rng
// we make our methods slightly more generic and this works just fine
// comment out when we need random version!
let mut rng = StdRng::seed_from_u64(42);
let mut rng = get_rng(&Determinism::Predictable);
// todo: add actual tests
// password with alphnumeric