PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.2.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.2.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / libs / Authenticator / TwoFactorAuthenticator.php
matomo / app / libs / Authenticator Last commit date
LICENSE.md 6 years ago README.md 6 years ago TwoFactorAuthenticator.php 2 years ago
TwoFactorAuthenticator.php
225 lines
1 <?php
2
3 namespace {
4 /**
5 * PHP Class for handling Google Authenticator 2-factor authentication
6 *
7 * @author Michael Kliewe
8 * @copyright 2012 Michael Kliewe
9 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
10 * @link http://www.phpgangsta.de/
11 *
12 * small adjustments by @sgiehl / matomo.org
13 * - renamed class
14 * - removed method getQRCodeGoogleUrl
15 * small adjustments by matomo.org
16 * - use better random secret generator
17 */
18 class TwoFactorAuthenticator
19 {
20 protected $_codeLength = 6;
21 /**
22 * Create new secret.
23 * 16 characters, randomly chosen from the allowed base32 characters.
24 *
25 * @param int $secretLength
26 * @return string
27 */
28 public function createSecret($secretLength = 16)
29 {
30 $validChars = $this->_getBase32LookupTable();
31 unset($validChars[32]);
32 // modified by matomo.org
33 return \Piwik\Common::getRandomString($secretLength, \implode('', $validChars));
34 }
35 /**
36 * Calculate the code, with given secret and point in time
37 *
38 * @param string $secret
39 * @param int|null $timeSlice
40 * @return string
41 */
42 public function getCode($secret, $timeSlice = null)
43 {
44 if ($timeSlice === null) {
45 $timeSlice = \floor(\time() / 30);
46 }
47 $secretkey = $this->_base32Decode($secret);
48 // Pack time into binary string
49 $time = \chr(0) . \chr(0) . \chr(0) . \chr(0) . \pack('N*', $timeSlice);
50 // Hash it with users secret key
51 $hm = \hash_hmac('SHA1', $time, $secretkey, \true);
52 // Use last nipple of result as index/offset
53 $offset = \ord(\substr($hm, -1)) & 0xf;
54 // grab 4 bytes of the result
55 $hashpart = \substr($hm, $offset, 4);
56 // Unpak binary value
57 $value = \unpack('N', $hashpart);
58 $value = $value[1];
59 // Only 32 bits
60 $value = $value & 0x7fffffff;
61 $modulo = \pow(10, $this->_codeLength);
62 return \str_pad($value % $modulo, $this->_codeLength, '0', \STR_PAD_LEFT);
63 }
64 /**
65 * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now
66 *
67 * @param string $secret
68 * @param string $code
69 * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
70 * @param int|null $currentTimeSlice time slice if we want use other that time()
71 * @return bool
72 */
73 public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
74 {
75 if ($currentTimeSlice === null) {
76 $currentTimeSlice = \floor(\time() / 30);
77 }
78 for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
79 $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
80 if ($calculatedCode == $code) {
81 return \true;
82 }
83 }
84 return \false;
85 }
86 /**
87 * Set the code length, should be >=6
88 *
89 * @param int $length
90 * @return self
91 */
92 public function setCodeLength($length)
93 {
94 $this->_codeLength = $length;
95 return $this;
96 }
97 /**
98 * Helper class to decode base32
99 *
100 * @param $secret
101 * @return bool|string
102 */
103 protected function _base32Decode($secret)
104 {
105 if (empty($secret)) {
106 return '';
107 }
108 $base32chars = $this->_getBase32LookupTable();
109 $base32charsFlipped = \array_flip($base32chars);
110 $paddingCharCount = \substr_count($secret, $base32chars[32]);
111 $allowedValues = array(6, 4, 3, 1, 0);
112 if (!\in_array($paddingCharCount, $allowedValues)) {
113 return \false;
114 }
115 for ($i = 0; $i < 4; $i++) {
116 if ($paddingCharCount == $allowedValues[$i] && \substr($secret, -$allowedValues[$i]) != \str_repeat($base32chars[32], $allowedValues[$i])) {
117 return \false;
118 }
119 }
120 $secret = \str_replace('=', '', $secret);
121 $secret = \str_split($secret);
122 $binaryString = "";
123 for ($i = 0; $i < \count($secret); $i = $i + 8) {
124 $x = "";
125 if (!\in_array($secret[$i], $base32chars)) {
126 return \false;
127 }
128 for ($j = 0; $j < 8; $j++) {
129 $x .= \str_pad(\base_convert($base32charsFlipped[@$secret[$i + $j]] ?? '', 10, 2), 5, '0', \STR_PAD_LEFT);
130 }
131 $eightBits = \str_split($x, 8);
132 for ($z = 0; $z < \count($eightBits); $z++) {
133 $binaryString .= ($y = \chr(\base_convert($eightBits[$z], 2, 10))) || \ord($y) == 48 ? $y : "";
134 }
135 }
136 return $binaryString;
137 }
138 /**
139 * Helper class to encode base32
140 *
141 * @param string $secret
142 * @param bool $padding
143 * @return string
144 */
145 protected function _base32Encode($secret, $padding = \true)
146 {
147 if (empty($secret)) {
148 return '';
149 }
150 $base32chars = $this->_getBase32LookupTable();
151 $secret = \str_split($secret);
152 $binaryString = "";
153 for ($i = 0; $i < \count($secret); $i++) {
154 $binaryString .= \str_pad(\base_convert(\ord($secret[$i]), 10, 2), 8, '0', \STR_PAD_LEFT);
155 }
156 $fiveBitBinaryArray = \str_split($binaryString, 5);
157 $base32 = "";
158 $i = 0;
159 while ($i < \count($fiveBitBinaryArray)) {
160 $base32 .= $base32chars[\base_convert(\str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)];
161 $i++;
162 }
163 if ($padding && ($x = \strlen($binaryString) % 40) != 0) {
164 if ($x == 8) {
165 $base32 .= \str_repeat($base32chars[32], 6);
166 } elseif ($x == 16) {
167 $base32 .= \str_repeat($base32chars[32], 4);
168 } elseif ($x == 24) {
169 $base32 .= \str_repeat($base32chars[32], 3);
170 } elseif ($x == 32) {
171 $base32 .= $base32chars[32];
172 }
173 }
174 return $base32;
175 }
176 /**
177 * Get array with all 32 characters for decoding from/encoding to base32
178 *
179 * @return array
180 */
181 protected function _getBase32LookupTable()
182 {
183 return array(
184 'A',
185 'B',
186 'C',
187 'D',
188 'E',
189 'F',
190 'G',
191 'H',
192 // 7
193 'I',
194 'J',
195 'K',
196 'L',
197 'M',
198 'N',
199 'O',
200 'P',
201 // 15
202 'Q',
203 'R',
204 'S',
205 'T',
206 'U',
207 'V',
208 'W',
209 'X',
210 // 23
211 'Y',
212 'Z',
213 '2',
214 '3',
215 '4',
216 '5',
217 '6',
218 '7',
219 // 31
220 '=',
221 );
222 }
223 }
224 }
225