[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

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


Generated : Sat Dec 21 08:20:01 2024 Cross-referenced by PHPXref