bitwarden_core/auth/password/
strength.rs

1use zxcvbn::zxcvbn;
2
3const GLOBAL_INPUTS: [&str; 3] = ["bitwarden", "bit", "warden"];
4
5pub(crate) fn password_strength(
6    password: String,
7    email: String,
8    additional_inputs: Vec<String>,
9) -> u8 {
10    let mut inputs = email_to_user_inputs(&email);
11    inputs.extend(additional_inputs);
12
13    let mut arr: Vec<_> = inputs.iter().map(String::as_str).collect();
14    arr.extend(GLOBAL_INPUTS);
15
16    zxcvbn(&password, &arr).score().into()
17}
18
19fn email_to_user_inputs(email: &str) -> Vec<String> {
20    let parts = email.split_once('@');
21    match parts {
22        Some((prefix, _)) => prefix
23            .trim()
24            .to_lowercase()
25            .split(|c: char| !c.is_alphanumeric())
26            .map(str::to_owned)
27            .collect(),
28        None => vec![],
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::{email_to_user_inputs, password_strength};
35
36    #[test]
37    fn test_password_strength() {
38        let cases = vec![
39            ("password", "[email protected]", 0),
40            ("password11", "[email protected]", 1),
41            ("Weakpass2", "[email protected]", 2),
42            ("GoodPass3!", "[email protected]", 3),
43            ("VeryStrong123@#", "[email protected]", 4),
44        ];
45
46        for (password, email, expected) in cases {
47            let result = password_strength(password.to_owned(), email.to_owned(), vec![]);
48            assert_eq!(expected, result, "{email}: {password}");
49        }
50    }
51
52    #[test]
53    fn test_penalize_email() {
54        let password = "asdfjkhkjwer!";
55
56        let result = password_strength(
57            password.to_owned(),
58            "[email protected]".to_owned(),
59            vec![],
60        );
61        assert_eq!(result, 4);
62
63        let result = password_strength(
64            password.to_owned(),
65            "[email protected]".to_owned(),
66            vec![],
67        );
68        assert_eq!(result, 1);
69    }
70
71    #[test]
72    fn test_email_to_user_inputs() {
73        let email = "[email protected]";
74        let result = email_to_user_inputs(email);
75
76        assert_eq!(result, vec!["random"]);
77    }
78}