<?php

if (class_exists('ParagonIE_Sodium_Crypto32', false)) {
    return;
}
abstract class ParagonIE_Sodium_Crypto32
{
    const aead_chacha20poly1305_KEYBYTES = 32;
    const aead_chacha20poly1305_NSECBYTES = 0;
    const aead_chacha20poly1305_NPUBBYTES = 8;
    const aead_chacha20poly1305_ABYTES = 16;
    const aead_chacha20poly1305_IETF_KEYBYTES = 32;
    const aead_chacha20poly1305_IETF_NSECBYTES = 0;
    const aead_chacha20poly1305_IETF_NPUBBYTES = 12;
    const aead_chacha20poly1305_IETF_ABYTES = 16;
    const aead_xchacha20poly1305_IETF_KEYBYTES = 32;
    const aead_xchacha20poly1305_IETF_NSECBYTES = 0;
    const aead_xchacha20poly1305_IETF_NPUBBYTES = 24;
    const aead_xchacha20poly1305_IETF_ABYTES = 16;
    const box_curve25519xsalsa20poly1305_SEEDBYTES = 32;
    const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32;
    const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32;
    const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32;
    const box_curve25519xsalsa20poly1305_NONCEBYTES = 24;
    const box_curve25519xsalsa20poly1305_MACBYTES = 16;
    const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16;
    const box_curve25519xsalsa20poly1305_ZEROBYTES = 32;
    const onetimeauth_poly1305_BYTES = 16;
    const onetimeauth_poly1305_KEYBYTES = 32;
    const secretbox_xsalsa20poly1305_KEYBYTES = 32;
    const secretbox_xsalsa20poly1305_NONCEBYTES = 24;
    const secretbox_xsalsa20poly1305_MACBYTES = 16;
    const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16;
    const secretbox_xsalsa20poly1305_ZEROBYTES = 32;
    const secretbox_xchacha20poly1305_KEYBYTES = 32;
    const secretbox_xchacha20poly1305_NONCEBYTES = 24;
    const secretbox_xchacha20poly1305_MACBYTES = 16;
    const secretbox_xchacha20poly1305_BOXZEROBYTES = 16;
    const secretbox_xchacha20poly1305_ZEROBYTES = 32;
    const stream_salsa20_KEYBYTES = 32;
    public static function aead_chacha20poly1305_decrypt($message = '', $ad = '', $nonce = '', $key = '')
    {
        $len = ParagonIE_Sodium_Core32_Util::strlen($message);
        $clen = $len - self::aead_chacha20poly1305_ABYTES;
        $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
        $mac = ParagonIE_Sodium_Core32_Util::substr($message, $clen, self::aead_chacha20poly1305_ABYTES);
        $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $clen);
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(32, $nonce, $key);
        $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
        try {
            ParagonIE_Sodium_Compat::memzero($block0);
        } catch (SodiumException $ex) {
            $block0 = null;
        }
        $state->update($ad);
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
        $state->update($ciphertext);
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
        $computed_mac = $state->finish();
        if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) {
            throw new SodiumException('Invalid MAC');
        }
        return ParagonIE_Sodium_Core32_ChaCha20::streamXorIc($ciphertext, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1));
    }
    public static function aead_chacha20poly1305_encrypt($message = '', $ad = '', $nonce = '', $key = '')
    {
        $len = ParagonIE_Sodium_Core32_Util::strlen($message);
        $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(32, $nonce, $key);
        $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
        try {
            ParagonIE_Sodium_Compat::memzero($block0);
        } catch (SodiumException $ex) {
            $block0 = null;
        }
        $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc($message, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1));
        $state->update($ad);
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
        $state->update($ciphertext);
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len));
        return $ciphertext . $state->finish();
    }
    public static function aead_chacha20poly1305_ietf_decrypt($message = '', $ad = '', $nonce = '', $key = '')
    {
        $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
        $len = ParagonIE_Sodium_Core32_Util::strlen($message);
        $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES;
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $nonce, $key);
        $mac = ParagonIE_Sodium_Core32_Util::substr($message, $len - self::aead_chacha20poly1305_IETF_ABYTES, self::aead_chacha20poly1305_IETF_ABYTES);
        $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES);
        $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
        try {
            ParagonIE_Sodium_Compat::memzero($block0);
        } catch (SodiumException $ex) {
            $block0 = null;
        }
        $state->update($ad);
        $state->update(str_repeat("\0", 0x10 - $adlen & 0xf));
        $state->update($ciphertext);
        $state->update(str_repeat("\0", 0x10 - $clen & 0xf));
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
        $computed_mac = $state->finish();
        if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) {
            throw new SodiumException('Invalid MAC');
        }
        return ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc($ciphertext, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1));
    }
    public static function aead_chacha20poly1305_ietf_encrypt($message = '', $ad = '', $nonce = '', $key = '')
    {
        $len = ParagonIE_Sodium_Core32_Util::strlen($message);
        $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $nonce, $key);
        $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
        try {
            ParagonIE_Sodium_Compat::memzero($block0);
        } catch (SodiumException $ex) {
            $block0 = null;
        }
        $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc($message, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1));
        $state->update($ad);
        $state->update(str_repeat("\0", 0x10 - $adlen & 0xf));
        $state->update($ciphertext);
        $state->update(str_repeat("\0", 0x10 - $len & 0xf));
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len));
        return $ciphertext . $state->finish();
    }
    public static function aead_xchacha20poly1305_ietf_decrypt($message = '', $ad = '', $nonce = '', $key = '')
    {
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key);
        $nonceLast = "\0\0\0\0" . ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
        return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey);
    }
    public static function aead_xchacha20poly1305_ietf_encrypt($message = '', $ad = '', $nonce = '', $key = '')
    {
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key);
        $nonceLast = "\0\0\0\0" . ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
        return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey);
    }
    public static function auth($message, $key)
    {
        return ParagonIE_Sodium_Core32_Util::substr(hash_hmac('sha512', $message, $key, true), 0, 32);
    }
    public static function auth_verify($mac, $message, $key)
    {
        return ParagonIE_Sodium_Core32_Util::hashEquals($mac, self::auth($message, $key));
    }
    public static function box($plaintext, $nonce, $keypair)
    {
        return self::secretbox($plaintext, $nonce, self::box_beforenm(self::box_secretkey($keypair), self::box_publickey($keypair)));
    }
    public static function box_seal($message, $publicKey)
    {
        $ephemeralKeypair = self::box_keypair();
        $ephemeralSK = self::box_secretkey($ephemeralKeypair);
        $ephemeralPK = self::box_publickey($ephemeralKeypair);
        $nonce = self::generichash($ephemeralPK . $publicKey, '', 24);
        $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
        $ciphertext = self::box($message, $nonce, $keypair);
        try {
            ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
            ParagonIE_Sodium_Compat::memzero($ephemeralSK);
            ParagonIE_Sodium_Compat::memzero($nonce);
        } catch (SodiumException $ex) {
            $ephemeralKeypair = null;
            $ephemeralSK = null;
            $nonce = null;
        }
        return $ephemeralPK . $ciphertext;
    }
    public static function box_seal_open($message, $keypair)
    {
        $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32);
        $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32);
        $secretKey = self::box_secretkey($keypair);
        $publicKey = self::box_publickey($keypair);
        $nonce = self::generichash($ephemeralPK . $publicKey, '', 24);
        $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
        $m = self::box_open($ciphertext, $nonce, $keypair);
        try {
            ParagonIE_Sodium_Compat::memzero($secretKey);
            ParagonIE_Sodium_Compat::memzero($ephemeralPK);
            ParagonIE_Sodium_Compat::memzero($nonce);
        } catch (SodiumException $ex) {
            $secretKey = null;
            $ephemeralPK = null;
            $nonce = null;
        }
        return $m;
    }
    public static function box_beforenm($sk, $pk)
    {
        return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20(str_repeat("\0", 16), self::scalarmult($sk, $pk));
    }
    public static function box_keypair()
    {
        $sKey = random_bytes(32);
        $pKey = self::scalarmult_base($sKey);
        return $sKey . $pKey;
    }
    public static function box_seed_keypair($seed)
    {
        $sKey = ParagonIE_Sodium_Core32_Util::substr(hash('sha512', $seed, true), 0, 32);
        $pKey = self::scalarmult_base($sKey);
        return $sKey . $pKey;
    }
    public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
    {
        return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32);
    }
    public static function box_secretkey($keypair)
    {
        if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) {
            throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.');
        }
        return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32);
    }
    public static function box_publickey($keypair)
    {
        if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
            throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.');
        }
        return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32);
    }
    public static function box_publickey_from_secretkey($sKey)
    {
        if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
            throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.');
        }
        return self::scalarmult_base($sKey);
    }
    public static function box_open($ciphertext, $nonce, $keypair)
    {
        return self::secretbox_open($ciphertext, $nonce, self::box_beforenm(self::box_secretkey($keypair), self::box_publickey($keypair)));
    }
    public static function generichash($message, $key = '', $outlen = 32)
    {
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
        $k = null;
        if (!empty($key)) {
            $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
            if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
                throw new RangeException('Invalid key size');
            }
        }
        $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
        $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen);
        ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count());
        $out = new SplFixedArray($outlen);
        $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out);
        $outArray = $out->toArray();
        return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
    }
    public static function generichash_final($ctx, $outlen = 32)
    {
        if (!is_string($ctx)) {
            throw new TypeError('Context must be a string');
        }
        $out = new SplFixedArray($outlen);
        $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
        $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out);
        $outArray = $out->toArray();
        return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
    }
    public static function generichash_init($key = '', $outputLength = 32)
    {
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
        $k = null;
        if (!empty($key)) {
            $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
            if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
                throw new RangeException('Invalid key size');
            }
        }
        $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength);
        return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
    }
    public static function generichash_init_salt_personal($key = '', $outputLength = 32, $salt = '', $personal = '')
    {
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
        $k = null;
        if (!empty($key)) {
            $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
            if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
                throw new RangeException('Invalid key size');
            }
        }
        if (!empty($salt)) {
            $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt);
        } else {
            $s = null;
        }
        if (!empty($salt)) {
            $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal);
        } else {
            $p = null;
        }
        $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p);
        return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
    }
    public static function generichash_update($ctx, $message)
    {
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
        $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
        $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
        ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
        return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
    }
    public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
    {
        return self::generichash(self::scalarmult($my_sk, $their_pk) . $client_pk . $server_pk);
    }
    public static function scalarmult($sKey, $pKey)
    {
        $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
        self::scalarmult_throw_if_zero($q);
        return $q;
    }
    public static function scalarmult_base($secret)
    {
        $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
        self::scalarmult_throw_if_zero($q);
        return $q;
    }
    protected static function scalarmult_throw_if_zero($q)
    {
        $d = 0;
        for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
            $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
        }
        if (-(1 & $d - 1 >> 8)) {
            throw new SodiumException('Zero public key is not allowed');
        }
    }
    public static function secretbox($plaintext, $nonce, $key)
    {
        $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
        $block0 = str_repeat("\0", 32);
        $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
        $mlen0 = $mlen;
        if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
            $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
        }
        $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
        $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor($block0, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey);
        $c = ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES);
        if ($mlen > $mlen0) {
            $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(ParagonIE_Sodium_Core32_Util::substr($plaintext, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), 1, $subkey);
        }
        $state = new ParagonIE_Sodium_Core32_Poly1305_State(ParagonIE_Sodium_Core32_Util::substr($block0, 0, self::onetimeauth_poly1305_KEYBYTES));
        try {
            ParagonIE_Sodium_Compat::memzero($block0);
            ParagonIE_Sodium_Compat::memzero($subkey);
        } catch (SodiumException $ex) {
            $block0 = null;
            $subkey = null;
        }
        $state->update($c);
        $c = $state->finish() . $c;
        unset($state);
        return $c;
    }
    public static function secretbox_open($ciphertext, $nonce, $key)
    {
        $mac = ParagonIE_Sodium_Core32_Util::substr($ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES);
        $c = ParagonIE_Sodium_Core32_Util::substr($ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES);
        $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
        $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
        $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey);
        $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify($mac, $c, ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32));
        if (!$verified) {
            try {
                ParagonIE_Sodium_Compat::memzero($subkey);
            } catch (SodiumException $ex) {
                $subkey = null;
            }
            throw new SodiumException('Invalid MAC');
        }
        $m = ParagonIE_Sodium_Core32_Util::xorStrings(ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES));
        if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
            $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(ParagonIE_Sodium_Core32_Util::substr($c, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), 1, (string) $subkey);
        }
        return $m;
    }
    public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
    {
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key);
        $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
        $block0 = str_repeat("\0", 32);
        $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
        $mlen0 = $mlen;
        if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
            $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
        }
        $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc($block0, $nonceLast, $subkey);
        $c = ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES);
        if ($mlen > $mlen0) {
            $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(ParagonIE_Sodium_Core32_Util::substr($plaintext, self::secretbox_xchacha20poly1305_ZEROBYTES), $nonceLast, $subkey, ParagonIE_Sodium_Core32_Util::store64_le(1));
        }
        $state = new ParagonIE_Sodium_Core32_Poly1305_State(ParagonIE_Sodium_Core32_Util::substr($block0, 0, self::onetimeauth_poly1305_KEYBYTES));
        try {
            ParagonIE_Sodium_Compat::memzero($block0);
            ParagonIE_Sodium_Compat::memzero($subkey);
        } catch (SodiumException $ex) {
            $block0 = null;
            $subkey = null;
        }
        $state->update($c);
        $c = $state->finish() . $c;
        unset($state);
        return $c;
    }
    public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
    {
        $mac = ParagonIE_Sodium_Core32_Util::substr($ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES);
        $c = ParagonIE_Sodium_Core32_Util::substr($ciphertext, self::secretbox_xchacha20poly1305_MACBYTES);
        $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey);
        $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify($mac, $c, ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32));
        if (!$verified) {
            try {
                ParagonIE_Sodium_Compat::memzero($subkey);
            } catch (SodiumException $ex) {
                $subkey = null;
            }
            throw new SodiumException('Invalid MAC');
        }
        $m = ParagonIE_Sodium_Core32_Util::xorStrings(ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES));
        if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
            $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(ParagonIE_Sodium_Core32_Util::substr($c, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core32_Util::store64_le(1));
        }
        return $m;
    }
    public static function secretstream_xchacha20poly1305_init_push($key)
    {
        $out = random_bytes(24);
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key);
        $state = new ParagonIE_Sodium_Core32_SecretStream_State($subkey, ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4));
        $state->counterReset();
        return array($state->toString(), $out);
    }
    public static function secretstream_xchacha20poly1305_init_pull($key, $header)
    {
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(ParagonIE_Sodium_Core32_Util::substr($header, 0, 16), $key);
        $state = new ParagonIE_Sodium_Core32_SecretStream_State($subkey, ParagonIE_Sodium_Core32_Util::substr($header, 16));
        $state->counterReset();
        return $state->toString();
    }
    public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
    {
        $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
        $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg);
        $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
        if ($msglen + 63 >> 6 > 0xfffffffe) {
            throw new SodiumException('message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes');
        }
        $auth = new ParagonIE_Sodium_Core32_Poly1305_State(ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()));
        $auth->update($aad);
        $auth->update(str_repeat("\0", 0x10 - $aadlen & 0xf));
        $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(1));
        $auth->update($block);
        $out = $block[0];
        $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc($msg, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(2));
        $auth->update($cipher);
        $out .= $cipher;
        unset($cipher);
        $auth->update(str_repeat("\0", 0x10 - 64 + $msglen & 0xf));
        $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
        $auth->update($slen);
        $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
        $auth->update($slen);
        $mac = $auth->finish();
        $out .= $mac;
        unset($auth);
        $st->xorNonce($mac);
        $st->incrementCounter();
        $state = $st->toString();
        $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
        if ($rekey || $st->needsRekey()) {
            self::secretstream_xchacha20poly1305_rekey($state);
        }
        return $out;
    }
    public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
    {
        $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
        $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher);
        $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
        $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
        if ($msglen + 63 >> 6 > 0xfffffffe) {
            throw new SodiumException('message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes');
        }
        $auth = new ParagonIE_Sodium_Core32_Poly1305_State(ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()));
        $auth->update($aad);
        $auth->update(str_repeat("\0", 0x10 - $aadlen & 0xf));
        $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc($cipher[0] . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(1));
        $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]);
        $block[0] = $cipher[0];
        $auth->update($block);
        $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen));
        $auth->update(str_repeat("\0", 0x10 - 64 + $msglen & 0xf));
        $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
        $auth->update($slen);
        $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
        $auth->update($slen);
        $mac = $auth->finish();
        $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16);
        if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) {
            return false;
        }
        $out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(2));
        $st->xorNonce($mac);
        $st->incrementCounter();
        $state = $st->toString();
        $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
        if ($rekey || $st->needsRekey()) {
            self::secretstream_xchacha20poly1305_rekey($state);
        }
        return array($out, $tag);
    }
    public static function secretstream_xchacha20poly1305_rekey(&$state)
    {
        $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
        $new_key_and_inonce = $st->getKey();
        $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8);
        $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc($new_key_and_inonce, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(0)));
        $st->counterReset();
        $state = $st->toString();
    }
    public static function sign_detached($message, $sk)
    {
        return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
    }
    public static function sign($message, $sk)
    {
        return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
    }
    public static function sign_open($signedMessage, $pk)
    {
        return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
    }
    public static function sign_verify_detached($signature, $message, $pk)
    {
        return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
    }
}