Class: Zxcvbn::Scorer
- Inherits:
-
Object
- Object
- Zxcvbn::Scorer
- Defined in:
- lib/zxcvbn/scorer.rb
Constant Summary
Constants included from CrackTime
CrackTime::NUM_ATTACKERS, CrackTime::SECONDS_PER_GUESS, CrackTime::SINGLE_GUESS
Constants included from Entropy
Entropy::ALL_LOWER, Entropy::ALL_UPPER, Entropy::END_UPPER, Entropy::NUM_DAYS, Entropy::NUM_MONTHS, Entropy::NUM_YEARS, Entropy::START_UPPER
Instance Method Summary collapse
Methods included from CrackTime
#crack_time_to_score, #display_time, #entropy_to_crack_time
Methods included from Entropy
#calc_entropy, #date_entropy, #dictionary_entropy, #digits_entropy, #extra_l33t_entropy, #extra_uppercase_entropy, #repeat_entropy, #sequence_entropy, #spatial_entropy, #year_entropy
Methods included from Math
#average_degree_for_graph, #bruteforce_cardinality, #lg, #min, #nCk, #starting_positions_for_graph
Instance Method Details
#minimum_entropy_match_sequence(password, matches) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/zxcvbn/scorer.rb', line 6 def minimum_entropy_match_sequence(password, matches) bruteforce_cardinality = bruteforce_cardinality(password) # e.g. 26 for lowercase up_to_k = [] # minimum entropy up to k. backpointers = [] # for the optimal sequence of matches up to k, holds the final match (match.j == k). null means the sequence ends w/ a brute-force character. (0...password.length).each do |k| # starting scenario to try and beat: adding a brute-force character to the minimum entropy sequence at k-1. previous_k_entropy = k == 0 ? 0 : up_to_k[k-1] up_to_k[k] = previous_k_entropy + lg(bruteforce_cardinality) backpointers[k] = nil matches.each do |match| next unless match.j == k i, j = match.i, match.j # see if best entropy up to i-1 + entropy of this match is less than the current minimum at j. previous_i_entropy = i > 0 ? up_to_k[i-1] : 0 candidate_entropy = previous_i_entropy + calc_entropy(match) if up_to_k[j] && candidate_entropy < up_to_k[j] up_to_k[j] = candidate_entropy backpointers[j] = match end end end # walk backwards and decode the best sequence match_sequence = [] k = password.length - 1 while k >= 0 match = backpointers[k] if match match_sequence.push match k = match.i - 1 else k -= 1 end end match_sequence.reverse! # fill in the blanks between pattern matches with bruteforce "matches" # that way the match sequence fully covers the password: match1.j == match2.i - 1 for every adjacent match1, match2. make_bruteforce_match = lambda do |i, j| Match.new( :pattern => 'bruteforce', :i => i, :j => j, :token => password[i..j], :entropy => lg(bruteforce_cardinality ** (j - i + 1)), :cardinality => bruteforce_cardinality ) end k = 0 match_sequence_copy = [] match_sequence.each do |match| i, j = match.i, match.j if i - k > 0 debugger if i == 0 match_sequence_copy << make_bruteforce_match.call(k, i - 1) end k = j + 1 match_sequence_copy.push match end if k < password.length match_sequence_copy.push make_bruteforce_match.call(k, password.length - 1) end match_sequence = match_sequence_copy min_entropy = up_to_k[password.length - 1] || 0 # or 0 corner case is for an empty password '' crack_time = entropy_to_crack_time(min_entropy) # final result object Score.new( :password => password, :entropy => min_entropy.round(3), :match_sequence => match_sequence, :crack_time => crack_time.round(3), :crack_time_display => display_time(crack_time), :score => crack_time_to_score(crack_time) ) end |