-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathHOTP.php
More file actions
105 lines (91 loc) · 3.47 KB
/
HOTP.php
File metadata and controls
105 lines (91 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php
declare(strict_types=1);
namespace jakobo\HOTP;
/**
* HOTP Class
* Based on the work of OAuth, and the sample implementation of HMAC OTP
* http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04#appendix-D
* @author Jakob Heuser (firstname)@felocity.com
* @copyright 2011-2020
* @license BSD-3-Clause
*/
class HOTP
{
/**
* Generate a HOTP key based on a counter value (event based HOTP)
* @param string $key the key to use for hashing
* @param int $counter the number of attempts represented in this hashing
* @return HOTPResult a HOTP Result which can be truncated or output
*/
public static function generateByCounter(string $key, int $counter): HOTPResult
{
// The counter value can be more than one byte long,
// so we need to pack it down properly.
$curCounter = [ 0, 0, 0, 0, 0, 0, 0, 0 ];
for ($i = 7; $i >= 0; $i--) {
$curCounter[$i] = pack('C*', $counter);
$counter = $counter >> 8;
}
// Pad to 8 chars
$binCounter = str_pad(implode("", $curCounter), 8, chr(0), STR_PAD_LEFT);
// HMAC
$hash = hash_hmac('sha1', $binCounter, $key);
return new HOTPResult($hash);
}
/**
* Generate a HOTP key based on a timestamp and window size
* @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|false $timestamp a timestamp to calculate for, defaults to time()
* @return HOTPResult a HOTP Result which can be truncated or output
*/
public static function generateByTime(string $key, int $window, int|false $timestamp = false): HOTPResult
{
if (!$timestamp && $timestamp !== 0) {
// @codeCoverageIgnoreStart
$timestamp = self::getTime();
// @codeCoverageIgnoreEnd
}
$counter = intval($timestamp / $window) ;
return self::generateByCounter($key, $counter);
}
/**
* 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|false $timestamp a timestamp to calculate for, defaults to time()
* @return HOTPResult[]
*/
public static function generateByTimeWindow(string $key, int $window, int $min = -1, int $max = 1, int|false $timestamp = false): array
{
if (!$timestamp && $timestamp !== 0) {
// @codeCoverageIgnoreStart
$timestamp = self::getTime();
// @codeCoverageIgnoreEnd
}
$counter = intval($timestamp / $window);
$window = range($min, $max);
$out = [];
foreach ($window as $value) {
$shiftCounter = $counter + $value;
$out[$shiftCounter] = self::generateByCounter($key, $shiftCounter);
}
return $out;
}
/**
* Gets the current time
* Ensures we are operating in UTC for the entire framework
* Restores the timezone on exit.
* @return int the current time
* @codeCoverageIgnore
*/
public static function getTime(): int
{
// PHP's time is always UTC
return time();
}
}