Spomky-Labs / otphp

:closed_lock_with_key: A PHP library for generating one time passwords according to RFC 4226 (HOTP) and the RFC 6238 (TOTP)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TOTP -> verify doesn't seem to be correctly implemented to include leeway

aramhovsepyan opened this issue · comments

Version(s) affected

11.0

Description

Hi,
We've noticed that the latest implementation of the verify method in TOTP.php seems to be completely off. It should compare all possible otps between now and each period increment up to leeway. The current implementation only compares the current otp, the one +leeway and -leeway.
The presumably buggy code seems to appear in the 15Feb commit.

Apologies if I've missed something and thanks for the great work!

How to reproduce

Leeway = 4 used to work as +-4 periods before and after, now it works as +-4 seconds before and after.
PS: this makes it impossible to use a period of 30seconds and have a window of 1 minute extra.

Possible Solution

Bring back the old implementation.

Additional Context

No response

Hi,

Leeway = 4 used to work as +-4 periods before and after, now it works as +-4 seconds before and after.

This is correct. The behavior of the leeway parameter changed in v11. This has been introduced by #152.

PS: this makes it impossible to use a period of 30seconds and have a window of 1 minute extra.
This is right. This is actually the expected protection induced by this PR. The problem with the previous version is that it is easy to reduce the security of the OTP. With a window of 4, you divide by 9 the number of possibilities (4 periods before and after + the current period).

If you still need such flexibility, you have to implement it explicitly:

$window = abs($window);

for ($i = 0; $i <= $window; ++$i) {
    $next = $i * $this->getPeriod() + $timestamp;
    $previous = -$i * $this->getPeriod() + $timestamp;
    $valid = $this->compareOTP($this->at($next), $otp) ||
    $this->compareOTP($this->at($previous), $otp);

    if ($valid) {
        return true;
    }
}

return false;