[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

   1  <?php
   2  
   3  /**
   4   * Libsodium compatibility layer
   5   *
   6   * This is the only class you should be interfacing with, as a user of
   7   * sodium_compat.
   8   *
   9   * If the PHP extension for libsodium is installed, it will always use that
  10   * instead of our implementations. You get better performance and stronger
  11   * guarantees against side-channels that way.
  12   *
  13   * However, if your users don't have the PHP extension installed, we offer a
  14   * compatible interface here. It will give you the correct results as if the
  15   * PHP extension was installed. It won't be as fast, of course.
  16   *
  17   * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION *
  18   *                                                                               *
  19   *     Until audited, this is probably not safe to use! DANGER WILL ROBINSON     *
  20   *                                                                               *
  21   * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION *
  22   */
  23  
  24  if (class_exists('ParagonIE_Sodium_Compat', false)) {
  25      return;
  26  }
  27  
  28  class ParagonIE_Sodium_Compat
  29  {
  30      /**
  31       * This parameter prevents the use of the PECL extension.
  32       * It should only be used for unit testing.
  33       *
  34       * @var bool
  35       */
  36      public static $disableFallbackForUnitTests = false;
  37  
  38      /**
  39       * Use fast multiplication rather than our constant-time multiplication
  40       * implementation. Can be enabled at runtime. Only enable this if you
  41       * are absolutely certain that there is no timing leak on your platform.
  42       *
  43       * @var bool
  44       */
  45      public static $fastMult = false;
  46  
  47      const LIBRARY_VERSION_MAJOR = 9;
  48      const LIBRARY_VERSION_MINOR = 1;
  49      const VERSION_STRING = 'polyfill-1.0.8';
  50  
  51      // From libsodium
  52      const BASE64_VARIANT_ORIGINAL = 1;
  53      const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
  54      const BASE64_VARIANT_URLSAFE = 5;
  55      const BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
  56      const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
  57      const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
  58      const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
  59      const CRYPTO_AEAD_AES256GCM_ABYTES = 16;
  60      const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
  61      const CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0;
  62      const CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8;
  63      const CRYPTO_AEAD_CHACHA20POLY1305_ABYTES = 16;
  64      const CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES = 32;
  65      const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES = 0;
  66      const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12;
  67      const CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES = 16;
  68      const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES = 32;
  69      const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES = 0;
  70      const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES = 24;
  71      const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES = 16;
  72      const CRYPTO_AUTH_BYTES = 32;
  73      const CRYPTO_AUTH_KEYBYTES = 32;
  74      const CRYPTO_BOX_SEALBYTES = 16;
  75      const CRYPTO_BOX_SECRETKEYBYTES = 32;
  76      const CRYPTO_BOX_PUBLICKEYBYTES = 32;
  77      const CRYPTO_BOX_KEYPAIRBYTES = 64;
  78      const CRYPTO_BOX_MACBYTES = 16;
  79      const CRYPTO_BOX_NONCEBYTES = 24;
  80      const CRYPTO_BOX_SEEDBYTES = 32;
  81      const CRYPTO_KDF_BYTES_MIN = 16;
  82      const CRYPTO_KDF_BYTES_MAX = 64;
  83      const CRYPTO_KDF_CONTEXTBYTES = 8;
  84      const CRYPTO_KDF_KEYBYTES = 32;
  85      const CRYPTO_KX_BYTES = 32;
  86      const CRYPTO_KX_PRIMITIVE = 'x25519blake2b';
  87      const CRYPTO_KX_SEEDBYTES = 32;
  88      const CRYPTO_KX_KEYPAIRBYTES = 64;
  89      const CRYPTO_KX_PUBLICKEYBYTES = 32;
  90      const CRYPTO_KX_SECRETKEYBYTES = 32;
  91      const CRYPTO_KX_SESSIONKEYBYTES = 32;
  92      const CRYPTO_GENERICHASH_BYTES = 32;
  93      const CRYPTO_GENERICHASH_BYTES_MIN = 16;
  94      const CRYPTO_GENERICHASH_BYTES_MAX = 64;
  95      const CRYPTO_GENERICHASH_KEYBYTES = 32;
  96      const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
  97      const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
  98      const CRYPTO_PWHASH_SALTBYTES = 16;
  99      const CRYPTO_PWHASH_STRPREFIX = '$argon2id$';
 100      const CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
 101      const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
 102      const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432;
 103      const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4;
 104      const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728;
 105      const CRYPTO_PWHASH_OPSLIMIT_MODERATE = 6;
 106      const CRYPTO_PWHASH_MEMLIMIT_SENSITIVE = 536870912;
 107      const CRYPTO_PWHASH_OPSLIMIT_SENSITIVE = 8;
 108      const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES = 32;
 109      const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX = '$7$';
 110      const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE = 534288;
 111      const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE = 16777216;
 112      const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE = 33554432;
 113      const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE = 1073741824;
 114      const CRYPTO_SCALARMULT_BYTES = 32;
 115      const CRYPTO_SCALARMULT_SCALARBYTES = 32;
 116      const CRYPTO_SHORTHASH_BYTES = 8;
 117      const CRYPTO_SHORTHASH_KEYBYTES = 16;
 118      const CRYPTO_SECRETBOX_KEYBYTES = 32;
 119      const CRYPTO_SECRETBOX_MACBYTES = 16;
 120      const CRYPTO_SECRETBOX_NONCEBYTES = 24;
 121      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17;
 122      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24;
 123      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32;
 124      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0;
 125      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1;
 126      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2;
 127      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3;
 128      const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80;
 129      const CRYPTO_SIGN_BYTES = 64;
 130      const CRYPTO_SIGN_SEEDBYTES = 32;
 131      const CRYPTO_SIGN_PUBLICKEYBYTES = 32;
 132      const CRYPTO_SIGN_SECRETKEYBYTES = 64;
 133      const CRYPTO_SIGN_KEYPAIRBYTES = 96;
 134      const CRYPTO_STREAM_KEYBYTES = 32;
 135      const CRYPTO_STREAM_NONCEBYTES = 24;
 136  
 137      /**
 138       * Add two numbers (little-endian unsigned), storing the value in the first
 139       * parameter.
 140       *
 141       * This mutates $val.
 142       *
 143       * @param string $val
 144       * @param string $addv
 145       * @return void
 146       * @throws SodiumException
 147       */
 148      public static function add(&$val, $addv)
 149      {
 150          $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
 151          $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
 152          if ($val_len !== $addv_len) {
 153              throw new SodiumException('values must have the same length');
 154          }
 155          $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
 156          $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
 157  
 158          $c = 0;
 159          for ($i = 0; $i < $val_len; $i++) {
 160              $c += ($A[$i] + $B[$i]);
 161              $A[$i] = ($c & 0xff);
 162              $c >>= 8;
 163          }
 164          $val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
 165      }
 166  
 167      /**
 168       * @param string $encoded
 169       * @param int $variant
 170       * @param string $ignore
 171       * @return string
 172       * @throws SodiumException
 173       */
 174      public static function base642bin($encoded, $variant, $ignore = '')
 175      {
 176          /* Type checks: */
 177          ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1);
 178  
 179          /** @var string $encoded */
 180          $encoded = (string) $encoded;
 181          if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) {
 182              return '';
 183          }
 184  
 185          // Just strip before decoding
 186          if (!empty($ignore)) {
 187              $encoded = str_replace($ignore, '', $encoded);
 188          }
 189  
 190          try {
 191              switch ($variant) {
 192                  case self::BASE64_VARIANT_ORIGINAL:
 193                      return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true);
 194                  case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
 195                      return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false);
 196                  case self::BASE64_VARIANT_URLSAFE:
 197                      return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true);
 198                  case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
 199                      return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false);
 200                  default:
 201                      throw new SodiumException('invalid base64 variant identifier');
 202              }
 203          } catch (Exception $ex) {
 204              if ($ex instanceof SodiumException) {
 205                  throw $ex;
 206              }
 207              throw new SodiumException('invalid base64 string');
 208          }
 209      }
 210  
 211      /**
 212       * @param string $decoded
 213       * @param int $variant
 214       * @return string
 215       * @throws SodiumException
 216       */
 217      public static function bin2base64($decoded, $variant)
 218      {
 219          /* Type checks: */
 220          ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1);
 221          /** @var string $decoded */
 222          $decoded = (string) $decoded;
 223          if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) {
 224              return '';
 225          }
 226  
 227          switch ($variant) {
 228              case self::BASE64_VARIANT_ORIGINAL:
 229                  return ParagonIE_Sodium_Core_Base64_Original::encode($decoded);
 230              case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
 231                  return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded);
 232              case self::BASE64_VARIANT_URLSAFE:
 233                  return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded);
 234              case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
 235                  return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded);
 236              default:
 237                  throw new SodiumException('invalid base64 variant identifier');
 238          }
 239      }
 240  
 241      /**
 242       * Cache-timing-safe implementation of bin2hex().
 243       *
 244       * @param string $string A string (probably raw binary)
 245       * @return string        A hexadecimal-encoded string
 246       * @throws SodiumException
 247       * @throws TypeError
 248       * @psalm-suppress MixedArgument
 249       */
 250      public static function bin2hex($string)
 251      {
 252          /* Type checks: */
 253          ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
 254  
 255          if (self::useNewSodiumAPI()) {
 256              return (string) sodium_bin2hex($string);
 257          }
 258          if (self::use_fallback('bin2hex')) {
 259              return (string) call_user_func('\\Sodium\\bin2hex', $string);
 260          }
 261          return ParagonIE_Sodium_Core_Util::bin2hex($string);
 262      }
 263  
 264      /**
 265       * Compare two strings, in constant-time.
 266       * Compared to memcmp(), compare() is more useful for sorting.
 267       *
 268       * @param string $left  The left operand; must be a string
 269       * @param string $right The right operand; must be a string
 270       * @return int          If < 0 if the left operand is less than the right
 271       *                      If = 0 if both strings are equal
 272       *                      If > 0 if the right operand is less than the left
 273       * @throws SodiumException
 274       * @throws TypeError
 275       * @psalm-suppress MixedArgument
 276       */
 277      public static function compare($left, $right)
 278      {
 279          /* Type checks: */
 280          ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
 281          ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
 282  
 283          if (self::useNewSodiumAPI()) {
 284              return (int) sodium_compare($left, $right);
 285          }
 286          if (self::use_fallback('compare')) {
 287              return (int) call_user_func('\\Sodium\\compare', $left, $right);
 288          }
 289          return ParagonIE_Sodium_Core_Util::compare($left, $right);
 290      }
 291  
 292      /**
 293       * Is AES-256-GCM even available to use?
 294       *
 295       * @return bool
 296       * @psalm-suppress UndefinedFunction
 297       * @psalm-suppress MixedInferredReturnType
 298       * @psalm-suppress MixedReturnStatement
 299       */
 300      public static function crypto_aead_aes256gcm_is_available()
 301      {
 302          if (self::useNewSodiumAPI()) {
 303              return sodium_crypto_aead_aes256gcm_is_available();
 304          }
 305          if (self::use_fallback('crypto_aead_aes256gcm_is_available')) {
 306              return call_user_func('\\Sodium\\crypto_aead_aes256gcm_is_available');
 307          }
 308          if (PHP_VERSION_ID < 70100) {
 309              // OpenSSL doesn't support AEAD before 7.1.0
 310              return false;
 311          }
 312          if (!is_callable('openssl_encrypt') || !is_callable('openssl_decrypt')) {
 313              // OpenSSL isn't installed
 314              return false;
 315          }
 316          return (bool) in_array('aes-256-gcm', openssl_get_cipher_methods());
 317      }
 318  
 319      /**
 320       * Authenticated Encryption with Associated Data: Decryption
 321       *
 322       * Algorithm:
 323       *     AES-256-GCM
 324       *
 325       * This mode uses a 64-bit random nonce with a 64-bit counter.
 326       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 327       *
 328       * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
 329       * @param string $assocData  Authenticated Associated Data (unencrypted)
 330       * @param string $nonce      Number to be used only Once; must be 8 bytes
 331       * @param string $key        Encryption key
 332       *
 333       * @return string|bool       The original plaintext message
 334       * @throws SodiumException
 335       * @throws TypeError
 336       * @psalm-suppress MixedArgument
 337       * @psalm-suppress MixedInferredReturnType
 338       * @psalm-suppress MixedReturnStatement
 339       */
 340      public static function crypto_aead_aes256gcm_decrypt(
 341          $ciphertext = '',
 342          $assocData = '',
 343          $nonce = '',
 344          $key = ''
 345      ) {
 346          if (!self::crypto_aead_aes256gcm_is_available()) {
 347              throw new SodiumException('AES-256-GCM is not available');
 348          }
 349          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
 350          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 351          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 352          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 353  
 354          /* Input validation: */
 355          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) {
 356              throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long');
 357          }
 358          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) {
 359              throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long');
 360          }
 361          if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_AES256GCM_ABYTES) {
 362              throw new SodiumException('Message must be at least CRYPTO_AEAD_AES256GCM_ABYTES long');
 363          }
 364          if (!is_callable('openssl_decrypt')) {
 365              throw new SodiumException('The OpenSSL extension is not installed, or openssl_decrypt() is not available');
 366          }
 367  
 368          /** @var string $ctext */
 369          $ctext = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, -self::CRYPTO_AEAD_AES256GCM_ABYTES);
 370          /** @var string $authTag */
 371          $authTag = ParagonIE_Sodium_Core_Util::substr($ciphertext, -self::CRYPTO_AEAD_AES256GCM_ABYTES, 16);
 372          return openssl_decrypt(
 373              $ctext,
 374              'aes-256-gcm',
 375              $key,
 376              OPENSSL_RAW_DATA,
 377              $nonce,
 378              $authTag,
 379              $assocData
 380          );
 381      }
 382  
 383      /**
 384       * Authenticated Encryption with Associated Data: Encryption
 385       *
 386       * Algorithm:
 387       *     AES-256-GCM
 388       *
 389       * @param string $plaintext Message to be encrypted
 390       * @param string $assocData Authenticated Associated Data (unencrypted)
 391       * @param string $nonce     Number to be used only Once; must be 8 bytes
 392       * @param string $key       Encryption key
 393       *
 394       * @return string           Ciphertext with a 16-byte GCM message
 395       *                          authentication code appended
 396       * @throws SodiumException
 397       * @throws TypeError
 398       * @psalm-suppress MixedArgument
 399       */
 400      public static function crypto_aead_aes256gcm_encrypt(
 401          $plaintext = '',
 402          $assocData = '',
 403          $nonce = '',
 404          $key = ''
 405      ) {
 406          if (!self::crypto_aead_aes256gcm_is_available()) {
 407              throw new SodiumException('AES-256-GCM is not available');
 408          }
 409          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
 410          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 411          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 412          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 413  
 414          /* Input validation: */
 415          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) {
 416              throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long');
 417          }
 418          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) {
 419              throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long');
 420          }
 421  
 422          if (!is_callable('openssl_encrypt')) {
 423              throw new SodiumException('The OpenSSL extension is not installed, or openssl_encrypt() is not available');
 424          }
 425  
 426          $authTag = '';
 427          $ciphertext = openssl_encrypt(
 428              $plaintext,
 429              'aes-256-gcm',
 430              $key,
 431              OPENSSL_RAW_DATA,
 432              $nonce,
 433              $authTag,
 434              $assocData
 435          );
 436          return $ciphertext . $authTag;
 437      }
 438  
 439      /**
 440       * Return a secure random key for use with the AES-256-GCM
 441       * symmetric AEAD interface.
 442       *
 443       * @return string
 444       * @throws Exception
 445       * @throws Error
 446       */
 447      public static function crypto_aead_aes256gcm_keygen()
 448      {
 449          return random_bytes(self::CRYPTO_AEAD_AES256GCM_KEYBYTES);
 450      }
 451  
 452      /**
 453       * Authenticated Encryption with Associated Data: Decryption
 454       *
 455       * Algorithm:
 456       *     ChaCha20-Poly1305
 457       *
 458       * This mode uses a 64-bit random nonce with a 64-bit counter.
 459       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 460       *
 461       * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
 462       * @param string $assocData  Authenticated Associated Data (unencrypted)
 463       * @param string $nonce      Number to be used only Once; must be 8 bytes
 464       * @param string $key        Encryption key
 465       *
 466       * @return string            The original plaintext message
 467       * @throws SodiumException
 468       * @throws TypeError
 469       * @psalm-suppress MixedArgument
 470       * @psalm-suppress MixedInferredReturnType
 471       * @psalm-suppress MixedReturnStatement
 472       */
 473      public static function crypto_aead_chacha20poly1305_decrypt(
 474          $ciphertext = '',
 475          $assocData = '',
 476          $nonce = '',
 477          $key = ''
 478      ) {
 479          /* Type checks: */
 480          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
 481          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 482          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 483          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 484  
 485          /* Input validation: */
 486          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) {
 487              throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long');
 488          }
 489          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
 490              throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
 491          }
 492          if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) {
 493              throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long');
 494          }
 495  
 496          if (self::useNewSodiumAPI()) {
 497              /**
 498               * @psalm-suppress InvalidReturnStatement
 499               * @psalm-suppress FalsableReturnStatement
 500               */
 501              return sodium_crypto_aead_chacha20poly1305_decrypt(
 502                  $ciphertext,
 503                  $assocData,
 504                  $nonce,
 505                  $key
 506              );
 507          }
 508          if (self::use_fallback('crypto_aead_chacha20poly1305_decrypt')) {
 509              return call_user_func(
 510                  '\\Sodium\\crypto_aead_chacha20poly1305_decrypt',
 511                  $ciphertext,
 512                  $assocData,
 513                  $nonce,
 514                  $key
 515              );
 516          }
 517          if (PHP_INT_SIZE === 4) {
 518              return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_decrypt(
 519                  $ciphertext,
 520                  $assocData,
 521                  $nonce,
 522                  $key
 523              );
 524          }
 525          return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt(
 526              $ciphertext,
 527              $assocData,
 528              $nonce,
 529              $key
 530          );
 531      }
 532  
 533      /**
 534       * Authenticated Encryption with Associated Data
 535       *
 536       * Algorithm:
 537       *     ChaCha20-Poly1305
 538       *
 539       * This mode uses a 64-bit random nonce with a 64-bit counter.
 540       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 541       *
 542       * @param string $plaintext Message to be encrypted
 543       * @param string $assocData Authenticated Associated Data (unencrypted)
 544       * @param string $nonce     Number to be used only Once; must be 8 bytes
 545       * @param string $key       Encryption key
 546       *
 547       * @return string           Ciphertext with a 16-byte Poly1305 message
 548       *                          authentication code appended
 549       * @throws SodiumException
 550       * @throws TypeError
 551       * @psalm-suppress MixedArgument
 552       */
 553      public static function crypto_aead_chacha20poly1305_encrypt(
 554          $plaintext = '',
 555          $assocData = '',
 556          $nonce = '',
 557          $key = ''
 558      ) {
 559          /* Type checks: */
 560          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
 561          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 562          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 563          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 564  
 565          /* Input validation: */
 566          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) {
 567              throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long');
 568          }
 569          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
 570              throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
 571          }
 572  
 573          if (self::useNewSodiumAPI()) {
 574              return (string) sodium_crypto_aead_chacha20poly1305_encrypt(
 575                  $plaintext,
 576                  $assocData,
 577                  $nonce,
 578                  $key
 579              );
 580          }
 581          if (self::use_fallback('crypto_aead_chacha20poly1305_encrypt')) {
 582              return (string) call_user_func(
 583                  '\\Sodium\\crypto_aead_chacha20poly1305_encrypt',
 584                  $plaintext,
 585                  $assocData,
 586                  $nonce,
 587                  $key
 588              );
 589          }
 590          if (PHP_INT_SIZE === 4) {
 591              return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_encrypt(
 592                  $plaintext,
 593                  $assocData,
 594                  $nonce,
 595                  $key
 596              );
 597          }
 598          return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_encrypt(
 599              $plaintext,
 600              $assocData,
 601              $nonce,
 602              $key
 603          );
 604      }
 605  
 606      /**
 607       * Authenticated Encryption with Associated Data: Decryption
 608       *
 609       * Algorithm:
 610       *     ChaCha20-Poly1305
 611       *
 612       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 613       * Regular mode uses a 64-bit random nonce with a 64-bit counter.
 614       *
 615       * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
 616       * @param string $assocData  Authenticated Associated Data (unencrypted)
 617       * @param string $nonce      Number to be used only Once; must be 12 bytes
 618       * @param string $key        Encryption key
 619       *
 620       * @return string            The original plaintext message
 621       * @throws SodiumException
 622       * @throws TypeError
 623       * @psalm-suppress MixedArgument
 624       * @psalm-suppress MixedInferredReturnType
 625       * @psalm-suppress MixedReturnStatement
 626       */
 627      public static function crypto_aead_chacha20poly1305_ietf_decrypt(
 628          $ciphertext = '',
 629          $assocData = '',
 630          $nonce = '',
 631          $key = ''
 632      ) {
 633          /* Type checks: */
 634          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
 635          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 636          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 637          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 638  
 639          /* Input validation: */
 640          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) {
 641              throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
 642          }
 643          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
 644              throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
 645          }
 646          if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) {
 647              throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long');
 648          }
 649  
 650          if (self::useNewSodiumAPI()) {
 651              /**
 652               * @psalm-suppress InvalidReturnStatement
 653               * @psalm-suppress FalsableReturnStatement
 654               */
 655              return sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
 656                  $ciphertext,
 657                  $assocData,
 658                  $nonce,
 659                  $key
 660              );
 661          }
 662          if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_decrypt')) {
 663              return call_user_func(
 664                  '\\Sodium\\crypto_aead_chacha20poly1305_ietf_decrypt',
 665                  $ciphertext,
 666                  $assocData,
 667                  $nonce,
 668                  $key
 669              );
 670          }
 671          if (PHP_INT_SIZE === 4) {
 672              return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_decrypt(
 673                  $ciphertext,
 674                  $assocData,
 675                  $nonce,
 676                  $key
 677              );
 678          }
 679          return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt(
 680              $ciphertext,
 681              $assocData,
 682              $nonce,
 683              $key
 684          );
 685      }
 686  
 687      /**
 688       * Return a secure random key for use with the ChaCha20-Poly1305
 689       * symmetric AEAD interface.
 690       *
 691       * @return string
 692       * @throws Exception
 693       * @throws Error
 694       */
 695      public static function crypto_aead_chacha20poly1305_keygen()
 696      {
 697          return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
 698      }
 699  
 700      /**
 701       * Authenticated Encryption with Associated Data
 702       *
 703       * Algorithm:
 704       *     ChaCha20-Poly1305
 705       *
 706       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 707       * Regular mode uses a 64-bit random nonce with a 64-bit counter.
 708       *
 709       * @param string $plaintext Message to be encrypted
 710       * @param string $assocData Authenticated Associated Data (unencrypted)
 711       * @param string $nonce Number to be used only Once; must be 8 bytes
 712       * @param string $key Encryption key
 713       *
 714       * @return string           Ciphertext with a 16-byte Poly1305 message
 715       *                          authentication code appended
 716       * @throws SodiumException
 717       * @throws TypeError
 718       * @psalm-suppress MixedArgument
 719       */
 720      public static function crypto_aead_chacha20poly1305_ietf_encrypt(
 721          $plaintext = '',
 722          $assocData = '',
 723          $nonce = '',
 724          $key = ''
 725      ) {
 726          /* Type checks: */
 727          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
 728          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 729          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 730          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 731  
 732          /* Input validation: */
 733          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) {
 734              throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
 735          }
 736          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
 737              throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
 738          }
 739  
 740          if (self::useNewSodiumAPI()) {
 741              return (string) sodium_crypto_aead_chacha20poly1305_ietf_encrypt(
 742                  $plaintext,
 743                  $assocData,
 744                  $nonce,
 745                  $key
 746              );
 747          }
 748          if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_encrypt')) {
 749              return (string) call_user_func(
 750                  '\\Sodium\\crypto_aead_chacha20poly1305_ietf_encrypt',
 751                  $plaintext,
 752                  $assocData,
 753                  $nonce,
 754                  $key
 755              );
 756          }
 757          if (PHP_INT_SIZE === 4) {
 758              return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_encrypt(
 759                  $plaintext,
 760                  $assocData,
 761                  $nonce,
 762                  $key
 763              );
 764          }
 765          return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_encrypt(
 766              $plaintext,
 767              $assocData,
 768              $nonce,
 769              $key
 770          );
 771      }
 772  
 773      /**
 774       * Return a secure random key for use with the ChaCha20-Poly1305
 775       * symmetric AEAD interface. (IETF version)
 776       *
 777       * @return string
 778       * @throws Exception
 779       * @throws Error
 780       */
 781      public static function crypto_aead_chacha20poly1305_ietf_keygen()
 782      {
 783          return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES);
 784      }
 785  
 786      /**
 787       * Authenticated Encryption with Associated Data: Decryption
 788       *
 789       * Algorithm:
 790       *     XChaCha20-Poly1305
 791       *
 792       * This mode uses a 64-bit random nonce with a 64-bit counter.
 793       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 794       *
 795       * @param string $ciphertext   Encrypted message (with Poly1305 MAC appended)
 796       * @param string $assocData    Authenticated Associated Data (unencrypted)
 797       * @param string $nonce        Number to be used only Once; must be 8 bytes
 798       * @param string $key          Encryption key
 799       * @param bool   $dontFallback Don't fallback to ext/sodium
 800       *
 801       * @return string|bool         The original plaintext message
 802       * @throws SodiumException
 803       * @throws TypeError
 804       * @psalm-suppress MixedArgument
 805       */
 806      public static function crypto_aead_xchacha20poly1305_ietf_decrypt(
 807          $ciphertext = '',
 808          $assocData = '',
 809          $nonce = '',
 810          $key = '',
 811          $dontFallback = false
 812      ) {
 813          /* Type checks: */
 814          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
 815          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 816          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 817          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 818  
 819          /* Input validation: */
 820          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
 821              throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES long');
 822          }
 823          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
 824              throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES long');
 825          }
 826          if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES) {
 827              throw new SodiumException('Message must be at least CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES long');
 828          }
 829          if (self::useNewSodiumAPI() && !$dontFallback) {
 830              if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
 831                  return sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
 832                      $ciphertext,
 833                      $assocData,
 834                      $nonce,
 835                      $key
 836                  );
 837              }
 838          }
 839  
 840          if (PHP_INT_SIZE === 4) {
 841              return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_decrypt(
 842                  $ciphertext,
 843                  $assocData,
 844                  $nonce,
 845                  $key
 846              );
 847          }
 848          return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt(
 849              $ciphertext,
 850              $assocData,
 851              $nonce,
 852              $key
 853          );
 854      }
 855  
 856      /**
 857       * Authenticated Encryption with Associated Data
 858       *
 859       * Algorithm:
 860       *     XChaCha20-Poly1305
 861       *
 862       * This mode uses a 64-bit random nonce with a 64-bit counter.
 863       * IETF mode uses a 96-bit random nonce with a 32-bit counter.
 864       *
 865       * @param string $plaintext    Message to be encrypted
 866       * @param string $assocData    Authenticated Associated Data (unencrypted)
 867       * @param string $nonce        Number to be used only Once; must be 8 bytes
 868       * @param string $key          Encryption key
 869       * @param bool   $dontFallback Don't fallback to ext/sodium
 870       *
 871       * @return string           Ciphertext with a 16-byte Poly1305 message
 872       *                          authentication code appended
 873       * @throws SodiumException
 874       * @throws TypeError
 875       * @psalm-suppress MixedArgument
 876       */
 877      public static function crypto_aead_xchacha20poly1305_ietf_encrypt(
 878          $plaintext = '',
 879          $assocData = '',
 880          $nonce = '',
 881          $key = '',
 882          $dontFallback = false
 883      ) {
 884          /* Type checks: */
 885          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
 886          ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
 887          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
 888          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
 889  
 890          /* Input validation: */
 891          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
 892              throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_NPUBBYTES long');
 893          }
 894          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
 895              throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_KEYBYTES long');
 896          }
 897          if (self::useNewSodiumAPI() && !$dontFallback) {
 898              if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
 899                  return sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
 900                      $plaintext,
 901                      $assocData,
 902                      $nonce,
 903                      $key
 904                  );
 905              }
 906          }
 907  
 908          if (PHP_INT_SIZE === 4) {
 909              return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_encrypt(
 910                  $plaintext,
 911                  $assocData,
 912                  $nonce,
 913                  $key
 914              );
 915          }
 916          return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt(
 917              $plaintext,
 918              $assocData,
 919              $nonce,
 920              $key
 921          );
 922      }
 923  
 924      /**
 925       * Return a secure random key for use with the XChaCha20-Poly1305
 926       * symmetric AEAD interface.
 927       *
 928       * @return string
 929       * @throws Exception
 930       * @throws Error
 931       */
 932      public static function crypto_aead_xchacha20poly1305_ietf_keygen()
 933      {
 934          return random_bytes(self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES);
 935      }
 936  
 937      /**
 938       * Authenticate a message. Uses symmetric-key cryptography.
 939       *
 940       * Algorithm:
 941       *     HMAC-SHA512-256. Which is HMAC-SHA-512 truncated to 256 bits.
 942       *     Not to be confused with HMAC-SHA-512/256 which would use the
 943       *     SHA-512/256 hash function (uses different initial parameters
 944       *     but still truncates to 256 bits to sidestep length-extension
 945       *     attacks).
 946       *
 947       * @param string $message Message to be authenticated
 948       * @param string $key Symmetric authentication key
 949       * @return string         Message authentication code
 950       * @throws SodiumException
 951       * @throws TypeError
 952       * @psalm-suppress MixedArgument
 953       */
 954      public static function crypto_auth($message, $key)
 955      {
 956          /* Type checks: */
 957          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
 958          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
 959  
 960          /* Input validation: */
 961          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) {
 962              throw new SodiumException('Argument 2 must be CRYPTO_AUTH_KEYBYTES long.');
 963          }
 964  
 965          if (self::useNewSodiumAPI()) {
 966              return (string) sodium_crypto_auth($message, $key);
 967          }
 968          if (self::use_fallback('crypto_auth')) {
 969              return (string) call_user_func('\\Sodium\\crypto_auth', $message, $key);
 970          }
 971          if (PHP_INT_SIZE === 4) {
 972              return ParagonIE_Sodium_Crypto32::auth($message, $key);
 973          }
 974          return ParagonIE_Sodium_Crypto::auth($message, $key);
 975      }
 976  
 977      /**
 978       * @return string
 979       * @throws Exception
 980       * @throws Error
 981       */
 982      public static function crypto_auth_keygen()
 983      {
 984          return random_bytes(self::CRYPTO_AUTH_KEYBYTES);
 985      }
 986  
 987      /**
 988       * Verify the MAC of a message previously authenticated with crypto_auth.
 989       *
 990       * @param string $mac Message authentication code
 991       * @param string $message Message whose authenticity you are attempting to
 992       *                        verify (with a given MAC and key)
 993       * @param string $key Symmetric authentication key
 994       * @return bool           TRUE if authenticated, FALSE otherwise
 995       * @throws SodiumException
 996       * @throws TypeError
 997       * @psalm-suppress MixedArgument
 998       */
 999      public static function crypto_auth_verify($mac, $message, $key)
