abstracted Rng
This commit is contained in:
+38
-14
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user