14 template<
class T,
class U>
15 struct hash<
std::pair<T, U>> {
16 std::size_t
operator()(
const std::pair<T, U> & v)
const {
17 return std::hash<T>()(v.first) ^ std::hash<U>()(v.second);
30 template<
class Tret,
class Tin>
35 for (Tin i = 2; i <= n; ++i) {
41 template<
class M,
class K,
class V>
43 void insert_or_assign(M & m,
const K & k, V && v) {
44 auto p = m.insert(std::make_pair(k, std::forward<V>(v)));
46 p.first->second = std::forward<V>(v);
54 std::size_t token_len(
const Match & m) {
55 std::size_t result = m.j - m.i + 1;
94 std::vector<Match> & matches,
95 bool exclude_additive) {
96 auto n = password.length();
99 std::unordered_map<idx_t, std::vector<std::reference_wrapper<Match>>> matches_by_j;
100 for (
auto & m : matches) {
101 matches_by_j[m.j].push_back(m);
104 for (
auto & item : matches_by_j) {
105 std::sort(item.second.begin(), item.second.end(),
106 [&] (
const std::reference_wrapper<Match> & a,
107 const std::reference_wrapper<Match> &
b) {
108 return a.get().i < b.get().i;
117 std::unordered_map<idx_t, std::unordered_map<idx_t, std::reference_wrapper<Match>>> m;
121 std::unordered_map<idx_t, std::unordered_map<idx_t, guesses_t>> pi;
124 std::unordered_map<idx_t, std::unordered_map<idx_t, guesses_t>> g;
136 pi *= optimal.pi[m.
i - 1][l - 1];
139 auto g = factorial<guesses_t>(l) * pi;
140 if (!exclude_additive) {
146 for (
const auto & item : optimal.g[k]) {
147 auto & competing_l = item.first;
148 auto & competing_g = item.second;
149 if (competing_l > l)
continue;
150 if (competing_g <= g)
return;
153 insert_or_assign(optimal.g[k], l, g);
154 insert_or_assign(optimal.m[k], l, std::ref(m));
155 insert_or_assign(optimal.pi[k], l, pi);
161 std::unordered_map<std::pair<idx_t, idx_t>, std::unique_ptr<Match>> bruteforces;
162 auto make_bruteforce_match = [&] (
idx_t i,
idx_t j) -> std::reference_wrapper<Match> {
163 auto p = bruteforces.insert(std::make_pair(std::make_pair(i, j),
164 std::make_unique<Match>
166 password.substr(i, j - i + 1),
168 return std::ref(*p.first->second);
172 auto bruteforce_update = [&] (
idx_t k) {
174 auto m = make_bruteforce_match(0, k);
176 for (
idx_t i = 1; i <= k; ++i) {
180 auto m2 = make_bruteforce_match(i, k);
181 for (
const auto & item : optimal.m[i - 1]) {
182 auto & l = item.first;
183 auto & last_m = item.second;
188 if (last_m.get().get_pattern() == MatchPattern::BRUTEFORCE)
continue;
197 auto unwind = [&] (
idx_t n) {
198 std::vector<std::reference_wrapper<Match>> optimal_match_sequence;
199 if (!n)
return optimal_match_sequence;
201 idx_t l = optimal.g[k].begin()->first;
202 auto g = optimal.g[k].begin()->second;
203 for (
const auto & item : optimal.g[k]) {
204 auto & candidate_l = item.first;
205 auto & candidate_g = item.second;
206 if (candidate_g < g) {
212 auto it = optimal.m[k].find(l);
213 assert(it != optimal.m[k].end());
214 auto & m = it->second;
215 optimal_match_sequence.push_back(m);
216 if (!m.get().
i)
break;
220 std::reverse(optimal_match_sequence.begin(), optimal_match_sequence.end());
221 return optimal_match_sequence;
224 for (
idx_t k = 0; k < n; ++k) {
225 for (
const auto & m : matches_by_j[k]) {
227 for (
const auto & item : optimal.m[m.get().
i - 1]) {
228 auto & l = item.first;
236 bruteforce_update(k);
238 auto optimal_match_sequence = unwind(n);
239 auto optimal_l = optimal_match_sequence.size();
243 if (password.length() == 0) {
247 guesses = optimal.g[n - 1][optimal_l];
251 std::vector<std::unique_ptr<Match>> bruteforce_matches;
252 for (
const auto & ref : optimal_match_sequence) {
253 auto & m = ref.get();
254 if (m.
get_pattern() != MatchPattern::BRUTEFORCE)
continue;
255 auto it = bruteforces.find(std::make_pair(m.
i, m.
j));
256 if (it == bruteforces.end())
continue;
257 bruteforce_matches.push_back(std::move(it->second));
264 std::move(bruteforce_matches),
265 std::move(optimal_match_sequence),
276 if (match.
token.length() < password.length()) {
277 min_guesses = (token_len(match) == 1)
281 #define MATCH_FN(title, upper, lower) \
282 : match.get_pattern() == MatchPattern::upper ? lower##_guesses
286 assert(estimation_function !=
nullptr);
287 auto guesses = estimation_function(match);
288 match.
guesses = std::max(guesses, min_guesses);
302 auto min_guesses = (token_len(match) == 1)
305 return std::max(guesses, min_guesses);
309 return match.get_repeat().base_guesses * match.get_repeat().repeat_count;
314 auto first_chr = std::string(match.
token.begin(), second_chr_pos);
317 if (first_chr ==
"a" || first_chr ==
"A" || first_chr ==
"z" ||
318 first_chr ==
"Z" || first_chr ==
"0" || first_chr ==
"1" ||
332 if (!match.get_sequence().ascending) {
337 return base_guesses * token_len(match);
341 switch (match.get_regex().regex_tag) {
346 auto pre_year_space = std::stoul(match.get_regex().regex_match.matches[0]);
359 switch (match.get_regex().regex_tag) {
362 default: assert(
false);
return 0;
365 return std::pow(base, token_len(match));
374 auto pre_year_space = match.get_date().year;
384 auto guesses = year_space * 365;
386 if (match.get_date().has_full_year) guesses *= 2;
388 if (match.get_date().separator.length()) guesses *= 4;
405 auto L = token_len(match);
406 auto t =
static_cast<decltype(
L)
>(match.get_spatial().turns);
408 for (decltype(
L) i = 2; i <=
L; ++i) {
409 auto possible_turns = std::min(t, i - 1);
410 for (decltype(possible_turns) j = 1; j <= possible_turns; ++j) {
411 guesses +=
nCk(i - 1, j - 1) * s * std::pow(d, j);
416 if (match.get_spatial().shifted_count) {
417 auto S = match.get_spatial().shifted_count;
418 decltype(
S) U = token_len(match) - match.get_spatial().shifted_count;
419 if (
S == 0 || U == 0) {
423 auto shifted_variations = 0;
424 for (decltype(
S) i = 1; i <= std::min(
S, U); ++i) {
425 shifted_variations +=
nCk(
S + U, i);
427 guesses *= shifted_variations;
435 auto base_guesses = match.get_dictionary().rank;
438 auto reversed_variations = match.get_dictionary().reversed ? 2 : 1;
439 return (base_guesses * uppercase_variations_ * l33t_variations_ * reversed_variations);
443 auto & word = match.
token;
454 auto match_chr = [] (
const std::string & str,
const std::regex & regex) {
455 decltype(str.length()) toret = 0;
456 for (
auto it = str.begin(); it != str.end();) {
458 auto s = std::string(it, it2);
466 auto U = match_chr(word, std::regex(R
"([A-Z])"));
467 auto L = match_chr(word, std::regex(R
"([a-z])"));
469 for (decltype(U) i = 1; i <= std::min(U,
L); ++i) {
470 variations +=
nCk(U +
L, i);
476 auto & dmatch = match.get_dictionary();
477 if (!dmatch.l33t)
return 1;
479 for (
const auto & item : dmatch.sub) {
480 auto & subbed = item.first;
481 auto & unsubbed = item.second;
487 for (
auto it = ltoken.begin(); it != ltoken.end();) {
489 auto cs = std::string(it, it2);
490 if (cs == subbed)
S += 1;
491 if (cs == unsubbed) U += 1;
503 auto p = std::min(U,
S);
505 for (decltype(p) i = 1; i <= p; ++i) {
506 possibilities +=
nCk(U +
S, i);
508 variations *= possibilities;