1000      {
1001          /* Type checks: */
1002          ParagonIE_Sodium_Core_Util::declareScalarType($mac, 'string', 1);
1003          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
1004          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
1005  
1006          /* Input validation: */
1007          if (ParagonIE_Sodium_Core_Util::strlen($mac) !== self::CRYPTO_AUTH_BYTES) {
1008              throw new SodiumException('Argument 1 must be CRYPTO_AUTH_BYTES long.');
1009          }
1010          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) {
1011              throw new SodiumException('Argument 3 must be CRYPTO_AUTH_KEYBYTES long.');
1012          }
1013  
1014          if (self::useNewSodiumAPI()) {
1015              return (bool) sodium_crypto_auth_verify($mac, $message, $key);
1016          }
1017          if (self::use_fallback('crypto_auth_verify')) {
1018              return (bool) call_user_func('\\Sodium\\crypto_auth_verify', $mac, $message, $key);
1019          }
1020          if (PHP_INT_SIZE === 4) {
1021              return ParagonIE_Sodium_Crypto32::auth_verify($mac, $message, $key);
1022          }
1023          return ParagonIE_Sodium_Crypto::auth_verify($mac, $message, $key);
1024      }
1025  
1026      /**
1027       * Authenticated asymmetric-key encryption. Both the sender and recipient
1028       * may decrypt messages.
1029       *
1030       * Algorithm: X25519-XSalsa20-Poly1305.
1031       *     X25519: Elliptic-Curve Diffie Hellman over Curve25519.
1032       *     XSalsa20: Extended-nonce variant of salsa20.
1033       *     Poyl1305: Polynomial MAC for one-time message authentication.
1034       *
1035       * @param string $plaintext The message to be encrypted
1036       * @param string $nonce A Number to only be used Once; must be 24 bytes
1037       * @param string $keypair Your secret key and your recipient's public key
1038       * @return string           Ciphertext with 16-byte Poly1305 MAC
1039       * @throws SodiumException
1040       * @throws TypeError
1041       * @psalm-suppress MixedArgument
1042       */
1043      public static function crypto_box($plaintext, $nonce, $keypair)
1044      {
1045          /* Type checks: */
1046          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
1047          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
1048          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3);
1049  
1050          /* Input validation: */
1051          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) {
1052              throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.');
1053          }
1054          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1055              throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1056          }
1057  
1058          if (self::useNewSodiumAPI()) {
1059              return (string) sodium_crypto_box($plaintext, $nonce, $keypair);
1060          }
1061          if (self::use_fallback('crypto_box')) {
1062              return (string) call_user_func('\\Sodium\\crypto_box', $plaintext, $nonce, $keypair);
1063          }
1064          if (PHP_INT_SIZE === 4) {
1065              return ParagonIE_Sodium_Crypto32::box($plaintext, $nonce, $keypair);
1066          }
1067          return ParagonIE_Sodium_Crypto::box($plaintext, $nonce, $keypair);
1068      }
1069  
1070      /**
1071       * Anonymous public-key encryption. Only the recipient may decrypt messages.
1072       *
1073       * Algorithm: X25519-XSalsa20-Poly1305, as with crypto_box.
1074       *     The sender's X25519 keypair is ephemeral.
1075       *     Nonce is generated from the BLAKE2b hash of both public keys.
1076       *
1077       * This provides ciphertext integrity.
1078       *
1079       * @param string $plaintext Message to be sealed
1080       * @param string $publicKey Your recipient's public key
1081       * @return string           Sealed message that only your recipient can
1082       *                          decrypt
1083       * @throws SodiumException
1084       * @throws TypeError
1085       * @psalm-suppress MixedArgument
1086       */
1087      public static function crypto_box_seal($plaintext, $publicKey)
1088      {
1089          /* Type checks: */
1090          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
1091          ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
1092  
1093          /* Input validation: */
1094          if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1095              throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1096          }
1097  
1098          if (self::useNewSodiumAPI()) {
1099              return (string) sodium_crypto_box_seal($plaintext, $publicKey);
1100          }
1101          if (self::use_fallback('crypto_box_seal')) {
1102              return (string) call_user_func('\\Sodium\\crypto_box_seal', $plaintext, $publicKey);
1103          }
1104          if (PHP_INT_SIZE === 4) {
1105              return ParagonIE_Sodium_Crypto32::box_seal($plaintext, $publicKey);
1106          }
1107          return ParagonIE_Sodium_Crypto::box_seal($plaintext, $publicKey);
1108      }
1109  
1110      /**
1111       * Opens a message encrypted with crypto_box_seal(). Requires
1112       * the recipient's keypair (sk || pk) to decrypt successfully.
1113       *
1114       * This validates ciphertext integrity.
1115       *
1116       * @param string $ciphertext Sealed message to be opened
1117       * @param string $keypair    Your crypto_box keypair
1118       * @return string            The original plaintext message
1119       * @throws SodiumException
1120       * @throws TypeError
1121       * @psalm-suppress MixedArgument
1122       * @psalm-suppress MixedInferredReturnType
1123       * @psalm-suppress MixedReturnStatement
1124       */
1125      public static function crypto_box_seal_open($ciphertext, $keypair)
1126      {
1127          /* Type checks: */
1128          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
1129          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 2);
1130  
1131          /* Input validation: */
1132          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1133              throw new SodiumException('Argument 2 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1134          }
1135  
1136          if (self::useNewSodiumAPI()) {
1137              /**
1138               * @psalm-suppress InvalidReturnStatement
1139               * @psalm-suppress FalsableReturnStatement
1140               */
1141              return sodium_crypto_box_seal_open($ciphertext, $keypair);
1142          }
1143          if (self::use_fallback('crypto_box_seal_open')) {
1144              return call_user_func('\\Sodium\\crypto_box_seal_open', $ciphertext, $keypair);
1145          }
1146          if (PHP_INT_SIZE === 4) {
1147              return ParagonIE_Sodium_Crypto32::box_seal_open($ciphertext, $keypair);
1148          }
1149          return ParagonIE_Sodium_Crypto::box_seal_open($ciphertext, $keypair);
1150      }
1151  
1152      /**
1153       * Generate a new random X25519 keypair.
1154       *
1155       * @return string A 64-byte string; the first 32 are your secret key, while
1156       *                the last 32 are your public key. crypto_box_secretkey()
1157       *                and crypto_box_publickey() exist to separate them so you
1158       *                don't accidentally get them mixed up!
1159       * @throws SodiumException
1160       * @throws TypeError
1161       * @psalm-suppress MixedArgument
1162       */
1163      public static function crypto_box_keypair()
1164      {
1165          if (self::useNewSodiumAPI()) {
1166              return (string) sodium_crypto_box_keypair();
1167          }
1168          if (self::use_fallback('crypto_box_keypair')) {
1169              return (string) call_user_func('\\Sodium\\crypto_box_keypair');
1170          }
1171          if (PHP_INT_SIZE === 4) {
1172              return ParagonIE_Sodium_Crypto32::box_keypair();
1173          }
1174          return ParagonIE_Sodium_Crypto::box_keypair();
1175      }
1176  
1177      /**
1178       * Combine two keys into a keypair for use in library methods that expect
1179       * a keypair. This doesn't necessarily have to be the same person's keys.
1180       *
1181       * @param string $secretKey Secret key
1182       * @param string $publicKey Public key
1183       * @return string    Keypair
1184       * @throws SodiumException
1185       * @throws TypeError
1186       * @psalm-suppress MixedArgument
1187       */
1188      public static function crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey)
1189      {
1190          /* Type checks: */
1191          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
1192          ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
1193  
1194          /* Input validation: */
1195          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
1196              throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
1197          }
1198          if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1199              throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1200          }
1201  
1202          if (self::useNewSodiumAPI()) {
1203              return (string) sodium_crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
1204          }
1205          if (self::use_fallback('crypto_box_keypair_from_secretkey_and_publickey')) {
1206              return (string) call_user_func('\\Sodium\\crypto_box_keypair_from_secretkey_and_publickey', $secretKey, $publicKey);
1207          }
1208          if (PHP_INT_SIZE === 4) {
1209              return ParagonIE_Sodium_Crypto32::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
1210          }
1211          return ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
1212      }
1213  
1214      /**
1215       * Decrypt a message previously encrypted with crypto_box().
1216       *
1217       * @param string $ciphertext Encrypted message
1218       * @param string $nonce      Number to only be used Once; must be 24 bytes
1219       * @param string $keypair    Your secret key and the sender's public key
1220       * @return string            The original plaintext message
1221       * @throws SodiumException
1222       * @throws TypeError
1223       * @psalm-suppress MixedArgument
1224       * @psalm-suppress MixedInferredReturnType
1225       * @psalm-suppress MixedReturnStatement
1226       */
1227      public static function crypto_box_open($ciphertext, $nonce, $keypair)
1228      {
1229          /* Type checks: */
1230          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
1231          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
1232          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3);
1233  
1234          /* Input validation: */
1235          if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_BOX_MACBYTES) {
1236              throw new SodiumException('Argument 1 must be at least CRYPTO_BOX_MACBYTES long.');
1237          }
1238          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) {
1239              throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.');
1240          }
1241          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1242              throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1243          }
1244  
1245          if (self::useNewSodiumAPI()) {
1246              /**
1247               * @psalm-suppress InvalidReturnStatement
1248               * @psalm-suppress FalsableReturnStatement
1249               */
1250              return sodium_crypto_box_open($ciphertext, $nonce, $keypair);
1251          }
1252          if (self::use_fallback('crypto_box_open')) {
1253              return call_user_func('\\Sodium\\crypto_box_open', $ciphertext, $nonce, $keypair);
1254          }
1255          if (PHP_INT_SIZE === 4) {
1256              return ParagonIE_Sodium_Crypto32::box_open($ciphertext, $nonce, $keypair);
1257          }
1258          return ParagonIE_Sodium_Crypto::box_open($ciphertext, $nonce, $keypair);
1259      }
1260  
1261      /**
1262       * Extract the public key from a crypto_box keypair.
1263       *
1264       * @param string $keypair Keypair containing secret and public key
1265       * @return string         Your crypto_box public key
1266       * @throws SodiumException
1267       * @throws TypeError
1268       * @psalm-suppress MixedArgument
1269       */
1270      public static function crypto_box_publickey($keypair)
1271      {
1272          /* Type checks: */
1273          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
1274  
1275          /* Input validation: */
1276          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1277              throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1278          }
1279  
1280          if (self::useNewSodiumAPI()) {
1281              return (string) sodium_crypto_box_publickey($keypair);
1282          }
1283          if (self::use_fallback('crypto_box_publickey')) {
1284              return (string) call_user_func('\\Sodium\\crypto_box_publickey', $keypair);
1285          }
1286          if (PHP_INT_SIZE === 4) {
1287              return ParagonIE_Sodium_Crypto32::box_publickey($keypair);
1288          }
1289          return ParagonIE_Sodium_Crypto::box_publickey($keypair);
1290      }
1291  
1292      /**
1293       * Calculate the X25519 public key from a given X25519 secret key.
1294       *
1295       * @param string $secretKey Any X25519 secret key
1296       * @return string           The corresponding X25519 public key
1297       * @throws SodiumException
1298       * @throws TypeError
1299       * @psalm-suppress MixedArgument
1300       */
1301      public static function crypto_box_publickey_from_secretkey($secretKey)
1302      {
1303          /* Type checks: */
1304          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
1305  
1306          /* Input validation: */
1307          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
1308              throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
1309          }
1310  
1311          if (self::useNewSodiumAPI()) {
1312              return (string) sodium_crypto_box_publickey_from_secretkey($secretKey);
1313          }
1314          if (self::use_fallback('crypto_box_publickey_from_secretkey')) {
1315              return (string) call_user_func('\\Sodium\\crypto_box_publickey_from_secretkey', $secretKey);
1316          }
1317          if (PHP_INT_SIZE === 4) {
1318              return ParagonIE_Sodium_Crypto32::box_publickey_from_secretkey($secretKey);
1319          }
1320          return ParagonIE_Sodium_Crypto::box_publickey_from_secretkey($secretKey);
1321      }
1322  
1323      /**
1324       * Extract the secret key from a crypto_box keypair.
1325       *
1326       * @param string $keypair
1327       * @return string         Your crypto_box secret key
1328       * @throws SodiumException
1329       * @throws TypeError
1330       * @psalm-suppress MixedArgument
1331       */
1332      public static function crypto_box_secretkey($keypair)
1333      {
1334          /* Type checks: */
1335          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
1336  
1337          /* Input validation: */
1338          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1339              throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1340          }
1341  
1342          if (self::useNewSodiumAPI()) {
1343              return (string) sodium_crypto_box_secretkey($keypair);
1344          }
1345          if (self::use_fallback('crypto_box_secretkey')) {
1346              return (string) call_user_func('\\Sodium\\crypto_box_secretkey', $keypair);
1347          }
1348          if (PHP_INT_SIZE === 4) {
1349              return ParagonIE_Sodium_Crypto32::box_secretkey($keypair);
1350          }
1351          return ParagonIE_Sodium_Crypto::box_secretkey($keypair);
1352      }
1353  
1354      /**
1355       * Generate an X25519 keypair from a seed.
1356       *
1357       * @param string $seed
1358       * @return string
1359       * @throws SodiumException
1360       * @throws TypeError
1361       * @psalm-suppress MixedArgument
1362       * @psalm-suppress UndefinedFunction
1363       */
1364      public static function crypto_box_seed_keypair($seed)
1365      {
1366          /* Type checks: */
1367          ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
1368  
1369          if (self::useNewSodiumAPI()) {
1370              return (string) sodium_crypto_box_seed_keypair($seed);
1371          }
1372          if (self::use_fallback('crypto_box_seed_keypair')) {
1373              return (string) call_user_func('\\Sodium\\crypto_box_seed_keypair', $seed);
1374          }
1375          if (PHP_INT_SIZE === 4) {
1376              return ParagonIE_Sodium_Crypto32::box_seed_keypair($seed);
1377          }
1378          return ParagonIE_Sodium_Crypto::box_seed_keypair($seed);
1379      }
1380  
1381      /**
1382       * Calculates a BLAKE2b hash, with an optional key.
1383       *
1384       * @param string      $message The message to be hashed
1385       * @param string|null $key     If specified, must be a string between 16
1386       *                             and 64 bytes long
1387       * @param int         $length  Output length in bytes; must be between 16
1388       *                             and 64 (default = 32)
1389       * @return string              Raw binary
1390       * @throws SodiumException
1391       * @throws TypeError
1392       * @psalm-suppress MixedArgument
1393       */
1394      public static function crypto_generichash($message, $key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
1395      {
1396          /* Type checks: */
1397          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
1398          if (is_null($key)) {
1399              $key = '';
1400          }
1401          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
1402          ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 3);
1403  
1404          /* Input validation: */
1405          if (!empty($key)) {
1406              if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
1407                  throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
1408              }
1409              if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
1410                  throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
1411              }
1412          }
1413  
1414          if (self::useNewSodiumAPI()) {
1415              return (string) sodium_crypto_generichash($message, $key, $length);
1416          }
1417          if (self::use_fallback('crypto_generichash')) {
1418              return (string) call_user_func('\\Sodium\\crypto_generichash', $message, $key, $length);
1419          }
1420          if (PHP_INT_SIZE === 4) {
1421              return ParagonIE_Sodium_Crypto32::generichash($message, $key, $length);
1422          }
1423          return ParagonIE_Sodium_Crypto::generichash($message, $key, $length);
1424      }
1425  
1426      /**
1427       * Get the final BLAKE2b hash output for a given context.
1428       *
1429       * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init().
1430       * @param int $length Hash output size.
1431       * @return string     Final BLAKE2b hash.
1432       * @throws SodiumException
1433       * @throws TypeError
1434       * @psalm-suppress MixedArgument
1435       * @psalm-suppress ReferenceConstraintViolation
1436       * @psalm-suppress ConflictingReferenceConstraint
1437       */
1438      public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES)
1439      {
1440          /* Type checks: */
1441          ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
1442          ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
1443  
1444          if (self::useNewSodiumAPI()) {
1445              return sodium_crypto_generichash_final($ctx, $length);
1446          }
1447          if (self::use_fallback('crypto_generichash_final')) {
1448              $func = '\\Sodium\\crypto_generichash_final';
1449              return (string) $func($ctx, $length);
1450          }
1451          if ($length < 1) {
1452              try {
1453                  self::memzero($ctx);
1454              } catch (SodiumException $ex) {
1455                  unset($ctx);
1456              }
1457              return '';
1458          }
1459          if (PHP_INT_SIZE === 4) {
1460              $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length);
1461          } else {
1462              $result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length);
1463          }
1464          try {
1465              self::memzero($ctx);
1466          } catch (SodiumException $ex) {
1467              unset($ctx);
1468          }
1469          return $result;
1470      }
1471  
1472      /**
1473       * Initialize a BLAKE2b hashing context, for use in a streaming interface.
1474       *
1475       * @param string|null $key If specified must be a string between 16 and 64 bytes
1476       * @param int $length      The size of the desired hash output
1477       * @return string          A BLAKE2 hashing context, encoded as a string
1478       *                         (To be 100% compatible with ext/libsodium)
1479       * @throws SodiumException
1480       * @throws TypeError
1481       * @psalm-suppress MixedArgument
1482       */
1483      public static function crypto_generichash_init($key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
1484      {
1485          /* Type checks: */
1486          if (is_null($key)) {
1487              $key = '';
1488          }
1489          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
1490          ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
1491  
1492          /* Input validation: */
1493          if (!empty($key)) {
1494              if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
1495                  throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
1496              }
1497              if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
1498                  throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
1499              }
1500          }
1501  
1502          if (self::useNewSodiumAPI()) {
1503              return sodium_crypto_generichash_init($key, $length);
1504          }
1505          if (self::use_fallback('crypto_generichash_init')) {
1506              return (string) call_user_func('\\Sodium\\crypto_generichash_init', $key, $length);
1507          }
1508          if (PHP_INT_SIZE === 4) {
1509              return ParagonIE_Sodium_Crypto32::generichash_init($key, $length);
1510          }
1511          return ParagonIE_Sodium_Crypto::generichash_init($key, $length);
1512      }
1513  
1514      /**
1515       * Initialize a BLAKE2b hashing context, for use in a streaming interface.
1516       *
1517       * @param string|null $key If specified must be a string between 16 and 64 bytes
1518       * @param int $length      The size of the desired hash output
1519       * @param string $salt     Salt (up to 16 bytes)
1520       * @param string $personal Personalization string (up to 16 bytes)
1521       * @return string          A BLAKE2 hashing context, encoded as a string
1522       *                         (To be 100% compatible with ext/libsodium)
1523       * @throws SodiumException
1524       * @throws TypeError
1525       * @psalm-suppress MixedArgument
1526       */
1527      public static function crypto_generichash_init_salt_personal(
1528          $key = '',
1529          $length = self::CRYPTO_GENERICHASH_BYTES,
1530          $salt = '',
1531          $personal = ''
1532      ) {
1533          /* Type checks: */
1534          if (is_null($key)) {
1535              $key = '';
1536          }
1537          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
1538          ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
1539          ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
1540          ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4);
1541          $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT);
1542          $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT);
1543  
1544          /* Input validation: */
1545          if (!empty($key)) {
1546              /*
1547              if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
1548                  throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
1549              }
1550              */
1551              if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
1552                  throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
1553              }
1554          }
1555          if (PHP_INT_SIZE === 4) {
1556              return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal);
1557          }
1558          return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal);
1559      }
1560  
1561      /**
1562       * Update a BLAKE2b hashing context with additional data.
1563       *
1564       * @param string $ctx    BLAKE2 hashing context. Generated by crypto_generichash_init().
1565       *                       $ctx is passed by reference and gets updated in-place.
1566       * @param-out string $ctx
1567       * @param string $message The message to append to the existing hash state.
1568       * @return void
1569       * @throws SodiumException
1570       * @throws TypeError
1571       * @psalm-suppress MixedArgument
1572       * @psalm-suppress ReferenceConstraintViolation
1573       */
1574      public static function crypto_generichash_update(&$ctx, $message)
1575      {
1576          /* Type checks: */
1577          ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
1578          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
1579  
1580          if (self::useNewSodiumAPI()) {
1581              sodium_crypto_generichash_update($ctx, $message);
1582              return;
1583          }
1584          if (self::use_fallback('crypto_generichash_update')) {
1585              $func = '\\Sodium\\crypto_generichash_update';
1586              $func($ctx, $message);
1587              return;
1588          }
1589          if (PHP_INT_SIZE === 4) {
1590              $ctx = ParagonIE_Sodium_Crypto32::generichash_update($ctx, $message);
1591          } else {
1592              $ctx = ParagonIE_Sodium_Crypto::generichash_update($ctx, $message);
1593          }
1594      }
1595  
1596      /**
1597       * @return string
1598       * @throws Exception
1599       * @throws Error
1600       */
1601      public static function crypto_generichash_keygen()
1602      {
1603          return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES);
1604      }
1605  
1606      /**
1607       * @param int $subkey_len
1608       * @param int $subkey_id
1609       * @param string $context
1610       * @param string $key
1611       * @return string
1612       * @throws SodiumException
1613       */
1614      public static function crypto_kdf_derive_from_key(
1615          $subkey_len,
1616          $subkey_id,
1617          $context,
1618          $key
1619      ) {
1620          ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
1621          ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2);
1622          ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3);
1623          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
1624          $subkey_id = (int) $subkey_id;
1625          $subkey_len = (int) $subkey_len;
1626          $context = (string) $context;
1627          $key = (string) $key;
1628  
1629          if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) {
1630              throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN');
1631          }
1632          if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) {
1633              throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX');
1634          }
1635          if ($subkey_id < 0) {
1636              throw new SodiumException('subkey_id cannot be negative');
1637          }
1638          if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) {
1639              throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes');
1640          }
1641          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) {
1642              throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes');
1643          }
1644  
1645          $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id);
1646          $state = self::crypto_generichash_init_salt_personal(
1647              $key,
1648              $subkey_len,
1649              $salt,
1650              $context
1651          );
1652          return self::crypto_generichash_final($state, $subkey_len);
1653      }
1654  
1655      /**
1656       * @return string
1657       * @throws Exception
1658       * @throws Error
1659       */
1660      public static function crypto_kdf_keygen()
1661      {
1662          return random_bytes(self::CRYPTO_KDF_KEYBYTES);
1663      }
1664  
1665      /**
1666       * Perform a key exchange, between a designated client and a server.
1667       *
1668       * Typically, you would designate one machine to be the client and the
1669       * other to be the server. The first two keys are what you'd expect for
1670       * scalarmult() below, but the latter two public keys don't swap places.
1671       *
1672       * | ALICE                          | BOB                                 |
1673       * | Client                         | Server                              |
1674       * |--------------------------------|-------------------------------------|
1675       * | shared = crypto_kx(            | shared = crypto_kx(                 |
1676       * |     alice_sk,                  |     bob_sk,                         | <- contextual
1677       * |     bob_pk,                    |     alice_pk,                       | <- contextual
1678       * |     alice_pk,                  |     alice_pk,                       | <----- static
1679       * |     bob_pk                     |     bob_pk                          | <----- static
1680       * | )                              | )                                   |
1681       *
1682       * They are used along with the scalarmult product to generate a 256-bit
1683       * BLAKE2b hash unique to the client and server keys.
1684       *
1685       * @param string $my_secret
1686       * @param string $their_public
1687       * @param string $client_public
1688       * @param string $server_public
1689       * @return string
1690       * @throws SodiumException
1691       * @throws TypeError
1692       * @psalm-suppress MixedArgument
1693       */
1694      public static function crypto_kx($my_secret, $their_public, $client_public, $server_public)
1695      {
1696          /* Type checks: */
1697          ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1);
1698          ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2);
1699          ParagonIE_Sodium_Core_Util::declareScalarType($client_public, 'string', 3);
1700          ParagonIE_Sodium_Core_Util::declareScalarType($server_public, 'string', 4);
1701  
1702          /* Input validation: */
1703          if (ParagonIE_Sodium_Core_Util::strlen($my_secret) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
1704              throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
1705          }
1706          if (ParagonIE_Sodium_Core_Util::strlen($their_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1707              throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1708          }
1709          if (ParagonIE_Sodium_Core_Util::strlen($client_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1710              throw new SodiumException('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1711          }
1712          if (ParagonIE_Sodium_Core_Util::strlen($server_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1713              throw new SodiumException('Argument 4 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1714          }
1715  
1716          if (self::useNewSodiumAPI()) {
1717              if (is_callable('sodium_crypto_kx')) {
1718                  return (string) sodium_crypto_kx(
1719                      $my_secret,
1720                      $their_public,
1721                      $client_public,
1722                      $server_public
1723                  );
1724              }
1725          }
1726          if (self::use_fallback('crypto_kx')) {
1727              return (string) call_user_func(
1728                  '\\Sodium\\crypto_kx',
1729                  $my_secret,
1730                  $their_public,
1731                  $client_public,
1732                  $server_public
1733              );
1734          }
1735          if (PHP_INT_SIZE === 4) {
1736              return ParagonIE_Sodium_Crypto32::keyExchange(
1737                  $my_secret,
1738                  $their_public,
1739                  $client_public,
1740                  $server_public
1741              );
1742          }
1743          return ParagonIE_Sodium_Crypto::keyExchange(
1744              $my_secret,
1745              $their_public,
1746              $client_public,
1747              $server_public
1748          );
1749      }
1750  
1751      /**
1752       * @param string $seed
1753       * @return string
1754       * @throws SodiumException
1755       */
1756      public static function crypto_kx_seed_keypair($seed)
1757      {
1758          ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
1759  
1760          $seed = (string) $seed;
1761  
1762          if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) {
1763              throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes');
1764          }
1765  
1766          $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES);
1767          $pk = self::crypto_scalarmult_base($sk);
1768          return $sk . $pk;
1769      }
1770  
1771      /**
1772       * @return string
1773       * @throws Exception
1774       */
1775      public static function crypto_kx_keypair()
1776      {
1777          $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES);
1778          $pk = self::crypto_scalarmult_base($sk);
1779          return $sk . $pk;
1780      }
1781  
1782      /**
1783       * @param string $keypair
1784       * @param string $serverPublicKey
1785       * @return array{0: string, 1: string}
1786       * @throws SodiumException
1787       */
1788      public static function crypto_kx_client_session_keys($keypair, $serverPublicKey)
1789      {
1790          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
1791          ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
1792  
1793          $keypair = (string) $keypair;
1794          $serverPublicKey = (string) $serverPublicKey;
1795  
1796          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
1797              throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
1798          }
1799          if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
1800              throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
1801          }
1802  
1803          $sk = self::crypto_kx_secretkey($keypair);
1804          $pk = self::crypto_kx_publickey($keypair);
1805          $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
1806          self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey));
1807          self::crypto_generichash_update($h, $pk);
1808          self::crypto_generichash_update($h, $serverPublicKey);
1809          $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
1810          return array(
1811              ParagonIE_Sodium_Core_Util::substr(
1812                  $sessionKeys,
1813                  0,
1814                  self::CRYPTO_KX_SESSIONKEYBYTES
1815              ),
1816              ParagonIE_Sodium_Core_Util::substr(
1817                  $sessionKeys,
1818                  self::CRYPTO_KX_SESSIONKEYBYTES,
1819                  self::CRYPTO_KX_SESSIONKEYBYTES
1820              )
1821          );
1822      }
1823  
1824      /**
1825       * @param string $keypair
1826       * @param string $clientPublicKey
1827       * @return array{0: string, 1: string}
1828       * @throws SodiumException
1829       */
1830      public static function crypto_kx_server_session_keys($keypair, $clientPublicKey)
1831      {
1832          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
1833          ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
1834  
1835          $keypair = (string) $keypair;
1836          $clientPublicKey = (string) $clientPublicKey;
1837  
1838          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
1839              throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
1840          }
1841          if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
1842              throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
1843          }
1844  
1845          $sk = self::crypto_kx_secretkey($keypair);
1846          $pk = self::crypto_kx_publickey($keypair);
1847          $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
1848          self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey));
1849          self::crypto_generichash_update($h, $clientPublicKey);
1850          self::crypto_generichash_update($h, $pk);
1851          $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
1852          return array(
1853              ParagonIE_Sodium_Core_Util::substr(
1854                  $sessionKeys,
1855                  self::CRYPTO_KX_SESSIONKEYBYTES,
1856                  self::CRYPTO_KX_SESSIONKEYBYTES
1857              ),
1858              ParagonIE_Sodium_Core_Util::substr(
1859                  $sessionKeys,
1860                  0,
1861                  self::CRYPTO_KX_SESSIONKEYBYTES
1862              )
1863          );
1864      }
1865  
1866      /**
1867       * @param string $kp
1868       * @return string
1869       * @throws SodiumException
1870       */
1871      public static function crypto_kx_secretkey($kp)
1872      {
1873          return ParagonIE_Sodium_Core_Util::substr(
1874              $kp,
1875              0,
1876              self::CRYPTO_KX_SECRETKEYBYTES
1877          );
1878      }
1879  
1880      /**
1881       * @param string $kp
1882       * @return string
1883       * @throws SodiumException
1884       */
1885      public static function crypto_kx_publickey($kp)
1886      {
1887          return ParagonIE_Sodium_Core_Util::substr(
1888              $kp,
1889              self::CRYPTO_KX_SECRETKEYBYTES,
1890              self::CRYPTO_KX_PUBLICKEYBYTES
1891          );
1892      }
1893  
1894      /**
1895       * @param int $outlen
1896       * @param string $passwd
1897       * @param string $salt
1898       * @param int $opslimit
1899       * @param int $memlimit
1900       * @param int|null $alg
1901       * @return string
1902       * @throws SodiumException
1903       * @throws TypeError
1904       * @psalm-suppress MixedArgument
1905       */
1906      public static function crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg = null)
1907      {
1908          ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
1909          ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
1910          ParagonIE_Sodium_Core_Util::declareScalarType($salt,  'string', 3);
1911          ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
1912          ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
1913  
1914          if (self::useNewSodiumAPI()) {
1915              if (!is_null($alg)) {
1916                  ParagonIE_Sodium_Core_Util::declareScalarType($alg, 'int', 6);
1917                  return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg);
1918              }
1919              return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit);
1920          }
1921          if (self::use_fallback('crypto_pwhash')) {
1922              return (string) call_user_func('\\Sodium\\crypto_pwhash', $outlen, $passwd, $salt, $opslimit, $memlimit);
1923          }
1924          // This is the best we can do.
1925          throw new SodiumException(
1926              'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
1927          );
1928      }
1929  
1930      /**
1931       * !Exclusive to sodium_compat!
1932       *
1933       * This returns TRUE if the native crypto_pwhash API is available by libsodium.
1934       * This returns FALSE if only sodium_compat is available.
1935       *
1936       * @return bool
1937       */
1938      public static function crypto_pwhash_is_available()
1939      {
1940          if (self::useNewSodiumAPI()) {
1941              return true;
1942          }
1943          if (self::use_fallback('crypto_pwhash')) {
1944              return true;
1945          }
1946          return false;
1947      }
1948  
1949      /**
1950       * @param string $passwd
1951       * @param int $opslimit
1952       * @param int $memlimit
1953       * @return string
1954       * @throws SodiumException
1955       * @throws TypeError
1956       * @psalm-suppress MixedArgument
1957       */
1958      public static function crypto_pwhash_str($passwd, $opslimit, $memlimit)
1959      {
1960          ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
1961          ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
1962          ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
1963  
1964          if (self::useNewSodiumAPI()) {
1965              return sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit);
1966          }
1967          if (self::use_fallback('crypto_pwhash_str')) {
1968              return (string) call_user_func('\\Sodium\\crypto_pwhash_str', $passwd, $opslimit, $memlimit);
1969          }
1970          // This is the best we can do.
1971          throw new SodiumException(
1972              'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
1973          );
1974      }
1975  
1976      /**
1977       * Do we need to rehash this password?
1978       *
1979       * @param string $hash
1980       * @param int $opslimit
1981       * @param int $memlimit
1982       * @return bool
1983       * @throws SodiumException
1984       */
1985      public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit)
1986      {
1987          ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1);
1988          ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
1989          ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
1990  
1991          // Just grab the first 4 pieces.
1992          $pieces = explode('$', (string) $hash);
1993          $prefix = implode('$', array_slice($pieces, 0, 4));
1994  
1995          // Rebuild the expected header.
1996          /** @var int $ops */
1997          $ops = (int) $opslimit;
1998          /** @var int $mem */
1999          $mem = (int) $memlimit >> 10;
2000          $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1';
2001  
2002          // Do they match? If so, we don't need to rehash, so return false.
2003          return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix);
2004      }
2005  
2006      /**
2007       * @param string $passwd
2008       * @param string $hash
2009       * @return bool
2010       * @throws SodiumException
2011       * @throws TypeError
2012       * @psalm-suppress MixedArgument
2013       */
2014      public static function crypto_pwhash_str_verify($passwd, $hash)
2015      {
2016          ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2017          ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
2018  
2019          if (self::useNewSodiumAPI()) {
2020              return (bool) sodium_crypto_pwhash_str_verify($passwd, $hash);
2021          }
2022          if (self::use_fallback('crypto_pwhash_str_verify')) {
2023              return (bool) call_user_func('\\Sodium\\crypto_pwhash_str_verify', $passwd, $hash);
2024          }
2025          // This is the best we can do.
2026          throw new SodiumException(
2027              'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
2028          );
2029      }
2030  
2031      /**
2032       * @param int $outlen
2033       * @param string $passwd
2034       * @param string $salt
2035       * @param int $opslimit
2036       * @param int $memlimit
2037       * @return string
2038       * @throws SodiumException
2039       * @throws TypeError
2040       */
2041      public static function crypto_pwhash_scryptsalsa208sha256($outlen, $passwd, $salt, $opslimit, $memlimit)
2042      {
2043          ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
2044          ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
2045          ParagonIE_Sodium_Core_Util::declareScalarType($salt,  'string', 3);
2046          ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
2047          ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
2048  
2049          if (self::useNewSodiumAPI()) {
2050              return (string) sodium_crypto_pwhash_scryptsalsa208sha256(
2051                  (int) $outlen,
2052                  (string) $passwd,
2053                  (string) $salt,
2054                  (int) $opslimit,
2055                  (int) $memlimit
2056              );
2057          }
2058          if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) {
2059              return (string) call_user_func(
2060                  '\\Sodium\\crypto_pwhash_scryptsalsa208sha256',
2061                  (int) $outlen,
2062                  (string) $passwd,
2063                  (string) $salt,
2064                  (int) $opslimit,
2065                  (int) $memlimit
2066              );
2067          }
2068          // This is the best we can do.
2069          throw new SodiumException(
2070              'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
2071          );
2072      }
2073  
2074      /**
2075       * !Exclusive to sodium_compat!
2076       *
2077       * This returns TRUE if the native crypto_pwhash API is available by libsodium.
2078       * This returns FALSE if only sodium_compat is available.
2079       *
2080       * @return bool
2081       */
2082      public static function crypto_pwhash_scryptsalsa208sha256_is_available()
2083      {
2084          if (self::useNewSodiumAPI()) {
2085              return true;
2086          }
2087          if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) {
2088              return true;
2089          }
2090          return false;
2091      }
2092  
2093      /**
2094       * @param string $passwd
2095       * @param int $opslimit
2096       * @param int $memlimit
2097       * @return string
2098       * @throws SodiumException
2099       * @throws TypeError
2100       */
2101      public static function crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit)
2102      {
2103          ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2104          ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
2105          ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
2106  
2107          if (self::useNewSodiumAPI()) {
2108              return (string) sodium_crypto_pwhash_scryptsalsa208sha256_str(
2109                  (string) $passwd,
2110                  (int) $opslimit,
2111                  (int) $memlimit
2112              );
2113          }
2114          if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str')) {
2115              return (string) call_user_func(
2116                  '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str',
2117                  (string) $passwd,
2118                  (int) $opslimit,
2119                  (int) $memlimit
2120              );
2121          }
2122          // This is the best we can do.
2123          throw new SodiumException(
2124              'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
2125          );
2126      }
2127  
2128      /**
2129       * @param string $passwd
2130       * @param string $hash
2131       * @return bool
2132       * @throws SodiumException
2133       * @throws TypeError
2134       */
2135      public static function crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash)
2136      {
2137          ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2138          ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
2139  
2140          if (self::useNewSodiumAPI()) {
2141              return (bool) sodium_crypto_pwhash_scryptsalsa208sha256_str_verify(
2142                  (string) $passwd,
2143                  (string) $hash
2144              );
2145          }
2146          if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str_verify')) {
2147              return (bool) call_user_func(
2148                  '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str_verify',
2149                  (string) $passwd,
2150                  (string) $hash
2151              );
2152          }
2153          // This is the best we can do.
2154          throw new SodiumException(
2155              'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
2156          );
2157      }
2158  
2159      /**
2160       * Calculate the shared secret between your secret key and your
2161       * recipient's public key.
2162       *
2163       * Algorithm: X25519 (ECDH over Curve25519)
2164       *
2165       * @param string $secretKey
2166       * @param string $publicKey
2167       * @return string
2168       * @throws SodiumException
2169       * @throws TypeError
2170       * @psalm-suppress MixedArgument
2171       */
2172      public static function crypto_scalarmult($secretKey, $publicKey)
2173      {
2174          /* Type checks: */
2175          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
2176          ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
2177  
2178          /* Input validation: */
2179          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
2180              throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
2181          }
2182          if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
2183              throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
2184          }
2185  
2186          if (self::useNewSodiumAPI()) {
2187              return sodium_crypto_scalarmult($secretKey, $publicKey);
2188          }
2189          if (self::use_fallback('crypto_scalarmult')) {
2190              return (string) call_user_func('\\Sodium\\crypto_scalarmult', $secretKey, $publicKey);
2191          }
2192  
2193          /* Output validation: Forbid all-zero keys */
2194          if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) {
2195              throw new SodiumException('Zero secret key is not allowed');
2196          }
2197          if (ParagonIE_Sodium_Core_Util::hashEquals($publicKey, str_repeat("\0", self::CRYPTO_BOX_PUBLICKEYBYTES))) {
2198              throw new SodiumException('Zero public key is not allowed');
2199          }
2200          if (PHP_INT_SIZE === 4) {
2201              return ParagonIE_Sodium_Crypto32::scalarmult($secretKey, $publicKey);
2202          }
2203          return ParagonIE_Sodium_Crypto::scalarmult($secretKey, $publicKey);
2204      }
2205  
2206      /**
2207       * Calculate an X25519 public key from an X25519 secret key.
2208       *
2209       * @param string $secretKey
2210       * @return string
2211       * @throws SodiumException
2212       * @throws TypeError
2213       * @psalm-suppress TooFewArguments
2214       * @psalm-suppress MixedArgument
2215       */
2216      public static function crypto_scalarmult_base($secretKey)
2217      {
2218          /* Type checks: */
2219          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
2220  
2221          /* Input validation: */
2222          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
2223              throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
2224          }
2225  
2226          if (self::useNewSodiumAPI()) {
2227              return sodium_crypto_scalarmult_base($secretKey);
2228          }
2229          if (self::use_fallback('crypto_scalarmult_base')) {
2230              return (string) call_user_func('\\Sodium\\crypto_scalarmult_base', $secretKey);
2231          }
2232          if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) {
2233              throw new SodiumException('Zero secret key is not allowed');
2234          }
2235          if (PHP_INT_SIZE === 4) {
2236              return ParagonIE_Sodium_Crypto32::scalarmult_base($secretKey);
2237          }
2238          return ParagonIE_Sodium_Crypto::scalarmult_base($secretKey);
2239      }
2240  
2241      /**
2242       * Authenticated symmetric-key encryption.
2243       *
2244       * Algorithm: XSalsa20-Poly1305
2245       *
2246       * @param string $plaintext The message you're encrypting
2247       * @param string $nonce A Number to be used Once; must be 24 bytes
2248       * @param string $key Symmetric encryption key
2249       * @return string           Ciphertext with Poly1305 MAC
2250       * @throws SodiumException
2251       * @throws TypeError
2252       * @psalm-suppress MixedArgument
2253       */
2254      public static function crypto_secretbox($plaintext, $nonce, $key)
2255      {
2256          /* Type checks: */
2257          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
2258          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2259          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2260  
2261          /* Input validation: */
2262          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2263              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2264          }
2265          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2266              throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2267          }
2268  
2269          if (self::useNewSodiumAPI()) {
2270              return sodium_crypto_secretbox($plaintext, $nonce, $key);
2271          }
2272          if (self::use_fallback('crypto_secretbox')) {
2273              return (string) call_user_func('\\Sodium\\crypto_secretbox', $plaintext, $nonce, $key);
2274          }
2275          if (PHP_INT_SIZE === 4) {
2276              return ParagonIE_Sodium_Crypto32::secretbox($plaintext, $nonce, $key);
2277          }
2278          return ParagonIE_Sodium_Crypto::secretbox($plaintext, $nonce, $key);
2279      }
2280  
2281      /**
2282       * Decrypts a message previously encrypted with crypto_secretbox().
2283       *
2284       * @param string $ciphertext Ciphertext with Poly1305 MAC
2285       * @param string $nonce      A Number to be used Once; must be 24 bytes
2286       * @param string $key        Symmetric encryption key
2287       * @return string            Original plaintext message
2288       * @throws SodiumException
2289       * @throws TypeError
2290       * @psalm-suppress MixedArgument
2291       * @psalm-suppress MixedInferredReturnType
2292       * @psalm-suppress MixedReturnStatement
2293       */
2294      public static function crypto_secretbox_open($ciphertext, $nonce, $key)
2295      {
2296          /* Type checks: */
2297          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
2298          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2299          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2300  
2301          /* Input validation: */
2302          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2303              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2304          }
2305          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2306              throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2307          }
2308  
2309          if (self::useNewSodiumAPI()) {
2310              /**
2311               * @psalm-suppress InvalidReturnStatement
2312               * @psalm-suppress FalsableReturnStatement
2313               */
2314              return sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
2315          }
2316          if (self::use_fallback('crypto_secretbox_open')) {
2317              return call_user_func('\\Sodium\\crypto_secretbox_open', $ciphertext, $nonce, $key);
2318          }
2319          if (PHP_INT_SIZE === 4) {
2320              return ParagonIE_Sodium_Crypto32::secretbox_open($ciphertext, $nonce, $key);
2321          }
2322          return ParagonIE_Sodium_Crypto::secretbox_open($ciphertext, $nonce, $key);
2323      }
2324  
2325      /**
2326       * Return a secure random key for use with crypto_secretbox
2327       *
2328       * @return string
2329       * @throws Exception
2330       * @throws Error
2331       */
2332      public static function crypto_secretbox_keygen()
2333      {
2334          return random_bytes(self::CRYPTO_SECRETBOX_KEYBYTES);
2335      }
2336  
2337      /**
2338       * Authenticated symmetric-key encryption.
2339       *
2340       * Algorithm: XChaCha20-Poly1305
2341       *
2342       * @param string $plaintext The message you're encrypting
2343       * @param string $nonce     A Number to be used Once; must be 24 bytes
2344       * @param string $key       Symmetric encryption key
2345       * @return string           Ciphertext with Poly1305 MAC
2346       * @throws SodiumException
2347       * @throws TypeError
2348       * @psalm-suppress MixedArgument
2349       */
2350      public static function crypto_secretbox_xchacha20poly1305($plaintext, $nonce, $key)
2351      {
2352          /* Type checks: */
2353          ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
2354          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2355          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2356  
2357          /* Input validation: */
2358          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2359              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2360          }
2361          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2362              throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2363          }
2364          if (PHP_INT_SIZE === 4) {
2365              return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305($plaintext, $nonce, $key);
2366          }
2367          return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305($plaintext, $nonce, $key);
2368      }
2369      /**
2370       * Decrypts a message previously encrypted with crypto_secretbox_xchacha20poly1305().
2371       *
2372       * @param string $ciphertext Ciphertext with Poly1305 MAC
2373       * @param string $nonce      A Number to be used Once; must be 24 bytes
2374       * @param string $key        Symmetric encryption key
2375       * @return string            Original plaintext message
2376       * @throws SodiumException
2377       * @throws TypeError
2378       * @psalm-suppress MixedArgument
2379       */
2380      public static function crypto_secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
2381      {
2382          /* Type checks: */
2383          ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
2384          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2385          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2386  
2387          /* Input validation: */
2388          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2389              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2390          }
2391          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2392              throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2393          }
2394  
2395          if (PHP_INT_SIZE === 4) {
2396              return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
2397          }
2398          return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
2399      }
2400  
2401      /**
2402       * @param string $key
2403       * @return array<int, string> Returns a state and a header.
2404       * @throws Exception
2405       * @throws SodiumException
2406       */
2407      public static function crypto_secretstream_xchacha20poly1305_init_push($key)
2408      {
2409          if (PHP_INT_SIZE === 4) {
2410              return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key);
2411          }
2412          return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key);
2413      }
2414  
2415      /**
2416       * @param string $header
2417       * @param string $key
2418       * @return string Returns a state.
2419       * @throws Exception
2420       */
2421      public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key)
2422      {
2423          if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
2424              throw new SodiumException(
2425                  'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes'
2426              );
2427          }
2428          if (PHP_INT_SIZE === 4) {
2429              return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header);
2430          }
2431          return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header);
2432      }
2433  
2434      /**
2435       * @param string $state
2436       * @param string $msg
2437       * @param string $aad
2438       * @param int $tag
2439       * @return string
2440       * @throws SodiumException
2441       */
2442      public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
2443      {
2444          if (PHP_INT_SIZE === 4) {
2445              return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push(
2446                  $state,
2447                  $msg,
2448                  $aad,
2449                  $tag
2450              );
2451          }
2452          return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push(
2453              $state,
2454              $msg,
2455              $aad,
2456              $tag
2457          );
2458      }
2459  
2460      /**
2461       * @param string $state
2462       * @param string $msg
2463       * @param string $aad
2464       * @return bool|array{0: string, 1: int}
2465       * @throws SodiumException
2466       */
2467      public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '')
2468      {
2469          if (PHP_INT_SIZE === 4) {
2470              return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull(
2471                  $state,
2472                  $msg,
2473                  $aad
2474              );
2475          }
2476          return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull(
2477              $state,
2478              $msg,
2479              $aad
2480          );
2481      }
2482  
2483      /**
2484       * @return string
2485       * @throws Exception
2486       */
2487      public static function crypto_secretstream_xchacha20poly1305_keygen()
2488      {
2489          return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES);
2490      }
2491  
2492      /**
2493       * @param string $state
2494       * @return void
2495       * @throws SodiumException
2496       */
2497      public static function crypto_secretstream_xchacha20poly1305_rekey(&$state)
2498      {
2499          if (PHP_INT_SIZE === 4) {
2500              ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state);
2501          } else {
2502              ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state);
2503          }
2504      }
2505  
2506      /**
2507       * Calculates a SipHash-2-4 hash of a message for a given key.
2508       *
2509       * @param string $message Input message
2510       * @param string $key SipHash-2-4 key
2511       * @return string         Hash
2512       * @throws SodiumException
2513       * @throws TypeError
2514       * @psalm-suppress MixedArgument
2515       * @psalm-suppress MixedInferredReturnType
2516       * @psalm-suppress MixedReturnStatement
2517       */
2518      public static function crypto_shorthash($message, $key)
2519      {
2520          /* Type checks: */
2521          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
2522          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
2523  
2524          /* Input validation: */
2525          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SHORTHASH_KEYBYTES) {
2526              throw new SodiumException('Argument 2 must be CRYPTO_SHORTHASH_KEYBYTES long.');
2527          }
2528  
2529          if (self::useNewSodiumAPI()) {
2530              return sodium_crypto_shorthash($message, $key);
2531          }
2532          if (self::use_fallback('crypto_shorthash')) {
2533              return (string) call_user_func('\\Sodium\\crypto_shorthash', $message, $key);
2534          }
2535          if (PHP_INT_SIZE === 4) {
2536              return ParagonIE_Sodium_Core32_SipHash::sipHash24($message, $key);
2537          }
2538          return ParagonIE_Sodium_Core_SipHash::sipHash24($message, $key);
2539      }
2540  
2541      /**
2542       * Return a secure random key for use with crypto_shorthash
2543       *
2544       * @return string
2545       * @throws Exception
2546       * @throws Error
2547       */
2548      public static function crypto_shorthash_keygen()
2549      {
2550          return random_bytes(self::CRYPTO_SHORTHASH_KEYBYTES);
2551      }
2552  
2553      /**
2554       * Returns a signed message. You probably want crypto_sign_detached()
2555       * instead, which only returns the signature.
2556       *
2557       * Algorithm: Ed25519 (EdDSA over Curve25519)
2558       *
2559       * @param string $message Message to be signed.
2560       * @param string $secretKey Secret signing key.
2561       * @return string           Signed message (signature is prefixed).
2562       * @throws SodiumException
2563       * @throws TypeError
2564       * @psalm-suppress MixedArgument
2565       * @psalm-suppress MixedInferredReturnType
2566       * @psalm-suppress MixedReturnStatement
2567       */
2568      public static function crypto_sign($message, $secretKey)
2569      {
2570          /* Type checks: */
2571          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
2572          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
2573  
2574          /* Input validation: */
2575          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
2576              throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
2577          }
2578  
2579          if (self::useNewSodiumAPI()) {
2580              return sodium_crypto_sign($message, $secretKey);
2581          }
2582          if (self::use_fallback('crypto_sign')) {
2583              return (string) call_user_func('\\Sodium\\crypto_sign', $message, $secretKey);
2584          }
2585          if (PHP_INT_SIZE === 4) {
2586              return ParagonIE_Sodium_Crypto32::sign($message, $secretKey);
2587          }
2588          return ParagonIE_Sodium_Crypto::sign($message, $secretKey);
2589      }
2590  
2591      /**
2592       * Validates a signed message then returns the message.
2593       *
2594       * @param string $signedMessage A signed message
2595       * @param string $publicKey A public key
2596       * @return string               The original message (if the signature is
2597       *                              valid for this public key)
2598       * @throws SodiumException
2599       * @throws TypeError
2600       * @psalm-suppress MixedArgument
2601       * @psalm-suppress MixedInferredReturnType
2602       * @psalm-suppress MixedReturnStatement
2603       */
2604      public static function crypto_sign_open($signedMessage, $publicKey)
2605      {
2606          /* Type checks: */
2607          ParagonIE_Sodium_Core_Util::declareScalarType($signedMessage, 'string', 1);
2608          ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
2609  
2610          /* Input validation: */
2611          if (ParagonIE_Sodium_Core_Util::strlen($signedMessage) < self::CRYPTO_SIGN_BYTES) {
2612              throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_BYTES long.');
2613          }
2614          if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
2615              throw new SodiumException('Argument 2 must be CRYPTO_SIGN_PUBLICKEYBYTES long.');
2616          }
2617  
2618          if (self::useNewSodiumAPI()) {
2619              /**
2620               * @psalm-suppress InvalidReturnStatement
2621               * @psalm-suppress FalsableReturnStatement
2622               */
2623              return sodium_crypto_sign_open($signedMessage, $publicKey);
2624          }
2625          if (self::use_fallback('crypto_sign_open')) {
2626              return call_user_func('\\Sodium\\crypto_sign_open', $signedMessage, $publicKey);
2627          }
2628          if (PHP_INT_SIZE === 4) {
2629              return ParagonIE_Sodium_Crypto32::sign_open($signedMessage, $publicKey);
2630          }
2631          return ParagonIE_Sodium_Crypto::sign_open($signedMessage, $publicKey);
2632      }
2633  
2634      /**
2635       * Generate a new random Ed25519 keypair.
2636       *
2637       * @return string
2638       * @throws SodiumException
2639       * @throws TypeError
2640       */
2641      public static function crypto_sign_keypair()
2642      {
2643          if (self::useNewSodiumAPI()) {
2644              return sodium_crypto_sign_keypair();
2645          }
2646          if (self::use_fallback('crypto_sign_keypair')) {
2647              return (string) call_user_func('\\Sodium\\crypto_sign_keypair');
2648          }
2649          if (PHP_INT_SIZE === 4) {
2650              return ParagonIE_Sodium_Core32_Ed25519::keypair();
2651          }
2652          return ParagonIE_Sodium_Core_Ed25519::keypair();
2653      }
2654  
2655      /**
2656       * @param string $sk
2657       * @param string $pk
2658       * @return string
2659       * @throws SodiumException
2660       */
2661      public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk)
2662      {
2663          ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
2664          ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
2665          $sk = (string) $sk;
2666          $pk = (string) $pk;
2667  
2668          if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
2669              throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes');
2670          }
2671          if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
2672              throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes');
2673          }
2674  
2675          if (self::useNewSodiumAPI()) {
2676              return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk);
2677          }
2678          return $sk . $pk;
2679      }
2680  
2681      /**
2682       * Generate an Ed25519 keypair from a seed.
2683       *
2684       * @param string $seed Input seed
2685       * @return string      Keypair
2686       * @throws SodiumException
2687       * @throws TypeError
2688       * @psalm-suppress MixedArgument
2689       */
2690      public static function crypto_sign_seed_keypair($seed)
2691      {
2692          ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
2693  
2694          if (self::useNewSodiumAPI()) {
2695              return sodium_crypto_sign_seed_keypair($seed);
2696          }
2697          if (self::use_fallback('crypto_sign_keypair')) {
2698              return (string) call_user_func('\\Sodium\\crypto_sign_seed_keypair', $seed);
2699          }
2700          $publicKey = '';
2701          $secretKey = '';
2702          if (PHP_INT_SIZE === 4) {
2703              ParagonIE_Sodium_Core32_Ed25519::seed_keypair($publicKey, $secretKey, $seed);
2704          } else {
2705              ParagonIE_Sodium_Core_Ed25519::seed_keypair($publicKey, $secretKey, $seed);
2706          }
2707          return $secretKey . $publicKey;
2708      }
2709  
2710      /**
2711       * Extract an Ed25519 public key from an Ed25519 keypair.
2712       *
2713       * @param string $keypair Keypair
2714       * @return string         Public key
2715       * @throws SodiumException
2716       * @throws TypeError
2717       * @psalm-suppress MixedArgument
2718       */
2719      public static function crypto_sign_publickey($keypair)
2720      {
2721          /* Type checks: */
2722          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
2723  
2724          /* Input validation: */
2725          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) {
2726              throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.');
2727          }
2728  
2729          if (self::useNewSodiumAPI()) {
2730              return sodium_crypto_sign_publickey($keypair);
2731          }
2732          if (self::use_fallback('crypto_sign_publickey')) {
2733              return (string) call_user_func('\\Sodium\\crypto_sign_publickey', $keypair);
2734          }
2735          if (PHP_INT_SIZE === 4) {
2736              return ParagonIE_Sodium_Core32_Ed25519::publickey($keypair);
2737          }
2738          return ParagonIE_Sodium_Core_Ed25519::publickey($keypair);
2739      }
2740  
2741      /**
2742       * Calculate an Ed25519 public key from an Ed25519 secret key.
2743       *
2744       * @param string $secretKey Your Ed25519 secret key
2745       * @return string           The corresponding Ed25519 public key
2746       * @throws SodiumException
2747       * @throws TypeError
2748       * @psalm-suppress MixedArgument
2749       */
2750      public static function crypto_sign_publickey_from_secretkey($secretKey)
2751      {
2752          /* Type checks: */
2753          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
2754  
2755          /* Input validation: */
2756          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
2757              throw new SodiumException('Argument 1 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
2758          }
2759  
2760          if (self::useNewSodiumAPI()) {
2761              return sodium_crypto_sign_publickey_from_secretkey($secretKey);
2762          }
2763          if (self::use_fallback('crypto_sign_publickey_from_secretkey')) {
2764              return (string) call_user_func('\\Sodium\\crypto_sign_publickey_from_secretkey', $secretKey);
2765          }
2766          if (PHP_INT_SIZE === 4) {
2767              return ParagonIE_Sodium_Core32_Ed25519::publickey_from_secretkey($secretKey);
2768          }
2769          return ParagonIE_Sodium_Core_Ed25519::publickey_from_secretkey($secretKey);
2770      }
2771  
2772      /**
2773       * Extract an Ed25519 secret key from an Ed25519 keypair.
2774       *
2775       * @param string $keypair Keypair
2776       * @return string         Secret key
2777       * @throws SodiumException
2778       * @throws TypeError
2779       * @psalm-suppress MixedArgument
2780       */
2781      public static function crypto_sign_secretkey($keypair)
2782      {
2783          /* Type checks: */
2784          ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
2785  
2786          /* Input validation: */
2787          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) {
2788              throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.');
2789          }
2790  
2791          if (self::useNewSodiumAPI()) {
2792              return sodium_crypto_sign_secretkey($keypair);
2793          }
2794          if (self::use_fallback('crypto_sign_secretkey')) {
2795              return (string) call_user_func('\\Sodium\\crypto_sign_secretkey', $keypair);
2796          }
2797          if (PHP_INT_SIZE === 4) {
2798              return ParagonIE_Sodium_Core32_Ed25519::secretkey($keypair);
2799          }
2800          return ParagonIE_Sodium_Core_Ed25519::secretkey($keypair);
2801      }
2802  
2803      /**
2804       * Calculate the Ed25519 signature of a message and return ONLY the signature.
2805       *
2806       * Algorithm: Ed25519 (EdDSA over Curve25519)
2807       *
2808       * @param string $message Message to be signed
2809       * @param string $secretKey Secret signing key
2810       * @return string           Digital signature
2811       * @throws SodiumException
2812       * @throws TypeError
2813       * @psalm-suppress MixedArgument
2814       */
2815      public static function crypto_sign_detached($message, $secretKey)
2816      {
2817          /* Type checks: */
2818          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
2819          ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
2820  
2821          /* Input validation: */
2822          if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
2823              throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
2824          }
2825  
2826          if (self::useNewSodiumAPI()) {
2827              return sodium_crypto_sign_detached($message, $secretKey);
2828          }
2829          if (self::use_fallback('crypto_sign_detached')) {
2830              return (string) call_user_func('\\Sodium\\crypto_sign_detached', $message, $secretKey);
2831          }
2832          if (PHP_INT_SIZE === 4) {
2833              return ParagonIE_Sodium_Crypto32::sign_detached($message, $secretKey);
2834          }
2835          return ParagonIE_Sodium_Crypto::sign_detached($message, $secretKey);
2836      }
2837  
2838      /**
2839       * Verify the Ed25519 signature of a message.
2840       *
2841       * @param string $signature Digital sginature
2842       * @param string $message Message to be verified
2843       * @param string $publicKey Public key
2844       * @return bool             TRUE if this signature is good for this public key;
2845       *                          FALSE otherwise
2846       * @throws SodiumException
2847       * @throws TypeError
2848       * @psalm-suppress MixedArgument
2849       */
2850      public static function crypto_sign_verify_detached($signature, $message, $publicKey)
2851      {
2852          /* Type checks: */
2853          ParagonIE_Sodium_Core_Util::declareScalarType($signature, 'string', 1);
2854          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
2855          ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 3);
2856  
2857          /* Input validation: */
2858          if (ParagonIE_Sodium_Core_Util::strlen($signature) !== self::CRYPTO_SIGN_BYTES) {
2859              throw new SodiumException('Argument 1 must be CRYPTO_SIGN_BYTES long.');
2860          }
2861          if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
2862              throw new SodiumException('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES long.');
2863          }
2864  
2865          if (self::useNewSodiumAPI()) {
2866              return sodium_crypto_sign_verify_detached($signature, $message, $publicKey);
2867          }
2868          if (self::use_fallback('crypto_sign_verify_detached')) {
2869              return (bool) call_user_func(
2870                  '\\Sodium\\crypto_sign_verify_detached',
2871                  $signature,
2872                  $message,
2873                  $publicKey
2874              );
2875          }
2876          if (PHP_INT_SIZE === 4) {
2877              return ParagonIE_Sodium_Crypto32::sign_verify_detached($signature, $message, $publicKey);
2878          }
2879          return ParagonIE_Sodium_Crypto::sign_verify_detached($signature, $message, $publicKey);
2880      }
2881  
2882      /**
2883       * Convert an Ed25519 public key to a Curve25519 public key
2884       *
2885       * @param string $pk
2886       * @return string
2887       * @throws SodiumException
2888       * @throws TypeError
2889       * @psalm-suppress MixedArgument
2890       */
2891      public static function crypto_sign_ed25519_pk_to_curve25519($pk)
2892      {
2893          /* Type checks: */
2894          ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
2895  
2896          /* Input validation: */
2897          if (ParagonIE_Sodium_Core_Util::strlen($pk) < self::CRYPTO_SIGN_PUBLICKEYBYTES) {
2898              throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_PUBLICKEYBYTES long.');
2899          }
2900          if (self::useNewSodiumAPI()) {
2901              if (is_callable('crypto_sign_ed25519_pk_to_curve25519')) {
2902                  return (string) sodium_crypto_sign_ed25519_pk_to_curve25519($pk);
2903              }
2904          }
2905          if (self::use_fallback('crypto_sign_ed25519_pk_to_curve25519')) {
2906              return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_pk_to_curve25519', $pk);
2907          }
2908          if (PHP_INT_SIZE === 4) {
2909              return ParagonIE_Sodium_Core32_Ed25519::pk_to_curve25519($pk);
2910          }
2911          return ParagonIE_Sodium_Core_Ed25519::pk_to_curve25519($pk);
2912      }
2913  
2914      /**
2915       * Convert an Ed25519 secret key to a Curve25519 secret key
2916       *
2917       * @param string $sk
2918       * @return string
2919       * @throws SodiumException
2920       * @throws TypeError
2921       * @psalm-suppress MixedArgument
2922       */
2923      public static function crypto_sign_ed25519_sk_to_curve25519($sk)
2924      {
2925          /* Type checks: */
2926          ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
2927  
2928          /* Input validation: */
2929          if (ParagonIE_Sodium_Core_Util::strlen($sk) < self::CRYPTO_SIGN_SEEDBYTES) {
2930              throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_SEEDBYTES long.');
2931          }
2932          if (self::useNewSodiumAPI()) {
2933              if (is_callable('crypto_sign_ed25519_sk_to_curve25519')) {
2934                  return sodium_crypto_sign_ed25519_sk_to_curve25519($sk);
2935              }
2936          }
2937          if (self::use_fallback('crypto_sign_ed25519_sk_to_curve25519')) {
2938              return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_sk_to_curve25519', $sk);
2939          }
2940  
2941          $h = hash('sha512', ParagonIE_Sodium_Core_Util::substr($sk, 0, 32), true);
2942          $h[0] = ParagonIE_Sodium_Core_Util::intToChr(
2943              ParagonIE_Sodium_Core_Util::chrToInt($h[0]) & 248
2944          );
2945          $h[31] = ParagonIE_Sodium_Core_Util::intToChr(
2946              (ParagonIE_Sodium_Core_Util::chrToInt($h[31]) & 127) | 64
2947          );
2948          return ParagonIE_Sodium_Core_Util::substr($h, 0, 32);
2949      }
2950  
2951      /**
2952       * Expand a key and nonce into a keystream of pseudorandom bytes.
2953       *
2954       * @param int $len Number of bytes desired
2955       * @param string $nonce Number to be used Once; must be 24 bytes
2956       * @param string $key XSalsa20 key
2957       * @return string       Pseudorandom stream that can be XORed with messages
2958       *                      to provide encryption (but not authentication; see
2959       *                      Poly1305 or crypto_auth() for that, which is not
2960       *                      optional for security)
2961       * @throws SodiumException
2962       * @throws TypeError
2963       * @psalm-suppress MixedArgument
2964       */
2965      public static function crypto_stream($len, $nonce, $key)
2966      {
2967          /* Type checks: */
2968          ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
2969          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2970          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2971  
2972          /* Input validation: */
2973          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) {
2974              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2975          }
2976          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) {
2977              throw new SodiumException('Argument 3 must be CRYPTO_STREAM_KEYBYTES long.');
2978          }
2979  
2980          if (self::useNewSodiumAPI()) {
2981              return sodium_crypto_stream($len, $nonce, $key);
2982          }
2983          if (self::use_fallback('crypto_stream')) {
2984              return (string) call_user_func('\\Sodium\\crypto_stream', $len, $nonce, $key);
2985          }
2986          if (PHP_INT_SIZE === 4) {
2987              return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20($len, $nonce, $key);
2988          }
2989          return ParagonIE_Sodium_Core_XSalsa20::xsalsa20($len, $nonce, $key);
2990      }
2991  
2992      /**
2993       * DANGER! UNAUTHENTICATED ENCRYPTION!
2994       *
2995       * Unless you are following expert advice, do not used this feature.
2996       *
2997       * Algorithm: XSalsa20
2998       *
2999       * This DOES NOT provide ciphertext integrity.
3000       *
3001       * @param string $message Plaintext message
3002       * @param string $nonce Number to be used Once; must be 24 bytes
3003       * @param string $key Encryption key
3004       * @return string         Encrypted text which is vulnerable to chosen-
3005       *                        ciphertext attacks unless you implement some
3006       *                        other mitigation to the ciphertext (i.e.
3007       *                        Encrypt then MAC)
3008       * @throws SodiumException
3009       * @throws TypeError
3010       * @psalm-suppress MixedArgument
3011       */
3012      public static function crypto_stream_xor($message, $nonce, $key)
3013      {
3014          /* Type checks: */
3015          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
3016          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3017          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
3018  
3019          /* Input validation: */
3020          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) {
3021              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
3022          }
3023          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) {
3024              throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
3025          }
3026  
3027          if (self::useNewSodiumAPI()) {
3028              return sodium_crypto_stream_xor($message, $nonce, $key);
3029          }
3030          if (self::use_fallback('crypto_stream_xor')) {
3031              return (string) call_user_func('\\Sodium\\crypto_stream_xor', $message, $nonce, $key);
3032          }
3033          if (PHP_INT_SIZE === 4) {
3034              return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20_xor($message, $nonce, $key);
3035          }
3036          return ParagonIE_Sodium_Core_XSalsa20::xsalsa20_xor($message, $nonce, $key);
3037      }
3038  
3039      /**
3040       * Return a secure random key for use with crypto_stream
3041       *
3042       * @return string
3043       * @throws Exception
3044       * @throws Error
3045       */
3046      public static function crypto_stream_keygen()
3047      {
3048          return random_bytes(self::CRYPTO_STREAM_KEYBYTES);
3049      }
3050  
3051      /**
3052       * Cache-timing-safe implementation of hex2bin().
3053       *
3054       * @param string $string Hexadecimal string
3055       * @return string        Raw binary string
3056       * @throws SodiumException
3057       * @throws TypeError
3058       * @psalm-suppress TooFewArguments
3059       * @psalm-suppress MixedArgument
3060       */
3061      public static function hex2bin($string)
3062      {
3063          /* Type checks: */
3064          ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
3065  
3066          if (self::useNewSodiumAPI()) {
3067              if (is_callable('sodium_hex2bin')) {
3068                  return (string) sodium_hex2bin($string);
3069              }
3070          }
3071          if (self::use_fallback('hex2bin')) {
3072              return (string) call_user_func('\\Sodium\\hex2bin', $string);
3073          }
3074          return ParagonIE_Sodium_Core_Util::hex2bin($string);
3075      }
3076  
3077      /**
3078       * Increase a string (little endian)
3079       *
3080       * @param string $var
3081       *
3082       * @return void
3083       * @throws SodiumException
3084       * @throws TypeError
3085       * @psalm-suppress MixedArgument
3086       */
3087      public static function increment(&$var)
3088      {
3089          /* Type checks: */
3090          ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
3091  
3092          if (self::useNewSodiumAPI()) {
3093              sodium_increment($var);
3094              return;
3095          }
3096          if (self::use_fallback('increment')) {
3097              $func = '\\Sodium\\increment';
3098              $func($var);
3099              return;
3100          }
3101  
3102          $len = ParagonIE_Sodium_Core_Util::strlen($var);
3103          $c = 1;
3104          $copy = '';
3105          for ($i = 0; $i < $len; ++$i) {
3106              $c += ParagonIE_Sodium_Core_Util::chrToInt(
3107                  ParagonIE_Sodium_Core_Util::substr($var, $i, 1)
3108              );
3109              $copy .= ParagonIE_Sodium_Core_Util::intToChr($c);
3110              $c >>= 8;
3111          }
3112          $var = $copy;
3113      }
3114  
3115      /**
3116       * The equivalent to the libsodium minor version we aim to be compatible
3117       * with (sans pwhash and memzero).
3118       *
3119       * @return int
3120       * @psalm-suppress MixedInferredReturnType
3121       * @psalm-suppress UndefinedFunction
3122       */
3123      public static function library_version_major()
3124      {
3125          if (self::useNewSodiumAPI()) {
3126              return sodium_library_version_major();
3127          }
3128          if (self::use_fallback('library_version_major')) {
3129              return (int) call_user_func('\\Sodium\\library_version_major');
3130          }
3131          return self::LIBRARY_VERSION_MAJOR;
3132      }
3133  
3134      /**
3135       * The equivalent to the libsodium minor version we aim to be compatible
3136       * with (sans pwhash and memzero).
3137       *
3138       * @return int
3139       * @psalm-suppress MixedInferredReturnType
3140       * @psalm-suppress UndefinedFunction
3141       */
3142      public static function library_version_minor()
3143      {
3144          if (self::useNewSodiumAPI()) {
3145              return sodium_library_version_minor();
3146          }
3147          if (self::use_fallback('library_version_minor')) {
3148              return (int) call_user_func('\\Sodium\\library_version_minor');
3149          }
3150          return self::LIBRARY_VERSION_MINOR;
3151      }
3152  
3153      /**
3154       * Compare two strings.
3155       *
3156       * @param string $left
3157       * @param string $right
3158       * @return int
3159       * @throws SodiumException
3160       * @throws TypeError
3161       * @psalm-suppress MixedArgument
3162       */
3163      public static function memcmp($left, $right)
3164      {
3165          /* Type checks: */
3166          ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
3167          ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
3168  
3169          if (self::useNewSodiumAPI()) {
3170              return sodium_memcmp($left, $right);
3171          }
3172          if (self::use_fallback('memcmp')) {
3173              return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
3174          }
3175          /** @var string $left */
3176          /** @var string $right */
3177          return ParagonIE_Sodium_Core_Util::memcmp($left, $right);
3178      }
3179  
3180      /**
3181       * It's actually not possible to zero memory buffers in PHP. You need the
3182       * native library for that.
3183       *
3184       * @param string|null $var
3185       * @param-out string|null $var
3186       *
3187       * @return void
3188       * @throws SodiumException (Unless libsodium is installed)
3189       * @throws TypeError
3190       * @psalm-suppress TooFewArguments
3191       */
3192      public static function memzero(&$var)
3193      {
3194          /* Type checks: */
3195          ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
3196  
3197          if (self::useNewSodiumAPI()) {
3198              /** @psalm-suppress MixedArgument */
3199              sodium_memzero($var);
3200              return;
3201          }
3202          if (self::use_fallback('memzero')) {
3203              $func = '\\Sodium\\memzero';
3204              $func($var);
3205              if ($var === null) {
3206                  return;
3207              }
3208          }
3209          // This is the best we can do.
3210          throw new SodiumException(
3211              'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' .
3212              'To fix this error, make sure libsodium is installed and the PHP extension is enabled.'
3213          );
3214      }
3215  
3216      /**
3217       * @param string $unpadded
3218       * @param int $blockSize
3219       * @param bool $dontFallback
3220       * @return string
3221       * @throws SodiumException
3222       */
3223      public static function pad($unpadded, $blockSize, $dontFallback = false)
3224      {
3225          /* Type checks: */
3226          ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
3227          ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
3228  
3229          $unpadded = (string) $unpadded;
3230          $blockSize = (int) $blockSize;
3231  
3232          if (self::useNewSodiumAPI() && !$dontFallback) {
3233              return (string) sodium_pad($unpadded, $blockSize);
3234          }
3235  
3236          if ($blockSize <= 0) {
3237              throw new SodiumException(
3238                  'block size cannot be less than 1'
3239              );
3240          }
3241          $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded);
3242          $xpadlen = ($blockSize - 1);
3243          if (($blockSize & ($blockSize - 1)) === 0) {
3244              $xpadlen -= $unpadded_len & ($blockSize - 1);
3245          } else {
3246              $xpadlen -= $unpadded_len % $blockSize;
3247          }
3248  
3249          $xpadded_len = $unpadded_len + $xpadlen;
3250          $padded = str_repeat("\0", $xpadded_len - 1);
3251          if ($unpadded_len > 0) {
3252              $st = 1;
3253              $i = 0;
3254              $k = $unpadded_len;
3255              for ($j = 0; $j <= $xpadded_len; ++$j) {
3256                  $i = (int) $i;
3257                  $k = (int) $k;
3258                  $st = (int) $st;
3259                  if ($j >= $unpadded_len) {
3260                      $padded[$j] = "\0";
3261                  } else {
3262                      $padded[$j] = $unpadded[$j];
3263                  }
3264                  /** @var int $k */
3265                  $k -= $st;
3266                  $st = (int) (~(
3267                              (
3268                                  (
3269                                      ($k >> 48)
3270                                          |
3271                                      ($k >> 32)
3272                                          |
3273                                      ($k >> 16)
3274                                          |
3275                                      $k
3276                                  ) - 1
3277                              ) >> 16
3278                          )
3279                      ) & 1;
3280                  $i += $st;
3281              }
3282          }
3283  
3284          $mask = 0;
3285          $tail = $xpadded_len;
3286          for ($i = 0; $i < $blockSize; ++$i) {
3287              # barrier_mask = (unsigned char)
3288              #     (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
3289              $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1);
3290              # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask);
3291              $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr(
3292                  (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask)
3293                      |
3294                  (0x80 & $barrier_mask)
3295              );
3296              # mask |= barrier_mask;
3297              $mask |= $barrier_mask;
3298          }
3299          return $padded;
3300      }
3301  
3302      /**
3303       * @param string $padded
3304       * @param int $blockSize
3305       * @param bool $dontFallback
3306       * @return string
3307       * @throws SodiumException
3308       */
3309      public static function unpad($padded, $blockSize, $dontFallback = false)
3310      {
3311          /* Type checks: */
3312          ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
3313          ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
3314  
3315          $padded = (string) $padded;
3316          $blockSize = (int) $blockSize;
3317  
3318          if (self::useNewSodiumAPI() && !$dontFallback) {
3319              return (string) sodium_unpad($padded, $blockSize);
3320          }
3321          if ($blockSize <= 0) {
3322              throw new SodiumException('block size cannot be less than 1');
3323          }
3324          $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded);
3325          if ($padded_len < $blockSize) {
3326              throw new SodiumException('invalid padding');
3327          }
3328  
3329          # tail = &padded[padded_len - 1U];
3330          $tail = $padded_len - 1;
3331  
3332          $acc = 0;
3333          $valid = 0;
3334          $pad_len = 0;
3335  
3336          $found = 0;
3337          for ($i = 0; $i < $blockSize; ++$i) {
3338              # c = tail[-i];
3339              $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]);
3340  
3341              # is_barrier =
3342              #     (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U;
3343              $is_barrier = (
3344                  (
3345                      ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1)
3346                  ) >> 7
3347              ) & 1;
3348              $is_barrier &= ~$found;
3349              $found |= $is_barrier;
3350  
3351              # acc |= c;
3352              $acc |= $c;
3353  
3354              # pad_len |= i & (1U + ~is_barrier);
3355              $pad_len |= $i & (1 + ~$is_barrier);
3356  
3357              # valid |= (unsigned char) is_barrier;
3358              $valid |= ($is_barrier & 0xff);
3359          }
3360          # unpadded_len = padded_len - 1U - pad_len;
3361          $unpadded_len = $padded_len - 1 - $pad_len;
3362          if ($valid !== 1) {
3363              throw new SodiumException('invalid padding');
3364          }
3365          return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len);
3366      }
3367  
3368      /**
3369       * Will sodium_compat run fast on the current hardware and PHP configuration?
3370       *
3371       * @return bool
3372       */
3373      public static function polyfill_is_fast()
3374      {
3375          if (extension_loaded('sodium')) {
3376              return true;
3377          }
3378          if (extension_loaded('libsodium')) {
3379              return true;
3380          }
3381          return PHP_INT_SIZE === 8;
3382      }
3383  
3384      /**
3385       * Generate a string of bytes from the kernel's CSPRNG.
3386       * Proudly uses /dev/urandom (if getrandom(2) is not available).
3387       *
3388       * @param int $numBytes
3389       * @return string
3390       * @throws Exception
3391       * @throws TypeError
3392       */
3393      public static function randombytes_buf($numBytes)
3394      {
3395          /* Type checks: */
3396          if (!is_int($numBytes)) {
3397              if (is_numeric($numBytes)) {
3398                  $numBytes = (int) $numBytes;
3399              } else {
3400                  throw new TypeError(
3401                      'Argument 1 must be an integer, ' . gettype($numBytes) . ' given.'
3402                  );
3403              }
3404          }
3405          if (self::use_fallback('randombytes_buf')) {
3406              return (string) call_user_func('\\Sodium\\randombytes_buf', $numBytes);
3407          }
3408          return random_bytes($numBytes);
3409      }
3410  
3411      /**
3412       * Generate an integer between 0 and $range (non-inclusive).
3413       *
3414       * @param int $range
3415       * @return int
3416       * @throws Exception
3417       * @throws Error
3418       * @throws TypeError
3419       */
3420      public static function randombytes_uniform($range)
3421      {
3422          /* Type checks: */
3423          if (!is_int($range)) {
3424              if (is_numeric($range)) {
3425                  $range = (int) $range;
3426              } else {
3427                  throw new TypeError(
3428                      'Argument 1 must be an integer, ' . gettype($range) . ' given.'
3429                  );
3430              }
3431          }
3432          if (self::use_fallback('randombytes_uniform')) {
3433              return (int) call_user_func('\\Sodium\\randombytes_uniform', $range);
3434          }
3435          return random_int(0, $range - 1);
3436      }
3437  
3438      /**
3439       * Generate a random 16-bit integer.
3440       *
3441       * @return int
3442       * @throws Exception
3443       * @throws Error
3444       * @throws TypeError
3445       */
3446      public static function randombytes_random16()
3447      {
3448          if (self::use_fallback('randombytes_random16')) {
3449              return (int) call_user_func('\\Sodium\\randombytes_random16');
3450          }
3451          return random_int(0, 65535);
3452      }
3453  
3454      /**
3455       * Runtime testing method for 32-bit platforms.
3456       *
3457       * Usage: If runtime_speed_test() returns FALSE, then our 32-bit
3458       *        implementation is to slow to use safely without risking timeouts.
3459       *        If this happens, install sodium from PECL to get acceptable
3460       *        performance.
3461       *
3462       * @param int $iterations Number of multiplications to attempt
3463       * @param int $maxTimeout Milliseconds
3464       * @return bool           TRUE if we're fast enough, FALSE is not
3465       * @throws SodiumException
3466       */
3467      public static function runtime_speed_test($iterations, $maxTimeout)
3468      {
3469          if (self::polyfill_is_fast()) {
3470              return true;
3471          }
3472          /** @var float $end */
3473          $end = 0.0;
3474          /** @var float $start */
3475          $start = microtime(true);
3476          /** @var ParagonIE_Sodium_Core32_Int64 $a */
3477          $a = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
3478          for ($i = 0; $i < $iterations; ++$i) {
3479              /** @var ParagonIE_Sodium_Core32_Int64 $b */
3480              $b = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
3481              $a->mulInt64($b);
3482          }
3483          /** @var float $end */
3484          $end = microtime(true);
3485          /** @var int $diff */
3486          $diff = (int) ceil(($end - $start) * 1000);
3487          return $diff < $maxTimeout;
3488      }
3489  
3490      /**
3491       * This emulates libsodium's version_string() function, except ours is
3492       * prefixed with 'polyfill-'.
3493       *
3494       * @return string
3495       * @psalm-suppress MixedInferredReturnType
3496       * @psalm-suppress UndefinedFunction
3497       */
3498      public static function version_string()
3499      {
3500          if (self::useNewSodiumAPI()) {
3501              return (string) sodium_version_string();
3502          }
3503          if (self::use_fallback('version_string')) {
3504              return (string) call_user_func('\\Sodium\\version_string');
3505          }
3506          return (string) self::VERSION_STRING;
3507      }
3508  
3509      /**
3510       * Should we use the libsodium core function instead?
3511       * This is always a good idea, if it's available. (Unless we're in the
3512       * middle of running our unit test suite.)
3513       *
3514       * If ext/libsodium is available, use it. Return TRUE.
3515       * Otherwise, we have to use the code provided herein. Return FALSE.
3516       *
3517       * @param string $sodium_func_name
3518       *
3519       * @return bool
3520       */
3521      protected static function use_fallback($sodium_func_name = '')
3522      {
3523          static $res = null;
3524          if ($res === null) {
3525              $res = extension_loaded('libsodium') && PHP_VERSION_ID >= 50300;
3526          }
3527          if ($res === false) {
3528              // No libsodium installed
3529              return false;
3530          }
3531          if (self::$disableFallbackForUnitTests) {
3532              // Don't fallback. Use the PHP implementation.
3533              return false;
3534          }
3535          if (!empty($sodium_func_name)) {
3536              return is_callable('\\Sodium\\' . $sodium_func_name);
3537          }
3538          return true;
3539      }
3540  
3541      /**
3542       * Libsodium as implemented in PHP 7.2
3543       * and/or ext/sodium (via PECL)
3544       *
3545       * @ref https://wiki.php.net/rfc/libsodium
3546       * @return bool
3547       */
3548      protected static function useNewSodiumAPI()
3549      {
3550          static $res = null;
3551          if ($res === null) {
3552              $res = PHP_VERSION_ID >= 70000 && extension_loaded('sodium');
3553          }
3554          if (self::$disableFallbackForUnitTests) {
3555              // Don't fallback. Use the PHP implementation.
3556              return false;
3557          }
3558          return (bool) $res;
3559      }
3560  }


Generated: Tue Apr 7 01:00:07 2020 Cross-referenced by PHPXref 0.7.1