coffeenode-zxcvbn
Version:
realistic password strength estimation
686 lines (657 loc) • 19.5 kB
JavaScript
// Generated by CoffeeScript 1.6.3
var SEQUENCES, build_dict_matcher, build_ranked_dict, check_date, date_match, date_rx_year_prefix, date_rx_year_suffix, date_sep_match, date_without_sep_match, dictionary_match, digits_match, digits_rx, empty, enumerate_l33t_subs, extend, findall, l33t_match, l33t_table, omnimatch, relevent_l33t_subtable, repeat, repeat_match, sequence_match, spatial_match, spatial_match_helper, translate, year_match, year_rx;
empty = function(obj) {
var k;
return ((function() {
var _results;
_results = [];
for (k in obj) {
_results.push(k);
}
return _results;
})()).length === 0;
};
extend = function(lst, lst2) {
return lst.push.apply(lst, lst2);
};
translate = function(string, chr_map) {
var chr;
return ((function() {
var _i, _len, _ref, _results;
_ref = string.split('');
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
chr = _ref[_i];
_results.push(chr_map[chr] || chr);
}
return _results;
})()).join('');
};
omnimatch = function(password) {
var matcher, matches, _i, _len;
matches = [];
for (_i = 0, _len = MATCHERS.length; _i < _len; _i++) {
matcher = MATCHERS[_i];
extend(matches, matcher(password));
}
return matches.sort(function(match1, match2) {
return (match1.i - match2.i) || (match1.j - match2.j);
});
};
dictionary_match = function(password, ranked_dict) {
var i, j, len, password_lower, rank, result, word, _i, _j;
result = [];
len = password.length;
password_lower = password.toLowerCase();
for (i = _i = 0; 0 <= len ? _i < len : _i > len; i = 0 <= len ? ++_i : --_i) {
for (j = _j = i; i <= len ? _j < len : _j > len; j = i <= len ? ++_j : --_j) {
if (password_lower.slice(i, +j + 1 || 9e9) in ranked_dict) {
word = password_lower.slice(i, +j + 1 || 9e9);
rank = ranked_dict[word];
result.push({
pattern: 'dictionary',
i: i,
j: j,
token: password.slice(i, +j + 1 || 9e9),
matched_word: word,
rank: rank
});
}
}
}
return result;
};
build_ranked_dict = function(unranked_list) {
var i, result, word, _i, _len;
result = {};
i = 1;
for (_i = 0, _len = unranked_list.length; _i < _len; _i++) {
word = unranked_list[_i];
result[word] = i;
i += 1;
}
return result;
};
build_dict_matcher = function(dict_name, ranked_dict) {
return function(password) {
var match, matches, _i, _len;
matches = dictionary_match(password, ranked_dict);
for (_i = 0, _len = matches.length; _i < _len; _i++) {
match = matches[_i];
match.dictionary_name = dict_name;
}
return matches;
};
};
l33t_table = {
a: ['4', '@'],
b: ['8'],
c: ['(', '{', '[', '<'],
e: ['3'],
g: ['6', '9'],
i: ['1', '!', '|'],
l: ['1', '|', '7'],
o: ['0'],
s: ['$', '5'],
t: ['+', '7'],
x: ['%'],
z: ['2']
};
relevent_l33t_subtable = function(password) {
var chr, filtered, letter, password_chars, relevent_subs, sub, subs, _i, _len, _ref;
password_chars = {};
_ref = password.split('');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
chr = _ref[_i];
password_chars[chr] = true;
}
filtered = {};
for (letter in l33t_table) {
subs = l33t_table[letter];
relevent_subs = (function() {
var _j, _len1, _results;
_results = [];
for (_j = 0, _len1 = subs.length; _j < _len1; _j++) {
sub = subs[_j];
if (sub in password_chars) {
_results.push(sub);
}
}
return _results;
})();
if (relevent_subs.length > 0) {
filtered[letter] = relevent_subs;
}
}
return filtered;
};
enumerate_l33t_subs = function(table) {
var chr, dedup, helper, k, keys, l33t_chr, sub, sub_dict, sub_dicts, subs, _i, _j, _len, _len1, _ref;
keys = (function() {
var _results;
_results = [];
for (k in table) {
_results.push(k);
}
return _results;
})();
subs = [[]];
dedup = function(subs) {
var assoc, deduped, label, members, sub, v, _i, _len;
deduped = [];
members = {};
for (_i = 0, _len = subs.length; _i < _len; _i++) {
sub = subs[_i];
assoc = (function() {
var _j, _len1, _results;
_results = [];
for (v = _j = 0, _len1 = sub.length; _j < _len1; v = ++_j) {
k = sub[v];
_results.push([k, v]);
}
return _results;
})();
assoc.sort();
label = ((function() {
var _j, _len1, _results;
_results = [];
for (v = _j = 0, _len1 = assoc.length; _j < _len1; v = ++_j) {
k = assoc[v];
_results.push(k + ',' + v);
}
return _results;
})()).join('-');
if (!(label in members)) {
members[label] = true;
deduped.push(sub);
}
}
return deduped;
};
helper = function(keys) {
var dup_l33t_index, first_key, i, l33t_chr, next_subs, rest_keys, sub, sub_alternative, sub_extension, _i, _j, _k, _len, _len1, _ref, _ref1;
if (!keys.length) {
return;
}
first_key = keys[0];
rest_keys = keys.slice(1);
next_subs = [];
_ref = table[first_key];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
l33t_chr = _ref[_i];
for (_j = 0, _len1 = subs.length; _j < _len1; _j++) {
sub = subs[_j];
dup_l33t_index = -1;
for (i = _k = 0, _ref1 = sub.length; 0 <= _ref1 ? _k < _ref1 : _k > _ref1; i = 0 <= _ref1 ? ++_k : --_k) {
if (sub[i][0] === l33t_chr) {
dup_l33t_index = i;
break;
}
}
if (dup_l33t_index === -1) {
sub_extension = sub.concat([[l33t_chr, first_key]]);
next_subs.push(sub_extension);
} else {
sub_alternative = sub.slice(0);
sub_alternative.splice(dup_l33t_index, 1);
sub_alternative.push([l33t_chr, first_key]);
next_subs.push(sub);
next_subs.push(sub_alternative);
}
}
}
subs = dedup(next_subs);
return helper(rest_keys);
};
helper(keys);
sub_dicts = [];
for (_i = 0, _len = subs.length; _i < _len; _i++) {
sub = subs[_i];
sub_dict = {};
for (_j = 0, _len1 = sub.length; _j < _len1; _j++) {
_ref = sub[_j], l33t_chr = _ref[0], chr = _ref[1];
sub_dict[l33t_chr] = chr;
}
sub_dicts.push(sub_dict);
}
return sub_dicts;
};
l33t_match = function(password) {
var chr, k, match, match_sub, matcher, matches, sub, subbed_chr, subbed_password, token, v, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
matches = [];
_ref = enumerate_l33t_subs(relevent_l33t_subtable(password));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
sub = _ref[_i];
if (empty(sub)) {
break;
}
for (_j = 0, _len1 = DICTIONARY_MATCHERS.length; _j < _len1; _j++) {
matcher = DICTIONARY_MATCHERS[_j];
subbed_password = translate(password, sub);
_ref1 = matcher(subbed_password);
for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
match = _ref1[_k];
token = password.slice(match.i, +match.j + 1 || 9e9);
if (token.toLowerCase() === match.matched_word) {
continue;
}
match_sub = {};
for (subbed_chr in sub) {
chr = sub[subbed_chr];
if (token.indexOf(subbed_chr) !== -1) {
match_sub[subbed_chr] = chr;
}
}
match.l33t = true;
match.token = token;
match.sub = match_sub;
match.sub_display = ((function() {
var _results;
_results = [];
for (k in match_sub) {
v = match_sub[k];
_results.push("" + k + " -> " + v);
}
return _results;
})()).join(', ');
matches.push(match);
}
}
}
return matches;
};
spatial_match = function(password) {
var graph, graph_name, matches;
matches = [];
for (graph_name in GRAPHS) {
graph = GRAPHS[graph_name];
extend(matches, spatial_match_helper(password, graph, graph_name));
}
return matches;
};
spatial_match_helper = function(password, graph, graph_name) {
var adj, adjacents, cur_char, cur_direction, found, found_direction, i, j, last_direction, prev_char, result, shifted_count, turns, _i, _len;
result = [];
i = 0;
while (i < password.length - 1) {
j = i + 1;
last_direction = null;
turns = 0;
shifted_count = 0;
while (true) {
prev_char = password.charAt(j - 1);
found = false;
found_direction = -1;
cur_direction = -1;
adjacents = graph[prev_char] || [];
if (j < password.length) {
cur_char = password.charAt(j);
for (_i = 0, _len = adjacents.length; _i < _len; _i++) {
adj = adjacents[_i];
cur_direction += 1;
if (adj && adj.indexOf(cur_char) !== -1) {
found = true;
found_direction = cur_direction;
if (adj.indexOf(cur_char) === 1) {
shifted_count += 1;
}
if (last_direction !== found_direction) {
turns += 1;
last_direction = found_direction;
}
break;
}
}
}
if (found) {
j += 1;
} else {
if (j - i > 2) {
result.push({
pattern: 'spatial',
i: i,
j: j - 1,
token: password.slice(i, j),
graph: graph_name,
turns: turns,
shifted_count: shifted_count
});
}
i = j;
break;
}
}
}
return result;
};
repeat_match = function(password) {
var cur_char, i, j, prev_char, result, _ref;
result = [];
i = 0;
while (i < password.length) {
j = i + 1;
while (true) {
_ref = password.slice(j - 1, +j + 1 || 9e9), prev_char = _ref[0], cur_char = _ref[1];
if (password.charAt(j - 1) === password.charAt(j)) {
j += 1;
} else {
if (j - i > 2) {
result.push({
pattern: 'repeat',
i: i,
j: j - 1,
token: password.slice(i, j),
repeated_char: password.charAt(i)
});
}
break;
}
}
i = j;
}
return result;
};
SEQUENCES = {
lower: 'abcdefghijklmnopqrstuvwxyz',
upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
digits: '01234567890'
};
sequence_match = function(password) {
var chr, cur_char, cur_n, direction, i, i_n, j, j_n, prev_char, prev_n, result, seq, seq_candidate, seq_candidate_name, seq_direction, seq_name, _ref, _ref1, _ref2;
result = [];
i = 0;
while (i < password.length) {
j = i + 1;
seq = null;
seq_name = null;
seq_direction = null;
for (seq_candidate_name in SEQUENCES) {
seq_candidate = SEQUENCES[seq_candidate_name];
_ref = (function() {
var _i, _len, _ref, _results;
_ref = [password.charAt(i), password.charAt(j)];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
chr = _ref[_i];
_results.push(seq_candidate.indexOf(chr));
}
return _results;
})(), i_n = _ref[0], j_n = _ref[1];
if (i_n > -1 && j_n > -1) {
direction = j_n - i_n;
if (direction === 1 || direction === (-1)) {
seq = seq_candidate;
seq_name = seq_candidate_name;
seq_direction = direction;
break;
}
}
}
if (seq) {
while (true) {
_ref1 = password.slice(j - 1, +j + 1 || 9e9), prev_char = _ref1[0], cur_char = _ref1[1];
_ref2 = (function() {
var _i, _len, _ref2, _results;
_ref2 = [prev_char, cur_char];
_results = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
chr = _ref2[_i];
_results.push(seq_candidate.indexOf(chr));
}
return _results;
})(), prev_n = _ref2[0], cur_n = _ref2[1];
if (cur_n - prev_n === seq_direction) {
j += 1;
} else {
if (j - i > 2) {
result.push({
pattern: 'sequence',
i: i,
j: j - 1,
token: password.slice(i, j),
sequence_name: seq_name,
sequence_space: seq.length,
ascending: seq_direction === 1
});
}
break;
}
}
}
i = j;
}
return result;
};
repeat = function(chr, n) {
var i;
return ((function() {
var _i, _results;
_results = [];
for (i = _i = 1; 1 <= n ? _i <= n : _i >= n; i = 1 <= n ? ++_i : --_i) {
_results.push(chr);
}
return _results;
})()).join('');
};
findall = function(password, rx) {
var match, matches;
matches = [];
while (true) {
match = password.match(rx);
if (!match) {
break;
}
match.i = match.index;
match.j = match.index + match[0].length - 1;
matches.push(match);
password = password.replace(match[0], repeat(' ', match[0].length));
}
return matches;
};
digits_rx = /\d{3,}/;
digits_match = function(password) {
var i, j, match, _i, _len, _ref, _ref1, _results;
_ref = findall(password, digits_rx);
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
match = _ref[_i];
_ref1 = [match.i, match.j], i = _ref1[0], j = _ref1[1];
_results.push({
pattern: 'digits',
i: i,
j: j,
token: password.slice(i, +j + 1 || 9e9)
});
}
return _results;
};
year_rx = /19\d\d|200\d|201\d/;
year_match = function(password) {
var i, j, match, _i, _len, _ref, _ref1, _results;
_ref = findall(password, year_rx);
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
match = _ref[_i];
_ref1 = [match.i, match.j], i = _ref1[0], j = _ref1[1];
_results.push({
pattern: 'year',
i: i,
j: j,
token: password.slice(i, +j + 1 || 9e9)
});
}
return _results;
};
date_match = function(password) {
return date_without_sep_match(password).concat(date_sep_match(password));
};
date_without_sep_match = function(password) {
var candidate, candidates_round_1, candidates_round_2, date_matches, day, digit_match, end, i, j, month, token, valid, year, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
date_matches = [];
_ref = findall(password, /\d{4,8}/);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
digit_match = _ref[_i];
_ref1 = [digit_match.i, digit_match.j], i = _ref1[0], j = _ref1[1];
token = password.slice(i, +j + 1 || 9e9);
end = token.length;
candidates_round_1 = [];
if (token.length <= 6) {
candidates_round_1.push({
daymonth: token.slice(2),
year: token.slice(0, 2),
i: i,
j: j
});
candidates_round_1.push({
daymonth: token.slice(0, end - 2),
year: token.slice(end - 2),
i: i,
j: j
});
}
if (token.length >= 6) {
candidates_round_1.push({
daymonth: token.slice(4),
year: token.slice(0, 4),
i: i,
j: j
});
candidates_round_1.push({
daymonth: token.slice(0, end - 4),
year: token.slice(end - 4),
i: i,
j: j
});
}
candidates_round_2 = [];
for (_j = 0, _len1 = candidates_round_1.length; _j < _len1; _j++) {
candidate = candidates_round_1[_j];
switch (candidate.daymonth.length) {
case 2:
candidates_round_2.push({
day: candidate.daymonth[0],
month: candidate.daymonth[1],
year: candidate.year,
i: candidate.i,
j: candidate.j
});
break;
case 3:
candidates_round_2.push({
day: candidate.daymonth.slice(0, 2),
month: candidate.daymonth[2],
year: candidate.year,
i: candidate.i,
j: candidate.j
});
candidates_round_2.push({
day: candidate.daymonth[0],
month: candidate.daymonth.slice(1, 3),
year: candidate.year,
i: candidate.i,
j: candidate.j
});
break;
case 4:
candidates_round_2.push({
day: candidate.daymonth.slice(0, 2),
month: candidate.daymonth.slice(2, 4),
year: candidate.year,
i: candidate.i,
j: candidate.j
});
}
}
for (_k = 0, _len2 = candidates_round_2.length; _k < _len2; _k++) {
candidate = candidates_round_2[_k];
day = parseInt(candidate.day);
month = parseInt(candidate.month);
year = parseInt(candidate.year);
_ref2 = check_date(day, month, year), valid = _ref2[0], (_ref3 = _ref2[1], day = _ref3[0], month = _ref3[1], year = _ref3[2]);
if (!valid) {
continue;
}
date_matches.push({
pattern: 'date',
i: candidate.i,
j: candidate.j,
token: password.slice(i, +j + 1 || 9e9),
separator: '',
day: day,
month: month,
year: year
});
}
}
return date_matches;
};
date_rx_year_suffix = /(\d{1,2})(\s|-|\/|\\|_|\.)(\d{1,2})\2(19\d{2}|200\d|201\d|\d{2})/;
date_rx_year_prefix = /(19\d{2}|200\d|201\d|\d{2})(\s|-|\/|\\|_|\.)(\d{1,2})\2(\d{1,2})/;
date_sep_match = function(password) {
var day, k, match, matches, month, valid, year, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _results;
matches = [];
_ref = findall(password, date_rx_year_suffix);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
match = _ref[_i];
_ref1 = (function() {
var _j, _len1, _ref1, _results;
_ref1 = [1, 3, 4];
_results = [];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
k = _ref1[_j];
_results.push(parseInt(match[k]));
}
return _results;
})(), match.day = _ref1[0], match.month = _ref1[1], match.year = _ref1[2];
match.sep = match[2];
matches.push(match);
}
_ref2 = findall(password, date_rx_year_prefix);
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
match = _ref2[_j];
_ref3 = (function() {
var _k, _len2, _ref3, _results;
_ref3 = [4, 3, 1];
_results = [];
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
k = _ref3[_k];
_results.push(parseInt(match[k]));
}
return _results;
})(), match.day = _ref3[0], match.month = _ref3[1], match.year = _ref3[2];
match.sep = match[2];
matches.push(match);
}
_results = [];
for (_k = 0, _len2 = matches.length; _k < _len2; _k++) {
match = matches[_k];
_ref4 = check_date(match.day, match.month, match.year), valid = _ref4[0], (_ref5 = _ref4[1], day = _ref5[0], month = _ref5[1], year = _ref5[2]);
if (!valid) {
continue;
}
_results.push({
pattern: 'date',
i: match.i,
j: match.j,
token: password.slice(match.i, +match.j + 1 || 9e9),
separator: match.sep,
day: day,
month: month,
year: year
});
}
return _results;
};
check_date = function(day, month, year) {
var _ref;
if ((12 <= month && month <= 31) && day <= 12) {
_ref = [month, day], day = _ref[0], month = _ref[1];
}
if (day > 31 || month > 12) {
return [false, []];
}
if (!((1900 <= year && year <= 2019))) {
return [false, []];
}
return [true, [day, month, year]];
};