diff --git a/src/main.rs b/src/main.rs index 1b6bc6e..fbad598 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 { +fn generate_password(config: &PasswordConfig, rng: &mut MyRng) -> Result { 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 Result { + word_list: &[&str], rng: &mut MyRng) -> Result { 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