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


Generated: Tue Mar 19 01:00:02 2024 Cross-referenced by PHPXref 0.7.1