: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
/** @var string $c - MAC || ciphertext */
$c = $state->finish() . $c;
* Decrypt a ciphertext generated via secretbox().
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
* @param string $ciphertext
* @throws SodiumException
public static function secretbox_open($ciphertext, $nonce, $key)
$mac = ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xsalsa20poly1305_MACBYTES
$c = ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xsalsa20poly1305_MACBYTES
$clen = ParagonIE_Sodium_Core_Util::strlen($c);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
$verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
throw new SodiumException('Invalid MAC');
/** @var string $m - Decrypted message */
$m = ParagonIE_Sodium_Core_Util::xorStrings(
ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
// We had more than 1 block, so let's continue to decrypt the rest.
$m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xsalsa20poly1305_ZEROBYTES
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
* XChaCha20-Poly1305 authenticated symmetric-key encryption.
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
* @param string $plaintext
* @throws SodiumException
public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
$nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
$mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
$block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
$c = ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xchacha20poly1305_ZEROBYTES
$c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xchacha20poly1305_ZEROBYTES
ParagonIE_Sodium_Core_Util::store64_le(1)
$state = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_Util::substr(
self::onetimeauth_poly1305_KEYBYTES
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
/** @var string $c - MAC || ciphertext */
$c = $state->finish() . $c;
* Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
* @param string $ciphertext
* @throws SodiumException
public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
$mac = ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xchacha20poly1305_MACBYTES
$c = ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xchacha20poly1305_MACBYTES
$clen = ParagonIE_Sodium_Core_Util::strlen($c);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
$verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
throw new SodiumException('Invalid MAC');
/** @var string $m - Decrypted message */
$m = ParagonIE_Sodium_Core_Util::xorStrings(
ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
// We had more than 1 block, so let's continue to decrypt the rest.
$m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
ParagonIE_Sodium_Core_Util::substr(
self::secretbox_xchacha20poly1305_ZEROBYTES
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
ParagonIE_Sodium_Core_Util::store64_le(1)
* @return array<int, string> Returns a state and a header.
* @throws SodiumException
public static function secretstream_xchacha20poly1305_init_push($key)
# randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
# crypto_core_hchacha20(state->k, out, k, NULL);
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key);
$state = new ParagonIE_Sodium_Core_SecretStream_State(
ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4)
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
# memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# memset(state->_pad, 0, sizeof state->_pad);
* @return string Returns a state.
public static function secretstream_xchacha20poly1305_init_pull($key, $header)
# crypto_core_hchacha20(state->k, in, k, NULL);
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core_Util::substr($header, 0, 16),
$state = new ParagonIE_Sodium_Core_SecretStream_State(
ParagonIE_Sodium_Core_Util::substr($header, 16)
# memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# memset(state->_pad, 0, sizeof state->_pad);
return $state->toString();
* @throws SodiumException
public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
# crypto_onetimeauth_poly1305_state poly1305_state;
# unsigned char block[64U];
# unsigned char slen[8U];
$msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
$aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
if ((($msglen + 63) >> 6) > 0xfffffffe) {
throw new SodiumException(
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
# if (outlen_p != NULL) {
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
# sodium_memzero(block, sizeof block);
$auth = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
# memset(block, 0, sizeof block);
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
# state->nonce, 1U, state->k);
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63),
ParagonIE_Sodium_Core_Util::store64_le(1)
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
# c = out + (sizeof tag);
# crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
$cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
ParagonIE_Sodium_Core_Util::store64_le(2)
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
# crypto_onetimeauth_poly1305_update
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
# STORE64_LE(slen, (uint64_t) adlen);
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
# STORE64_LE(slen, (sizeof block) + mlen);
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
# XOR_BUF(STATE_INONCE(state), mac,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# sodium_increment(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
// Overwrite by reference:
$state = $st->toString();
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
# sodium_is_zero(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
# crypto_secretstream_xchacha20poly1305_rekey(state);
if ($rekey || $st->needsRekey()) {
self::secretstream_xchacha20poly1305_rekey($state);
# if (outlen_p != NULL) {
# *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
* @return bool|array{0: string, 1: int}
* @throws SodiumException
public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
$cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);
# mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
$msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
$aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
if ((($msglen + 63) >> 6) > 0xfffffffe) {
throw new SodiumException(
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
# sodium_memzero(block, sizeof block);
$auth = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
# memset(block, 0, sizeof block);
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
# state->nonce, 1U, state->k);
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
$cipher[0] . str_repeat("\0", 63),
ParagonIE_Sodium_Core_Util::store64_le(1)
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
$tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
$auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));
# crypto_onetimeauth_poly1305_update
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
# STORE64_LE(slen, (uint64_t) adlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
# STORE64_LE(slen, (sizeof block) + mlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
# sodium_memzero(&poly1305_state, sizeof poly1305_state);