[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Mon Nov 18 01:00:04 2019 Cross-referenced by PHPXref 0.7.1