diff options
Diffstat (limited to 'vendor/spomky-labs/otphp/src/TOTP.php')
-rw-r--r-- | vendor/spomky-labs/otphp/src/TOTP.php | 92 |
1 files changed, 63 insertions, 29 deletions
diff --git a/vendor/spomky-labs/otphp/src/TOTP.php b/vendor/spomky-labs/otphp/src/TOTP.php index 3a7d72870..035e04f95 100644 --- a/vendor/spomky-labs/otphp/src/TOTP.php +++ b/vendor/spomky-labs/otphp/src/TOTP.php @@ -4,8 +4,9 @@ declare(strict_types=1); namespace OTPHP; -use function assert; use InvalidArgumentException; +use Psr\Clock\ClockInterface; +use function assert; use function is_int; /** @@ -13,16 +14,34 @@ use function is_int; */ final class TOTP extends OTP implements TOTPInterface { + private readonly ClockInterface $clock; + + public function __construct(string $secret, ?ClockInterface $clock = null) + { + parent::__construct($secret); + if ($clock === null) { + trigger_deprecation( + 'spomky-labs/otphp', + '11.3.0', + 'The parameter "$clock" will become mandatory in 12.0.0. Please set a valid PSR Clock implementation instead of "null".' + ); + $clock = new InternalClock(); + } + + $this->clock = $clock; + } + public static function create( null|string $secret = null, int $period = self::DEFAULT_PERIOD, string $digest = self::DEFAULT_DIGEST, int $digits = self::DEFAULT_DIGITS, - int $epoch = self::DEFAULT_EPOCH + int $epoch = self::DEFAULT_EPOCH, + ?ClockInterface $clock = null ): self { $totp = $secret !== null - ? self::createFromSecret($secret) - : self::generate() + ? self::createFromSecret($secret, $clock) + : self::generate($clock) ; $totp->setPeriod($period); $totp->setDigest($digest); @@ -32,9 +51,9 @@ final class TOTP extends OTP implements TOTPInterface return $totp; } - public static function createFromSecret(string $secret): self + public static function createFromSecret(string $secret, ?ClockInterface $clock = null): self { - $totp = new self($secret); + $totp = new self($secret, $clock); $totp->setPeriod(self::DEFAULT_PERIOD); $totp->setDigest(self::DEFAULT_DIGEST); $totp->setDigits(self::DEFAULT_DIGITS); @@ -43,9 +62,9 @@ final class TOTP extends OTP implements TOTPInterface return $totp; } - public static function generate(): self + public static function generate(?ClockInterface $clock = null): self { - return self::createFromSecret(self::generateSecret()); + return self::createFromSecret(self::generateSecret(), $clock); } public function getPeriod(): int @@ -68,9 +87,14 @@ final class TOTP extends OTP implements TOTPInterface { $period = $this->getPeriod(); - return $period - (time() % $this->getPeriod()); + return $period - ($this->clock->now()->getTimestamp() % $this->getPeriod()); } + /** + * The OTP at the specified input. + * + * @param 0|positive-int $input + */ public function at(int $input): string { return $this->generateOTP($this->timecode($input)); @@ -78,16 +102,24 @@ final class TOTP extends OTP implements TOTPInterface public function now(): string { - return $this->at(time()); + $timestamp = $this->clock->now() + ->getTimestamp(); + assert($timestamp >= 0, 'The timestamp must return a positive integer.'); + + return $this->at($timestamp); } /** * If no timestamp is provided, the OTP is verified at the actual timestamp. When used, the leeway parameter will * allow time drift. The passed value is in seconds. + * + * @param 0|positive-int $timestamp + * @param null|0|positive-int $leeway */ public function verify(string $otp, null|int $timestamp = null, null|int $leeway = null): bool { - $timestamp ??= time(); + $timestamp ??= $this->clock->now() + ->getTimestamp(); $timestamp >= 0 || throw new InvalidArgumentException('Timestamp must be at least 0.'); if ($leeway === null) { @@ -98,8 +130,12 @@ final class TOTP extends OTP implements TOTPInterface $leeway < $this->getPeriod() || throw new InvalidArgumentException( 'The leeway must be lower than the TOTP period' ); + $timestampMinusLeeway = $timestamp - $leeway; + $timestampMinusLeeway >= 0 || throw new InvalidArgumentException( + 'The timestamp must be greater than or equal to the leeway.' + ); - return $this->compareOTP($this->at($timestamp - $leeway), $otp) + return $this->compareOTP($this->at($timestampMinusLeeway), $otp) || $this->compareOTP($this->at($timestamp), $otp) || $this->compareOTP($this->at($timestamp + $leeway), $otp); } @@ -133,23 +169,21 @@ final class TOTP extends OTP implements TOTPInterface */ protected function getParameterMap(): array { - return array_merge( - parent::getParameterMap(), - [ - 'period' => static function ($value): int { - (int) $value > 0 || throw new InvalidArgumentException('Period must be at least 1.'); - - return (int) $value; - }, - 'epoch' => static function ($value): int { - (int) $value >= 0 || throw new InvalidArgumentException( - 'Epoch must be greater than or equal to 0.' - ); - - return (int) $value; - }, - ] - ); + return [ + ...parent::getParameterMap(), + 'period' => static function ($value): int { + (int) $value > 0 || throw new InvalidArgumentException('Period must be at least 1.'); + + return (int) $value; + }, + 'epoch' => static function ($value): int { + (int) $value >= 0 || throw new InvalidArgumentException( + 'Epoch must be greater than or equal to 0.' + ); + + return (int) $value; + }, + ]; } /** |