[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

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       * DANGER! UNAUTHENTICATED ENCRYPTION!
3159       *
3160       * Unless you are following expert advice, do not use this feature.
3161       *
3162       * Algorithm: XChaCha20
3163       *
3164       * This DOES NOT provide ciphertext integrity.
3165       *
3166       * @param string $message Plaintext message
3167       * @param string $nonce Number to be used Once; must be 24 bytes
3168       * @param int $counter
3169       * @param string $key Encryption key
3170       * @return string         Encrypted text which is vulnerable to chosen-
3171       *                        ciphertext attacks unless you implement some
3172       *                        other mitigation to the ciphertext (i.e.
3173       *                        Encrypt then MAC)
3174       * @param bool $dontFallback
3175       * @throws SodiumException
3176       * @throws TypeError
3177       * @psalm-suppress MixedArgument
3178       */
3179      public static function crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key, $dontFallback = false)
3180      {
3181          /* Type checks: */
3182          ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
3183          ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3184          ParagonIE_Sodium_Core_Util::declareScalarType($counter, 'int', 3);
3185          ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
3186  
3187          /* Input validation: */
3188          if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
3189              throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
3190          }
3191          if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
3192              throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.');
3193          }
3194  
3195          if (is_callable('sodium_crypto_stream_xchacha20_xor_ic') && !$dontFallback) {
3196              return sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key);
3197          }
3198  
3199          $ic = ParagonIE_Sodium_Core_Util::store64_le($counter);
3200          if (PHP_INT_SIZE === 4) {
3201              return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
3202          }
3203          return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
3204      }
3205  
3206      /**
3207       * Return a secure random key for use with crypto_stream_xchacha20
3208       *
3209       * @return string
3210       * @throws Exception
3211       * @throws Error
3212       */
3213      public static function crypto_stream_xchacha20_keygen()
3214      {
3215          return random_bytes(self::CRYPTO_STREAM_XCHACHA20_KEYBYTES);
3216      }
3217  
3218      /**
3219       * Cache-timing-safe implementation of hex2bin().
3220       *
3221       * @param string $string Hexadecimal string
3222       * @param string $ignore List of characters to ignore; useful for whitespace
3223       * @return string        Raw binary string
3224       * @throws SodiumException
3225       * @throws TypeError
3226       * @psalm-suppress TooFewArguments
3227       * @psalm-suppress MixedArgument
3228       */
3229      public static function hex2bin($string, $ignore = '')
3230      {
3231          /* Type checks: */
3232          ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
3233          ParagonIE_Sodium_Core_Util::declareScalarType($ignore, 'string', 2);
3234  
3235          if (self::useNewSodiumAPI()) {
3236              if (is_callable('sodium_hex2bin')) {
3237                  return (string) sodium_hex2bin($string, $ignore);
3238              }
3239          }
3240          if (self::use_fallback('hex2bin')) {
3241              return (string) call_user_func('\\Sodium\\hex2bin', $string, $ignore);
3242          }
3243          return ParagonIE_Sodium_Core_Util::hex2bin($string, $ignore);
3244      }
3245  
3246      /**
3247       * Increase a string (little endian)
3248       *
3249       * @param string $var
3250       *
3251       * @return void
3252       * @throws SodiumException
3253       * @throws TypeError
3254       * @psalm-suppress MixedArgument
3255       */
3256      public static function increment(&$var)
3257      {
3258          /* Type checks: */
3259          ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
3260  
3261          if (self::useNewSodiumAPI()) {
3262              sodium_increment($var);
3263              return;
3264          }
3265          if (self::use_fallback('increment')) {
3266              $func = '\\Sodium\\increment';
3267              $func($var);
3268              return;
3269          }
3270  
3271          $len = ParagonIE_Sodium_Core_Util::strlen($var);
3272          $c = 1;
3273          $copy = '';
3274          for ($i = 0; $i < $len; ++$i) {
3275              $c += ParagonIE_Sodium_Core_Util::chrToInt(
3276                  ParagonIE_Sodium_Core_Util::substr($var, $i, 1)
3277              );
3278              $copy .= ParagonIE_Sodium_Core_Util::intToChr($c);
3279              $c >>= 8;
3280          }
3281          $var = $copy;
3282      }
3283  
3284      /**
3285       * @param string $str
3286       * @return bool
3287       *
3288       * @throws SodiumException
3289       */
3290      public static function is_zero($str)
3291      {
3292          $d = 0;
3293          for ($i = 0; $i < 32; ++$i) {
3294              $d |= ParagonIE_Sodium_Core_Util::chrToInt($str[$i]);
3295          }
3296          return ((($d - 1) >> 31) & 1) === 1;
3297      }
3298  
3299      /**
3300       * The equivalent to the libsodium minor version we aim to be compatible
3301       * with (sans pwhash and memzero).
3302       *
3303       * @return int
3304       */
3305      public static function library_version_major()
3306      {
3307          if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MAJOR_VERSION')) {
3308              return SODIUM_LIBRARY_MAJOR_VERSION;
3309          }
3310          if (self::use_fallback('library_version_major')) {
3311              /** @psalm-suppress UndefinedFunction */
3312              return (int) call_user_func('\\Sodium\\library_version_major');
3313          }
3314          return self::LIBRARY_VERSION_MAJOR;
3315      }
3316  
3317      /**
3318       * The equivalent to the libsodium minor version we aim to be compatible
3319       * with (sans pwhash and memzero).
3320       *
3321       * @return int
3322       */
3323      public static function library_version_minor()
3324      {
3325          if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MINOR_VERSION')) {
3326              return SODIUM_LIBRARY_MINOR_VERSION;
3327          }
3328          if (self::use_fallback('library_version_minor')) {
3329              /** @psalm-suppress UndefinedFunction */
3330              return (int) call_user_func('\\Sodium\\library_version_minor');
3331          }
3332          return self::LIBRARY_VERSION_MINOR;
3333      }
3334  
3335      /**
3336       * Compare two strings.
3337       *
3338       * @param string $left
3339       * @param string $right
3340       * @return int
3341       * @throws SodiumException
3342       * @throws TypeError
3343       * @psalm-suppress MixedArgument
3344       */
3345      public static function memcmp($left, $right)
3346      {
3347          /* Type checks: */
3348          ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
3349          ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
3350  
3351          if (self::useNewSodiumAPI()) {
3352              return sodium_memcmp($left, $right);
3353          }
3354          if (self::use_fallback('memcmp')) {
3355              return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
3356          }
3357          /** @var string $left */
3358          /** @var string $right */
3359          return ParagonIE_Sodium_Core_Util::memcmp($left, $right);
3360      }
3361  
3362      /**
3363       * It's actually not possible to zero memory buffers in PHP. You need the
3364       * native library for that.
3365       *
3366       * @param string|null $var
3367       * @param-out string|null $var
3368       *
3369       * @return void
3370       * @throws SodiumException (Unless libsodium is installed)
3371       * @throws TypeError
3372       * @psalm-suppress TooFewArguments
3373       */
3374      public static function memzero(&$var)
3375      {
3376          /* Type checks: */
3377          ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
3378  
3379          if (self::useNewSodiumAPI()) {
3380              /** @psalm-suppress MixedArgument */
3381              sodium_memzero($var);
3382              return;
3383          }
3384          if (self::use_fallback('memzero')) {
3385              $func = '\\Sodium\\memzero';
3386              $func($var);
3387              if ($var === null) {
3388                  return;
3389              }
3390          }
3391          // This is the best we can do.
3392          throw new SodiumException(
3393              'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' .
3394              'To fix this error, make sure libsodium is installed and the PHP extension is enabled.'
3395          );
3396      }
3397  
3398      /**
3399       * @param string $unpadded
3400       * @param int $blockSize
3401       * @param bool $dontFallback
3402       * @return string
3403       * @throws SodiumException
3404       */
3405      public static function pad($unpadded, $blockSize, $dontFallback = false)
3406      {
3407          /* Type checks: */
3408          ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
3409          ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
3410  
3411          $unpadded = (string) $unpadded;
3412          $blockSize = (int) $blockSize;
3413  
3414          if (self::useNewSodiumAPI() && !$dontFallback) {
3415              return (string) sodium_pad($unpadded, $blockSize);
3416          }
3417  
3418          if ($blockSize <= 0) {
3419              throw new SodiumException(
3420                  'block size cannot be less than 1'
3421              );
3422          }
3423          $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded);
3424          $xpadlen = ($blockSize - 1);
3425          if (($blockSize & ($blockSize - 1)) === 0) {
3426              $xpadlen -= $unpadded_len & ($blockSize - 1);
3427          } else {
3428              $xpadlen -= $unpadded_len % $blockSize;
3429          }
3430  
3431          $xpadded_len = $unpadded_len + $xpadlen;
3432          $padded = str_repeat("\0", $xpadded_len - 1);
3433          if ($unpadded_len > 0) {
3434              $st = 1;
3435              $i = 0;
3436              $k = $unpadded_len;
3437              for ($j = 0; $j <= $xpadded_len; ++$j) {
3438                  $i = (int) $i;
3439                  $k = (int) $k;
3440                  $st = (int) $st;
3441                  if ($j >= $unpadded_len) {
3442                      $padded[$j] = "\0";
3443                  } else {
3444                      $padded[$j] = $unpadded[$j];
3445                  }
3446                  /** @var int $k */
3447                  $k -= $st;
3448                  $st = (int) (~(
3449                              (
3450                                  (
3451                                      ($k >> 48)
3452                                          |
3453                                      ($k >> 32)
3454                                          |
3455                                      ($k >> 16)
3456                                          |
3457                                      $k
3458                                  ) - 1
3459                              ) >> 16
3460                          )
3461                      ) & 1;
3462                  $i += $st;
3463              }
3464          }
3465  
3466          $mask = 0;
3467          $tail = $xpadded_len;
3468          for ($i = 0; $i < $blockSize; ++$i) {
3469              # barrier_mask = (unsigned char)
3470              #     (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
3471              $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1);
3472              # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask);
3473              $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr(
3474                  (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask)
3475                      |
3476                  (0x80 & $barrier_mask)
3477              );
3478              # mask |= barrier_mask;
3479              $mask |= $barrier_mask;
3480          }
3481          return $padded;
3482      }
3483  
3484      /**
3485       * @param string $padded
3486       * @param int $blockSize
3487       * @param bool $dontFallback
3488       * @return string
3489       * @throws SodiumException
3490       */
3491      public static function unpad($padded, $blockSize, $dontFallback = false)
3492      {
3493          /* Type checks: */
3494          ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
3495          ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
3496  
3497          $padded = (string) $padded;
3498          $blockSize = (int) $blockSize;
3499  
3500          if (self::useNewSodiumAPI() && !$dontFallback) {
3501              return (string) sodium_unpad($padded, $blockSize);
3502          }
3503          if ($blockSize <= 0) {
3504              throw new SodiumException('block size cannot be less than 1');
3505          }
3506          $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded);
3507          if ($padded_len < $blockSize) {
3508              throw new SodiumException('invalid padding');
3509          }
3510  
3511          # tail = &padded[padded_len - 1U];
3512          $tail = $padded_len - 1;
3513  
3514          $acc = 0;
3515          $valid = 0;
3516          $pad_len = 0;
3517  
3518          $found = 0;
3519          for ($i = 0; $i < $blockSize; ++$i) {
3520              # c = tail[-i];
3521              $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]);
3522  
3523              # is_barrier =
3524              #     (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U;
3525              $is_barrier = (
3526                  (
3527                      ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1)
3528                  ) >> 7
3529              ) & 1;
3530              $is_barrier &= ~$found;
3531              $found |= $is_barrier;
3532  
3533              # acc |= c;
3534              $acc |= $c;
3535  
3536              # pad_len |= i & (1U + ~is_barrier);
3537              $pad_len |= $i & (1 + ~$is_barrier);
3538  
3539              # valid |= (unsigned char) is_barrier;
3540              $valid |= ($is_barrier & 0xff);
3541          }
3542          # unpadded_len = padded_len - 1U - pad_len;
3543          $unpadded_len = $padded_len - 1 - $pad_len;
3544          if ($valid !== 1) {
3545              throw new SodiumException('invalid padding');
3546          }
3547          return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len);
3548      }
3549  
3550      /**
3551       * Will sodium_compat run fast on the current hardware and PHP configuration?
3552       *
3553       * @return bool
3554       */
3555      public static function polyfill_is_fast()
3556      {
3557          if (extension_loaded('sodium')) {
3558              return true;
3559          }
3560          if (extension_loaded('libsodium')) {
3561              return true;
3562          }
3563          return PHP_INT_SIZE === 8;
3564      }
3565  
3566      /**
3567       * Generate a string of bytes from the kernel's CSPRNG.
3568       * Proudly uses /dev/urandom (if getrandom(2) is not available).
3569       *
3570       * @param int $numBytes
3571       * @return string
3572       * @throws Exception
3573       * @throws TypeError
3574       */
3575      public static function randombytes_buf($numBytes)
3576      {
3577          /* Type checks: */
3578          if (!is_int($numBytes)) {
3579              if (is_numeric($numBytes)) {
3580                  $numBytes = (int) $numBytes;
3581              } else {
3582                  throw new TypeError(
3583                      'Argument 1 must be an integer, ' . gettype($numBytes) . ' given.'
3584                  );
3585              }
3586          }
3587          /** @var positive-int $numBytes */
3588          if (self::use_fallback('randombytes_buf')) {
3589              return (string) call_user_func('\\Sodium\\randombytes_buf', $numBytes);
3590          }
3591          if ($numBytes < 0) {
3592              throw new SodiumException("Number of bytes must be a positive integer");
3593          }
3594          return random_bytes($numBytes);
3595      }
3596  
3597      /**
3598       * Generate an integer between 0 and $range (non-inclusive).
3599       *
3600       * @param int $range
3601       * @return int
3602       * @throws Exception
3603       * @throws Error
3604       * @throws TypeError
3605       */
3606      public static function randombytes_uniform($range)
3607      {
3608          /* Type checks: */
3609          if (!is_int($range)) {
3610              if (is_numeric($range)) {
3611                  $range = (int) $range;
3612              } else {
3613                  throw new TypeError(
3614                      'Argument 1 must be an integer, ' . gettype($range) . ' given.'
3615                  );
3616              }
3617          }
3618          if (self::use_fallback('randombytes_uniform')) {
3619              return (int) call_user_func('\\Sodium\\randombytes_uniform', $range);
3620          }
3621          return random_int(0, $range - 1);
3622      }
3623  
3624      /**
3625       * Generate a random 16-bit integer.
3626       *
3627       * @return int
3628       * @throws Exception
3629       * @throws Error
3630       * @throws TypeError
3631       */
3632      public static function randombytes_random16()
3633      {
3634          if (self::use_fallback('randombytes_random16')) {
3635              return (int) call_user_func('\\Sodium\\randombytes_random16');
3636          }
3637          return random_int(0, 65535);
3638      }
3639  
3640      /**
3641       * @param string $p
3642       * @param bool $dontFallback
3643       * @return bool
3644       * @throws SodiumException
3645       */
3646      public static function ristretto255_is_valid_point($p, $dontFallback = false)
3647      {
3648          if (self::useNewSodiumAPI() && !$dontFallback) {
3649              return sodium_crypto_core_ristretto255_is_valid_point($p);
3650          }
3651          try {
3652              $r = ParagonIE_Sodium_Core_Ristretto255::ristretto255_frombytes($p);
3653              return $r['res'] === 0 &&
3654                  ParagonIE_Sodium_Core_Ristretto255::ristretto255_point_is_canonical($p) === 1;
3655          } catch (SodiumException $ex) {
3656              if ($ex->getMessage() === 'S is not canonical') {
3657                  return false;
3658              }
3659              throw $ex;
3660          }
3661      }
3662  
3663      /**
3664       * @param string $p
3665       * @param string $q
3666       * @param bool $dontFallback
3667       * @return string
3668       * @throws SodiumException
3669       */
3670      public static function ristretto255_add($p, $q, $dontFallback = false)
3671      {
3672          if (self::useNewSodiumAPI() && !$dontFallback) {
3673              return sodium_crypto_core_ristretto255_add($p, $q);
3674          }
3675          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_add($p, $q);
3676      }
3677  
3678      /**
3679       * @param string $p
3680       * @param string $q
3681       * @param bool $dontFallback
3682       * @return string
3683       * @throws SodiumException
3684       */
3685      public static function ristretto255_sub($p, $q, $dontFallback = false)
3686      {
3687          if (self::useNewSodiumAPI() && !$dontFallback) {
3688              return sodium_crypto_core_ristretto255_sub($p, $q);
3689          }
3690          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_sub($p, $q);
3691      }
3692  
3693      /**
3694       * @param string $r
3695       * @param bool $dontFallback
3696       * @return string
3697       *
3698       * @throws SodiumException
3699       */
3700      public static function ristretto255_from_hash($r, $dontFallback = false)
3701      {
3702          if (self::useNewSodiumAPI() && !$dontFallback) {
3703              return sodium_crypto_core_ristretto255_from_hash($r);
3704          }
3705          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_from_hash($r);
3706      }
3707  
3708      /**
3709       * @param bool $dontFallback
3710       * @return string
3711       *
3712       * @throws SodiumException
3713       */
3714      public static function ristretto255_random($dontFallback = false)
3715      {
3716          if (self::useNewSodiumAPI() && !$dontFallback) {
3717              return sodium_crypto_core_ristretto255_random();
3718          }
3719          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_random();
3720      }
3721  
3722      /**
3723       * @param bool $dontFallback
3724       * @return string
3725       *
3726       * @throws SodiumException
3727       */
3728      public static function ristretto255_scalar_random($dontFallback = false)
3729      {
3730          if (self::useNewSodiumAPI() && !$dontFallback) {
3731              return sodium_crypto_core_ristretto255_scalar_random();
3732          }
3733          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_random();
3734      }
3735  
3736      /**
3737       * @param string $s
3738       * @param bool $dontFallback
3739       * @return string
3740       * @throws SodiumException
3741       */
3742      public static function ristretto255_scalar_invert($s, $dontFallback = false)
3743      {
3744          if (self::useNewSodiumAPI() && !$dontFallback) {
3745              return sodium_crypto_core_ristretto255_scalar_invert($s);
3746          }
3747          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_invert($s);
3748      }
3749      /**
3750       * @param string $s
3751       * @param bool $dontFallback
3752       * @return string
3753       * @throws SodiumException
3754       */
3755      public static function ristretto255_scalar_negate($s, $dontFallback = false)
3756      {
3757          if (self::useNewSodiumAPI() && !$dontFallback) {
3758              return sodium_crypto_core_ristretto255_scalar_negate($s);
3759          }
3760          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_negate($s);
3761      }
3762  
3763      /**
3764       * @param string $s
3765       * @param bool $dontFallback
3766       * @return string
3767       * @throws SodiumException
3768       */
3769      public static function ristretto255_scalar_complement($s, $dontFallback = false)
3770      {
3771          if (self::useNewSodiumAPI() && !$dontFallback) {
3772              return sodium_crypto_core_ristretto255_scalar_complement($s);
3773          }
3774          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_complement($s);
3775      }
3776  
3777      /**
3778       * @param string $x
3779       * @param string $y
3780       * @param bool $dontFallback
3781       * @return string
3782       * @throws SodiumException
3783       */
3784      public static function ristretto255_scalar_add($x, $y, $dontFallback = false)
3785      {
3786          if (self::useNewSodiumAPI() && !$dontFallback) {
3787              return sodium_crypto_core_ristretto255_scalar_add($x, $y);
3788          }
3789          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_add($x, $y);
3790      }
3791  
3792      /**
3793       * @param string $x
3794       * @param string $y
3795       * @param bool $dontFallback
3796       * @return string
3797       * @throws SodiumException
3798       */
3799      public static function ristretto255_scalar_sub($x, $y, $dontFallback = false)
3800      {
3801          if (self::useNewSodiumAPI() && !$dontFallback) {
3802              return sodium_crypto_core_ristretto255_scalar_sub($x, $y);
3803          }
3804          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_sub($x, $y);
3805      }
3806  
3807      /**
3808       * @param string $x
3809       * @param string $y
3810       * @param bool $dontFallback
3811       * @return string
3812       * @throws SodiumException
3813       */
3814      public static function ristretto255_scalar_mul($x, $y, $dontFallback = false)
3815      {
3816          if (self::useNewSodiumAPI() && !$dontFallback) {
3817              return sodium_crypto_core_ristretto255_scalar_mul($x, $y);
3818          }
3819          return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_mul($x, $y);
3820      }
3821  
3822      /**
3823       * @param string $n
3824       * @param string $p
3825       * @param bool $dontFallback
3826       * @return string
3827       * @throws SodiumException
3828       */
3829      public static function scalarmult_ristretto255($n, $p, $dontFallback = false)
3830      {
3831          if (self::useNewSodiumAPI() && !$dontFallback) {
3832              return sodium_crypto_scalarmult_ristretto255($n, $p);
3833          }
3834          return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255($n, $p);
3835      }
3836  
3837      /**
3838       * @param string $n
3839       * @param string $p
3840       * @param bool $dontFallback
3841       * @return string
3842       * @throws SodiumException
3843       */
3844      public static function scalarmult_ristretto255_base($n, $dontFallback = false)
3845      {
3846          if (self::useNewSodiumAPI() && !$dontFallback) {
3847              return sodium_crypto_scalarmult_ristretto255_base($n);
3848          }
3849          return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255_base($n);
3850      }
3851  
3852      /**
3853       * @param string $s
3854       * @param bool $dontFallback
3855       * @return string
3856       * @throws SodiumException
3857       */
3858      public static function ristretto255_scalar_reduce($s, $dontFallback = false)
3859      {
3860          if (self::useNewSodiumAPI() && !$dontFallback) {
3861              return sodium_crypto_core_ristretto255_scalar_reduce($s);
3862          }
3863          return ParagonIE_Sodium_Core_Ristretto255::sc_reduce($s);
3864      }
3865  
3866      /**
3867       * Runtime testing method for 32-bit platforms.
3868       *
3869       * Usage: If runtime_speed_test() returns FALSE, then our 32-bit
3870       *        implementation is to slow to use safely without risking timeouts.
3871       *        If this happens, install sodium from PECL to get acceptable
3872       *        performance.
3873       *
3874       * @param int $iterations Number of multiplications to attempt
3875       * @param int $maxTimeout Milliseconds
3876       * @return bool           TRUE if we're fast enough, FALSE is not
3877       * @throws SodiumException
3878       */
3879      public static function runtime_speed_test($iterations, $maxTimeout)
3880      {
3881          if (self::polyfill_is_fast()) {
3882              return true;
3883          }
3884          /** @var float $end */
3885          $end = 0.0;
3886          /** @var float $start */
3887          $start = microtime(true);
3888          /** @var ParagonIE_Sodium_Core32_Int64 $a */
3889          $a = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
3890          for ($i = 0; $i < $iterations; ++$i) {
3891              /** @var ParagonIE_Sodium_Core32_Int64 $b */
3892              $b = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
3893              $a->mulInt64($b);
3894          }
3895          /** @var float $end */
3896          $end = microtime(true);
3897          /** @var int $diff */
3898          $diff = (int) ceil(($end - $start) * 1000);
3899          return $diff < $maxTimeout;
3900      }
3901  
3902      /**
3903       * Add two numbers (little-endian unsigned), storing the value in the first
3904       * parameter.
3905       *
3906       * This mutates $val.
3907       *
3908       * @param string $val
3909       * @param string $addv
3910       * @return void
3911       * @throws SodiumException
3912       */
3913      public static function sub(&$val, $addv)
3914      {
3915          $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
3916          $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
3917          if ($val_len !== $addv_len) {
3918              throw new SodiumException('values must have the same length');
3919          }
3920          $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
3921          $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
3922  
3923          $c = 0;
3924          for ($i = 0; $i < $val_len; $i++) {
3925              $c = ($A[$i] - $B[$i] - $c);
3926              $A[$i] = ($c & 0xff);
3927              $c = ($c >> 8) & 1;
3928          }
3929          $val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
3930      }
3931  
3932      /**
3933       * This emulates libsodium's version_string() function, except ours is
3934       * prefixed with 'polyfill-'.
3935       *
3936       * @return string
3937       * @psalm-suppress MixedInferredReturnType
3938       * @psalm-suppress UndefinedFunction
3939       */
3940      public static function version_string()
3941      {
3942          if (self::useNewSodiumAPI()) {
3943              return (string) sodium_version_string();
3944          }
3945          if (self::use_fallback('version_string')) {
3946              return (string) call_user_func('\\Sodium\\version_string');
3947          }
3948          return (string) self::VERSION_STRING;
3949      }
3950  
3951      /**
3952       * Should we use the libsodium core function instead?
3953       * This is always a good idea, if it's available. (Unless we're in the
3954       * middle of running our unit test suite.)
3955       *
3956       * If ext/libsodium is available, use it. Return TRUE.
3957       * Otherwise, we have to use the code provided herein. Return FALSE.
3958       *
3959       * @param string $sodium_func_name
3960       *
3961       * @return bool
3962       */
3963      protected static function use_fallback($sodium_func_name = '')
3964      {
3965          static $res = null;
3966          if ($res === null) {
3967              $res = extension_loaded('libsodium') && PHP_VERSION_ID >= 50300;
3968          }
3969          if ($res === false) {
3970              // No libsodium installed
3971              return false;
3972          }
3973          if (self::$disableFallbackForUnitTests) {
3974              // Don't fallback. Use the PHP implementation.
3975              return false;
3976          }
3977          if (!empty($sodium_func_name)) {
3978              return is_callable('\\Sodium\\' . $sodium_func_name);
3979          }
3980          return true;
3981      }
3982  
3983      /**
3984       * Libsodium as implemented in PHP 7.2
3985       * and/or ext/sodium (via PECL)
3986       *
3987       * @ref https://wiki.php.net/rfc/libsodium
3988       * @return bool
3989       */
3990      protected static function useNewSodiumAPI()
3991      {
3992          static $res = null;
3993          if ($res === null) {
3994              $res = PHP_VERSION_ID >= 70000 && extension_loaded('sodium');
3995          }
3996          if (self::$disableFallbackForUnitTests) {
3997              // Don't fallback. Use the PHP implementation.
3998              return false;
3999          }
4000          return (bool) $res;
4001      }
4002  }


Generated : Fri May 10 08:20:01 2024 Cross-referenced by PHPXref