UNPKG

13.6 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.10.0
2var BRUTEFORCE_CARDINALITY, MIN_GUESSES_BEFORE_GROWING_SEQUENCE, MIN_SUBMATCH_GUESSES_MULTI_CHAR, MIN_SUBMATCH_GUESSES_SINGLE_CHAR, adjacency_graphs, calc_average_degree, k, scoring, v;
3
4adjacency_graphs = require('./adjacency_graphs');
5
6calc_average_degree = function(graph) {
7 var average, k, key, n, neighbors, v;
8 average = 0;
9 for (key in graph) {
10 neighbors = graph[key];
11 average += ((function() {
12 var len, m, results;
13 results = [];
14 for (m = 0, len = neighbors.length; m < len; m++) {
15 n = neighbors[m];
16 if (n) {
17 results.push(n);
18 }
19 }
20 return results;
21 })()).length;
22 }
23 average /= ((function() {
24 var results;
25 results = [];
26 for (k in graph) {
27 v = graph[k];
28 results.push(k);
29 }
30 return results;
31 })()).length;
32 return average;
33};
34
35BRUTEFORCE_CARDINALITY = 10;
36
37MIN_GUESSES_BEFORE_GROWING_SEQUENCE = 10000;
38
39MIN_SUBMATCH_GUESSES_SINGLE_CHAR = 10;
40
41MIN_SUBMATCH_GUESSES_MULTI_CHAR = 50;
42
43scoring = {
44 nCk: function(n, k) {
45 var d, m, r, ref;
46 if (k > n) {
47 return 0;
48 }
49 if (k === 0) {
50 return 1;
51 }
52 r = 1;
53 for (d = m = 1, ref = k; 1 <= ref ? m <= ref : m >= ref; d = 1 <= ref ? ++m : --m) {
54 r *= n;
55 r /= d;
56 n -= 1;
57 }
58 return r;
59 },
60 log10: function(n) {
61 return Math.log(n) / Math.log(10);
62 },
63 log2: function(n) {
64 return Math.log(n) / Math.log(2);
65 },
66 factorial: function(n) {
67 var f, i, m, ref;
68 if (n < 2) {
69 return 1;
70 }
71 f = 1;
72 for (i = m = 2, ref = n; 2 <= ref ? m <= ref : m >= ref; i = 2 <= ref ? ++m : --m) {
73 f *= i;
74 }
75 return f;
76 },
77 most_guessable_match_sequence: function(password, matches, _exclude_additive) {
78 var backpointers, bf_i, bf_j, bf_match, candidate_product, candidate_score, consider_bruteforce, guesses, i, j, k, l, len, m, make_bruteforce_match, match, match_sequence, max_l, new_l, o, optimal_l, optimal_product, optimal_score, prev_j, prev_l, q, ref, ref1, ref2, ref3, ref4, ref5, ref6, score;
79 if (_exclude_additive == null) {
80 _exclude_additive = false;
81 }
82 optimal_product = [];
83 backpointers = [];
84 max_l = 0;
85 optimal_l = null;
86 make_bruteforce_match = (function(_this) {
87 return function(i, j) {
88 var match;
89 match = {
90 pattern: 'bruteforce',
91 token: password.slice(i, +j + 1 || 9e9),
92 i: i,
93 j: j
94 };
95 return match;
96 };
97 })(this);
98 score = (function(_this) {
99 return function(guess_product, sequence_length) {
100 var result;
101 result = _this.factorial(sequence_length) * guess_product;
102 if (!_exclude_additive) {
103 result += Math.pow(MIN_GUESSES_BEFORE_GROWING_SEQUENCE, sequence_length - 1);
104 }
105 return result;
106 };
107 })(this);
108 for (k = m = 0, ref = password.length; 0 <= ref ? m < ref : m > ref; k = 0 <= ref ? ++m : --m) {
109 backpointers[k] = [];
110 optimal_product[k] = [];
111 optimal_score = Infinity;
112 for (prev_l = o = 0, ref1 = max_l; 0 <= ref1 ? o <= ref1 : o >= ref1; prev_l = 0 <= ref1 ? ++o : --o) {
113 consider_bruteforce = true;
114 bf_j = k;
115 if (prev_l === 0) {
116 bf_i = 0;
117 new_l = 1;
118 } else if (((ref2 = backpointers[k - 1]) != null ? (ref3 = ref2[prev_l]) != null ? ref3.pattern : void 0 : void 0) === 'bruteforce') {
119 bf_i = backpointers[k - 1][prev_l].i;
120 new_l = prev_l;
121 } else if (((ref4 = backpointers[k - 1]) != null ? ref4[prev_l] : void 0) != null) {
122 bf_i = k;
123 new_l = prev_l + 1;
124 } else {
125 consider_bruteforce = false;
126 }
127 if (consider_bruteforce) {
128 bf_match = make_bruteforce_match(bf_i, bf_j);
129 prev_j = k - bf_match.token.length;
130 candidate_product = this.estimate_guesses(bf_match, password);
131 if (new_l > 1) {
132 candidate_product *= optimal_product[prev_j][new_l - 1];
133 }
134 candidate_score = score(candidate_product, new_l);
135 if (candidate_score < optimal_score) {
136 optimal_score = candidate_score;
137 optimal_product[k][new_l] = candidate_product;
138 optimal_l = new_l;
139 max_l = Math.max(max_l, new_l);
140 backpointers[k][new_l] = bf_match;
141 }
142 }
143 for (q = 0, len = matches.length; q < len; q++) {
144 match = matches[q];
145 if (!(match.j === k)) {
146 continue;
147 }
148 ref5 = [match.i, match.j], i = ref5[0], j = ref5[1];
149 if (prev_l === 0) {
150 if (i !== 0) {
151 continue;
152 }
153 } else {
154 if (((ref6 = optimal_product[i - 1]) != null ? ref6[prev_l] : void 0) == null) {
155 continue;
156 }
157 }
158 candidate_product = this.estimate_guesses(match, password);
159 if (prev_l > 0) {
160 candidate_product *= optimal_product[i - 1][prev_l];
161 }
162 candidate_score = score(candidate_product, prev_l + 1);
163 if (candidate_score < optimal_score) {
164 optimal_score = candidate_score;
165 optimal_product[k][prev_l + 1] = candidate_product;
166 optimal_l = prev_l + 1;
167 max_l = Math.max(max_l, prev_l + 1);
168 backpointers[k][prev_l + 1] = match;
169 }
170 }
171 }
172 }
173 match_sequence = [];
174 l = optimal_l;
175 k = password.length - 1;
176 while (k >= 0) {
177 match = backpointers[k][l];
178 match_sequence.push(match);
179 k = match.i - 1;
180 l -= 1;
181 }
182 match_sequence.reverse();
183 if (password.length === 0) {
184 guesses = 1;
185 } else {
186 guesses = optimal_score;
187 }
188 return {
189 password: password,
190 guesses: guesses,
191 guesses_log10: this.log10(guesses),
192 sequence: match_sequence
193 };
194 },
195 estimate_guesses: function(match, password) {
196 var estimation_functions, guesses, min_guesses;
197 if (match.guesses != null) {
198 return match.guesses;
199 }
200 min_guesses = 1;
201 if (match.token.length < password.length) {
202 min_guesses = match.token.length === 1 ? MIN_SUBMATCH_GUESSES_SINGLE_CHAR : MIN_SUBMATCH_GUESSES_MULTI_CHAR;
203 }
204 estimation_functions = {
205 bruteforce: this.bruteforce_guesses,
206 dictionary: this.dictionary_guesses,
207 spatial: this.spatial_guesses,
208 repeat: this.repeat_guesses,
209 sequence: this.sequence_guesses,
210 regex: this.regex_guesses,
211 date: this.date_guesses
212 };
213 guesses = estimation_functions[match.pattern].call(this, match);
214 match.guesses = Math.max(guesses, min_guesses);
215 match.guesses_log10 = this.log10(match.guesses);
216 return match.guesses;
217 },
218 bruteforce_guesses: function(match) {
219 var guesses, min_guesses;
220 guesses = Math.pow(BRUTEFORCE_CARDINALITY, match.token.length);
221 min_guesses = match.token.length === 1 ? MIN_SUBMATCH_GUESSES_SINGLE_CHAR + 1 : MIN_SUBMATCH_GUESSES_MULTI_CHAR + 1;
222 return Math.max(guesses, min_guesses);
223 },
224 repeat_guesses: function(match) {
225 return match.base_guesses * match.repeat_count;
226 },
227 sequence_guesses: function(match) {
228 var base_guesses, first_chr;
229 first_chr = match.token.charAt(0);
230 if (first_chr === 'a' || first_chr === 'A' || first_chr === 'z' || first_chr === 'Z' || first_chr === '0' || first_chr === '1' || first_chr === '9') {
231 base_guesses = 4;
232 } else {
233 if (first_chr.match(/\d/)) {
234 base_guesses = 10;
235 } else {
236 base_guesses = 26;
237 }
238 }
239 if (!match.ascending) {
240 base_guesses *= 2;
241 }
242 return base_guesses * match.token.length;
243 },
244 MIN_YEAR_SPACE: 20,
245 REFERENCE_YEAR: 2000,
246 regex_guesses: function(match) {
247 var char_class_bases, year_space;
248 char_class_bases = {
249 alpha_lower: 26,
250 alpha_upper: 26,
251 alpha: 52,
252 alphanumeric: 62,
253 digits: 10,
254 symbols: 33
255 };
256 if (match.regex_name in char_class_bases) {
257 return Math.pow(char_class_bases[match.regex_name], match.token.length);
258 } else {
259 switch (match.regex_name) {
260 case 'recent_year':
261 year_space = Math.abs(parseInt(match.regex_match[0]) - this.REFERENCE_YEAR);
262 year_space = Math.max(year_space, this.MIN_YEAR_SPACE);
263 return year_space;
264 }
265 }
266 },
267 date_guesses: function(match) {
268 var guesses, year_space;
269 year_space = Math.max(Math.abs(match.year - this.REFERENCE_YEAR), this.MIN_YEAR_SPACE);
270 guesses = year_space * 31 * 12;
271 if (match.has_full_year) {
272 guesses *= 2;
273 }
274 if (match.separator) {
275 guesses *= 4;
276 }
277 return guesses;
278 },
279 KEYBOARD_AVERAGE_DEGREE: calc_average_degree(adjacency_graphs.qwerty),
280 KEYPAD_AVERAGE_DEGREE: calc_average_degree(adjacency_graphs.keypad),
281 KEYBOARD_STARTING_POSITIONS: ((function() {
282 var ref, results;
283 ref = adjacency_graphs.qwerty;
284 results = [];
285 for (k in ref) {
286 v = ref[k];
287 results.push(k);
288 }
289 return results;
290 })()).length,
291 KEYPAD_STARTING_POSITIONS: ((function() {
292 var ref, results;
293 ref = adjacency_graphs.keypad;
294 results = [];
295 for (k in ref) {
296 v = ref[k];
297 results.push(k);
298 }
299 return results;
300 })()).length,
301 spatial_guesses: function(match) {
302 var L, S, U, d, guesses, i, j, m, o, possible_turns, q, ref, ref1, ref2, ref3, s, shifted_variations, t;
303 if ((ref = match.graph) === 'qwerty' || ref === 'dvorak') {
304 s = this.KEYBOARD_STARTING_POSITIONS;
305 d = this.KEYBOARD_AVERAGE_DEGREE;
306 } else {
307 s = this.KEYPAD_STARTING_POSITIONS;
308 d = this.KEYPAD_AVERAGE_DEGREE;
309 }
310 guesses = 0;
311 L = match.token.length;
312 t = match.turns;
313 for (i = m = 2, ref1 = L; 2 <= ref1 ? m <= ref1 : m >= ref1; i = 2 <= ref1 ? ++m : --m) {
314 possible_turns = Math.min(t, i - 1);
315 for (j = o = 1, ref2 = possible_turns; 1 <= ref2 ? o <= ref2 : o >= ref2; j = 1 <= ref2 ? ++o : --o) {
316 guesses += this.nCk(i - 1, j - 1) * s * Math.pow(d, j);
317 }
318 }
319 if (match.shifted_count) {
320 S = match.shifted_count;
321 U = match.token.length - match.shifted_count;
322 if (S === 0 || U === 0) {
323 guesses *= 2;
324 } else {
325 shifted_variations = 0;
326 for (i = q = 1, ref3 = Math.min(S, U); 1 <= ref3 ? q <= ref3 : q >= ref3; i = 1 <= ref3 ? ++q : --q) {
327 shifted_variations += this.nCk(S + U, i);
328 }
329 guesses *= shifted_variations;
330 }
331 }
332 return guesses;
333 },
334 dictionary_guesses: function(match) {
335 var reversed_variations;
336 match.base_guesses = match.rank;
337 match.uppercase_variations = this.uppercase_variations(match);
338 match.l33t_variations = this.l33t_variations(match);
339 reversed_variations = match.reversed && 2 || 1;
340 return match.base_guesses * match.uppercase_variations * match.l33t_variations * reversed_variations;
341 },
342 START_UPPER: /^[A-Z][^A-Z]+$/,
343 END_UPPER: /^[^A-Z]+[A-Z]$/,
344 ALL_UPPER: /^[^a-z]+$/,
345 ALL_LOWER: /^[^A-Z]+$/,
346 uppercase_variations: function(match) {
347 var L, U, chr, i, len, m, o, ref, ref1, regex, variations, word;
348 word = match.token;
349 if (word.match(this.ALL_LOWER)) {
350 return 1;
351 }
352 ref = [this.START_UPPER, this.END_UPPER, this.ALL_UPPER];
353 for (m = 0, len = ref.length; m < len; m++) {
354 regex = ref[m];
355 if (word.match(regex)) {
356 return 2;
357 }
358 }
359 U = ((function() {
360 var len1, o, ref1, results;
361 ref1 = word.split('');
362 results = [];
363 for (o = 0, len1 = ref1.length; o < len1; o++) {
364 chr = ref1[o];
365 if (chr.match(/[A-Z]/)) {
366 results.push(chr);
367 }
368 }
369 return results;
370 })()).length;
371 L = ((function() {
372 var len1, o, ref1, results;
373 ref1 = word.split('');
374 results = [];
375 for (o = 0, len1 = ref1.length; o < len1; o++) {
376 chr = ref1[o];
377 if (chr.match(/[a-z]/)) {
378 results.push(chr);
379 }
380 }
381 return results;
382 })()).length;
383 variations = 0;
384 for (i = o = 1, ref1 = Math.min(U, L); 1 <= ref1 ? o <= ref1 : o >= ref1; i = 1 <= ref1 ? ++o : --o) {
385 variations += this.nCk(U + L, i);
386 }
387 return variations;
388 },
389 l33t_variations: function(match) {
390 var S, U, chr, chrs, i, m, p, possibilities, ref, ref1, subbed, unsubbed, variations;
391 if (!match.l33t) {
392 return 1;
393 }
394 variations = 1;
395 ref = match.sub;
396 for (subbed in ref) {
397 unsubbed = ref[subbed];
398 chrs = match.token.toLowerCase().split('');
399 S = ((function() {
400 var len, m, results;
401 results = [];
402 for (m = 0, len = chrs.length; m < len; m++) {
403 chr = chrs[m];
404 if (chr === subbed) {
405 results.push(chr);
406 }
407 }
408 return results;
409 })()).length;
410 U = ((function() {
411 var len, m, results;
412 results = [];
413 for (m = 0, len = chrs.length; m < len; m++) {
414 chr = chrs[m];
415 if (chr === unsubbed) {
416 results.push(chr);
417 }
418 }
419 return results;
420 })()).length;
421 if (S === 0 || U === 0) {
422 variations *= 2;
423 } else {
424 p = Math.min(U, S);
425 possibilities = 0;
426 for (i = m = 1, ref1 = p; 1 <= ref1 ? m <= ref1 : m >= ref1; i = 1 <= ref1 ? ++m : --m) {
427 possibilities += this.nCk(U + S, i);
428 }
429 variations *= possibilities;
430 }
431 }
432 return variations;
433 }
434};
435
436module.exports = scoring;
437
438//# sourceMappingURL=scoring.js.map