1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use crate::frequency_lists::DictionaryType;
use crate::matching::Match;
use std::collections::HashMap;

/// Pattern type used to detect a match
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ser", derive(Serialize))]
#[cfg_attr(feature = "ser", serde(tag = "pattern"))]
#[cfg_attr(feature = "ser", serde(rename_all = "lowercase"))]
pub enum MatchPattern {
    /// A match based on a word in a dictionary
    Dictionary(DictionaryPattern),
    /// A match based on keys being close to one another on the keyboard
    Spatial(SpatialPattern),
    /// A match based on repeating patterns
    Repeat(RepeatPattern),
    /// A match based on sequences of characters, e.g. "abcd"
    Sequence(SequencePattern),
    /// A match based on one of the regex patterns used in zxcvbn.
    Regex(RegexPattern),
    /// A match based on date patterns
    Date(DatePattern),
    /// A match based on bruteforce attempting to guess a password
    BruteForce,
}

impl MatchPattern {
    #[cfg(test)]
    pub(crate) fn variant(&self) -> &str {
        match *self {
            MatchPattern::Dictionary(_) => "dictionary",
            MatchPattern::Spatial(_) => "spatial",
            MatchPattern::Repeat(_) => "repeat",
            MatchPattern::Sequence(_) => "sequence",
            MatchPattern::Regex(_) => "regex",
            MatchPattern::Date(_) => "date",
            MatchPattern::BruteForce => "bruteforce",
        }
    }
}

impl Default for MatchPattern {
    fn default() -> Self {
        MatchPattern::BruteForce
    }
}

/// A match based on a word in a dictionary
#[derive(Debug, Clone, PartialEq, Default, Builder)]
#[builder(default)]
#[cfg_attr(feature = "ser", derive(Serialize))]
pub struct DictionaryPattern {
    /// Word that has been found in a dictionary.
    pub matched_word: String,
    /// Rank of the the word found in a dictionary.
    pub rank: usize,
    /// Name of the dictionary in which a word has been found.
    pub dictionary_name: DictionaryType,
    /// Whether a reversed word has been found in a dictionary.
    pub reversed: bool,
    /// Whether a l33t-substituted word has been found in a dictionary.
    pub l33t: bool,
    /// Substitutions used for the match.
    pub sub: Option<HashMap<char, char>>,
    /// String for displaying the substitutions used for the match.
    pub sub_display: Option<String>,
    /// Number of variations of the matched dictionary word.
    pub uppercase_variations: u64,
    /// Number of variations of the matched dictionary word.
    pub l33t_variations: u64,
    /// Estimated number of tries for guessing the dictionary word.
    pub base_guesses: u64,
}

/// A match based on keys being close to one another on the keyboard
#[derive(Debug, Clone, PartialEq, Default, Builder)]
#[builder(default)]
#[cfg_attr(feature = "ser", derive(Serialize))]
pub struct SpatialPattern {
    /// Name of the graph for which a spatial match has been found.
    pub graph: String,
    /// Number of turns in the matched spatial pattern.
    pub turns: usize,
    /// Number of shifts in the matched spatial pattern.
    pub shifted_count: usize,
}

/// A match based on repeating patterns
#[derive(Debug, Clone, PartialEq, Default, Builder)]
#[builder(default)]
#[cfg_attr(feature = "ser", derive(Serialize))]
pub struct RepeatPattern {
    /// Base token that repeats in the matched pattern.
    pub base_token: String,
    /// Matches for the repeating token.
    pub base_matches: Vec<Match>,
    /// Estimated number of tries for guessing the repeating token.
    pub base_guesses: u64,
    /// Number of repetitions in the matched pattern.
    pub repeat_count: usize,
}

/// A match based on sequences of characters, e.g. "abcd"
#[derive(Debug, Clone, PartialEq, Default, Builder)]
#[builder(default)]
#[cfg_attr(feature = "ser", derive(Serialize))]
pub struct SequencePattern {
    /// Name of the sequence that was matched.
    pub sequence_name: &'static str,
    /// Size of the sequence that was matched.
    pub sequence_space: u8,
    /// Whether the matched sequence is ascending.
    pub ascending: bool,
}

/// A match based on one of the regex patterns used in zxcvbn.
#[derive(Debug, Clone, PartialEq, Default, Builder)]
#[builder(default)]
#[cfg_attr(feature = "ser", derive(Serialize))]
pub struct RegexPattern {
    /// Name of the regular expression that was matched.
    pub regex_name: &'static str,
    /// Matches of the regular expression.
    pub regex_match: Vec<String>,
}

/// A match based on date patterns
#[derive(Debug, Clone, PartialEq, Default, Builder)]
#[builder(default)]
#[cfg_attr(feature = "ser", derive(Serialize))]
pub struct DatePattern {
    /// Separator of a date that was matched.
    pub separator: String,
    /// Year that was matched.
    pub year: i32,
    /// Month that was matched.
    pub month: i8,
    /// Day that was matched.
    pub day: i8,
}