/** * Generate a HOTP key collection based on a timestamp and window size * all keys that could exist between a start and end time will be included * in the returned array * @param string $key the key to use for hashing * @param int $window the size of the window a key is valid for in seconds * @param int $min the minimum window to accept before $timestamp * @param int $max the maximum window to accept after $timestamp * @param int $timestamp a timestamp to calculate for, defaults to time() * @return array of HOTPResult */ public static function generateByTimeWindow($key, $window, $min = -1, $max = 1, $timestamp = false) { if (!$timestamp && $timestamp !== 0) { $timestamp = HOTP::getTime(); } $counter = intval($timestamp / $window); $window = range($min, $max); $out = array(); for ($i = 0; $i < count($window); $i++) { $shift_counter = $window[$i]; $out[$shift_counter] = HOTP::generateByCounter($key, $counter + $shift_counter); } return $out; }
/** * {@inheritdoc} */ public function validate($otp, $counter = null) { if ($counter === null) { $counter = time(); } $window = $this->getWindow(); $counter = self::timestampToCounter($counter, $this->getTimeStep()); $valid = false; $offset = null; $counterLow = max(0, $counter - intval(floor($window / 2))); $counterHigh = max(0, $counter + intval(ceil($window / 2))); for ($current = $counterLow; $current <= $counterHigh; ++$current) { if ($otp === parent::calculate($current)) { $valid = true; $offset = $current - $counter; break; } } $this->lastCounterOffset = $offset; return $valid; }
public function testCheck1() { $hotp = new HOTP(new HOTPSecret("3132333435363738393031323334353637383930", HOTPSecret::FORMAT_HEX)); $this->assertSame(-1, $hotp->check("359152", 0, 1)); $this->assertSame(1, $hotp->check("359152", 1, 1)); $this->assertSame(0, $hotp->check("359152", 2, 1)); $this->assertSame(-1, $hotp->check("359152", 3, 1)); $this->assertSame(-1, $hotp->check("359152", 4, 1)); $this->assertSame(-1, $hotp->check("359152", 5, 1)); }
echo ' '; $method = 'to' . ucfirst(str_replace('HMAC', 'string', $type)); echo str_pad($calc == $hotp->{$method}(6) ? '[OK]' : '[FAIL]', 9, ' ', STR_PAD_LEFT); echo "\n"; } } echo <<<DOCBLOCK TOTP Tests: DOCBLOCK; echo "Time (sec) Value Pass/Fail\n"; echo "----------------------------------------------------------------------\n"; // now echo over the TOTP table foreach ($table['TOTP'] as $seed => $results) { $totp = HOTP::generateByTime($key, 30, $seed); $first = true; foreach ($results as $type => $calc) { if ($first) { echo str_pad($seed, 10, ' ', STR_PAD_LEFT); $first = false; } else { echo ' '; } echo ' '; echo str_pad($calc, 47, ' ', STR_PAD_RIGHT); echo ' '; $method = 'to' . ucfirst(str_replace('totp', 'hotp', $type)); echo str_pad($calc == $totp->{$method}(8) ? '[OK]' : '[FAIL]', 9, ' ', STR_PAD_LEFT); echo "\n"; }