This repository has been archived by the owner on Sep 27, 2024. It is now read-only.
forked from rchouinard/rych-otp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TOTP.php
143 lines (125 loc) · 3.69 KB
/
TOTP.php
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?php
/**
* Ryan's OATH-OTP Library
*
* @package Rych\OTP
* @author Ryan Chouinard <rchouinard@gmail.com>
* @copyright Copyright (c) 2013, Ryan Chouinard
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*/
namespace Rych\OTP;
/**
* RFC-6238 Time-Based One-Time Passwords
*
* @package Rych\OTP
* @author Ryan Chouinard <rchouinard@gmail.com>
* @copyright Copyright (c) 2013, Ryan Chouinard
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*/
class TOTP extends HOTP
{
/**
* @var integer
*/
protected $timeStep;
/**
* Class constructor
*
* @param string|\Rych\OTP\Seed $secret The shared secret key as a string
* or an instance of {@link \Rych\OTP\Seed}.
* @param array $options An array of configuration options.
* @return void
*/
public function __construct($secret, array $options = array ())
{
$options = array_merge(
array (
'window' => 0,
'timestep' => 30,
),
array_change_key_case($options, CASE_LOWER)
);
$this->setTimeStep($options['timestep']);
parent::__construct($secret, $options);
}
/**
* Generate a one-time password from a given counter value
*
* @param integer $counter The counter value. Defaults to current
* timestamp.
* @return string Returns the generated one-time password.
*/
public function calculate($counter = null)
{
if ($counter === null) {
$counter = time();
}
$counter = $this->timestampToCounter($counter);
$otp = parent::calculate($counter);
return $otp;
}
/**
* Get the timestep value
*
* @return integer The timestep value.
*/
public function getTimeStep()
{
return $this->timeStep;
}
/**
* Set the timestep value
*
* @param integer $timeStep The timestep value.
* @return \Rych\OTP\TOTP Returns an instance of self for method chaining.
*/
public function setTimeStep($timeStep)
{
$timeStep = abs(intval($timeStep));
$this->timeStep = $timeStep;
return $this;
}
/**
* Validate an OTP
*
* @param string $otp The OTP value.
* @param integer $counter The counter value. Defaults to current
* timestamp.
* @return boolean Returns true if the supplied counter value is valid
* within the configured counter window, false otherwise.
*/
public function validate($otp, $counter = null)
{
if ($counter === null) {
$counter = time();
}
$window = $this->getWindow();
$counter = $this->timestampToCounter($counter);
$valid = false;
$offset = null;
$counterLow = max(0, $counter - intval(floor($window / 2)));
$counterHigh = max(0, $counter + intval(ceil($window / 2)));
for ($current = $counterLow; $current <= $counterHigh; ++$current) {
if ($otp == parent::calculate($current)) {
$valid = true;
$offset = $current - $counter;
break;
}
}
$this->lastValidCounterOffset = $offset;
return $valid;
}
/**
* Convert a timestamp into a usable counter value
*
* @param integer $timestamp A UNIX timestamp.
* @return integer The calculated counter value.
*/
private function timestampToCounter($timestamp)
{
$timeStep = $this->getTimeStep();
$timestamp = abs(intval($timestamp));
$counter = intval(($timestamp * 1000) / ($timeStep * 1000));
return $counter;
}
}