From 2e2bb6b89e67fe802cda26a5e891c5ebffa86231 Mon Sep 17 00:00:00 2001 From: Ed Guloien Date: Wed, 22 Apr 2026 22:48:25 -0400 Subject: [PATCH] added better error handling for weak passwords --- src/main.rs | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0f6135c..a495463 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,7 +31,6 @@ struct PasswordConfig { struct PasswordPolicy(PasswordConfig); // todo: support multiple password policies -// todo: add support for Errors fn validate_password(password: &str, policy: &PasswordPolicy) -> bool { if policy.0.charset.contains(CharSet::UPPERCASE) { 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") } -// todo: protect from small len +// todo: does not guarantee that a password will contain the necessary chars // // How many bits of entropy? // max_chars_available -> 80 // log2(possible_symbols^len) yields bits of entropy // so say len is 20 and we use all 80 chars: // 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 { + if config.len < 8 { + return Err(String::from("Password length not long enough")); + } // String is a thin wrapper around Vec so we can expect re-allocation // and copy only when we exceed the capacity of the vector // However, with_capacity will perform only one allocation anyway @@ -106,20 +108,23 @@ fn generate_random_chars(config: PasswordConfig, rng: &mut OsRng) -> String { for _ in 0..config.len { 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: // capacity, mutation (push/pop), allocation strategy // // How many bits of entropy? -// log2(104078) -> 16.6 bits per word derived from word_list size +// log2(104078) -> 16.6 bits per word derived from word_list size // log2(18) -> 4.1 bits for separator assuming seporator is one of our SYMBOLs // total 70.5 bits of entropy fn generate_passphrase(phrase_count: usize, - word_list: &[&str], separator: char, rng: &mut OsRng) -> String { + word_list: &[&str], separator: char, rng: &mut OsRng) -> Result { + 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 for i in 0..phrase_count { res.push_str(random_word(word_list, rng)); @@ -127,7 +132,7 @@ fn generate_passphrase(phrase_count: usize, res.push(separator); } } - res + Ok(res) } fn random_word<'a>(word_list: &[&'a str], rng: &mut OsRng) -> &'a str { @@ -154,17 +159,24 @@ fn main() { let mut rng = OsRng; // todo: add actual tests - let chars = generate_random_chars(Default::default(), &mut rng); - println!("generated random chars (alphanumeric): {}", chars.as_str()); - assert!(validate_password(&chars, &Default::default())); + // password with alphnumeric + let password_res = generate_random_chars(Default::default(), &mut rng); + 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 { charset: CharSet::SYMBOLS, ..Default::default() }; - let chars = generate_random_chars(config, &mut rng); - println!("generated random chars (with symbols): {}", chars.as_str()); + let password_res2 = generate_random_chars(config, &mut rng); + println!("generated random chars (with symbols): {}", password_res2.unwrap().as_str()); - let phrase = generate_passphrase(3, &word_list, '-', &mut rng); - println!("generated passphrase: {}", phrase.as_str()); + let passphrase_res = generate_passphrase(3, &word_list, '-', &mut rng); + println!("generated passphrase: {}", passphrase_res.unwrap().as_str()); }