ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

Compat.php (147622B)


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