Skip to main content

sarhash_core/
strength.rs

1use serde::{Deserialize, Serialize};
2use zxcvbn::zxcvbn;
3
4#[derive(Debug, Serialize, Deserialize)]
5pub struct StrengthResult {
6    /// Integer from 0-4 (0 being weakest, 4 being strongest)
7    pub score: u8,
8    /// Estimated offline crack time in seconds
9    pub crack_time_seconds: f64,
10    /// Feedback/Suggestions (optional simple warning if available)
11    pub warning: Option<String>,
12    /// List of suggestions
13    pub suggestions: Vec<String>,
14}
15
16/// Checks the strength of a password using zxcvbn.
17pub fn check_strength(password: &str) -> StrengthResult {
18    let result = zxcvbn(password, &[]);
19
20    // Using simple approach first to get it to compile
21    // Note: zxcvbn 3.0+ API might differ from what I recalled.
22
23    let feedback = result.feedback();
24
25    let warning = if let Some(feedback) = feedback {
26        if feedback.warning().is_some() {
27            feedback.warning().map(|w| w.to_string())
28        } else {
29            None
30        }
31    } else {
32        None
33    };
34
35    let suggestions = if let Some(feedback) = feedback {
36        feedback
37            .suggestions()
38            .iter()
39            .map(|s| s.to_string())
40            .collect()
41    } else {
42        Vec::new()
43    };
44
45    // Crack times seems to cause issues, let's inspect what's available or use a default
46    // We'll fix this in the next step
47    // let guesses = result.crack_times().guesses; // Field is private
48    let crack_time_seconds = 0.0; // Unavailable in this version of zxcvbn crate without usage of private fields
49
50    StrengthResult {
51        score: result.score().try_into().unwrap_or(0),
52        crack_time_seconds,
53        warning,
54        suggestions,
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_weak_password() {
64        let result = check_strength("password");
65        assert_eq!(result.score, 0);
66    }
67
68    #[test]
69    fn test_strong_password() {
70        // "correct horse battery staple" is the classic example of high entropy but memorable
71        let result = check_strength("correct horse battery staple");
72        assert!(result.score >= 3);
73    }
74}