Diff: STRATO-apps/wordpress_03/app/wp-includes/sodium_compat/src/Core/Util.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 +
3 + if (class_exists('ParagonIE_Sodium_Core_Util', false)) {
4 + return;
5 + }
6 +
7 + /**
8 + * Class ParagonIE_Sodium_Core_Util
9 + */
10 + abstract class ParagonIE_Sodium_Core_Util
11 + {
12 + const U32_MAX = 0xFFFFFFFF;
13 +
14 + /**
15 + * @param int $integer
16 + * @param int $size (16, 32, 64)
17 + * @return int
18 + */
19 + public static function abs($integer, $size = 0)
20 + {
21 + /** @var int $realSize */
22 + $realSize = (PHP_INT_SIZE << 3) - 1;
23 + if ($size) {
24 + --$size;
25 + } else {
26 + /** @var int $size */
27 + $size = $realSize;
28 + }
29 +
30 + $negative = -(($integer >> $size) & 1);
31 + return (int) (
32 + ($integer ^ $negative)
33 + +
34 + (($negative >> $realSize) & 1)
35 + );
36 + }
37 +
38 + /**
39 + * @param string $a
40 + * @param string $b
41 + * @return string
42 + * @throws SodiumException
43 + */
44 + public static function andStrings($a, $b)
45 + {
46 + /* Type checks: */
47 + if (!is_string($a)) {
48 + throw new TypeError('Argument 1 must be a string');
49 + }
50 + if (!is_string($b)) {
51 + throw new TypeError('Argument 2 must be a string');
52 + }
53 + $len = self::strlen($a);
54 + if (self::strlen($b) !== $len) {
55 + throw new SodiumException('Both strings must be of equal length to combine with bitwise AND');
56 + }
57 + return $a & $b;
58 + }
59 +
60 + /**
61 + * Convert a binary string into a hexadecimal string without cache-timing
62 + * leaks
63 + *
64 + * @internal You should not use this directly from another application
65 + *
66 + * @param string $binaryString (raw binary)
67 + * @return string
68 + * @throws TypeError
69 + */
70 + public static function bin2hex($binaryString)
71 + {
72 + /* Type checks: */
73 + if (!is_string($binaryString)) {
74 + throw new TypeError('Argument 1 must be a string, ' . gettype($binaryString) . ' given.');
75 + }
76 +
77 + $hex = '';
78 + $len = self::strlen($binaryString);
79 + for ($i = 0; $i < $len; ++$i) {
80 + /** @var array<int, int> $chunk */
81 + $chunk = unpack('C', $binaryString[$i]);
82 + /** @var int $c */
83 + $c = $chunk[1] & 0xf;
84 + /** @var int $b */
85 + $b = $chunk[1] >> 4;
86 + $hex .= pack(
87 + 'CC',
88 + (87 + $b + ((($b - 10) >> 8) & ~38)),
89 + (87 + $c + ((($c - 10) >> 8) & ~38))
90 + );
91 + }
92 + return $hex;
93 + }
94 +
95 + /**
96 + * Convert a binary string into a hexadecimal string without cache-timing
97 + * leaks, returning uppercase letters (as per RFC 4648)
98 + *
99 + * @internal You should not use this directly from another application
100 + *
101 + * @param string $bin_string (raw binary)
102 + * @return string
103 + * @throws TypeError
104 + */
105 + public static function bin2hexUpper($bin_string)
106 + {
107 + $hex = '';
108 + $len = self::strlen($bin_string);
109 + for ($i = 0; $i < $len; ++$i) {
110 + /** @var array<int, int> $chunk */
111 + $chunk = unpack('C', $bin_string[$i]);
112 + /**
113 + * Lower 16 bits
114 + *
115 + * @var int $c
116 + */
117 + $c = $chunk[1] & 0xf;
118 +
119 + /**
120 + * Upper 16 bits
121 + * @var int $b
122 + */
123 + $b = $chunk[1] >> 4;
124 +
125 + /**
126 + * Use pack() and binary operators to turn the two integers
127 + * into hexadecimal characters. We don't use chr() here, because
128 + * it uses a lookup table internally and we want to avoid
129 + * cache-timing side-channels.
130 + */
131 + $hex .= pack(
132 + 'CC',
133 + (55 + $b + ((($b - 10) >> 8) & ~6)),
134 + (55 + $c + ((($c - 10) >> 8) & ~6))
135 + );
136 + }
137 + return $hex;
138 + }
139 +
140 + /**
141 + * Cache-timing-safe variant of ord()
142 + *
143 + * @internal You should not use this directly from another application
144 + *
145 + * @param string $chr
146 + * @return int
147 + * @throws SodiumException
148 + * @throws TypeError
149 + */
150 + public static function chrToInt($chr)
151 + {
152 + /* Type checks: */
153 + if (!is_string($chr)) {
154 + throw new TypeError('Argument 1 must be a string, ' . gettype($chr) . ' given.');
155 + }
156 + if (self::strlen($chr) !== 1) {
157 + throw new SodiumException('chrToInt() expects a string that is exactly 1 character long');
158 + }
159 + /** @var array<int, int> $chunk */
160 + $chunk = unpack('C', $chr);
161 + return (int) ($chunk[1]);
162 + }
163 +
164 + /**
165 + * Compares two strings.
166 + *
167 + * @internal You should not use this directly from another application
168 + *
169 + * @param string $left
170 + * @param string $right
171 + * @param int $len
172 + * @return int
173 + * @throws SodiumException
174 + * @throws TypeError
175 + */
176 + public static function compare($left, $right, $len = null)
177 + {
178 + $leftLen = self::strlen($left);
179 + $rightLen = self::strlen($right);
180 + if ($len === null) {
181 + $len = max($leftLen, $rightLen);
182 + $left = str_pad($left, $len, "\x00", STR_PAD_RIGHT);
183 + $right = str_pad($right, $len, "\x00", STR_PAD_RIGHT);
184 + } elseif ($leftLen !== $rightLen) {
185 + throw new SodiumException("Argument #1 and argument #2 must have the same length");
186 + }
187 +
188 + $gt = 0;
189 + $eq = 1;
190 + $i = $len;
191 + while ($i !== 0) {
192 + --$i;
193 + $gt |= ((self::chrToInt($right[$i]) - self::chrToInt($left[$i])) >> 8) & $eq;
194 + $eq &= ((self::chrToInt($right[$i]) ^ self::chrToInt($left[$i])) - 1) >> 8;
195 + }
196 + return ($gt + $gt + $eq) - 1;
197 + }
198 +
199 + /**
200 + * If a variable does not match a given type, throw a TypeError.
201 + *
202 + * @param mixed $mixedVar
203 + * @param string $type
204 + * @param int $argumentIndex
205 + * @throws TypeError
206 + * @throws SodiumException
207 + * @return void
208 + */
209 + public static function declareScalarType(&$mixedVar = null, $type = 'void', $argumentIndex = 0)
210 + {
211 + if (func_num_args() === 0) {
212 + /* Tautology, by default */
213 + return;
214 + }
215 + if (func_num_args() === 1) {
216 + throw new TypeError('Declared void, but passed a variable');
217 + }
218 + $realType = strtolower(gettype($mixedVar));
219 + $type = strtolower($type);
220 + switch ($type) {
221 + case 'null':
222 + if ($mixedVar !== null) {
223 + throw new TypeError('Argument ' . $argumentIndex . ' must be null, ' . $realType . ' given.');
224 + }
225 + break;
226 + case 'integer':
227 + case 'int':
228 + $allow = array('int', 'integer');
229 + if (!in_array($type, $allow)) {
230 + throw new TypeError('Argument ' . $argumentIndex . ' must be an integer, ' . $realType . ' given.');
231 + }
232 + $mixedVar = (int) $mixedVar;
233 + break;
234 + case 'boolean':
235 + case 'bool':
236 + $allow = array('bool', 'boolean');
237 + if (!in_array($type, $allow)) {
238 + throw new TypeError('Argument ' . $argumentIndex . ' must be a boolean, ' . $realType . ' given.');
239 + }
240 + $mixedVar = (bool) $mixedVar;
241 + break;
242 + case 'string':
243 + if (!is_string($mixedVar)) {
244 + throw new TypeError('Argument ' . $argumentIndex . ' must be a string, ' . $realType . ' given.');
245 + }
246 + $mixedVar = (string) $mixedVar;
247 + break;
248 + case 'decimal':
249 + case 'double':
250 + case 'float':
251 + $allow = array('decimal', 'double', 'float');
252 + if (!in_array($type, $allow)) {
253 + throw new TypeError('Argument ' . $argumentIndex . ' must be a float, ' . $realType . ' given.');
254 + }
255 + $mixedVar = (float) $mixedVar;
256 + break;
257 + case 'object':
258 + if (!is_object($mixedVar)) {
259 + throw new TypeError('Argument ' . $argumentIndex . ' must be an object, ' . $realType . ' given.');
260 + }
261 + break;
262 + case 'array':
263 + if (!is_array($mixedVar)) {
264 + if (is_object($mixedVar)) {
265 + if ($mixedVar instanceof ArrayAccess) {
266 + return;
267 + }
268 + }
269 + throw new TypeError('Argument ' . $argumentIndex . ' must be an array, ' . $realType . ' given.');
270 + }
271 + break;
272 + default:
273 + throw new SodiumException('Unknown type (' . $realType .') does not match expect type (' . $type . ')');
274 + }
275 + }
276 +
277 + /**
278 + * Evaluate whether or not two strings are equal (in constant-time)
279 + *
280 + * @param string $left
281 + * @param string $right
282 + * @return bool
283 + * @throws SodiumException
284 + * @throws TypeError
285 + */
286 + public static function hashEquals($left, $right)
287 + {
288 + /* Type checks: */
289 + if (!is_string($left)) {
290 + throw new TypeError('Argument 1 must be a string, ' . gettype($left) . ' given.');
291 + }
292 + if (!is_string($right)) {
293 + throw new TypeError('Argument 2 must be a string, ' . gettype($right) . ' given.');
294 + }
295 +
296 + if (is_callable('hash_equals')) {
297 + return hash_equals($left, $right);
298 + }
299 + $d = 0;
300 + /** @var int $len */
301 + $len = self::strlen($left);
302 + if ($len !== self::strlen($right)) {
303 + return false;
304 + }
305 + for ($i = 0; $i < $len; ++$i) {
306 + $d |= self::chrToInt($left[$i]) ^ self::chrToInt($right[$i]);
307 + }
308 +
309 + if ($d !== 0) {
310 + return false;
311 + }
312 + return $left === $right;
313 + }
314 +
315 + /**
316 + * Catch hash_update() failures and throw instead of silently proceeding
317 + *
318 + * @param HashContext|resource &$hs
319 + * @param string $data
320 + * @return void
321 + * @throws SodiumException
322 + * @psalm-suppress PossiblyInvalidArgument
323 + */
324 + protected static function hash_update(&$hs, $data)
325 + {
326 + if (!hash_update($hs, $data)) {
327 + throw new SodiumException('hash_update() failed');
328 + }
329 + }
330 +
331 + /**
332 + * Convert a hexadecimal string into a binary string without cache-timing
333 + * leaks
334 + *
335 + * @internal You should not use this directly from another application
336 + *
337 + * @param string $hexString
338 + * @param string $ignore
339 + * @param bool $strictPadding
340 + * @return string (raw binary)
341 + *
342 + * @throws SodiumException
343 + * @throws TypeError
344 + */
345 + public static function hex2bin($hexString, $ignore = '', $strictPadding = false)
346 + {
347 + /* Type checks: */
348 + if (!is_string($hexString)) {
349 + throw new TypeError('Argument 1 must be a string, ' . gettype($hexString) . ' given.');
350 + }
351 + if (!is_string($ignore)) {
352 + throw new TypeError('Argument 2 must be a string, ' . gettype($hexString) . ' given.');
353 + }
354 +
355 + $hex_pos = 0;
356 + $bin = '';
357 + $c_acc = 0;
358 + $hex_len = self::strlen($hexString);
359 + $state = 0;
360 +
361 + $chunk = unpack('C*', $hexString);
362 + while ($hex_pos < $hex_len) {
363 + ++$hex_pos;
364 + /** @var int $c */
365 + $c = $chunk[$hex_pos];
366 + $c_num = $c ^ 48;
367 + $c_num0 = ($c_num - 10) >> 8;
368 + $c_alpha = ($c & ~32) - 55;
369 + $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
370 + if (($c_num0 | $c_alpha0) === 0) {
371 + if ($ignore && $state === 0 && strpos($ignore, self::intToChr($c)) !== false) {
372 + continue;
373 + }
374 + throw new RangeException(
375 + 'hex2bin() only expects hexadecimal characters'
376 + );
377 + }
378 + $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
379 + if ($state === 0) {
380 + $c_acc = $c_val * 16;
381 + } else {
382 + $bin .= pack('C', $c_acc | $c_val);
383 + }
384 + $state ^= 1;
385 + }
386 + if ($strictPadding && $state !== 0) {
387 + throw new SodiumException(
388 + 'Expected an even number of hexadecimal characters'
389 + );
390 + }
391 + return $bin;
392 + }
393 +
394 + /**
395 + * Turn an array of integers into a string
396 + *
397 + * @internal You should not use this directly from another application
398 + *
399 + * @param array<int, int> $ints
400 + * @return string
401 + */
402 + public static function intArrayToString(array $ints)
403 + {
404 + $args = $ints;
405 + foreach ($args as $i => $v) {
406 + $args[$i] = (int) ($v & 0xff);
407 + }
408 + array_unshift($args, str_repeat('C', count($ints)));
409 + return (string) (call_user_func_array('pack', $args));
410 + }
411 +
412 + /**
413 + * Cache-timing-safe variant of ord()
414 + *
415 + * @internal You should not use this directly from another application
416 + *
417 + * @param int $int
418 + * @return string
419 + * @throws TypeError
420 + */
421 + public static function intToChr($int)
422 + {
423 + return pack('C', $int);
424 + }
425 +
426 + /**
427 + * Load a 3 character substring into an integer
428 + *
429 + * @internal You should not use this directly from another application
430 + *
431 + * @param string $string
432 + * @return int
433 + * @throws RangeException
434 + * @throws TypeError
435 + */
436 + public static function load_3($string)
437 + {
438 + /* Type checks: */
439 + if (!is_string($string)) {
440 + throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
441 + }
442 +
443 + /* Input validation: */
444 + if (self::strlen($string) < 3) {
445 + throw new RangeException(
446 + 'String must be 3 bytes or more; ' . self::strlen($string) . ' given.'
447 + );
448 + }
449 + /** @var array<int, int> $unpacked */
450 + $unpacked = unpack('V', $string . "\0");
451 + return (int) ($unpacked[1] & 0xffffff);
452 + }
453 +
454 + /**
455 + * Load a 4 character substring into an integer
456 + *
457 + * @internal You should not use this directly from another application
458 + *
459 + * @param string $string
460 + * @return int
461 + * @throws RangeException
462 + * @throws TypeError
463 + */
464 + public static function load_4($string)
465 + {
466 + /* Type checks: */
467 + if (!is_string($string)) {
468 + throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
469 + }
470 +
471 + /* Input validation: */
472 + if (self::strlen($string) < 4) {
473 + throw new RangeException(
474 + 'String must be 4 bytes or more; ' . self::strlen($string) . ' given.'
475 + );
476 + }
477 + /** @var array<int, int> $unpacked */
478 + $unpacked = unpack('V', $string);
479 + return (int) $unpacked[1];
480 + }
481 +
482 + /**
483 + * Load a 8 character substring into an integer
484 + *
485 + * @internal You should not use this directly from another application
486 + *
487 + * @param string $string
488 + * @return int
489 + * @throws RangeException
490 + * @throws SodiumException
491 + * @throws TypeError
492 + */
493 + public static function load64_le($string)
494 + {
495 + /* Type checks: */
496 + if (!is_string($string)) {
497 + throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
498 + }
499 +
500 + /* Input validation: */
501 + if (self::strlen($string) < 4) {
502 + throw new RangeException(
503 + 'String must be 4 bytes or more; ' . self::strlen($string) . ' given.'
504 + );
505 + }
506 + if (PHP_VERSION_ID >= 50603 && PHP_INT_SIZE === 8) {
507 + /** @var array<int, int> $unpacked */
508 + $unpacked = unpack('P', $string);
509 + return (int) $unpacked[1];
510 + }
511 +
512 + /** @var int $result */
513 + $result = (self::chrToInt($string[0]) & 0xff);
514 + $result |= (self::chrToInt($string[1]) & 0xff) << 8;
515 + $result |= (self::chrToInt($string[2]) & 0xff) << 16;
516 + $result |= (self::chrToInt($string[3]) & 0xff) << 24;
517 + $result |= (self::chrToInt($string[4]) & 0xff) << 32;
518 + $result |= (self::chrToInt($string[5]) & 0xff) << 40;
519 + $result |= (self::chrToInt($string[6]) & 0xff) << 48;
520 + $result |= (self::chrToInt($string[7]) & 0xff) << 56;
521 + return (int) $result;
522 + }
523 +
524 + /**
525 + * @internal You should not use this directly from another application
526 + *
527 + * @param string $left
528 + * @param string $right
529 + * @return int
530 + * @throws SodiumException
531 + * @throws TypeError
532 + */
533 + public static function memcmp($left, $right)
534 + {
535 + $e = (int) !self::hashEquals($left, $right);
536 + return 0 - $e;
537 + }
538 +
539 + /**
540 + * Multiply two integers in constant-time
541 + *
542 + * Micro-architecture timing side-channels caused by how your CPU
543 + * implements multiplication are best prevented by never using the
544 + * multiplication operators and ensuring that our code always takes
545 + * the same number of operations to complete, regardless of the values
546 + * of $a and $b.
547 + *
548 + * @internal You should not use this directly from another application
549 + *
550 + * @param int $a
551 + * @param int $b
552 + * @param int $size Limits the number of operations (useful for small,
553 + * constant operands)
554 + * @return int
555 + */
556 + public static function mul($a, $b, $size = 0)
557 + {
558 + if (ParagonIE_Sodium_Compat::$fastMult) {
559 + return (int) ($a * $b);
560 + }
561 +
562 + static $defaultSize = null;
563 + /** @var int $defaultSize */
564 + if (!$defaultSize) {
565 + /** @var int $defaultSize */
566 + $defaultSize = (PHP_INT_SIZE << 3) - 1;
567 + }
568 + if ($size < 1) {
569 + /** @var int $size */
570 + $size = $defaultSize;
571 + }
572 + /** @var int $size */
573 +
574 + $c = 0;
575 +
576 + /**
577 + * Mask is either -1 or 0.
578 + *
579 + * -1 in binary looks like 0x1111 ... 1111
580 + * 0 in binary looks like 0x0000 ... 0000
581 + *
582 + * @var int
583 + */
584 + $mask = -(($b >> ((int) $defaultSize)) & 1);
585 +
586 + /**
587 + * Ensure $b is a positive integer, without creating
588 + * a branching side-channel
589 + *
590 + * @var int $b
591 + */
592 + $b = ($b & ~$mask) | ($mask & -$b);
593 +
594 + /**
595 + * Unless $size is provided:
596 + *
597 + * This loop always runs 32 times when PHP_INT_SIZE is 4.
598 + * This loop always runs 64 times when PHP_INT_SIZE is 8.
599 + */
600 + for ($i = $size; $i >= 0; --$i) {
601 + $c += (int) ($a & -($b & 1));
602 + $a <<= 1;
603 + $b >>= 1;
604 + }
605 + $c = (int) @($c & -1);
606 +
607 + /**
608 + * If $b was negative, we then apply the same value to $c here.
609 + * It doesn't matter much if $a was negative; the $c += above would
610 + * have produced a negative integer to begin with. But a negative $b
611 + * makes $b >>= 1 never return 0, so we would end up with incorrect
612 + * results.
613 + *
614 + * The end result is what we'd expect from integer multiplication.
615 + */
616 + return (int) (($c & ~$mask) | ($mask & -$c));
617 + }
618 +
619 + /**
620 + * Convert any arbitrary numbers into two 32-bit integers that represent
621 + * a 64-bit integer.
622 + *
623 + * @internal You should not use this directly from another application
624 + *
625 + * @param int|float $num
626 + * @return array<int, int>
627 + */
628 + public static function numericTo64BitInteger($num)
629 + {
630 + $high = 0;
631 + /** @var int $low */
632 + if (PHP_INT_SIZE === 4) {
633 + $low = (int) $num;
634 + } else {
635 + $low = $num & 0xffffffff;
636 + }
637 +
638 + if ((+(abs($num))) >= 1) {
639 + if ($num > 0) {
640 + /** @var int $high */
641 + $high = min((+(floor($num/4294967296))), 4294967295);
642 + } else {
643 + /** @var int $high */
644 + $high = ~~((+(ceil(($num - (+((~~($num)))))/4294967296))));
645 + }
646 + }
647 + return array((int) $high, (int) $low);
648 + }
649 +
650 + /**
651 + * Store a 24-bit integer into a string, treating it as big-endian.
652 + *
653 + * @internal You should not use this directly from another application
654 + *
655 + * @param int $int
656 + * @return string
657 + * @throws TypeError
658 + */
659 + public static function store_3($int)
660 + {
661 + /* Type checks: */
662 + if (!is_int($int)) {
663 + if (is_numeric($int)) {
664 + $int = (int) $int;
665 + } else {
666 + throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
667 + }
668 + }
669 + /** @var string $packed */
670 + $packed = pack('N', $int);
671 + return self::substr($packed, 1, 3);
672 + }
673 +
674 + /**
675 + * Store a 32-bit integer into a string, treating it as little-endian.
676 + *
677 + * @internal You should not use this directly from another application
678 + *
679 + * @param int $int
680 + * @return string
681 + * @throws TypeError
682 + */
683 + public static function store32_le($int)
684 + {
685 + /* Type checks: */
686 + if (!is_int($int)) {
687 + if (is_numeric($int)) {
688 + $int = (int) $int;
689 + } else {
690 + throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
691 + }
692 + }
693 +
694 + /** @var string $packed */
695 + $packed = pack('V', $int);
696 + return $packed;
697 + }
698 +
699 + /**
700 + * Store a 32-bit integer into a string, treating it as big-endian.
701 + *
702 + * @internal You should not use this directly from another application
703 + *
704 + * @param int $int
705 + * @return string
706 + * @throws TypeError
707 + */
708 + public static function store_4($int)
709 + {
710 + /* Type checks: */
711 + if (!is_int($int)) {
712 + if (is_numeric($int)) {
713 + $int = (int) $int;
714 + } else {
715 + throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
716 + }
717 + }
718 +
719 + /** @var string $packed */
720 + $packed = pack('N', $int);
721 + return $packed;
722 + }
723 +
724 + /**
725 + * Stores a 64-bit integer as an string, treating it as little-endian.
726 + *
727 + * @internal You should not use this directly from another application
728 + *
729 + * @param int $int
730 + * @return string
731 + * @throws TypeError
732 + */
733 + public static function store64_le($int)
734 + {
735 + /* Type checks: */
736 + if (!is_int($int)) {
737 + if (is_numeric($int)) {
738 + $int = (int) $int;
739 + } else {
740 + throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
741 + }
742 + }
743 +
744 + if (PHP_INT_SIZE === 8) {
745 + if (PHP_VERSION_ID >= 50603) {
746 + /** @var string $packed */
747 + $packed = pack('P', $int);
748 + return $packed;
749 + }
750 + return self::intToChr($int & 0xff) .
751 + self::intToChr(($int >> 8) & 0xff) .
752 + self::intToChr(($int >> 16) & 0xff) .
753 + self::intToChr(($int >> 24) & 0xff) .
754 + self::intToChr(($int >> 32) & 0xff) .
755 + self::intToChr(($int >> 40) & 0xff) .
756 + self::intToChr(($int >> 48) & 0xff) .
757 + self::intToChr(($int >> 56) & 0xff);
758 + }
759 + if ($int > PHP_INT_MAX) {
760 + list($hiB, $int) = self::numericTo64BitInteger($int);
761 + } else {
762 + $hiB = 0;
763 + }
764 + return
765 + self::intToChr(($int ) & 0xff) .
766 + self::intToChr(($int >> 8) & 0xff) .
767 + self::intToChr(($int >> 16) & 0xff) .
768 + self::intToChr(($int >> 24) & 0xff) .
769 + self::intToChr($hiB & 0xff) .
770 + self::intToChr(($hiB >> 8) & 0xff) .
771 + self::intToChr(($hiB >> 16) & 0xff) .
772 + self::intToChr(($hiB >> 24) & 0xff);
773 + }
774 +
775 + /**
776 + * Safe string length
777 + *
778 + * @internal You should not use this directly from another application
779 + *
780 + * @ref mbstring.func_overload
781 + *
782 + * @param string $str
783 + * @return int
784 + * @throws TypeError
785 + */
786 + public static function strlen($str)
787 + {
788 + /* Type checks: */
789 + if (!is_string($str)) {
790 + throw new TypeError('String expected');
791 + }
792 +
793 + return (int) (
794 + self::isMbStringOverride()
795 + ? mb_strlen($str, '8bit')
796 + : strlen($str)
797 + );
798 + }
799 +
800 + /**
801 + * Turn a string into an array of integers
802 + *
803 + * @internal You should not use this directly from another application
804 + *
805 + * @param string $string
806 + * @return array<int, int>
807 + * @throws TypeError
808 + */
809 + public static function stringToIntArray($string)
810 + {
811 + if (!is_string($string)) {
812 + throw new TypeError('String expected');
813 + }
814 + /**
815 + * @var array<int, int>
816 + */
817 + $values = array_values(
818 + unpack('C*', $string)
819 + );
820 + return $values;
821 + }
822 +
823 + /**
824 + * Safe substring
825 + *
826 + * @internal You should not use this directly from another application
827 + *
828 + * @ref mbstring.func_overload
829 + *
830 + * @param string $str
831 + * @param int $start
832 + * @param int $length
833 + * @return string
834 + * @throws TypeError
835 + */
836 + public static function substr($str, $start = 0, $length = null)
837 + {
838 + /* Type checks: */
839 + if (!is_string($str)) {
840 + throw new TypeError('String expected');
841 + }
842 +
843 + if ($length === 0) {
844 + return '';
845 + }
846 +
847 + if (self::isMbStringOverride()) {
848 + if (PHP_VERSION_ID < 50400 && $length === null) {
849 + $length = self::strlen($str);
850 + }
851 + $sub = (string) mb_substr($str, $start, $length, '8bit');
852 + } elseif ($length === null) {
853 + $sub = (string) substr($str, $start);
854 + } else {
855 + $sub = (string) substr($str, $start, $length);
856 + }
857 + if ($sub !== '') {
858 + return $sub;
859 + }
860 + return '';
861 + }
862 +
863 + /**
864 + * Compare a 16-character byte string in constant time.
865 + *
866 + * @internal You should not use this directly from another application
867 + *
868 + * @param string $a
869 + * @param string $b
870 + * @return bool
871 + * @throws SodiumException
872 + * @throws TypeError
873 + */
874 + public static function verify_16($a, $b)
875 + {
876 + /* Type checks: */
877 + if (!is_string($a)) {
878 + throw new TypeError('String expected');
879 + }
880 + if (!is_string($b)) {
881 + throw new TypeError('String expected');
882 + }
883 + return self::hashEquals(
884 + self::substr($a, 0, 16),
885 + self::substr($b, 0, 16)
886 + );
887 + }
888 +
889 + /**
890 + * Compare a 32-character byte string in constant time.
891 + *
892 + * @internal You should not use this directly from another application
893 + *
894 + * @param string $a
895 + * @param string $b
896 + * @return bool
897 + * @throws SodiumException
898 + * @throws TypeError
899 + */
900 + public static function verify_32($a, $b)
901 + {
902 + /* Type checks: */
903 + if (!is_string($a)) {
904 + throw new TypeError('String expected');
905 + }
906 + if (!is_string($b)) {
907 + throw new TypeError('String expected');
908 + }
909 + return self::hashEquals(
910 + self::substr($a, 0, 32),
911 + self::substr($b, 0, 32)
912 + );
913 + }
914 +
915 + /**
916 + * Calculate $a ^ $b for two strings.
917 + *
918 + * @internal You should not use this directly from another application
919 + *
920 + * @param string $a
921 + * @param string $b
922 + * @return string
923 + * @throws TypeError
924 + */
925 + public static function xorStrings($a, $b)
926 + {
927 + /* Type checks: */
928 + if (!is_string($a)) {
929 + throw new TypeError('Argument 1 must be a string');
930 + }
931 + if (!is_string($b)) {
932 + throw new TypeError('Argument 2 must be a string');
933 + }
934 +
935 + return (string) ($a ^ $b);
936 + }
937 +
938 + /**
939 + * Returns whether or not mbstring.func_overload is in effect.
940 + *
941 + * @internal You should not use this directly from another application
942 + *
943 + * Note: MB_OVERLOAD_STRING === 2, but we don't reference the constant
944 + * (for nuisance-free PHP 8 support)
945 + *
946 + * @return bool
947 + */
948 + protected static function isMbStringOverride()
949 + {
950 + static $mbstring = null;
951 +
952 + if ($mbstring === null) {
953 + if (!defined('MB_OVERLOAD_STRING')) {
954 + $mbstring = false;
955 + return $mbstring;
956 + }
957 + $mbstring = extension_loaded('mbstring')
958 + && defined('MB_OVERLOAD_STRING')
959 + &&
960 + ((int) (ini_get('mbstring.func_overload')) & 2);
961 + // MB_OVERLOAD_STRING === 2
962 + }
963 + /** @var bool $mbstring */
964 +
965 + return $mbstring;
966 + }
967 + }
968 +