: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @psalm-suppress MixedArgument
public static function crypto_generichash_init($key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
if (self::useNewSodiumAPI()) {
return sodium_crypto_generichash_init($key, $length);
if (self::use_fallback('crypto_generichash_init')) {
return (string) call_user_func('\\Sodium\\crypto_generichash_init', $key, $length);
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::generichash_init($key, $length);
return ParagonIE_Sodium_Crypto::generichash_init($key, $length);
* Initialize a BLAKE2b hashing context, for use in a streaming interface.
* @param string|null $key If specified must be a string between 16 and 64 bytes
* @param int $length The size of the desired hash output
* @param string $salt Salt (up to 16 bytes)
* @param string $personal Personalization string (up to 16 bytes)
* @return string A BLAKE2 hashing context, encoded as a string
* (To be 100% compatible with ext/libsodium)
* @throws SodiumException
* @psalm-suppress MixedArgument
public static function crypto_generichash_init_salt_personal(
$length = self::CRYPTO_GENERICHASH_BYTES,
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4);
$salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT);
$personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT);
if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal);
return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal);
* Update a BLAKE2b hashing context with additional data.
* @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init().
* $ctx is passed by reference and gets updated in-place.
* @param string $message The message to append to the existing hash state.
* @throws SodiumException
* @psalm-suppress MixedArgument
* @psalm-suppress ReferenceConstraintViolation
public static function crypto_generichash_update(&$ctx, $message)
ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
if (self::useNewSodiumAPI()) {
sodium_crypto_generichash_update($ctx, $message);
if (self::use_fallback('crypto_generichash_update')) {
$func = '\\Sodium\\crypto_generichash_update';
if (PHP_INT_SIZE === 4) {
$ctx = ParagonIE_Sodium_Crypto32::generichash_update($ctx, $message);
$ctx = ParagonIE_Sodium_Crypto::generichash_update($ctx, $message);
public static function crypto_generichash_keygen()
return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES);
* @throws SodiumException
public static function crypto_kdf_derive_from_key(
ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
$subkey_id = (int) $subkey_id;
$subkey_len = (int) $subkey_len;
$context = (string) $context;
if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) {
throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN');
if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) {
throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX');
throw new SodiumException('subkey_id cannot be negative');
if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) {
throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes');
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) {
throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes');
$salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id);
$state = self::crypto_generichash_init_salt_personal(
return self::crypto_generichash_final($state, $subkey_len);
public static function crypto_kdf_keygen()
return random_bytes(self::CRYPTO_KDF_KEYBYTES);
* Perform a key exchange, between a designated client and a server.
* Typically, you would designate one machine to be the client and the
* other to be the server. The first two keys are what you'd expect for
* scalarmult() below, but the latter two public keys don't swap places.
* |--------------------------------|-------------------------------------|
* | shared = crypto_kx( | shared = crypto_kx( |
* | alice_sk, | bob_sk, | <- contextual
* | bob_pk, | alice_pk, | <- contextual
* | alice_pk, | alice_pk, | <----- static
* | bob_pk | bob_pk | <----- static
* They are used along with the scalarmult product to generate a 256-bit
* BLAKE2b hash unique to the client and server keys.
* @param string $my_secret
* @param string $their_public
* @param string $client_public
* @param string $server_public
* @param bool $dontFallback
* @throws SodiumException
* @psalm-suppress MixedArgument
public static function crypto_kx($my_secret, $their_public, $client_public, $server_public, $dontFallback = false)
ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($client_public, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($server_public, 'string', 4);
if (ParagonIE_Sodium_Core_Util::strlen($my_secret) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
if (ParagonIE_Sodium_Core_Util::strlen($their_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
if (ParagonIE_Sodium_Core_Util::strlen($client_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
if (ParagonIE_Sodium_Core_Util::strlen($server_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 4 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
if (self::useNewSodiumAPI() && !$dontFallback) {
if (is_callable('sodium_crypto_kx')) {
return (string) sodium_crypto_kx(
if (self::use_fallback('crypto_kx')) {
return (string) call_user_func(
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::keyExchange(
return ParagonIE_Sodium_Crypto::keyExchange(
* @throws SodiumException
public static function crypto_kx_seed_keypair($seed)
ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) {
throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes');
$sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES);
$pk = self::crypto_scalarmult_base($sk);
public static function crypto_kx_keypair()
$sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES);
$pk = self::crypto_scalarmult_base($sk);
* @param string $serverPublicKey
* @return array{0: string, 1: string}
* @throws SodiumException
public static function crypto_kx_client_session_keys($keypair, $serverPublicKey)
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
$keypair = (string) $keypair;
$serverPublicKey = (string) $serverPublicKey;
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
$sk = self::crypto_kx_secretkey($keypair);
$pk = self::crypto_kx_publickey($keypair);
$h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey));
self::crypto_generichash_update($h, $pk);
self::crypto_generichash_update($h, $serverPublicKey);
$sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
ParagonIE_Sodium_Core_Util::substr(
self::CRYPTO_KX_SESSIONKEYBYTES
ParagonIE_Sodium_Core_Util::substr(
self::CRYPTO_KX_SESSIONKEYBYTES,
self::CRYPTO_KX_SESSIONKEYBYTES
* @param string $clientPublicKey
* @return array{0: string, 1: string}
* @throws SodiumException
public static function crypto_kx_server_session_keys($keypair, $clientPublicKey)
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
$keypair = (string) $keypair;
$clientPublicKey = (string) $clientPublicKey;
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
$sk = self::crypto_kx_secretkey($keypair);
$pk = self::crypto_kx_publickey($keypair);
$h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey));
self::crypto_generichash_update($h, $clientPublicKey);
self::crypto_generichash_update($h, $pk);
$sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
ParagonIE_Sodium_Core_Util::substr(
self::CRYPTO_KX_SESSIONKEYBYTES,
self::CRYPTO_KX_SESSIONKEYBYTES
ParagonIE_Sodium_Core_Util::substr(
self::CRYPTO_KX_SESSIONKEYBYTES
* @throws SodiumException
public static function crypto_kx_secretkey($kp)
return ParagonIE_Sodium_Core_Util::substr(
self::CRYPTO_KX_SECRETKEYBYTES
* @throws SodiumException
public static function crypto_kx_publickey($kp)
return ParagonIE_Sodium_Core_Util::substr(
self::CRYPTO_KX_SECRETKEYBYTES,
self::CRYPTO_KX_PUBLICKEYBYTES
* @throws SodiumException
* @psalm-suppress MixedArgument
public static function crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg = null)
ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
if (self::useNewSodiumAPI()) {
ParagonIE_Sodium_Core_Util::declareScalarType($alg, 'int', 6);
return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg);
return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit);
if (self::use_fallback('crypto_pwhash')) {
return (string) call_user_func('\\Sodium\\crypto_pwhash', $outlen, $passwd, $salt, $opslimit, $memlimit);
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
* !Exclusive to sodium_compat!
* This returns TRUE if the native crypto_pwhash API is available by libsodium.
* This returns FALSE if only sodium_compat is available.
public static function crypto_pwhash_is_available()
if (self::useNewSodiumAPI()) {
if (self::use_fallback('crypto_pwhash')) {
* @throws SodiumException
* @psalm-suppress MixedArgument
public static function crypto_pwhash_str($passwd, $opslimit, $memlimit)
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
if (self::useNewSodiumAPI()) {
return sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit);
if (self::use_fallback('crypto_pwhash_str')) {
return (string) call_user_func('\\Sodium\\crypto_pwhash_str', $passwd, $opslimit, $memlimit);
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
* Do we need to rehash this password?