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