use std::fmt;
use crate::scoring::Score;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "ser", derive(serde::Deserialize, serde::Serialize))]
pub struct CrackTimes {
guesses: u64,
}
impl CrackTimes {
pub fn new(guesses: u64) -> Self {
CrackTimes { guesses }
}
pub fn guesses(self) -> u64 {
self.guesses
}
pub fn online_throttling_100_per_hour(self) -> CrackTimeSeconds {
CrackTimeSeconds::Integer(self.guesses.saturating_mul(36))
}
pub fn online_no_throttling_10_per_second(self) -> CrackTimeSeconds {
CrackTimeSeconds::Float(self.guesses as f64 / 10.00)
}
pub fn offline_slow_hashing_1e4_per_second(self) -> CrackTimeSeconds {
CrackTimeSeconds::Float(self.guesses as f64 / 10_000.00)
}
pub fn offline_fast_hashing_1e10_per_second(self) -> CrackTimeSeconds {
CrackTimeSeconds::Float(self.guesses as f64 / 10_000_000_000.00)
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "ser", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "ser", serde(untagged))]
pub enum CrackTimeSeconds {
Integer(u64),
Float(f64),
}
impl fmt::Display for CrackTimeSeconds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let seconds = match self {
CrackTimeSeconds::Integer(i) => *i,
CrackTimeSeconds::Float(f) => *f as u64,
};
const MINUTE: u64 = 60;
const HOUR: u64 = MINUTE * 60;
const DAY: u64 = HOUR * 24;
const MONTH: u64 = DAY * 31;
const YEAR: u64 = MONTH * 12;
const CENTURY: u64 = YEAR * 100;
if seconds < 1 {
write!(f, "less than a second")
} else if seconds < MINUTE {
let base = seconds;
write!(f, "{} second{}", base, if base > 1 { "s" } else { "" })
} else if seconds < HOUR {
let base = seconds / MINUTE;
write!(f, "{} minute{}", base, if base > 1 { "s" } else { "" })
} else if seconds < DAY {
let base = seconds / HOUR;
write!(f, "{} hour{}", base, if base > 1 { "s" } else { "" })
} else if seconds < MONTH {
let base = seconds / DAY;
write!(f, "{} day{}", base, if base > 1 { "s" } else { "" })
} else if seconds < YEAR {
let base = seconds / MONTH;
write!(f, "{} month{}", base, if base > 1 { "s" } else { "" })
} else if seconds < CENTURY {
let base = seconds / YEAR;
write!(f, "{} year{}", base, if base > 1 { "s" } else { "" })
} else {
write!(f, "centuries")
}
}
}
impl From<CrackTimeSeconds> for std::time::Duration {
fn from(s: CrackTimeSeconds) -> std::time::Duration {
match s {
CrackTimeSeconds::Float(f) => std::time::Duration::from_secs(f as u64),
CrackTimeSeconds::Integer(i) => std::time::Duration::from_secs(i),
}
}
}
pub(crate) fn estimate_attack_times(guesses: u64) -> (CrackTimes, Score) {
(CrackTimes::new(guesses), calculate_score(guesses))
}
fn calculate_score(guesses: u64) -> Score {
const DELTA: u64 = 5;
if guesses < 1_000 + DELTA {
Score::Zero
} else if guesses < 1_000_000 + DELTA {
Score::One
} else if guesses < 100_000_000 + DELTA {
Score::Two
} else if guesses < 10_000_000_000 + DELTA {
Score::Three
} else {
Score::Four
}
}