[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/sodium_compat/src/Core/ -> Util.php (source)

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


Generated: Sat Apr 27 01:00:02 2024 Cross-referenced by PHPXref 0.7.1