/**
  * Calculate the minimum entropy for a password and its matches.
  *
  * @param string $password
  *   Password.
  * @param array $matches
  *   Array of Match objects on the password.
  *
  * @return float
  *   Minimum entropy for non-overlapping best matches of a password.
  */
 public function getMinimumEntropy($password, $matches)
 {
     $passwordLength = strlen($password);
     $entropyStack = array();
     // for the optimal sequence of matches up to k, holds the final match (match.end == k).
     // null means the sequence ends without a brute-force character.
     $backpointers = array();
     $bruteforceMatch = new Bruteforce($password, 0, $passwordLength - 1, $password);
     $charEntropy = log($bruteforceMatch->getCardinality(), 2);
     foreach (range(0, $passwordLength - 1) as $k) {
         // starting scenario to try and beat: adding a brute-force character to the minimum entropy sequence at k-1.
         $entropyStack[$k] = $this->prevValue($entropyStack, $k) + $charEntropy;
         $backpointers[$k] = null;
         foreach ($matches as $match) {
             if (!isset($match->begin) || $match->end != $k) {
                 continue;
             }
             // See if entropy prior to match + entropy of this match is less than
             // the current minimum top of the stack.
             $candidateEntropy = $this->prevValue($entropyStack, $match->begin) + $match->getEntropy();
             if ($candidateEntropy <= $entropyStack[$k]) {
                 $entropyStack[$k] = $candidateEntropy;
                 $backpointers[$k] = $match;
             }
         }
     }
     // Walk backwards and decode the best sequence
     $matchSequence = array();
     $k = $passwordLength - 1;
     while ($k >= 0) {
         $match = $backpointers[$k];
         if ($match) {
             $matchSequence[] = $match;
             $k = $match->begin - 1;
         } else {
             $k -= 1;
         }
     }
     $matchSequence = array_reverse($matchSequence);
     $s = 0;
     $matchSequenceCopy = array();
     // Handle subtrings that weren't matched as bruteforce match.
     foreach ($matchSequence as $match) {
         if ($match->begin - $s > 0) {
             $matchSequenceCopy[] = $this->makeBruteforceMatch($password, $s, $match->begin - 1, $bruteforceMatch->getCardinality());
         }
         $s = $match->end + 1;
         $matchSequenceCopy[] = $match;
     }
     if ($s < $passwordLength) {
         $matchSequenceCopy[] = $this->makeBruteforceMatch($password, $s, $passwordLength - 1, $bruteforceMatch->getCardinality());
     }
     $this->matchSequence = $matchSequenceCopy;
     $minEntropy = $entropyStack[$passwordLength - 1];
     return $minEntropy;
 }
Example #2
0
 public function testEntropy()
 {
     $match = new Bruteforce('99', 0, 1, '99');
     $this->assertSame(log(pow(10, 2), 2), $match->getEntropy());
     $password = '******';
     $match = new Bruteforce($password, 0, 3, $password);
     $this->assertSame(95, $match->getCardinality());
     $this->assertSame(log(pow(95, 4), 2), $match->getEntropy());
 }