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