function minimum_entropy_match_sequence($password, $matches) { # e.g. 26 for lowercase $bruteforce_cardinality = calc_bruteforce_cardinality($password); # minimum entropy up to k. $up_to_k = array(); # 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. $backpointers = array(); $password_len = strlen($password); for ($k = 0; $k < $password_len; $k++) { # starting scenario to try and beat: # adding a brute-force character to the minimum entropy sequence at k-1. $up_to_k[$k] = _get_index($up_to_k, $k - 1) + logarithm($bruteforce_cardinality); $backpointers[$k] = NULL; foreach ($matches as $match) { if ($match['j'] !== $k) { continue; } $i = $match['i']; $j = $match['j']; # see if best entropy up to i-1 + entropy of this match # is less than the current minimum at j. $candidate_entropy = _get_index($up_to_k, $i - 1) + calc_entropy($match); if ($candidate_entropy < $up_to_k[$j]) { $up_to_k[$j] = $candidate_entropy; $backpointers[$j] = $match; } } } # walk backwards and decode the best sequence $match_sequence = array(); $k = strlen($password) - 1; while ($k >= 0) { $match = $backpointers[$k]; if ($match) { $match_sequence[] = $match; $k = $match['i'] - 1; } else { $k -= 1; } } $match_sequence = array_reverse($match_sequence); # 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 = function ($i, $j) use($password, $bruteforce_cardinality) { return array('pattern' => 'bruteforce', 'i' => $i, 'j' => $j, 'token' => _slice($password, $i, $j + 1), 'entropy' => logarithm(pow($bruteforce_cardinality, $j - $i + 1)), 'cardinality' => $bruteforce_cardinality); }; $k = 0; $match_sequence_copy = array(); foreach ($match_sequence as $match) { $i = $match['i']; $j = $match['j']; if ($i - $k > 0) { $match_sequence_copy[] = $make_bruteforce_match($k, $i - 1); } $k = $j + 1; $match_sequence_copy[] = $match; } if ($k < strlen($password)) { $match_sequence_copy[] = $make_bruteforce_match($k, strlen($password) - 1); } $match_sequence = $match_sequence_copy; # or 0 corner case is for an empty password '' if (isset($up_to_k[strlen($password) - 1])) { $min_entropy = $up_to_k[strlen($password) - 1]; } else { $min_entropy = 0; } $crack_time = entropy_to_crack_time($min_entropy); # final result object $result = array('password' => $password, 'entropy' => round_to_x_digits($min_entropy, 3), 'match_sequence' => $match_sequence, 'crack_time_seconds' => round_to_x_digits($crack_time, 3), 'crack_time_display' => display_time($crack_time), 'score' => crack_time_to_score($crack_time)); return $result; }
function generic_list_siblings(&$app, $c) { global $lang; $db =& $app->db; $def =& $db->def; $p =& $app->ui; $id = $app->arg('id'); $id_last = $db->column($c->table, 'id_last', $id); $id_next = $db->column($c->table, 'id_next', $id); list($thisindex, $last) = _get_index($app, $c->table, $def->id_parent($c->table), $c->table, $id); if ($id_last) { $e = $app->event()->copy(); $e->set_arg('id', $id_last); $p->link($lang['previous'], $e); } echo ' ' . sprintf($lang['x of y'], $thisindex, $last) . ' '; if ($id_next) { $e = $app->event()->copy(); $e->set_arg('id', $id_next); $p->link($lang['next'], $e); } }