Skip to content

Commit 2f13de8

Browse files
committed
Add assertions and modify annotations for better code clarity
Several assertions were added to verify the integrity of the timestamp and counter values. Also, param annotations were modified and added for better understanding of the input parameters and return values. Furthermore, the test parameters were changed from an Iterator to an iterable for a wider compatibility with different data types.
1 parent 0d35971 commit 2f13de8

File tree

6 files changed

+42
-19
lines changed

6 files changed

+42
-19
lines changed

phpstan-baseline.neon

-10
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,3 @@ parameters:
3939
message: "#^Parameter \\#1 \\$dateTime of method OTPHP\\\\Test\\\\ClockMock\\:\\:setDateTime\\(\\) expects DateTimeImmutable\\|null, DateTimeImmutable\\|false given\\.$#"
4040
count: 5
4141
path: tests/TOTPTest.php
42-
43-
-
44-
message: "#^Parameter \\#1 \\$otp of method OTPHP\\\\TOTP\\:\\:verify\\(\\) expects non\\-empty\\-string, string given\\.$#"
45-
count: 2
46-
path: tests/TOTPTest.php
47-
48-
-
49-
message: "#^Parameter \\#3 \\$leeway of method OTPHP\\\\TOTP\\:\\:verify\\(\\) expects int\\<0, max\\>\\|null, int given\\.$#"
50-
count: 2
51-
path: tests/TOTPTest.php

src/HOTP.php

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public static function generate(): self
4646
return self::createFromSecret(self::generateSecret());
4747
}
4848

49+
/**
50+
* @return 0|positive-int
51+
*/
4952
public function getCounter(): int
5053
{
5154
$value = $this->getParameter('counter');
@@ -63,6 +66,8 @@ public function getProvisioningUri(): string
6366

6467
/**
6568
* If the counter is not provided, the OTP is verified at the actual counter.
69+
*
70+
* @param null|0|positive-int $counter
6671
*/
6772
public function verify(string $otp, null|int $counter = null, null|int $window = null): bool
6873
{
@@ -112,6 +117,7 @@ private function getWindow(null|int $window): int
112117

113118
/**
114119
* @param non-empty-string $otp
120+
* @param 0|positive-int $counter
115121
* @param null|0|positive-int $window
116122
*/
117123
private function verifyOtpWithWindow(string $otp, int $counter, null|int $window): bool

src/OTP.php

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public function getQrCodeUri(string $uri, string $placeholder): string
3535
return str_replace($placeholder, $provisioning_uri, $uri);
3636
}
3737

38+
/**
39+
* @param 0|positive-int $input
40+
*/
3841
public function at(int $input): string
3942
{
4043
return $this->generateOTP($input);
@@ -51,6 +54,8 @@ final protected static function generateSecret(): string
5154
/**
5255
* The OTP at the specified input.
5356
*
57+
* @param 0|positive-int $input
58+
*
5459
* @return non-empty-string
5560
*/
5661
protected function generateOTP(int $input): string

src/OTPInterface.php

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public function setDigits(int $digits): void;
3535
public function setDigest(string $digest): void;
3636

3737
/**
38+
* Generate the OTP at the specified input.
39+
*
40+
* @param 0|positive-int $input
41+
*
3842
* @return non-empty-string Return the OTP at the specified timestamp
3943
*/
4044
public function at(int $input): string;

src/TOTP.php

+18-2
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,31 @@ public function expiresIn(): int
9090
return $period - ($this->clock->now()->getTimestamp() % $this->getPeriod());
9191
}
9292

93+
/**
94+
* The OTP at the specified input.
95+
*
96+
* @param 0|positive-int $input
97+
*/
9398
public function at(int $input): string
9499
{
95100
return $this->generateOTP($this->timecode($input));
96101
}
97102

98103
public function now(): string
99104
{
100-
return $this->at($this->clock->now()->getTimestamp());
105+
$timestamp = $this->clock->now()
106+
->getTimestamp();
107+
assert($timestamp >= 0, 'The timestamp must return a positive integer.');
108+
109+
return $this->at($timestamp);
101110
}
102111

103112
/**
104113
* If no timestamp is provided, the OTP is verified at the actual timestamp. When used, the leeway parameter will
105114
* allow time drift. The passed value is in seconds.
115+
*
116+
* @param 0|positive-int $timestamp
117+
* @param null|0|positive-int $leeway
106118
*/
107119
public function verify(string $otp, null|int $timestamp = null, null|int $leeway = null): bool
108120
{
@@ -118,8 +130,12 @@ public function verify(string $otp, null|int $timestamp = null, null|int $leeway
118130
$leeway < $this->getPeriod() || throw new InvalidArgumentException(
119131
'The leeway must be lower than the TOTP period'
120132
);
133+
$timestampMinusLeeway = $timestamp - $leeway;
134+
$timestampMinusLeeway >= 0 || throw new InvalidArgumentException(
135+
'The timestamp must be greater than or equal to the leeway.'
136+
);
121137

122-
return $this->compareOTP($this->at($timestamp - $leeway), $otp)
138+
return $this->compareOTP($this->at($timestampMinusLeeway), $otp)
123139
|| $this->compareOTP($this->at($timestamp), $otp)
124140
|| $this->compareOTP($this->at($timestamp + $leeway), $otp);
125141
}

tests/TOTPTest.php

+9-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use DateTimeImmutable;
88
use InvalidArgumentException;
9-
use Iterator;
109
use OTPHP\InternalClock;
1110
use OTPHP\TOTP;
1211
use OTPHP\TOTPInterface;
@@ -240,9 +239,9 @@ public function vectors($totp, $timestamp, $expected_value): void
240239
* @see https://tools.ietf.org/html/rfc6238#appendix-B
241240
* @see http://www.rfc-editor.org/errata_search.php?rfc=6238
242241
*
243-
* @return array<int, mixed[]>
242+
* @return iterable<int, mixed[]>
244243
*/
245-
public static function dataVectors(): Iterator
244+
public static function dataVectors(): iterable
246245
{
247246
$sha1key = Base32::encodeUpper('12345678901234567890');
248247
assert($sha1key !== '');
@@ -318,7 +317,10 @@ public function verifyOtpWithEpochInWindow(
318317
static::assertSame($expectedResult, $otp->verify($input, null, $leeway));
319318
}
320319

321-
public static function dataLeewayWithEpoch(): Iterator
320+
/**
321+
* @return iterable<array-key, int|string|bool>[]
322+
*/
323+
public static function dataLeewayWithEpoch(): iterable
322324
{
323325
yield 'Leeway of 10 seconds, **out** the period of 11sec (11 second before)' => [
324326
319_690_889,
@@ -377,7 +379,7 @@ public function qRCodeUri(): void
377379
/**
378380
* @return int[][]
379381
*/
380-
public static function dataRemainingTimeBeforeExpiration(): Iterator
382+
public static function dataRemainingTimeBeforeExpiration(): iterable
381383
{
382384
yield [1_644_926_810, 90, 40];
383385
yield [1_644_926_810, 30, 10];
@@ -394,9 +396,9 @@ public static function dataRemainingTimeBeforeExpiration(): Iterator
394396
}
395397

396398
/**
397-
* @return array<int, int|string|bool>[]
399+
* @return iterable<int, int|string|bool>[]
398400
*/
399-
public static function dataLeeway(): Iterator
401+
public static function dataLeeway(): iterable
400402
{
401403
yield 'Leeway of 10 seconds, **out** the period of 11sec (11 second before)' => [
402404
319_690_789,

0 commit comments

Comments
 (0)