Rev 846 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
827 | daniel-mar | 1 | <?php |
2 | |||
3 | /** |
||
4 | * Wrapper around hash() and hash_hmac() functions supporting truncated hashes |
||
5 | * such as sha256-96. Any hash algorithm returned by hash_algos() (and |
||
6 | * truncated versions thereof) are supported. |
||
7 | * |
||
8 | * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will |
||
9 | * return the HMAC as opposed to the hash. |
||
10 | * |
||
11 | * Here's a short example of how to use this library: |
||
12 | * <code> |
||
13 | * <?php |
||
14 | * include 'vendor/autoload.php'; |
||
15 | * |
||
16 | * $hash = new \phpseclib3\Crypt\Hash('sha512'); |
||
17 | * |
||
18 | * $hash->setKey('abcdefg'); |
||
19 | * |
||
20 | * echo base64_encode($hash->hash('abcdefg')); |
||
21 | * ?> |
||
22 | * </code> |
||
23 | * |
||
874 | daniel-mar | 24 | * @category Crypt |
25 | * @package Hash |
||
827 | daniel-mar | 26 | * @author Jim Wigginton <terrafrost@php.net> |
27 | * @copyright 2015 Jim Wigginton |
||
28 | * @author Andreas Fischer <bantu@phpbb.com> |
||
29 | * @copyright 2015 Andreas Fischer |
||
30 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
||
31 | * @link http://phpseclib.sourceforge.net |
||
32 | */ |
||
33 | |||
34 | namespace phpseclib3\Crypt; |
||
35 | |||
36 | use phpseclib3\Common\Functions\Strings; |
||
37 | use phpseclib3\Exception\InsufficientSetupException; |
||
38 | use phpseclib3\Exception\UnsupportedAlgorithmException; |
||
39 | use phpseclib3\Math\BigInteger; |
||
40 | use phpseclib3\Math\PrimeField; |
||
41 | |||
42 | /** |
||
874 | daniel-mar | 43 | * @package Hash |
827 | daniel-mar | 44 | * @author Jim Wigginton <terrafrost@php.net> |
45 | * @author Andreas Fischer <bantu@phpbb.com> |
||
874 | daniel-mar | 46 | * @access public |
827 | daniel-mar | 47 | */ |
48 | class Hash |
||
49 | { |
||
50 | /** |
||
51 | * Padding Types |
||
52 | * |
||
874 | daniel-mar | 53 | * @access private |
827 | daniel-mar | 54 | */ |
55 | const PADDING_KECCAK = 1; |
||
56 | |||
57 | /** |
||
58 | * Padding Types |
||
59 | * |
||
874 | daniel-mar | 60 | * @access private |
827 | daniel-mar | 61 | */ |
62 | const PADDING_SHA3 = 2; |
||
63 | |||
64 | /** |
||
65 | * Padding Types |
||
66 | * |
||
874 | daniel-mar | 67 | * @access private |
827 | daniel-mar | 68 | */ |
69 | const PADDING_SHAKE = 3; |
||
70 | |||
71 | /** |
||
72 | * Padding Type |
||
73 | * |
||
74 | * Only used by SHA3 |
||
75 | * |
||
76 | * @var int |
||
874 | daniel-mar | 77 | * @access private |
827 | daniel-mar | 78 | */ |
79 | private $paddingType = 0; |
||
80 | |||
81 | /** |
||
82 | * Hash Parameter |
||
83 | * |
||
84 | * @see self::setHash() |
||
85 | * @var int |
||
874 | daniel-mar | 86 | * @access private |
827 | daniel-mar | 87 | */ |
88 | private $hashParam; |
||
89 | |||
90 | /** |
||
91 | * Byte-length of hash output (Internal HMAC) |
||
92 | * |
||
93 | * @see self::setHash() |
||
94 | * @var int |
||
874 | daniel-mar | 95 | * @access private |
827 | daniel-mar | 96 | */ |
97 | private $length; |
||
98 | |||
99 | /** |
||
100 | * Hash Algorithm |
||
101 | * |
||
102 | * @see self::setHash() |
||
103 | * @var string |
||
874 | daniel-mar | 104 | * @access private |
827 | daniel-mar | 105 | */ |
106 | private $algo; |
||
107 | |||
108 | /** |
||
109 | * Key |
||
110 | * |
||
111 | * @see self::setKey() |
||
112 | * @var string |
||
874 | daniel-mar | 113 | * @access private |
827 | daniel-mar | 114 | */ |
115 | private $key = false; |
||
116 | |||
117 | /** |
||
118 | * Nonce |
||
119 | * |
||
120 | * @see self::setNonce() |
||
121 | * @var string |
||
874 | daniel-mar | 122 | * @access private |
827 | daniel-mar | 123 | */ |
124 | private $nonce = false; |
||
125 | |||
126 | /** |
||
127 | * Hash Parameters |
||
128 | * |
||
129 | * @var array |
||
874 | daniel-mar | 130 | * @access private |
827 | daniel-mar | 131 | */ |
132 | private $parameters = []; |
||
133 | |||
134 | /** |
||
135 | * Computed Key |
||
136 | * |
||
137 | * @see self::_computeKey() |
||
138 | * @var string |
||
874 | daniel-mar | 139 | * @access private |
827 | daniel-mar | 140 | */ |
141 | private $computedKey = false; |
||
142 | |||
143 | /** |
||
144 | * Outer XOR (Internal HMAC) |
||
145 | * |
||
146 | * Used only for sha512/* |
||
147 | * |
||
148 | * @see self::hash() |
||
149 | * @var string |
||
874 | daniel-mar | 150 | * @access private |
827 | daniel-mar | 151 | */ |
152 | private $opad; |
||
153 | |||
154 | /** |
||
155 | * Inner XOR (Internal HMAC) |
||
156 | * |
||
157 | * Used only for sha512/* |
||
158 | * |
||
159 | * @see self::hash() |
||
160 | * @var string |
||
874 | daniel-mar | 161 | * @access private |
827 | daniel-mar | 162 | */ |
163 | private $ipad; |
||
164 | |||
165 | /** |
||
166 | * Recompute AES Key |
||
167 | * |
||
168 | * Used only for umac |
||
169 | * |
||
170 | * @see self::hash() |
||
171 | * @var boolean |
||
874 | daniel-mar | 172 | * @access private |
827 | daniel-mar | 173 | */ |
174 | private $recomputeAESKey; |
||
175 | |||
176 | /** |
||
177 | * umac cipher object |
||
178 | * |
||
179 | * @see self::hash() |
||
180 | * @var \phpseclib3\Crypt\AES |
||
874 | daniel-mar | 181 | * @access private |
827 | daniel-mar | 182 | */ |
183 | private $c; |
||
184 | |||
185 | /** |
||
186 | * umac pad |
||
187 | * |
||
188 | * @see self::hash() |
||
189 | * @var string |
||
874 | daniel-mar | 190 | * @access private |
827 | daniel-mar | 191 | */ |
192 | private $pad; |
||
193 | |||
194 | /**#@+ |
||
195 | * UMAC variables |
||
196 | * |
||
197 | * @var PrimeField |
||
198 | */ |
||
199 | private static $factory36; |
||
200 | private static $factory64; |
||
201 | private static $factory128; |
||
202 | private static $offset64; |
||
203 | private static $offset128; |
||
204 | private static $marker64; |
||
205 | private static $marker128; |
||
206 | private static $maxwordrange64; |
||
207 | private static $maxwordrange128; |
||
208 | /**#@-*/ |
||
209 | |||
210 | /** |
||
211 | * Default Constructor. |
||
212 | * |
||
213 | * @param string $hash |
||
874 | daniel-mar | 214 | * @access public |
827 | daniel-mar | 215 | */ |
216 | public function __construct($hash = 'sha256') |
||
217 | { |
||
218 | $this->setHash($hash); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Sets the key for HMACs |
||
223 | * |
||
224 | * Keys can be of any length. |
||
225 | * |
||
874 | daniel-mar | 226 | * @access public |
827 | daniel-mar | 227 | * @param string $key |
228 | */ |
||
229 | public function setKey($key = false) |
||
230 | { |
||
231 | $this->key = $key; |
||
232 | $this->computeKey(); |
||
233 | $this->recomputeAESKey = true; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Sets the nonce for UMACs |
||
238 | * |
||
239 | * Keys can be of any length. |
||
240 | * |
||
874 | daniel-mar | 241 | * @access public |
827 | daniel-mar | 242 | * @param string $nonce |
243 | */ |
||
244 | public function setNonce($nonce = false) |
||
245 | { |
||
246 | switch (true) { |
||
247 | case !is_string($nonce): |
||
248 | case strlen($nonce) > 0 && strlen($nonce) <= 16: |
||
249 | $this->recomputeAESKey = true; |
||
250 | $this->nonce = $nonce; |
||
251 | return; |
||
252 | } |
||
253 | |||
254 | throw new \LengthException('The nonce length must be between 1 and 16 bytes, inclusive'); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Pre-compute the key used by the HMAC |
||
259 | * |
||
260 | * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes |
||
261 | * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." |
||
262 | * |
||
263 | * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ |
||
264 | * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during |
||
265 | * every call |
||
266 | * |
||
874 | daniel-mar | 267 | * @access private |
827 | daniel-mar | 268 | */ |
269 | private function computeKey() |
||
270 | { |
||
271 | if ($this->key === false) { |
||
272 | $this->computedKey = false; |
||
273 | return; |
||
274 | } |
||
275 | |||
276 | if (strlen($this->key) <= $this->getBlockLengthInBytes()) { |
||
277 | $this->computedKey = $this->key; |
||
278 | return; |
||
279 | } |
||
280 | |||
281 | $this->computedKey = is_array($this->algo) ? |
||
282 | call_user_func($this->algo, $this->key) : |
||
283 | hash($this->algo, $this->key, true); |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Gets the hash function. |
||
288 | * |
||
289 | * As set by the constructor or by the setHash() method. |
||
290 | * |
||
874 | daniel-mar | 291 | * @access public |
827 | daniel-mar | 292 | * @return string |
293 | */ |
||
294 | public function getHash() |
||
295 | { |
||
296 | return $this->hashParam; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Sets the hash function. |
||
301 | * |
||
874 | daniel-mar | 302 | * @access public |
827 | daniel-mar | 303 | * @param string $hash |
304 | */ |
||
305 | public function setHash($hash) |
||
306 | { |
||
307 | $this->hashParam = $hash = strtolower($hash); |
||
308 | switch ($hash) { |
||
309 | case 'umac-32': |
||
310 | case 'umac-64': |
||
311 | case 'umac-96': |
||
312 | case 'umac-128': |
||
313 | $this->blockSize = 128; |
||
314 | $this->length = abs(substr($hash, -3)) >> 3; |
||
315 | $this->algo = 'umac'; |
||
316 | return; |
||
317 | case 'md2-96': |
||
318 | case 'md5-96': |
||
319 | case 'sha1-96': |
||
320 | case 'sha224-96': |
||
321 | case 'sha256-96': |
||
322 | case 'sha384-96': |
||
323 | case 'sha512-96': |
||
324 | case 'sha512/224-96': |
||
325 | case 'sha512/256-96': |
||
326 | $hash = substr($hash, 0, -3); |
||
327 | $this->length = 12; // 96 / 8 = 12 |
||
328 | break; |
||
329 | case 'md2': |
||
330 | case 'md5': |
||
331 | $this->length = 16; |
||
332 | break; |
||
333 | case 'sha1': |
||
334 | $this->length = 20; |
||
335 | break; |
||
336 | case 'sha224': |
||
337 | case 'sha512/224': |
||
338 | case 'sha3-224': |
||
339 | $this->length = 28; |
||
340 | break; |
||
341 | case 'keccak256': |
||
342 | $this->paddingType = self::PADDING_KECCAK; |
||
343 | // fall-through |
||
344 | case 'sha256': |
||
345 | case 'sha512/256': |
||
346 | case 'sha3-256': |
||
347 | $this->length = 32; |
||
348 | break; |
||
349 | case 'sha384': |
||
350 | case 'sha3-384': |
||
351 | $this->length = 48; |
||
352 | break; |
||
353 | case 'sha512': |
||
354 | case 'sha3-512': |
||
355 | $this->length = 64; |
||
356 | break; |
||
357 | default: |
||
358 | if (preg_match('#^(shake(?:128|256))-(\d+)$#', $hash, $matches)) { |
||
359 | $this->paddingType = self::PADDING_SHAKE; |
||
360 | $hash = $matches[1]; |
||
361 | $this->length = $matches[2] >> 3; |
||
362 | } else { |
||
363 | throw new UnsupportedAlgorithmException( |
||
364 | "$hash is not a supported algorithm" |
||
365 | ); |
||
366 | } |
||
367 | } |
||
368 | |||
369 | switch ($hash) { |
||
370 | case 'md2': |
||
371 | case 'md2-96': |
||
372 | $this->blockSize = 128; |
||
373 | break; |
||
374 | case 'md5-96': |
||
375 | case 'sha1-96': |
||
376 | case 'sha224-96': |
||
377 | case 'sha256-96': |
||
378 | case 'md5': |
||
379 | case 'sha1': |
||
380 | case 'sha224': |
||
381 | case 'sha256': |
||
382 | $this->blockSize = 512; |
||
383 | break; |
||
384 | case 'sha3-224': |
||
385 | $this->blockSize = 1152; // 1600 - 2*224 |
||
386 | break; |
||
387 | case 'sha3-256': |
||
388 | case 'shake256': |
||
389 | case 'keccak256': |
||
390 | $this->blockSize = 1088; // 1600 - 2*256 |
||
391 | break; |
||
392 | case 'sha3-384': |
||
393 | $this->blockSize = 832; // 1600 - 2*384 |
||
394 | break; |
||
395 | case 'sha3-512': |
||
396 | $this->blockSize = 576; // 1600 - 2*512 |
||
397 | break; |
||
398 | case 'shake128': |
||
399 | $this->blockSize = 1344; // 1600 - 2*128 |
||
400 | break; |
||
401 | default: |
||
402 | $this->blockSize = 1024; |
||
403 | } |
||
404 | |||
405 | if (in_array(substr($hash, 0, 5), ['sha3-', 'shake', 'kecca'])) { |
||
406 | // PHP 7.1.0 introduced support for "SHA3 fixed mode algorithms": |
||
407 | // http://php.net/ChangeLog-7.php#7.1.0 |
||
408 | if (version_compare(PHP_VERSION, '7.1.0') < 0 || substr($hash, 0, 5) != 'sha3-') { |
||
409 | //preg_match('#(\d+)$#', $hash, $matches); |
||
410 | //$this->parameters['capacity'] = 2 * $matches[1]; // 1600 - $this->blockSize |
||
411 | //$this->parameters['rate'] = 1600 - $this->parameters['capacity']; // == $this->blockSize |
||
412 | if (!$this->paddingType) { |
||
413 | $this->paddingType = self::PADDING_SHA3; |
||
414 | } |
||
415 | $this->parameters = [ |
||
416 | 'capacity' => 1600 - $this->blockSize, |
||
417 | 'rate' => $this->blockSize, |
||
418 | 'length' => $this->length, |
||
419 | 'padding' => $this->paddingType |
||
420 | ]; |
||
421 | $hash = ['phpseclib3\Crypt\Hash', PHP_INT_SIZE == 8 ? 'sha3_64' : 'sha3_32']; |
||
422 | } |
||
423 | } |
||
424 | |||
425 | if ($hash == 'sha512/224' || $hash == 'sha512/256') { |
||
426 | // PHP 7.1.0 introduced sha512/224 and sha512/256 support: |
||
427 | // http://php.net/ChangeLog-7.php#7.1.0 |
||
428 | if (version_compare(PHP_VERSION, '7.1.0') < 0) { |
||
429 | // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24 |
||
430 | $initial = $hash == 'sha512/256' ? |
||
431 | [ |
||
432 | '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD', |
||
433 | '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2' |
||
434 | ] : |
||
435 | [ |
||
436 | '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF', |
||
437 | '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1' |
||
438 | ]; |
||
439 | for ($i = 0; $i < 8; $i++) { |
||
440 | $initial[$i] = new BigInteger($initial[$i], 16); |
||
441 | $initial[$i]->setPrecision(64); |
||
442 | } |
||
443 | |||
444 | $this->parameters = compact('initial'); |
||
445 | |||
446 | $hash = ['phpseclib3\Crypt\Hash', 'sha512']; |
||
447 | } |
||
448 | } |
||
449 | |||
450 | if (is_array($hash)) { |
||
451 | $b = $this->blockSize >> 3; |
||
452 | $this->ipad = str_repeat(chr(0x36), $b); |
||
453 | $this->opad = str_repeat(chr(0x5C), $b); |
||
454 | } |
||
455 | |||
456 | $this->algo = $hash; |
||
457 | |||
458 | $this->computeKey(); |
||
459 | } |
||
460 | |||
461 | /** |
||
462 | * KDF: Key-Derivation Function |
||
463 | * |
||
464 | * The key-derivation function generates pseudorandom bits used to key the hash functions. |
||
465 | * |
||
466 | * @param int $index a non-negative integer less than 2^64 |
||
467 | * @param int $numbytes a non-negative integer less than 2^64 |
||
468 | * @return string string of length numbytes bytes |
||
469 | */ |
||
470 | private function kdf($index, $numbytes) |
||
471 | { |
||
472 | $this->c->setIV(pack('N4', 0, $index, 0, 1)); |
||
473 | |||
474 | return $this->c->encrypt(str_repeat("\0", $numbytes)); |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * PDF Algorithm |
||
479 | * |
||
480 | * @return string string of length taglen bytes. |
||
481 | */ |
||
482 | private function pdf() |
||
483 | { |
||
484 | $k = $this->key; |
||
485 | $nonce = $this->nonce; |
||
486 | $taglen = $this->length; |
||
487 | |||
488 | // |
||
489 | // Extract and zero low bit(s) of Nonce if needed |
||
490 | // |
||
491 | if ($taglen <= 8) { |
||
492 | $last = strlen($nonce) - 1; |
||
493 | $mask = $taglen == 4 ? "\3" : "\1"; |
||
494 | $index = $nonce[$last] & $mask; |
||
495 | $nonce[$last] = $nonce[$last] ^ $index; |
||
496 | } |
||
497 | |||
498 | // |
||
499 | // Make Nonce BLOCKLEN bytes by appending zeroes if needed |
||
500 | // |
||
501 | $nonce = str_pad($nonce, 16, "\0"); |
||
502 | |||
503 | // |
||
504 | // Generate subkey, encipher and extract indexed substring |
||
505 | // |
||
506 | $kp = $this->kdf(0, 16); |
||
507 | $c = new AES('ctr'); |
||
508 | $c->disablePadding(); |
||
509 | $c->setKey($kp); |
||
510 | $c->setIV($nonce); |
||
511 | $t = $c->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); |
||
512 | |||
513 | // we could use ord() but per https://paragonie.com/blog/2016/06/constant-time-encoding-boring-cryptography-rfc-4648-and-you |
||
514 | // unpack() doesn't leak timing info |
||
515 | return $taglen <= 8 ? |
||
516 | substr($t, unpack('C', $index)[1] * $taglen, $taglen) : |
||
517 | substr($t, 0, $taglen); |
||
518 | } |
||
519 | |||
520 | /** |
||
521 | * UHASH Algorithm |
||
522 | * |
||
523 | * @param string $m string of length less than 2^67 bits. |
||
524 | * @param int $taglen the integer 4, 8, 12 or 16. |
||
525 | * @return string string of length taglen bytes. |
||
526 | */ |
||
527 | private function uhash($m, $taglen) |
||
528 | { |
||
529 | // |
||
530 | // One internal iteration per 4 bytes of output |
||
531 | // |
||
532 | $iters = $taglen >> 2; |
||
533 | |||
534 | // |
||
535 | // Define total key needed for all iterations using KDF. |
||
536 | // L1Key reuses most key material between iterations. |
||
537 | // |
||
538 | //$L1Key = $this->kdf(1, 1024 + ($iters - 1) * 16); |
||
539 | $L1Key = $this->kdf(1, (1024 + ($iters - 1)) * 16); |
||
540 | $L2Key = $this->kdf(2, $iters * 24); |
||
541 | $L3Key1 = $this->kdf(3, $iters * 64); |
||
542 | $L3Key2 = $this->kdf(4, $iters * 4); |
||
543 | |||
544 | // |
||
545 | // For each iteration, extract key and do three-layer hash. |
||
546 | // If bytelength(M) <= 1024, then skip L2-HASH. |
||
547 | // |
||
548 | $y = ''; |
||
549 | for ($i = 0; $i < $iters; $i++) { |
||
550 | $L1Key_i = substr($L1Key, $i * 16, 1024); |
||
551 | $L2Key_i = substr($L2Key, $i * 24, 24); |
||
552 | $L3Key1_i = substr($L3Key1, $i * 64, 64); |
||
553 | $L3Key2_i = substr($L3Key2, $i * 4, 4); |
||
554 | |||
555 | $a = self::L1Hash($L1Key_i, $m); |
||
556 | $b = strlen($m) <= 1024 ? "\0\0\0\0\0\0\0\0$a" : self::L2Hash($L2Key_i, $a); |
||
557 | $c = self::L3Hash($L3Key1_i, $L3Key2_i, $b); |
||
558 | $y .= $c; |
||
559 | } |
||
560 | |||
561 | return $y; |
||
562 | } |
||
563 | |||
564 | /** |
||
565 | * L1-HASH Algorithm |
||
566 | * |
||
567 | * The first-layer hash breaks the message into 1024-byte chunks and |
||
568 | * hashes each with a function called NH. Concatenating the results |
||
569 | * forms a string, which is up to 128 times shorter than the original. |
||
570 | * |
||
571 | * @param string $k string of length 1024 bytes. |
||
572 | * @param string $m string of length less than 2^67 bits. |
||
573 | * @return string string of length (8 * ceil(bitlength(M)/8192)) bytes. |
||
574 | */ |
||
575 | private static function L1Hash($k, $m) |
||
576 | { |
||
577 | // |
||
578 | // Break M into 1024 byte chunks (final chunk may be shorter) |
||
579 | // |
||
580 | $m = str_split($m, 1024); |
||
581 | |||
582 | // |
||
583 | // For each chunk, except the last: endian-adjust, NH hash |
||
584 | // and add bit-length. Use results to build Y. |
||
585 | // |
||
586 | $length = new BigInteger(1024 * 8); |
||
587 | $y = ''; |
||
588 | for ($i = 0; $i < count($m) - 1; $i++) { |
||
589 | $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP |
||
590 | $y .= static::nh($k, $m[$i], $length); |
||
591 | } |
||
592 | |||
593 | // |
||
594 | // For the last chunk: pad to 32-byte boundary, endian-adjust, |
||
595 | // NH hash and add bit-length. Concatenate the result to Y. |
||
596 | // |
||
597 | $length = strlen($m[$i]); |
||
598 | $pad = 32 - ($length % 32); |
||
599 | $pad = max(32, $length + $pad % 32); |
||
600 | $m[$i] = str_pad($m[$i], $pad, "\0"); // zeropad |
||
601 | $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP |
||
602 | |||
603 | $y .= static::nh($k, $m[$i], new BigInteger($length * 8)); |
||
604 | |||
605 | return $y; |
||
606 | } |
||
607 | |||
608 | /** |
||
609 | * NH Algorithm |
||
610 | * |
||
611 | * @param string $k string of length 1024 bytes. |
||
612 | * @param string $m string with length divisible by 32 bytes. |
||
613 | * @return string string of length 8 bytes. |
||
614 | */ |
||
615 | private static function nh($k, $m, $length) |
||
616 | { |
||
617 | $toUInt32 = function ($x) { |
||
618 | $x = new BigInteger($x, 256); |
||
619 | $x->setPrecision(32); |
||
620 | return $x; |
||
621 | }; |
||
622 | |||
623 | // |
||
624 | // Break M and K into 4-byte chunks |
||
625 | // |
||
626 | //$t = strlen($m) >> 2; |
||
627 | $m = str_split($m, 4); |
||
628 | $t = count($m); |
||
629 | $k = str_split($k, 4); |
||
630 | $k = array_pad(array_slice($k, 0, $t), $t, 0); |
||
631 | |||
632 | $m = array_map($toUInt32, $m); |
||
633 | $k = array_map($toUInt32, $k); |
||
634 | |||
635 | // |
||
636 | // Perform NH hash on the chunks, pairing words for multiplication |
||
637 | // which are 4 apart to accommodate vector-parallelism. |
||
638 | // |
||
639 | $y = new BigInteger(); |
||
640 | $y->setPrecision(64); |
||
641 | $i = 0; |
||
642 | while ($i < $t) { |
||
643 | $temp = $m[$i]->add($k[$i]); |
||
644 | $temp->setPrecision(64); |
||
645 | $temp = $temp->multiply($m[$i + 4]->add($k[$i + 4])); |
||
646 | $y = $y->add($temp); |
||
647 | |||
648 | $temp = $m[$i + 1]->add($k[$i + 1]); |
||
649 | $temp->setPrecision(64); |
||
650 | $temp = $temp->multiply($m[$i + 5]->add($k[$i + 5])); |
||
651 | $y = $y->add($temp); |
||
652 | |||
653 | $temp = $m[$i + 2]->add($k[$i + 2]); |
||
654 | $temp->setPrecision(64); |
||
655 | $temp = $temp->multiply($m[$i + 6]->add($k[$i + 6])); |
||
656 | $y = $y->add($temp); |
||
657 | |||
658 | $temp = $m[$i + 3]->add($k[$i + 3]); |
||
659 | $temp->setPrecision(64); |
||
660 | $temp = $temp->multiply($m[$i + 7]->add($k[$i + 7])); |
||
661 | $y = $y->add($temp); |
||
662 | |||
663 | $i += 8; |
||
664 | } |
||
665 | |||
666 | return $y->add($length)->toBytes(); |
||
667 | } |
||
668 | |||
669 | /** |
||
670 | * L2-HASH: Second-Layer Hash |
||
671 | * |
||
672 | * The second-layer rehashes the L1-HASH output using a polynomial hash |
||
673 | * called POLY. If the L1-HASH output is long, then POLY is called once |
||
674 | * on a prefix of the L1-HASH output and called using different settings |
||
675 | * on the remainder. (This two-step hashing of the L1-HASH output is |
||
676 | * needed only if the message length is greater than 16 megabytes.) |
||
677 | * Careful implementation of POLY is necessary to avoid a possible |
||
678 | * timing attack (see Section 6.6 for more information). |
||
679 | * |
||
680 | * @param string $k string of length 24 bytes. |
||
681 | * @param string $m string of length less than 2^64 bytes. |
||
682 | * @return string string of length 16 bytes. |
||
683 | */ |
||
684 | private static function L2Hash($k, $m) |
||
685 | { |
||
686 | // |
||
687 | // Extract keys and restrict to special key-sets |
||
688 | // |
||
689 | $k64 = $k & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF"; |
||
690 | $k64 = new BigInteger($k64, 256); |
||
691 | $k128 = substr($k, 8) & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF"; |
||
692 | $k128 = new BigInteger($k128, 256); |
||
693 | |||
694 | // |
||
695 | // If M is no more than 2^17 bytes, hash under 64-bit prime, |
||
696 | // otherwise, hash first 2^17 bytes under 64-bit prime and |
||
697 | // remainder under 128-bit prime. |
||
698 | // |
||
699 | if (strlen($m) <= 0x20000) { // 2^14 64-bit words |
||
700 | $y = self::poly(64, self::$maxwordrange64, $k64, $m); |
||
701 | } else { |
||
702 | $m_1 = substr($m, 0, 0x20000); // 1 << 17 |
||
703 | $m_2 = substr($m, 0x20000) . "\x80"; |
||
704 | $length = strlen($m_2); |
||
705 | $pad = 16 - ($length % 16); |
||
706 | $pad %= 16; |
||
707 | $m_2 = str_pad($m_2, $length + $pad, "\0"); // zeropad |
||
708 | $y = self::poly(64, self::$maxwordrange64, $k64, $m_1); |
||
709 | $y = str_pad($y, 16, "\0", STR_PAD_LEFT); |
||
710 | $y = self::poly(128, self::$maxwordrange128, $k128, $y . $m_2); |
||
711 | } |
||
712 | |||
713 | return str_pad($y, 16, "\0", STR_PAD_LEFT); |
||
714 | } |
||
715 | |||
716 | /** |
||
717 | * POLY Algorithm |
||
718 | * |
||
719 | * @param int $wordbits the integer 64 or 128. |
||
720 | * @param BigInteger $maxwordrange positive integer less than 2^wordbits. |
||
721 | * @param BigInteger $k integer in the range 0 ... prime(wordbits) - 1. |
||
722 | * @param string $m string with length divisible by (wordbits / 8) bytes. |
||
723 | * @return integer in the range 0 ... prime(wordbits) - 1. |
||
724 | */ |
||
725 | private static function poly($wordbits, $maxwordrange, $k, $m) |
||
726 | { |
||
727 | // |
||
728 | // Define constants used for fixing out-of-range words |
||
729 | // |
||
730 | $wordbytes = $wordbits >> 3; |
||
731 | if ($wordbits == 128) { |
||
732 | $factory = self::$factory128; |
||
733 | $offset = self::$offset128; |
||
734 | $marker = self::$marker128; |
||
735 | } else { |
||
736 | $factory = self::$factory64; |
||
737 | $offset = self::$offset64; |
||
738 | $marker = self::$marker64; |
||
739 | } |
||
740 | |||
741 | $k = $factory->newInteger($k); |
||
742 | |||
743 | // |
||
744 | // Break M into chunks of length wordbytes bytes |
||
745 | // |
||
746 | $m_i = str_split($m, $wordbytes); |
||
747 | |||
748 | // |
||
749 | // Each input word m is compared with maxwordrange. If not smaller |
||
750 | // then 'marker' and (m - offset), both in range, are hashed. |
||
751 | // |
||
752 | $y = $factory->newInteger(new BigInteger(1)); |
||
753 | foreach ($m_i as $m) { |
||
754 | $m = $factory->newInteger(new BigInteger($m, 256)); |
||
755 | if ($m->compare($maxwordrange) >= 0) { |
||
756 | $y = $k->multiply($y)->add($marker); |
||
757 | $y = $k->multiply($y)->add($m->subtract($offset)); |
||
758 | } else { |
||
759 | $y = $k->multiply($y)->add($m); |
||
760 | } |
||
761 | } |
||
762 | |||
763 | return $y->toBytes(); |
||
764 | } |
||
765 | |||
766 | /** |
||
767 | * L3-HASH: Third-Layer Hash |
||
768 | * |
||
769 | * The output from L2-HASH is 16 bytes long. This final hash function |
||
770 | * hashes the 16-byte string to a fixed length of 4 bytes. |
||
771 | * |
||
772 | * @param string $k1 string of length 64 bytes. |
||
773 | * @param string $k2 string of length 4 bytes. |
||
774 | * @param string $m string of length 16 bytes. |
||
775 | * @return string string of length 4 bytes. |
||
776 | */ |
||
777 | private static function L3Hash($k1, $k2, $m) |
||
778 | { |
||
779 | $factory = self::$factory36; |
||
780 | |||
781 | $y = $factory->newInteger(new BigInteger()); |
||
782 | for ($i = 0; $i < 8; $i++) { |
||
783 | $m_i = $factory->newInteger(new BigInteger(substr($m, 2 * $i, 2), 256)); |
||
784 | $k_i = $factory->newInteger(new BigInteger(substr($k1, 8 * $i, 8), 256)); |
||
785 | $y = $y->add($m_i->multiply($k_i)); |
||
786 | } |
||
787 | $y = str_pad(substr($y->toBytes(), -4), 4, "\0", STR_PAD_LEFT); |
||
788 | $y = $y ^ $k2; |
||
789 | |||
790 | return $y; |
||
791 | } |
||
792 | |||
793 | /** |
||
794 | * Compute the Hash / HMAC / UMAC. |
||
795 | * |
||
874 | daniel-mar | 796 | * @access public |
827 | daniel-mar | 797 | * @param string $text |
798 | * @return string |
||
799 | */ |
||
800 | public function hash($text) |
||
801 | { |
||
802 | $algo = $this->algo; |
||
803 | if ($algo == 'umac') { |
||
804 | if ($this->recomputeAESKey) { |
||
805 | if (!is_string($this->nonce)) { |
||
806 | throw new InsufficientSetupException('No nonce has been set'); |
||
807 | } |
||
808 | if (!is_string($this->key)) { |
||
809 | throw new InsufficientSetupException('No key has been set'); |
||
810 | } |
||
811 | if (strlen($this->key) != 16) { |
||
812 | throw new \LengthException('Key must be 16 bytes long'); |
||
813 | } |
||
814 | |||
815 | if (!isset(self::$maxwordrange64)) { |
||
816 | $one = new BigInteger(1); |
||
817 | |||
818 | $prime36 = new BigInteger("\x00\x00\x00\x0F\xFF\xFF\xFF\xFB", 256); |
||
819 | self::$factory36 = new PrimeField($prime36); |
||
820 | |||
821 | $prime64 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC5", 256); |
||
822 | self::$factory64 = new PrimeField($prime64); |
||
823 | |||
824 | $prime128 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x61", 256); |
||
825 | self::$factory128 = new PrimeField($prime128); |
||
826 | |||
827 | self::$offset64 = new BigInteger("\1\0\0\0\0\0\0\0\0", 256); |
||
828 | self::$offset64 = self::$factory64->newInteger(self::$offset64->subtract($prime64)); |
||
829 | self::$offset128 = new BigInteger("\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 256); |
||
830 | self::$offset128 = self::$factory128->newInteger(self::$offset128->subtract($prime128)); |
||
831 | |||
832 | self::$marker64 = self::$factory64->newInteger($prime64->subtract($one)); |
||
833 | self::$marker128 = self::$factory128->newInteger($prime128->subtract($one)); |
||
834 | |||
835 | $maxwordrange64 = $one->bitwise_leftShift(64)->subtract($one->bitwise_leftShift(32)); |
||
836 | self::$maxwordrange64 = self::$factory64->newInteger($maxwordrange64); |
||
837 | |||
838 | $maxwordrange128 = $one->bitwise_leftShift(128)->subtract($one->bitwise_leftShift(96)); |
||
839 | self::$maxwordrange128 = self::$factory128->newInteger($maxwordrange128); |
||
840 | } |
||
841 | |||
842 | $this->c = new AES('ctr'); |
||
843 | $this->c->disablePadding(); |
||
844 | $this->c->setKey($this->key); |
||
845 | |||
846 | $this->pad = $this->pdf(); |
||
847 | |||
848 | $this->recomputeAESKey = false; |
||
849 | } |
||
850 | |||
851 | $hashedmessage = $this->uhash($text, $this->length); |
||
852 | return $hashedmessage ^ $this->pad; |
||
853 | } |
||
854 | |||
855 | if (is_array($algo)) { |
||
856 | if (empty($this->key) || !is_string($this->key)) { |
||
857 | return substr($algo($text, ...array_values($this->parameters)), 0, $this->length); |
||
858 | } |
||
859 | |||
860 | // SHA3 HMACs are discussed at https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=30 |
||
861 | |||
862 | $key = str_pad($this->computedKey, $b, chr(0)); |
||
863 | $temp = $this->ipad ^ $key; |
||
864 | $temp .= $text; |
||
865 | $temp = substr($algo($temp, ...array_values($this->parameters)), 0, $this->length); |
||
866 | $output = $this->opad ^ $key; |
||
867 | $output .= $temp; |
||
868 | $output = $algo($output, ...array_values($this->parameters)); |
||
869 | |||
870 | return substr($output, 0, $this->length); |
||
871 | } |
||
872 | |||
873 | $output = !empty($this->key) || is_string($this->key) ? |
||
874 | hash_hmac($algo, $text, $this->computedKey, true) : |
||
875 | hash($algo, $text, true); |
||
876 | |||
877 | return strlen($output) > $this->length |
||
878 | ? substr($output, 0, $this->length) |
||
879 | : $output; |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * Returns the hash length (in bits) |
||
884 | * |
||
874 | daniel-mar | 885 | * @access public |
827 | daniel-mar | 886 | * @return int |
887 | */ |
||
888 | public function getLength() |
||
889 | { |
||
890 | return $this->length << 3; |
||
891 | } |
||
892 | |||
893 | /** |
||
894 | * Returns the hash length (in bytes) |
||
895 | * |
||
874 | daniel-mar | 896 | * @access public |
827 | daniel-mar | 897 | * @return int |
898 | */ |
||
899 | public function getLengthInBytes() |
||
900 | { |
||
901 | return $this->length; |
||
902 | } |
||
903 | |||
904 | /** |
||
905 | * Returns the block length (in bits) |
||
906 | * |
||
874 | daniel-mar | 907 | * @access public |
827 | daniel-mar | 908 | * @return int |
909 | */ |
||
910 | public function getBlockLength() |
||
911 | { |
||
912 | return $this->blockSize; |
||
913 | } |
||
914 | |||
915 | /** |
||
916 | * Returns the block length (in bytes) |
||
917 | * |
||
874 | daniel-mar | 918 | * @access public |
827 | daniel-mar | 919 | * @return int |
920 | */ |
||
921 | public function getBlockLengthInBytes() |
||
922 | { |
||
923 | return $this->blockSize >> 3; |
||
924 | } |
||
925 | |||
926 | /** |
||
927 | * Pads SHA3 based on the mode |
||
928 | * |
||
874 | daniel-mar | 929 | * @access private |
827 | daniel-mar | 930 | * @param int $padLength |
931 | * @param int $padType |
||
932 | * @return string |
||
933 | */ |
||
934 | private static function sha3_pad($padLength, $padType) |
||
935 | { |
||
936 | switch ($padType) { |
||
937 | case self::PADDING_KECCAK: |
||
938 | $temp = chr(0x01) . str_repeat("\0", $padLength - 1); |
||
939 | $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80); |
||
940 | return $temp; |
||
941 | case self::PADDING_SHAKE: |
||
942 | $temp = chr(0x1F) . str_repeat("\0", $padLength - 1); |
||
943 | $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80); |
||
944 | return $temp; |
||
945 | //case self::PADDING_SHA3: |
||
946 | default: |
||
947 | // from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=36 |
||
948 | return $padLength == 1 ? chr(0x86) : chr(0x06) . str_repeat("\0", $padLength - 2) . chr(0x80); |
||
949 | } |
||
950 | } |
||
951 | |||
952 | /** |
||
953 | * Pure-PHP 32-bit implementation of SHA3 |
||
954 | * |
||
955 | * Whereas BigInteger.php's 32-bit engine works on PHP 64-bit this 32-bit implementation |
||
956 | * of SHA3 will *not* work on PHP 64-bit. This is because this implementation |
||
957 | * employees bitwise NOTs and bitwise left shifts. And the round constants only work |
||
958 | * on 32-bit PHP. eg. dechex(-2147483648) returns 80000000 on 32-bit PHP and |
||
959 | * FFFFFFFF80000000 on 64-bit PHP. Sure, we could do bitwise ANDs but that would slow |
||
960 | * things down. |
||
961 | * |
||
962 | * SHA512 requires BigInteger to simulate 64-bit unsigned integers because SHA2 employees |
||
963 | * addition whereas SHA3 just employees bitwise operators. PHP64 only supports signed |
||
964 | * 64-bit integers, which complicates addition, whereas that limitation isn't an issue |
||
965 | * for SHA3. |
||
966 | * |
||
967 | * In https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=919061#page=16 KECCAK[C] is |
||
968 | * defined as "the KECCAK instance with KECCAK-f[1600] as the underlying permutation and |
||
969 | * capacity c". This is relevant because, altho the KECCAK standard defines a mode |
||
970 | * (KECCAK-f[800]) designed for 32-bit machines that mode is incompatible with SHA3 |
||
971 | * |
||
874 | daniel-mar | 972 | * @access private |
827 | daniel-mar | 973 | * @param string $p |
974 | * @param int $c |
||
975 | * @param int $r |
||
976 | * @param int $d |
||
977 | * @param int $padType |
||
978 | */ |
||
979 | private static function sha3_32($p, $c, $r, $d, $padType) |
||
980 | { |
||
981 | $block_size = $r >> 3; |
||
982 | $padLength = $block_size - (strlen($p) % $block_size); |
||
983 | $num_ints = $block_size >> 2; |
||
984 | |||
985 | $p .= static::sha3_pad($padLength, $padType); |
||
986 | |||
987 | $n = strlen($p) / $r; // number of blocks |
||
988 | |||
989 | $s = [ |
||
990 | [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], |
||
991 | [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], |
||
992 | [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], |
||
993 | [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], |
||
994 | [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] |
||
995 | ]; |
||
996 | |||
997 | $p = str_split($p, $block_size); |
||
998 | |||
999 | foreach ($p as $pi) { |
||
1000 | $pi = unpack('V*', $pi); |
||
1001 | $x = $y = 0; |
||
1002 | for ($i = 1; $i <= $num_ints; $i += 2) { |
||
1003 | $s[$x][$y][0] ^= $pi[$i + 1]; |
||
1004 | $s[$x][$y][1] ^= $pi[$i]; |
||
1005 | if (++$y == 5) { |
||
1006 | $y = 0; |
||
1007 | $x++; |
||
1008 | } |
||
1009 | } |
||
1010 | static::processSHA3Block32($s); |
||
1011 | } |
||
1012 | |||
1013 | $z = ''; |
||
1014 | $i = $j = 0; |
||
1015 | while (strlen($z) < $d) { |
||
1016 | $z .= pack('V2', $s[$i][$j][1], $s[$i][$j++][0]); |
||
1017 | if ($j == 5) { |
||
1018 | $j = 0; |
||
1019 | $i++; |
||
1020 | if ($i == 5) { |
||
1021 | $i = 0; |
||
1022 | static::processSHA3Block32($s); |
||
1023 | } |
||
1024 | } |
||
1025 | } |
||
1026 | |||
1027 | return $z; |
||
1028 | } |
||
1029 | |||
1030 | /** |
||
1031 | * 32-bit block processing method for SHA3 |
||
1032 | * |
||
874 | daniel-mar | 1033 | * @access private |
827 | daniel-mar | 1034 | * @param array $s |
1035 | */ |
||
1036 | private static function processSHA3Block32(&$s) |
||
1037 | { |
||
1038 | static $rotationOffsets = [ |
||
1039 | [ 0, 1, 62, 28, 27], |
||
1040 | [36, 44, 6, 55, 20], |
||
1041 | [ 3, 10, 43, 25, 39], |
||
1042 | [41, 45, 15, 21, 8], |
||
1043 | [18, 2, 61, 56, 14] |
||
1044 | ]; |
||
1045 | |||
1046 | // the standards give these constants in hexadecimal notation. it's tempting to want to use |
||
1047 | // that same notation, here, however, we can't, because 0x80000000, on PHP32, is a positive |
||
1048 | // float - not the negative int that we need to be in PHP32. so we use -2147483648 instead |
||
1049 | static $roundConstants = [ |
||
1050 | [0, 1], |
||
1051 | [0, 32898], |
||
1052 | [-2147483648, 32906], |
||
1053 | [-2147483648, -2147450880], |
||
1054 | [0, 32907], |
||
1055 | [0, -2147483647], |
||
1056 | [-2147483648, -2147450751], |
||
1057 | [-2147483648, 32777], |
||
1058 | [0, 138], |
||
1059 | [0, 136], |
||
1060 | [0, -2147450871], |
||
1061 | [0, -2147483638], |
||
1062 | [0, -2147450741], |
||
1063 | [-2147483648, 139], |
||
1064 | [-2147483648, 32905], |
||
1065 | [-2147483648, 32771], |
||
1066 | [-2147483648, 32770], |
||
1067 | [-2147483648, 128], |
||
1068 | [0, 32778], |
||
1069 | [-2147483648, -2147483638], |
||
1070 | [-2147483648, -2147450751], |
||
1071 | [-2147483648, 32896], |
||
1072 | [0, -2147483647], |
||
1073 | [-2147483648, -2147450872] |
||
1074 | ]; |
||
1075 | |||
1076 | for ($round = 0; $round < 24; $round++) { |
||
1077 | // theta step |
||
1078 | $parity = $rotated = []; |
||
1079 | for ($i = 0; $i < 5; $i++) { |
||
1080 | $parity[] = [ |
||
1081 | $s[0][$i][0] ^ $s[1][$i][0] ^ $s[2][$i][0] ^ $s[3][$i][0] ^ $s[4][$i][0], |
||
1082 | $s[0][$i][1] ^ $s[1][$i][1] ^ $s[2][$i][1] ^ $s[3][$i][1] ^ $s[4][$i][1] |
||
1083 | ]; |
||
1084 | $rotated[] = static::rotateLeft32($parity[$i], 1); |
||
1085 | } |
||
1086 | |||
1087 | $temp = [ |
||
1088 | [$parity[4][0] ^ $rotated[1][0], $parity[4][1] ^ $rotated[1][1]], |
||
1089 | [$parity[0][0] ^ $rotated[2][0], $parity[0][1] ^ $rotated[2][1]], |
||
1090 | [$parity[1][0] ^ $rotated[3][0], $parity[1][1] ^ $rotated[3][1]], |
||
1091 | [$parity[2][0] ^ $rotated[4][0], $parity[2][1] ^ $rotated[4][1]], |
||
1092 | [$parity[3][0] ^ $rotated[0][0], $parity[3][1] ^ $rotated[0][1]] |
||
1093 | ]; |
||
1094 | for ($i = 0; $i < 5; $i++) { |
||
1095 | for ($j = 0; $j < 5; $j++) { |
||
1096 | $s[$i][$j][0] ^= $temp[$j][0]; |
||
1097 | $s[$i][$j][1] ^= $temp[$j][1]; |
||
1098 | } |
||
1099 | } |
||
1100 | |||
1101 | $st = $s; |
||
1102 | |||
1103 | // rho and pi steps |
||
1104 | for ($i = 0; $i < 5; $i++) { |
||
1105 | for ($j = 0; $j < 5; $j++) { |
||
1106 | $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft32($s[$j][$i], $rotationOffsets[$j][$i]); |
||
1107 | } |
||
1108 | } |
||
1109 | |||
1110 | // chi step |
||
1111 | for ($i = 0; $i < 5; $i++) { |
||
1112 | $s[$i][0] = [ |
||
1113 | $st[$i][0][0] ^ (~$st[$i][1][0] & $st[$i][2][0]), |
||
1114 | $st[$i][0][1] ^ (~$st[$i][1][1] & $st[$i][2][1]) |
||
1115 | ]; |
||
1116 | $s[$i][1] = [ |
||
1117 | $st[$i][1][0] ^ (~$st[$i][2][0] & $st[$i][3][0]), |
||
1118 | $st[$i][1][1] ^ (~$st[$i][2][1] & $st[$i][3][1]) |
||
1119 | ]; |
||
1120 | $s[$i][2] = [ |
||
1121 | $st[$i][2][0] ^ (~$st[$i][3][0] & $st[$i][4][0]), |
||
1122 | $st[$i][2][1] ^ (~$st[$i][3][1] & $st[$i][4][1]) |
||
1123 | ]; |
||
1124 | $s[$i][3] = [ |
||
1125 | $st[$i][3][0] ^ (~$st[$i][4][0] & $st[$i][0][0]), |
||
1126 | $st[$i][3][1] ^ (~$st[$i][4][1] & $st[$i][0][1]) |
||
1127 | ]; |
||
1128 | $s[$i][4] = [ |
||
1129 | $st[$i][4][0] ^ (~$st[$i][0][0] & $st[$i][1][0]), |
||
1130 | $st[$i][4][1] ^ (~$st[$i][0][1] & $st[$i][1][1]) |
||
1131 | ]; |
||
1132 | } |
||
1133 | |||
1134 | // iota step |
||
1135 | $s[0][0][0] ^= $roundConstants[$round][0]; |
||
1136 | $s[0][0][1] ^= $roundConstants[$round][1]; |
||
1137 | } |
||
1138 | } |
||
1139 | |||
1140 | /** |
||
1141 | * Rotate 32-bit int |
||
1142 | * |
||
874 | daniel-mar | 1143 | * @access private |
827 | daniel-mar | 1144 | * @param array $x |
1145 | * @param int $shift |
||
1146 | */ |
||
1147 | private static function rotateLeft32($x, $shift) |
||
1148 | { |
||
1149 | if ($shift < 32) { |
||
1150 | list($hi, $lo) = $x; |
||
1151 | } else { |
||
1152 | $shift -= 32; |
||
1153 | list($lo, $hi) = $x; |
||
1154 | } |
||
1155 | |||
1156 | return [ |
||
1157 | ($hi << $shift) | (($lo >> (32 - $shift)) & (1 << $shift) - 1), |
||
1158 | ($lo << $shift) | (($hi >> (32 - $shift)) & (1 << $shift) - 1) |
||
1159 | ]; |
||
1160 | } |
||
1161 | |||
1162 | /** |
||
1163 | * Pure-PHP 64-bit implementation of SHA3 |
||
1164 | * |
||
874 | daniel-mar | 1165 | * @access private |
827 | daniel-mar | 1166 | * @param string $p |
1167 | * @param int $c |
||
1168 | * @param int $r |
||
1169 | * @param int $d |
||
1170 | * @param int $padType |
||
1171 | */ |
||
1172 | private static function sha3_64($p, $c, $r, $d, $padType) |
||
1173 | { |
||
1174 | $block_size = $r >> 3; |
||
1175 | $padLength = $block_size - (strlen($p) % $block_size); |
||
1176 | $num_ints = $block_size >> 2; |
||
1177 | |||
1178 | $p .= static::sha3_pad($padLength, $padType); |
||
1179 | |||
1180 | $n = strlen($p) / $r; // number of blocks |
||
1181 | |||
1182 | $s = [ |
||
1183 | [0, 0, 0, 0, 0], |
||
1184 | [0, 0, 0, 0, 0], |
||
1185 | [0, 0, 0, 0, 0], |
||
1186 | [0, 0, 0, 0, 0], |
||
1187 | [0, 0, 0, 0, 0] |
||
1188 | ]; |
||
1189 | |||
1190 | $p = str_split($p, $block_size); |
||
1191 | |||
1192 | foreach ($p as $pi) { |
||
1193 | $pi = unpack('P*', $pi); |
||
1194 | $x = $y = 0; |
||
1195 | foreach ($pi as $subpi) { |
||
1196 | $s[$x][$y++] ^= $subpi; |
||
1197 | if ($y == 5) { |
||
1198 | $y = 0; |
||
1199 | $x++; |
||
1200 | } |
||
1201 | } |
||
1202 | static::processSHA3Block64($s); |
||
1203 | } |
||
1204 | |||
1205 | $z = ''; |
||
1206 | $i = $j = 0; |
||
1207 | while (strlen($z) < $d) { |
||
1208 | $z .= pack('P', $s[$i][$j++]); |
||
1209 | if ($j == 5) { |
||
1210 | $j = 0; |
||
1211 | $i++; |
||
1212 | if ($i == 5) { |
||
1213 | $i = 0; |
||
1214 | static::processSHA3Block64($s); |
||
1215 | } |
||
1216 | } |
||
1217 | } |
||
1218 | |||
1219 | return $z; |
||
1220 | } |
||
1221 | |||
1222 | /** |
||
1223 | * 64-bit block processing method for SHA3 |
||
1224 | * |
||
874 | daniel-mar | 1225 | * @access private |
827 | daniel-mar | 1226 | * @param array $s |
1227 | */ |
||
1228 | private static function processSHA3Block64(&$s) |
||
1229 | { |
||
1230 | static $rotationOffsets = [ |
||
1231 | [ 0, 1, 62, 28, 27], |
||
1232 | [36, 44, 6, 55, 20], |
||
1233 | [ 3, 10, 43, 25, 39], |
||
1234 | [41, 45, 15, 21, 8], |
||
1235 | [18, 2, 61, 56, 14] |
||
1236 | ]; |
||
1237 | |||
1238 | static $roundConstants = [ |
||
1239 | 1, |
||
1240 | 32898, |
||
1241 | -9223372036854742902, |
||
1242 | -9223372034707259392, |
||
1243 | 32907, |
||
1244 | 2147483649, |
||
1245 | -9223372034707259263, |
||
1246 | -9223372036854743031, |
||
1247 | 138, |
||
1248 | 136, |
||
1249 | 2147516425, |
||
1250 | 2147483658, |
||
1251 | 2147516555, |
||
1252 | -9223372036854775669, |
||
1253 | -9223372036854742903, |
||
1254 | -9223372036854743037, |
||
1255 | -9223372036854743038, |
||
1256 | -9223372036854775680, |
||
1257 | 32778, |
||
1258 | -9223372034707292150, |
||
1259 | -9223372034707259263, |
||
1260 | -9223372036854742912, |
||
1261 | 2147483649, |
||
1262 | -9223372034707259384 |
||
1263 | ]; |
||
1264 | |||
1265 | for ($round = 0; $round < 24; $round++) { |
||
1266 | // theta step |
||
1267 | $parity = []; |
||
1268 | for ($i = 0; $i < 5; $i++) { |
||
1269 | $parity[] = $s[0][$i] ^ $s[1][$i] ^ $s[2][$i] ^ $s[3][$i] ^ $s[4][$i]; |
||
1270 | } |
||
1271 | $temp = [ |
||
1272 | $parity[4] ^ static::rotateLeft64($parity[1], 1), |
||
1273 | $parity[0] ^ static::rotateLeft64($parity[2], 1), |
||
1274 | $parity[1] ^ static::rotateLeft64($parity[3], 1), |
||
1275 | $parity[2] ^ static::rotateLeft64($parity[4], 1), |
||
1276 | $parity[3] ^ static::rotateLeft64($parity[0], 1) |
||
1277 | ]; |
||
1278 | for ($i = 0; $i < 5; $i++) { |
||
1279 | for ($j = 0; $j < 5; $j++) { |
||
1280 | $s[$i][$j] ^= $temp[$j]; |
||
1281 | } |
||
1282 | } |
||
1283 | |||
1284 | $st = $s; |
||
1285 | |||
1286 | // rho and pi steps |
||
1287 | for ($i = 0; $i < 5; $i++) { |
||
1288 | for ($j = 0; $j < 5; $j++) { |
||
1289 | $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft64($s[$j][$i], $rotationOffsets[$j][$i]); |
||
1290 | } |
||
1291 | } |
||
1292 | |||
1293 | // chi step |
||
1294 | for ($i = 0; $i < 5; $i++) { |
||
1295 | $s[$i] = [ |
||
1296 | $st[$i][0] ^ (~$st[$i][1] & $st[$i][2]), |
||
1297 | $st[$i][1] ^ (~$st[$i][2] & $st[$i][3]), |
||
1298 | $st[$i][2] ^ (~$st[$i][3] & $st[$i][4]), |
||
1299 | $st[$i][3] ^ (~$st[$i][4] & $st[$i][0]), |
||
1300 | $st[$i][4] ^ (~$st[$i][0] & $st[$i][1]) |
||
1301 | ]; |
||
1302 | } |
||
1303 | |||
1304 | // iota step |
||
1305 | $s[0][0] ^= $roundConstants[$round]; |
||
1306 | } |
||
1307 | } |
||
1308 | |||
1309 | /** |
||
1310 | * Rotate 64-bit int |
||
1311 | * |
||
874 | daniel-mar | 1312 | * @access private |
827 | daniel-mar | 1313 | * @param int $x |
1314 | * @param int $shift |
||
1315 | */ |
||
1316 | private static function rotateLeft64($x, $shift) |
||
1317 | { |
||
1318 | return ($x << $shift) | (($x >> (64 - $shift)) & ((1 << $shift) - 1)); |
||
1319 | } |
||
1320 | |||
1321 | /** |
||
1322 | * Pure-PHP implementation of SHA512 |
||
1323 | * |
||
874 | daniel-mar | 1324 | * @access private |
827 | daniel-mar | 1325 | * @param string $m |
1326 | * @param array $hash |
||
1327 | * @return string |
||
1328 | */ |
||
1329 | private static function sha512($m, $hash) |
||
1330 | { |
||
1331 | static $k; |
||
1332 | |||
1333 | if (!isset($k)) { |
||
1334 | // Initialize table of round constants |
||
1335 | // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) |
||
1336 | $k = [ |
||
1337 | '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', |
||
1338 | '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', |
||
1339 | 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', |
||
1340 | '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', |
||
1341 | 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', |
||
1342 | '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', |
||
1343 | '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', |
||
1344 | 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', |
||
1345 | '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', |
||
1346 | '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', |
||
1347 | 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', |
||
1348 | 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', |
||
1349 | '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', |
||
1350 | '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', |
||
1351 | '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', |
||
1352 | '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', |
||
1353 | 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', |
||
1354 | '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', |
||
1355 | '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', |
||
1356 | '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817' |
||
1357 | ]; |
||
1358 | |||
1359 | for ($i = 0; $i < 80; $i++) { |
||
1360 | $k[$i] = new BigInteger($k[$i], 16); |
||
1361 | } |
||
1362 | } |
||
1363 | |||
1364 | // Pre-processing |
||
1365 | $length = strlen($m); |
||
1366 | // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 |
||
1367 | $m .= str_repeat(chr(0), 128 - (($length + 16) & 0x7F)); |
||
1368 | $m[$length] = chr(0x80); |
||
1369 | // we don't support hashing strings 512MB long |
||
1370 | $m .= pack('N4', 0, 0, 0, $length << 3); |
||
1371 | |||
1372 | // Process the message in successive 1024-bit chunks |
||
1373 | $chunks = str_split($m, 128); |
||
1374 | foreach ($chunks as $chunk) { |
||
1375 | $w = []; |
||
1376 | for ($i = 0; $i < 16; $i++) { |
||
1377 | $temp = new BigInteger(Strings::shift($chunk, 8), 256); |
||
1378 | $temp->setPrecision(64); |
||
1379 | $w[] = $temp; |
||
1380 | } |
||
1381 | |||
1382 | // Extend the sixteen 32-bit words into eighty 32-bit words |
||
1383 | for ($i = 16; $i < 80; $i++) { |
||
1384 | $temp = [ |
||
1385 | $w[$i - 15]->bitwise_rightRotate(1), |
||
1386 | $w[$i - 15]->bitwise_rightRotate(8), |
||
1387 | $w[$i - 15]->bitwise_rightShift(7) |
||
1388 | ]; |
||
1389 | $s0 = $temp[0]->bitwise_xor($temp[1]); |
||
1390 | $s0 = $s0->bitwise_xor($temp[2]); |
||
1391 | $temp = [ |
||
1392 | $w[$i - 2]->bitwise_rightRotate(19), |
||
1393 | $w[$i - 2]->bitwise_rightRotate(61), |
||
1394 | $w[$i - 2]->bitwise_rightShift(6) |
||
1395 | ]; |
||
1396 | $s1 = $temp[0]->bitwise_xor($temp[1]); |
||
1397 | $s1 = $s1->bitwise_xor($temp[2]); |
||
1398 | $w[$i] = clone $w[$i - 16]; |
||
1399 | $w[$i] = $w[$i]->add($s0); |
||
1400 | $w[$i] = $w[$i]->add($w[$i - 7]); |
||
1401 | $w[$i] = $w[$i]->add($s1); |
||
1402 | } |
||
1403 | |||
1404 | // Initialize hash value for this chunk |
||
1405 | $a = clone $hash[0]; |
||
1406 | $b = clone $hash[1]; |
||
1407 | $c = clone $hash[2]; |
||
1408 | $d = clone $hash[3]; |
||
1409 | $e = clone $hash[4]; |
||
1410 | $f = clone $hash[5]; |
||
1411 | $g = clone $hash[6]; |
||
1412 | $h = clone $hash[7]; |
||
1413 | |||
1414 | // Main loop |
||
1415 | for ($i = 0; $i < 80; $i++) { |
||
1416 | $temp = [ |
||
1417 | $a->bitwise_rightRotate(28), |
||
1418 | $a->bitwise_rightRotate(34), |
||
1419 | $a->bitwise_rightRotate(39) |
||
1420 | ]; |
||
1421 | $s0 = $temp[0]->bitwise_xor($temp[1]); |
||
1422 | $s0 = $s0->bitwise_xor($temp[2]); |
||
1423 | $temp = [ |
||
1424 | $a->bitwise_and($b), |
||
1425 | $a->bitwise_and($c), |
||
1426 | $b->bitwise_and($c) |
||
1427 | ]; |
||
1428 | $maj = $temp[0]->bitwise_xor($temp[1]); |
||
1429 | $maj = $maj->bitwise_xor($temp[2]); |
||
1430 | $t2 = $s0->add($maj); |
||
1431 | |||
1432 | $temp = [ |
||
1433 | $e->bitwise_rightRotate(14), |
||
1434 | $e->bitwise_rightRotate(18), |
||
1435 | $e->bitwise_rightRotate(41) |
||
1436 | ]; |
||
1437 | $s1 = $temp[0]->bitwise_xor($temp[1]); |
||
1438 | $s1 = $s1->bitwise_xor($temp[2]); |
||
1439 | $temp = [ |
||
1440 | $e->bitwise_and($f), |
||
1441 | $g->bitwise_and($e->bitwise_not()) |
||
1442 | ]; |
||
1443 | $ch = $temp[0]->bitwise_xor($temp[1]); |
||
1444 | $t1 = $h->add($s1); |
||
1445 | $t1 = $t1->add($ch); |
||
1446 | $t1 = $t1->add($k[$i]); |
||
1447 | $t1 = $t1->add($w[$i]); |
||
1448 | |||
1449 | $h = clone $g; |
||
1450 | $g = clone $f; |
||
1451 | $f = clone $e; |
||
1452 | $e = $d->add($t1); |
||
1453 | $d = clone $c; |
||
1454 | $c = clone $b; |
||
1455 | $b = clone $a; |
||
1456 | $a = $t1->add($t2); |
||
1457 | } |
||
1458 | |||
1459 | // Add this chunk's hash to result so far |
||
1460 | $hash = [ |
||
1461 | $hash[0]->add($a), |
||
1462 | $hash[1]->add($b), |
||
1463 | $hash[2]->add($c), |
||
1464 | $hash[3]->add($d), |
||
1465 | $hash[4]->add($e), |
||
1466 | $hash[5]->add($f), |
||
1467 | $hash[6]->add($g), |
||
1468 | $hash[7]->add($h) |
||
1469 | ]; |
||
1470 | } |
||
1471 | |||
1472 | // Produce the final hash value (big-endian) |
||
1473 | // (\phpseclib3\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) |
||
1474 | $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . |
||
1475 | $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes(); |
||
1476 | |||
1477 | return $temp; |
||
1478 | } |
||
1479 | |||
1480 | /** |
||
1481 | * __toString() magic method |
||
1482 | */ |
||
1483 | public function __toString() |
||
1484 | { |
||
1485 | return $this->getHash(); |
||
1486 | } |
||
1487 | } |