added better error handling for weak passwords
This commit is contained in:
+27
-15
@@ -31,7 +31,6 @@ struct PasswordConfig {
|
|||||||
struct PasswordPolicy(PasswordConfig);
|
struct PasswordPolicy(PasswordConfig);
|
||||||
|
|
||||||
// todo: support multiple password policies
|
// todo: support multiple password policies
|
||||||
// todo: add support for Errors
|
|
||||||
fn validate_password(password: &str, policy: &PasswordPolicy) -> bool {
|
fn validate_password(password: &str, policy: &PasswordPolicy) -> bool {
|
||||||
if policy.0.charset.contains(CharSet::UPPERCASE) {
|
if policy.0.charset.contains(CharSet::UPPERCASE) {
|
||||||
let mut found_upper = false;
|
let mut found_upper = false;
|
||||||
@@ -72,14 +71,17 @@ fn random_ascii(rng: &mut OsRng, char_set: &[u8]) -> u8 {
|
|||||||
*char_set.choose(rng).expect("char_set should not be empty")
|
*char_set.choose(rng).expect("char_set should not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: protect from small len
|
// todo: does not guarantee that a password will contain the necessary chars
|
||||||
//
|
//
|
||||||
// How many bits of entropy?
|
// How many bits of entropy?
|
||||||
// max_chars_available -> 80
|
// max_chars_available -> 80
|
||||||
// log2(possible_symbols^len) yields bits of entropy
|
// log2(possible_symbols^len) yields bits of entropy
|
||||||
// so say len is 20 and we use all 80 chars:
|
// so say len is 20 and we use all 80 chars:
|
||||||
// that's log2(80^20) -> 126.4 bits of entropy per password
|
// that's log2(80^20) -> 126.4 bits of entropy per password
|
||||||
fn generate_random_chars(config: PasswordConfig, rng: &mut OsRng) -> String {
|
fn generate_random_chars(config: PasswordConfig, rng: &mut OsRng) -> Result<String,String> {
|
||||||
|
if config.len < 8 {
|
||||||
|
return Err(String::from("Password length not long enough"));
|
||||||
|
}
|
||||||
// String is a thin wrapper around Vec<u8> so we can expect re-allocation
|
// String is a thin wrapper around Vec<u8> so we can expect re-allocation
|
||||||
// and copy only when we exceed the capacity of the vector
|
// and copy only when we exceed the capacity of the vector
|
||||||
// However, with_capacity will perform only one allocation anyway
|
// However, with_capacity will perform only one allocation anyway
|
||||||
@@ -106,11 +108,11 @@ fn generate_random_chars(config: PasswordConfig, rng: &mut OsRng) -> String {
|
|||||||
for _ in 0..config.len {
|
for _ in 0..config.len {
|
||||||
res.push(random_ascii(rng, &chars) as char);
|
res.push(random_ascii(rng, &chars) as char);
|
||||||
}
|
}
|
||||||
res
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: protect from small phrase_count
|
// todo: we should add symbols, numbers, and Capitalization
|
||||||
|
//
|
||||||
// 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
|
||||||
//
|
//
|
||||||
@@ -119,7 +121,10 @@ fn generate_random_chars(config: PasswordConfig, rng: &mut OsRng) -> String {
|
|||||||
// log2(18) -> 4.1 bits for separator assuming seporator is one of our SYMBOLs
|
// log2(18) -> 4.1 bits for separator assuming seporator is one of our SYMBOLs
|
||||||
// total 70.5 bits of entropy
|
// total 70.5 bits of entropy
|
||||||
fn generate_passphrase(phrase_count: usize,
|
fn generate_passphrase(phrase_count: usize,
|
||||||
word_list: &[&str], separator: char, rng: &mut OsRng) -> String {
|
word_list: &[&str], separator: char, rng: &mut OsRng) -> Result<String,String> {
|
||||||
|
if phrase_count < 2 {
|
||||||
|
return Err("Phrase count too low".to_string());
|
||||||
|
}
|
||||||
let mut res = String::new(); // don't bother saving allocations with a bad guess
|
let mut res = String::new(); // don't bother saving allocations with a bad guess
|
||||||
for i in 0..phrase_count {
|
for i in 0..phrase_count {
|
||||||
res.push_str(random_word(word_list, rng));
|
res.push_str(random_word(word_list, rng));
|
||||||
@@ -127,7 +132,7 @@ fn generate_passphrase(phrase_count: usize,
|
|||||||
res.push(separator);
|
res.push(separator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_word<'a>(word_list: &[&'a str], rng: &mut OsRng) -> &'a str {
|
fn random_word<'a>(word_list: &[&'a str], rng: &mut OsRng) -> &'a str {
|
||||||
@@ -154,17 +159,24 @@ fn main() {
|
|||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
// todo: add actual tests
|
// todo: add actual tests
|
||||||
let chars = generate_random_chars(Default::default(), &mut rng);
|
// password with alphnumeric
|
||||||
println!("generated random chars (alphanumeric): {}", chars.as_str());
|
let password_res = generate_random_chars(Default::default(), &mut rng);
|
||||||
assert!(validate_password(&chars, &Default::default()));
|
if let Err(e) = password_res {
|
||||||
|
println!("Error during first password gen: {}", e);
|
||||||
|
} else {
|
||||||
|
let password = password_res.unwrap();
|
||||||
|
println!("generated random chars (alphanumeric): {}", password.as_str());
|
||||||
|
assert!(validate_password(&password, &Default::default()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// password with alphanumeric + symbols
|
||||||
let config = PasswordConfig {
|
let config = PasswordConfig {
|
||||||
charset: CharSet::SYMBOLS,
|
charset: CharSet::SYMBOLS,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let chars = generate_random_chars(config, &mut rng);
|
let password_res2 = generate_random_chars(config, &mut rng);
|
||||||
println!("generated random chars (with symbols): {}", chars.as_str());
|
println!("generated random chars (with symbols): {}", password_res2.unwrap().as_str());
|
||||||
|
|
||||||
let phrase = generate_passphrase(3, &word_list, '-', &mut rng);
|
let passphrase_res = generate_passphrase(3, &word_list, '-', &mut rng);
|
||||||
println!("generated passphrase: {}", phrase.as_str());
|
println!("generated passphrase: {}", passphrase_res.unwrap().as_str());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user