PRCYCoin  2.0.0.7rc1
P2P Digital Currency
feedback.cpp
Go to the documentation of this file.
1 #include <zxcvbn/feedback.hpp>
2 
4 #include <zxcvbn/optional.hpp>
5 #include <zxcvbn/scoring.hpp>
6 #include <zxcvbn/util.hpp>
7 
8 #include <regex>
9 
10 namespace zxcvbn {
11 
13  "",
14  {
15  "Use a few words, avoid common phrases",
16  "No need for symbols, digits, or uppercase letters",
17  },
18 };
19 
20 static
21 optional::optional<Feedback> get_match_feedback(const Match & match, bool is_sole_match);
22 
23 static
24 Feedback get_dictionary_match_feedback(const Match & match, bool is_sole_match);
25 
27  const std::vector<Match> & sequence) {
28  // starting feedback
29  if (!sequence.size()) return DEFAULT_FEEDBACK;
30 
31  // no feedback if score is good or great.
32  if (score > 2) return {"", {}};
33 
34  // tie feedback to the longest match for longer sequences
35  auto longest_match = sequence.begin();
36  for (auto match = longest_match + 1; match != sequence.end(); ++match) {
37  if (match->token.length() > longest_match->token.length()) {
38  longest_match = match;
39  }
40  }
41 
42  auto maybe_feedback = get_match_feedback(*longest_match, sequence.size() == 1);
43  auto extra_feedback = "Add another word or two. Uncommon words are better.";
44  if (maybe_feedback) {
45  auto & feedback = *maybe_feedback;
46 
47  feedback.suggestions.insert(maybe_feedback->suggestions.begin(),
48  extra_feedback);
49 
50  return feedback;
51  }
52  else {
53  return {"", {extra_feedback}};
54  }
55 }
56 
57 optional::optional<Feedback> get_match_feedback(const Match & match_, bool is_sole_match) {
58  switch (match_.get_pattern()) {
59  case MatchPattern::DICTIONARY: {
60  return get_dictionary_match_feedback(match_, is_sole_match);
61  }
62 
63  case MatchPattern::SPATIAL: {
64  auto & match = match_.get_spatial();
65  auto warning = (match.turns == 1)
66  ? "Straight rows of keys are easy to guess"
67  : "Short keyboard patterns are easy to guess";
68 
69  return Feedback{warning, {
70  "Use a longer keyboard pattern with more turns",
71  }};
72  }
73 
74  case MatchPattern::REPEAT: {
75  auto warning = (match_.get_repeat().base_token.length() == 1)
76  ? "Repeats like \"aaa\" are easy to guess"
77  : "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"";
78 
79  return Feedback{warning, {
80  "Avoid repeated words and characters",
81  }};
82  }
83 
84  case MatchPattern::SEQUENCE: {
85  return Feedback{"Sequences like abc or 6543 are easy to guess",
86  {"Avoid sequences"},
87  };
88  }
89 
90  case MatchPattern::REGEX: {
91  auto & match = match_.get_regex();
92  if (match.regex_tag == RegexTag::RECENT_YEAR) {
93  return Feedback{"Recent years are easy to guess", {
94  "Avoid recent years",
95  "Avoid years that are associated with you",
96  }};
97  }
98  break;
99  }
100 
101  case MatchPattern::DATE: {
102  return Feedback{"Dates are often easy to guess", {
103  "Avoid dates and years that are associated with you",
104  }};
105  }
106  default:
107  break;
108  }
109  return optional::nullopt;
110 }
111 
112 static
113 Feedback get_dictionary_match_feedback(const Match & match_, bool is_sole_match) {
114  assert(match_.get_pattern() == MatchPattern::DICTIONARY);
115  auto & match = match_.get_dictionary();
116  auto warning = [&] {
117  if (match.dictionary_tag == DictionaryTag::PASSWORDS) {
118  if (is_sole_match and !match.l33t and !match.reversed) {
119  if (match.rank <= 10) {
120  return "This is a top-10 common password";
121  }
122  else if (match.rank <= 100) {
123  return "This is a top-100 common password";
124  }
125  else {
126  return "This is a very common password";
127  }
128  }
129  else if (match_.guesses_log10 <= 4) {
130  return "This is similar to a commonly used password";
131  }
132  }
133  else if (match.dictionary_tag == DictionaryTag::ENGLISH_WIKIPEDIA) {
134  if (is_sole_match) {
135  return "A word by itself is easy to guess";
136  }
137  }
138  else if (match.dictionary_tag == DictionaryTag::SURNAMES ||
139  match.dictionary_tag == DictionaryTag::MALE_NAMES ||
140  match.dictionary_tag == DictionaryTag::FEMALE_NAMES) {
141  if (is_sole_match) {
142  return "Names and surnames by themselves are easy to guess";
143  }
144  else {
145  return "Common names and surnames are easy to guess";
146  }
147  }
148 
149  return "";
150  }();
151 
152  std::vector<std::string> suggestions;
153  auto & word = match_.token;
154  if (std::regex_search(word, START_UPPER)) {
155  suggestions.push_back("Capitalization doesn't help very much");
156  }
157  else if (std::regex_search(word, ALL_UPPER) and
158  // XXX: UTF-8
159  util::ascii_lower(word) == word) {
160  suggestions.push_back("All-uppercase is almost as easy to guess as all-lowercase");
161  }
162 
163  if (match.reversed and match_.token.length() >= 4) {
164  suggestions.push_back("Reversed words aren't much harder to guess");
165  }
166  if (match.l33t) {
167  suggestions.push_back("Predictable substitutions like '@' instead of 'a' don't help very much");
168  }
169 
170  return {warning, suggestions};
171 }
172 
173 }
174 
zxcvbn::score_t
unsigned score_t
Definition: common.hpp:17
zxcvbn::Feedback
Definition: feedback.hpp:11
zxcvbn::util::ascii_lower
std::string ascii_lower(const std::string &in)
Definition: util.cpp:15
zxcvbn::ALL_UPPER
const auto ALL_UPPER
Definition: scoring.hpp:16
zxcvbn
Definition: _frequency_lists.cpp:7
zxcvbn::DEFAULT_FEEDBACK
const Feedback DEFAULT_FEEDBACK
Definition: feedback.cpp:12
zxcvbn::RegexTag::RECENT_YEAR
@ RECENT_YEAR
zxcvbn::START_UPPER
const auto START_UPPER
Definition: scoring.hpp:14
zxcvbn::optional::nullopt
constexpr nullopt_t nullopt
Definition: optional.hpp:23
scoring.hpp
feedback.hpp
frequency_lists.hpp
zxcvbn::get_feedback
Feedback get_feedback(score_t score, const std::vector< Match > &sequence)
Definition: feedback.cpp:26
util.hpp
optional.hpp