-
Notifications
You must be signed in to change notification settings - Fork 0
/
PHPOTP.php
118 lines (105 loc) · 3.36 KB
/
PHPOTP.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
<?php
/*
* PHPOTP - An implementation of RFC 4226 (HOTP) and RFC 6238 (TOTP)
* as a PHP class.
* Copyright (C) 2014 David Ludlow
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* [email protected] Toledo, OH
*/
class PHPOTP {
/* See RFC 4226 and RFC 6238 */
protected $Algo='sha1';
protected $Digits=6;
protected $Interval=30; /* Seconds */
public function __construct() {
}
public function SetDigits($Value) { $this->Digits=$Value; }
public function SetInterval($Value) { $this->Interval=$Value; }
public function GetDigits() { return $this->Digits; }
public function GetInterval() { return $this->Interval; }
public function GetAlgo() { return $this->Algo; }
/* Input: $Seed is a binary string
* Input: $Count is an increasing 32-bit counter
* Return: The resulting OTP value represented as a string with leading zeros.
*/
public function HOTP($Seed, $Count=0) {
/* See RFC 4226 */
$BinCount=str_pad(pack('N', $Count), 8, chr(0), STR_PAD_LEFT);
$Hash=hash_hmac($this->Algo, $BinCount, $Seed, true);
$Offset=ord($Hash[19]) & 0xf;
$OTPVal=0;
for ($i=0; $i<4; $i++)
$OTPVal|=ord($Hash[$Offset+$i])<<(8*(3-$i));
$OTPVal&=0x7FFFFFFF;
$OTPVal%=pow(10, $this->Digits);
return str_pad(strval($OTPVal), $this->Digits, '0', STR_PAD_LEFT);
}
/* Input: $Seed is a binary string
* Return: The resulting OTP value represented as a string with leading zeros.
*/
public function TOTP($Seed,$Time=null) {
/* See RFC 6238 */
if ($Time===null)
$Time=time();
$TimeSlice=floor($Time/$this->Interval);
return $this->HOTP($Seed,$TimeSlice);
}
public function HOTPAsURI($Issuer,$Label,$Base32Seed,$Count=0) {
return
'otpauth://hotp/'.rawurlencode($Issuer).':'.rawurlencode($Label).
'?secret='.$Base32Seed.
'&algorithm='.$this->Algo.
'&digits='.$this->Digits.
'&count='.$Count.
'&issuer='.rawurlencode($Issuer);
}
public function TOTPAsURI($Issuer,$Label,$Base32Seed) {
return
'otpauth://totp/'.rawurlencode($Issuer).':'.rawurlencode($Label).
'?secret='.$Base32Seed.
'&algorithm='.$this->Algo.
'&digits='.$this->Digits.
'&period='.$this->Interval.
'&issuer='.rawurlencode($Issuer);
}
/*
Algorithms other than SHA1 don't seem to work, so this is disabled for now.
public function SetAlgo($Value) {
if (array_search($Value,hash_algos())===FALSE)
return false;
$this->Algo=$Value;
return true;
}
*/
public function GenSeed($Length=null) {
if ($Length===null) {
switch ($this->Algo) {
case 'md5': $Length=128/8; break;
case 'sha1': $Length=160/8; break;
case 'sha256': $Length=256/8; break;
case 'sha512': $Length=512/8; break;
default: $Length=160/8; break;
}
}
$Seed='';
mt_srand();
for ($i=0; $i<$Length; $i++) {
$Seed.=chr(mt_rand(0,255));
}
return $Seed;
}
}
?>