deadbolt_parser/
password.rs1use deadbolt_crypto::rand::Random;
2use regex::Regex;
3use secrecy::Secret;
4
5const MIN_PASSWORD_LEN: usize = 18;
6
7fn is_no_repetition(password: &str) -> bool {
8 let n = 2;
10 let mut repetitions = password
11 .chars()
12 .map(Some)
13 .chain(std::iter::once(None))
14 .scan((0, None), |(count, ch), v| match ch {
15 Some(c) if *c == v => {
16 *count += 1;
17 Some((None, *count))
18 }
19 _ => Some((ch.replace(v), std::mem::replace(count, 1))),
20 })
21 .filter_map(|(ch, count)| match ch {
22 Some(Some(ch)) if count >= n => Some((ch, count)),
23 _ => None,
24 })
25 .peekable();
26
27 repetitions.peek().is_none()
28}
29
30fn is_good_distribution(password: &str, punctuation: Option<bool>) -> bool {
31 let uppercase_regex = Regex::new(r"[A-Z]").unwrap();
32 let lowercase_regex = Regex::new(r"[a-z]").unwrap();
33 let digit_regex = Regex::new(r"\d").unwrap();
34 let punctuation_regex = Regex::new(r"[~!@#\$%\^&\*()-_=\+\[\]\{\};':,\./<>?]").unwrap();
35
36 if uppercase_regex.is_match(password)
37 && lowercase_regex.is_match(password)
38 && digit_regex.is_match(password)
39 {
40 if let Some(punctuation) = punctuation {
41 if punctuation {
42 return punctuation_regex.is_match(password);
43 }
44 }
45 return true;
46 }
47 false
48}
49
50fn is_strong_password(password: &str, punctuation: Option<bool>) -> bool {
51 if !is_no_repetition(password) {
52 return false;
53 }
54 if !is_good_distribution(password, punctuation) {
55 return false;
56 }
57 true
58}
59
60pub fn get_strong_random_password(
61 length: Option<usize>,
62 punctuation: Option<bool>,
63) -> Secret<String> {
64 let mut password = Random::get_rand_chars(
65 length.unwrap_or(MIN_PASSWORD_LEN),
66 punctuation.unwrap_or(true),
67 );
68 loop {
69 if is_strong_password(&password, punctuation) {
70 break;
71 }
72 password = Random::get_rand_chars(
73 length.unwrap_or(MIN_PASSWORD_LEN),
74 punctuation.unwrap_or(true),
75 );
76 }
77
78 Secret::new(password)
79}