%PDF- %PDF-
Direktori : /www/varak.net/nextcloud.varak.net/apps/twofactor_totp/vendor/rullzer/easytotp/src/ |
Current File : /www/varak.net/nextcloud.varak.net/apps/twofactor_totp/vendor/rullzer/easytotp/src/TOTP.php |
<?php declare(strict_types=1); /** * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace EasyTOTP; class TOTP implements TOTPInterface { /** @var string */ private $secret; /** @var int */ private $digits; /** @var int */ private $offset; /** @var int */ private $timeStep; /** @var string */ private $hashFunction; /** @var TimeService */ private $timeService; public function __construct(string $secret, int $timeStep, int $digits, int $offset, string $hashFunction, TimeService $timeService) { $this->secret = $secret; $this->timeStep = $timeStep; $this->digits = $digits; $this->offset = $offset; $this->hashFunction = $hashFunction; $this->timeService = $timeService; } public function verify(string $otp, int $drift = 1, ?int $lastKnownCounter = null): TOTPResultInterface { $currentCounter = $this->getCurrentCounter(); $start = $currentCounter - $drift; $end = $currentCounter + $drift; for ($i = $start; $i <= $end; $i++) { // Skip counters smaller than the minimum if ($lastKnownCounter !== null && $i <= $lastKnownCounter) { continue; } if (hash_equals($this->hotp($i), $otp)) { return new TOTPValidResult( $i, $i - $currentCounter ); } } return new TOTPInvalidResult(); } public function getDigits(): int { return $this->digits; } public function getHashFunction(): string { return $this->hashFunction; } public function getOffset(): int { return $this->offset; } public function getSecret(): string { return $this->secret; } public function getTimeStep(): int { return $this->timeStep; } private function binaryCounter(int $counter): string { if (PHP_INT_SIZE === 4) { /* * Manually do 64bit magic * This will do boom in 2038 ;) */ return pack('N*', 0) . pack('N*', $counter); } return pack('J', $counter); } /** * See https://tools.ietf.org/html/rfc4226#section-5 */ private function hotp(int $counter): string { $hash = hash_hmac( $this->hashFunction, $this->binaryCounter($counter), $this->secret, true ); return str_pad((string)$this->truncate($hash), $this->digits, '0', STR_PAD_LEFT); } private function truncate(string $hash): int { $offset = \ord($hash[strlen($hash)-1]) & 0xf; return ( ((\ord($hash[$offset + 0]) & 0x7f) << 24) | ((\ord($hash[$offset + 1]) & 0xff) << 16) | ((\ord($hash[$offset + 2]) & 0xff) << 8) | (\ord($hash[$offset + 3]) & 0xff) ) % (10 ** $this->digits); } private function getCurrentCounter(): int { return (int)floor(($this->timeService->getTime() + $this->offset) / $this->timeStep); } }