[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Wed Oct 28 01:00:03 2020 Cross-referenced by PHPXref 0.7.1