abstracted Rng

This commit is contained in:
2026-04-25 08:19:29 -04:00
parent 3734306830
commit dcc9100d1b
+38 -14
View File
@@ -1,4 +1,5 @@
use rand::Rng; use rand::Rng;
use rand::RngCore;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::SeedableRng; use rand::SeedableRng;
use rand::rngs::StdRng; 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 // NOT IDEAL FOR CRYPTO/password generation
// let range_max = 122; // 125 - 33 // let range_max = 122; // 125 - 33
// let n = rng.next_u32() % range_max; // slightly modulo biased // 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 // todo: validate this fact
// this function under the hood creates a zone that divides evenly // this function under the hood creates a zone that divides evenly
// into the range so we don't encounter modulo bias // 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 // returns error if generated password doesn't contain the necessary chars
// to satisfy the policy // to satisfy the policy
// let the caller decide what to do // 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 { if config.len < 8 {
return Err(String::from("Password length not long enough")); 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: // we can take a slice of "words" because we don't care about:
// capacity, mutation (push/pop), allocation strategy // capacity, mutation (push/pop), allocation strategy
fn generate_passphrase(config: &PassphraseConfig, 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 { if config.phrases < 2 {
return Err("Phrase count too low".to_string()); return Err("Phrase count too low".to_string());
} }
@@ -237,8 +238,8 @@ fn generate_passphrase(config: &PassphraseConfig,
Ok(res) Ok(res)
} }
fn random_word<'a>(word_list: &[&'a str], rng: &mut impl Rng) -> &'a str { fn random_word<'a>(word_list: &[&'a str], rng: &mut MyRng) -> &'a str {
word_list[rng.gen_range(0..word_list.len())] word_list[rng.as_rng().gen_range(0..word_list.len())]
} }
fn get_word_list() -> &'static [&'static str] { fn get_word_list() -> &'static [&'static str] {
@@ -255,20 +256,43 @@ fn get_word_list() -> &'static [&'static str] {
Wordlist::get_list(&Wordlist::EffLong) Wordlist::get_list(&Wordlist::EffLong)
} }
fn main() { enum Determinism {
let word_list = get_word_list(); Predictable,
#[allow(dead_code)]
Random,
}
// we definitely want to use the OS for randomness enum MyRng { Std(StdRng), Os(OsRng) }
// 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;
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 // when we are looking for Reproducability we can use the same seed value
// to create our Rng. Because both StdRng and OsRng implement Rng // to create our Rng. Because both StdRng and OsRng implement Rng
// we make our methods slightly more generic and this works just fine // we make our methods slightly more generic and this works just fine
// comment out when we need random version! // comment out when we need random version!
let mut rng = StdRng::seed_from_u64(42); 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();
let mut rng = get_rng(&Determinism::Predictable);
// todo: add actual tests // todo: add actual tests
// password with alphnumeric // password with alphnumeric