Class: Zxcvbn::Matchers::L33t
- Inherits:
-
Object
- Object
- Zxcvbn::Matchers::L33t
- Defined in:
- lib/zxcvbn/matchers/l33t.rb,
lib/zxcvbn/matchers/new_l33t.rb
Constant Summary collapse
- 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'] }
Instance Method Summary collapse
- #dedup(subs) ⇒ Object
-
#expanded_substitutions(hash) ⇒ Object
expand possible combinations if multiple characters can be substituted e.g.
- #find_substitutions(subs, table, keys) ⇒ Object
-
#initialize(dictionary_matchers) ⇒ L33t
constructor
A new instance of L33t.
- #l33t_subs(table) ⇒ Object
- #matches(password) ⇒ Object
-
#relevant_l33t_substitutions(password) ⇒ Object
produces a l33t table of substitutions present in the given password.
- #relevent_l33t_subtable(password) ⇒ Object
- #substitute(password, substitution) ⇒ Object
-
#substitution_combinations(subs_hash) ⇒ Object
takes a character substitutions hash and produces an array of all possible substitution combinations.
- #translate(password, sub) ⇒ Object
Constructor Details
#initialize(dictionary_matchers) ⇒ L33t
Returns a new instance of L33t.
19 20 21 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 19 def initialize(dictionary_matchers) @dictionary_matchers = dictionary_matchers end |
Instance Method Details
#dedup(subs) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 110 def dedup(subs) deduped = [] members = [] subs.each do |sub| assoc = sub.dup assoc.sort! rescue debugger label = assoc.map{|k, v| "#{k},#{v}"}.join('-') unless members.include?(label) members << label deduped << sub end end deduped end |
#expanded_substitutions(hash) ⇒ Object
expand possible combinations if multiple characters can be substituted e.g. => [‘4’, ‘@’], ‘i’ => [‘1’] expands to
[{'a' => '4', 'i' => 1}, {'a' => '@', 'i' => '1'}]
111 112 113 114 115 116 |
# File 'lib/zxcvbn/matchers/new_l33t.rb', line 111 def (hash) return {} if hash.empty? values = hash.values product_values = values[0].product(*values[1..-1]) product_values.map{ |p| Hash[hash.keys.zip(p)] } end |
#find_substitutions(subs, table, keys) ⇒ Object
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 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 80 def find_substitutions(subs, table, keys) return subs if keys.empty? first_key = keys[0] rest_keys = keys[1..-1] next_subs = [] table[first_key].each do |l33t_char| subs.each do |sub| dup_l33t_index = -1 (0...sub.length).each do |i| if sub[i][0] == l33t_char dup_l33t_index = i break end end if dup_l33t_index == -1 sub_extension = sub + [[l33t_char, first_key]] next_subs << sub_extension else sub_alternative = sub.dup sub_alternative[dup_l33t_index, 1] = [[l33t_char, first_key]] next_subs << sub next_subs << sub_alternative end end end subs = dedup(next_subs) find_substitutions(subs, table, rest_keys) end |
#l33t_subs(table) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 65 def l33t_subs(table) keys = table.keys subs = [[]] subs = find_substitutions(subs, table, keys) new_subs = [] subs.each do |sub| hash = {} sub.each do |l33t_char, chr| hash[l33t_char] = chr end new_subs << hash end new_subs end |
#matches(password) ⇒ Object
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 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 23 def matches(password) matches = [] lowercased_password = password.downcase combinations_to_try = l33t_subs(relevent_l33t_subtable(lowercased_password)) combinations_to_try.each do |substitution| @dictionary_matchers.each do |matcher| subbed_password = translate(lowercased_password, substitution) matcher.matches(subbed_password).each do |match| token = password[match.i..match.j] next if token.downcase == match.matched_word.downcase match_substitutions = {} substitution.each do |substitution, letter| match_substitutions[substitution] = letter if token.include?(substitution) end match.l33t = true match.token = password[match.i..match.j] match.sub = match_substitutions match.sub_display = match_substitutions.map do |k, v| "#{k} -> #{v}" end.join(', ') matches << match end end end matches end |
#relevant_l33t_substitutions(password) ⇒ Object
produces a l33t table of substitutions present in the given password
61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/zxcvbn/matchers/new_l33t.rb', line 61 def relevant_l33t_substitutions(password) subs = Hash.new do |hash, key| hash[key] = [] end L33T_TABLE.each do |letter, substibutions| password.each_char do |password_char| if substibutions.include?(password_char) subs[letter] << password_char end end end subs end |
#relevent_l33t_subtable(password) ⇒ Object
56 57 58 59 60 61 62 63 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 56 def relevent_l33t_subtable(password) filtered = {} L33T_TABLE.each do |letter, subs| relevent_subs = subs.select { |s| password.include?(s) } filtered[letter] = relevent_subs unless relevent_subs.empty? end filtered end |
#substitute(password, substitution) ⇒ Object
52 53 54 55 56 57 58 |
# File 'lib/zxcvbn/matchers/new_l33t.rb', line 52 def substitute(password, substitution) subbed_password = password.dup substitution.each do |letter, substitution| subbed_password.gsub!(substitution, letter) end subbed_password end |
#substitution_combinations(subs_hash) ⇒ Object
takes a character substitutions hash and produces an array of all possible substitution combinations
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 |
# File 'lib/zxcvbn/matchers/new_l33t.rb', line 77 def substitution_combinations(subs_hash) combinations = [] = (subs_hash) # build an array of all possible combinations .each do |substitution_hash| # convert a hash to an array of hashes with 1 key each subs_array = substitution_hash.map do |letter, substitutions| {letter => substitutions} end combinations << subs_array # find all possible combinations for each number of combinations available subs_array.combination(subs_array.size).each do |combination| # Don't add duplicates combinations << combination unless combinations.include?(combination) end end # convert back to simple hash per substitution combination combination_hashes = combinations.map do |combination_set| hash = {} combination_set.each do |combination_hash| hash.merge!(combination_hash) end hash end combination_hashes end |
#translate(password, sub) ⇒ Object
50 51 52 53 54 |
# File 'lib/zxcvbn/matchers/l33t.rb', line 50 def translate(password, sub) password.split('').map do |chr| sub[chr] || chr end.join end |