Skip to content

Commit a27723e

Browse files
author
Randolf J
committed
Use enum for score
1 parent 3b81dce commit a27723e

File tree

4 files changed

+81
-24
lines changed

4 files changed

+81
-24
lines changed

src/feedback.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Contains structs and methods related to generating feedback strings
22
//! for providing help for the user to generate stronger passwords.
33
4-
use crate::frequency_lists::DictionaryType;
54
use crate::matching::patterns::*;
65
use crate::matching::Match;
6+
use crate::{frequency_lists::DictionaryType, scoring::Score};
77
use std::fmt;
88

99
/// A warning explains what's wrong with the password.
@@ -153,7 +153,7 @@ impl Feedback {
153153
}
154154
}
155155

156-
pub(crate) fn get_feedback(score: u8, sequence: &[Match]) -> Option<Feedback> {
156+
pub(crate) fn get_feedback(score: Score, sequence: &[Match]) -> Option<Feedback> {
157157
if sequence.is_empty() {
158158
// default feedback
159159
return Some(Feedback {
@@ -164,7 +164,7 @@ pub(crate) fn get_feedback(score: u8, sequence: &[Match]) -> Option<Feedback> {
164164
],
165165
});
166166
}
167-
if score >= 3 {
167+
if score >= Score::Three {
168168
return None;
169169
}
170170

src/lib.rs

+14-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::time::Duration;
2121
#[macro_use]
2222
extern crate quickcheck;
2323

24+
use scoring::Score;
2425
use time_estimates::CrackTimes;
2526
#[cfg(target_arch = "wasm32")]
2627
use wasm_bindgen::prelude::wasm_bindgen;
@@ -77,7 +78,7 @@ pub struct Entropy {
7778
crack_times: time_estimates::CrackTimes,
7879
/// Overall strength score from 0-4.
7980
/// Any score less than 3 should be considered too weak.
80-
score: u8,
81+
score: Score,
8182
/// Verbal feedback to help choose better passwords. Set when `score` <= 2.
8283
feedback: Option<feedback::Feedback>,
8384
/// The list of patterns the guess calculation was based on
@@ -104,7 +105,7 @@ impl Entropy {
104105

105106
/// Overall strength score from 0-4.
106107
/// Any score less than 3 should be considered too weak.
107-
pub fn score(&self) -> u8 {
108+
pub fn score(&self) -> Score {
108109
self.score
109110
}
110111

@@ -133,8 +134,8 @@ pub fn zxcvbn(password: &str, user_inputs: &[&str]) -> Entropy {
133134
guesses: 0,
134135
guesses_log10: f64::NEG_INFINITY,
135136
crack_times: CrackTimes::new(0),
136-
score: 0,
137-
feedback: feedback::get_feedback(0, &[]),
137+
score: Score::Zero,
138+
feedback: feedback::get_feedback(Score::Zero, &[]),
138139
sequence: Vec::default(),
139140
calc_time: Duration::from_secs(0),
140141
};
@@ -198,7 +199,7 @@ mod tests {
198199
let password = "r0sebudmaelstrom11/20/91aaaa";
199200
let entropy = zxcvbn(password, &[]);
200201
assert_eq!(entropy.guesses_log10 as u16, 14);
201-
assert_eq!(entropy.score, 4);
202+
assert_eq!(entropy.score, Score::Four);
202203
assert!(!entropy.sequence.is_empty());
203204
assert!(entropy.feedback.is_none());
204205
assert!(entropy.calc_time.as_nanos() > 0);
@@ -209,7 +210,7 @@ mod tests {
209210
fn test_zxcvbn_empty() {
210211
let password = "";
211212
let entropy = zxcvbn(password, &[]);
212-
assert_eq!(entropy.score, 0);
213+
assert_eq!(entropy.score, Score::Zero);
213214
assert_eq!(entropy.guesses, 0);
214215
assert_eq!(entropy.guesses_log10, f64::NEG_INFINITY);
215216
assert_eq!(entropy.crack_times, CrackTimes::new(0));
@@ -221,23 +222,23 @@ mod tests {
221222
fn test_zxcvbn_unicode() {
222223
let password = "𐰊𐰂𐰄𐰀𐰁";
223224
let entropy = zxcvbn(password, &[]);
224-
assert_eq!(entropy.score, 1);
225+
assert_eq!(entropy.score, Score::One);
225226
}
226227

227228
#[cfg_attr(not(target_arch = "wasm32"), test)]
228229
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
229230
fn test_zxcvbn_unicode_2() {
230231
let password = "r0sebudmaelstrom丂/20/91aaaa";
231232
let entropy = zxcvbn(password, &[]);
232-
assert_eq!(entropy.score, 4);
233+
assert_eq!(entropy.score, Score::Four);
233234
}
234235

235236
#[cfg_attr(not(target_arch = "wasm32"), test)]
236237
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
237238
fn test_issue_13() {
238239
let password = "Imaginative-Say-Shoulder-Dish-0";
239240
let entropy = zxcvbn(password, &[]);
240-
assert_eq!(entropy.score, 4);
241+
assert_eq!(entropy.score, Score::Four);
241242
}
242243

243244
#[cfg_attr(not(target_arch = "wasm32"), test)]
@@ -247,7 +248,7 @@ mod tests {
247248
let entropy = zxcvbn(password, &[]);
248249
assert_eq!(entropy.guesses, 372_010_000);
249250
assert!((entropy.guesses_log10 - 8.57055461430783).abs() < f64::EPSILON);
250-
assert_eq!(entropy.score, 3);
251+
assert_eq!(entropy.score, Score::Three);
251252
}
252253

253254
#[cfg_attr(not(target_arch = "wasm32"), test)]
@@ -257,7 +258,7 @@ mod tests {
257258
let entropy = zxcvbn(password, &[]);
258259
assert_eq!(entropy.guesses, 1_010_000);
259260
assert!((entropy.guesses_log10 - 6.004321373782642).abs() < f64::EPSILON);
260-
assert_eq!(entropy.score, 2);
261+
assert_eq!(entropy.score, Score::Two);
261262
}
262263

263264
#[cfg_attr(not(target_arch = "wasm32"), test)]
@@ -266,7 +267,7 @@ mod tests {
266267
let password = "!QASW@#EDFR$%TGHY^&UJKI*(OL";
267268
let entropy = zxcvbn(password, &[]);
268269
assert_eq!(entropy.guesses, u64::max_value());
269-
assert_eq!(entropy.score, 4);
270+
assert_eq!(entropy.score, Score::Four);
270271
}
271272

272273
#[cfg_attr(not(target_arch = "wasm32"), test)]
@@ -275,6 +276,6 @@ mod tests {
275276
let password = "08märz2010";
276277
let entropy = zxcvbn(password, &[]);
277278
assert_eq!(entropy.guesses, 100010000);
278-
assert_eq!(entropy.score, 3);
279+
assert_eq!(entropy.score, Score::Three);
279280
}
280281
}

src/scoring.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,61 @@
11
use crate::matching::patterns::*;
22
use crate::matching::Match;
3-
use std::cmp;
43
use std::collections::HashMap;
4+
use std::{cmp, fmt::Display};
5+
6+
#[derive(Debug, Clone, Copy, Hash)]
7+
#[non_exhaustive]
8+
pub enum Score {
9+
/// Can be cracked with 10^3 guesses or less.
10+
Zero = 0,
11+
/// Can be cracked with 10^6 guesses or less.
12+
One,
13+
/// Can be cracked with 10^8 guesses or less.
14+
Two,
15+
/// Can be cracked with 10^10 guesses or less.
16+
Three,
17+
/// Requires more than 10^10 guesses to crack.
18+
Four,
19+
}
20+
21+
impl From<Score> for i8 {
22+
fn from(score: Score) -> i8 {
23+
score as i8
24+
}
25+
}
26+
27+
impl<T: Into<i8> + Copy> PartialEq<T> for Score {
28+
fn eq(&self, other: &T) -> bool {
29+
i8::from(*self) == (*other).into()
30+
}
31+
}
32+
33+
impl Eq for Score {}
34+
35+
impl<T: Into<i8> + Copy> PartialOrd<T> for Score {
36+
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
37+
i8::from(*self).partial_cmp(&(*other).into())
38+
}
39+
}
40+
41+
impl Ord for Score {
42+
fn cmp(&self, other: &Self) -> cmp::Ordering {
43+
i8::from(*self).cmp(&i8::from(*other))
44+
}
45+
}
46+
47+
impl Display for Score {
48+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49+
write!(f, "{}", i8::from(*self))
50+
}
51+
}
52+
53+
#[cfg(feature = "ser")]
54+
impl serde::Serialize for Score {
55+
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
56+
i8::from(self).serialize(serializer)
57+
}
58+
}
559

660
#[derive(Debug, Clone)]
761
pub struct GuessCalculation {

src/time_estimates.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
2323
use std::fmt;
2424

25+
use crate::scoring::Score;
26+
2527
/// Back-of-the-envelope crack time estimations, in seconds, based on a few scenarios.
2628
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
2729
#[cfg_attr(feature = "ser", derive(Serialize))]
@@ -129,21 +131,21 @@ impl From<CrackTimeSeconds> for std::time::Duration {
129131
}
130132
}
131133

132-
pub(crate) fn estimate_attack_times(guesses: u64) -> (CrackTimes, u8) {
134+
pub(crate) fn estimate_attack_times(guesses: u64) -> (CrackTimes, Score) {
133135
(CrackTimes::new(guesses), calculate_score(guesses))
134136
}
135137

136-
fn calculate_score(guesses: u64) -> u8 {
138+
fn calculate_score(guesses: u64) -> Score {
137139
const DELTA: u64 = 5;
138140
if guesses < 1_000 + DELTA {
139-
0
141+
Score::Zero
140142
} else if guesses < 1_000_000 + DELTA {
141-
1
143+
Score::One
142144
} else if guesses < 100_000_000 + DELTA {
143-
2
145+
Score::Two
144146
} else if guesses < 10_000_000_000 + DELTA {
145-
3
147+
Score::Three
146148
} else {
147-
4
149+
Score::Four
148150
}
149151
}

0 commit comments

Comments
 (0)