diff options
Diffstat (limited to 'vendor/paragonie/sodium_compat/src/Crypto32.php')
-rw-r--r-- | vendor/paragonie/sodium_compat/src/Crypto32.php | 1654 |
1 files changed, 1654 insertions, 0 deletions
diff --git a/vendor/paragonie/sodium_compat/src/Crypto32.php b/vendor/paragonie/sodium_compat/src/Crypto32.php new file mode 100644 index 0000000..34c0d7a --- /dev/null +++ b/vendor/paragonie/sodium_compat/src/Crypto32.php @@ -0,0 +1,1654 @@ +<?php + +if (class_exists('ParagonIE_Sodium_Crypto32', false)) { + return; +} + +/** + * Class ParagonIE_Sodium_Crypto + * + * ATTENTION! + * + * If you are using this library, you should be using + * ParagonIE_Sodium_Compat in your code, not this class. + */ +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; + + /** + * AEAD Decryption with ChaCha20-Poly1305 + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $ad + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function aead_chacha20poly1305_decrypt( + $message = '', + $ad = '', + $nonce = '', + $key = '' + ) { + /** @var int $len - Length of message (ciphertext + MAC) */ + $len = ParagonIE_Sodium_Core32_Util::strlen($message); + + /** @var int $clen - Length of ciphertext */ + $clen = $len - self::aead_chacha20poly1305_ABYTES; + + /** @var int $adlen - Length of associated data */ + $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); + + /** @var string $mac - Message authentication code */ + $mac = ParagonIE_Sodium_Core32_Util::substr( + $message, + $clen, + self::aead_chacha20poly1305_ABYTES + ); + + /** @var string $ciphertext - The encrypted message (sans MAC) */ + $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $clen); + + /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ + $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( + 32, + $nonce, + $key + ); + + /* Recalculate the Poly1305 authentication tag (MAC): */ + $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(); + + /* Compare the given MAC with the recalculated MAC: */ + if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { + throw new SodiumException('Invalid MAC'); + } + + // Here, we know that the MAC is valid, so we decrypt and return the plaintext + return ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( + $ciphertext, + $nonce, + $key, + ParagonIE_Sodium_Core32_Util::store64_le(1) + ); + } + + /** + * AEAD Encryption with ChaCha20-Poly1305 + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $ad + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function aead_chacha20poly1305_encrypt( + $message = '', + $ad = '', + $nonce = '', + $key = '' + ) { + /** @var int $len - Length of the plaintext message */ + $len = ParagonIE_Sodium_Core32_Util::strlen($message); + + /** @var int $adlen - Length of the associated data */ + $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); + + /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ + $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; + } + + /** @var string $ciphertext - Raw encrypted data */ + $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(); + } + + /** + * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $ad + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function aead_chacha20poly1305_ietf_decrypt( + $message = '', + $ad = '', + $nonce = '', + $key = '' + ) { + /** @var int $adlen - Length of associated data */ + $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); + + /** @var int $len - Length of message (ciphertext + MAC) */ + $len = ParagonIE_Sodium_Core32_Util::strlen($message); + + /** @var int $clen - Length of ciphertext */ + $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; + + /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ + $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( + 32, + $nonce, + $key + ); + + /** @var string $mac - Message authentication code */ + $mac = ParagonIE_Sodium_Core32_Util::substr( + $message, + $len - self::aead_chacha20poly1305_IETF_ABYTES, + self::aead_chacha20poly1305_IETF_ABYTES + ); + + /** @var string $ciphertext - The encrypted message (sans MAC) */ + $ciphertext = ParagonIE_Sodium_Core32_Util::substr( + $message, + 0, + $len - self::aead_chacha20poly1305_IETF_ABYTES + ); + + /* Recalculate the Poly1305 authentication tag (MAC): */ + $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("\x00", ((0x10 - $adlen) & 0xf))); + $state->update($ciphertext); + $state->update(str_repeat("\x00", (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(); + + /* Compare the given MAC with the recalculated MAC: */ + if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { + throw new SodiumException('Invalid MAC'); + } + + // Here, we know that the MAC is valid, so we decrypt and return the plaintext + return ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( + $ciphertext, + $nonce, + $key, + ParagonIE_Sodium_Core32_Util::store64_le(1) + ); + } + + /** + * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $ad + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function aead_chacha20poly1305_ietf_encrypt( + $message = '', + $ad = '', + $nonce = '', + $key = '' + ) { + /** @var int $len - Length of the plaintext message */ + $len = ParagonIE_Sodium_Core32_Util::strlen($message); + + /** @var int $adlen - Length of the associated data */ + $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); + + /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ + $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; + } + + /** @var string $ciphertext - Raw encrypted data */ + $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( + $message, + $nonce, + $key, + ParagonIE_Sodium_Core32_Util::store64_le(1) + ); + + $state->update($ad); + $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); + $state->update($ciphertext); + $state->update(str_repeat("\x00", ((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(); + } + + /** + * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $ad + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + 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 = "\x00\x00\x00\x00" . + ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); + + return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); + } + + /** + * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $ad + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + 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 = "\x00\x00\x00\x00" . + ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); + + return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); + } + + /** + * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $key + * @return string + * @throws TypeError + */ + public static function auth($message, $key) + { + return ParagonIE_Sodium_Core32_Util::substr( + hash_hmac('sha512', $message, $key, true), + 0, + 32 + ); + } + + /** + * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $mac + * @param string $message + * @param string $key + * @return bool + * @throws SodiumException + * @throws TypeError + */ + public static function auth_verify($mac, $message, $key) + { + return ParagonIE_Sodium_Core32_Util::hashEquals( + $mac, + self::auth($message, $key) + ); + } + + /** + * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $plaintext + * @param string $nonce + * @param string $keypair + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function box($plaintext, $nonce, $keypair) + { + return self::secretbox( + $plaintext, + $nonce, + self::box_beforenm( + self::box_secretkey($keypair), + self::box_publickey($keypair) + ) + ); + } + + /** + * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $publicKey + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function box_seal($message, $publicKey) + { + /** @var string $ephemeralKeypair */ + $ephemeralKeypair = self::box_keypair(); + + /** @var string $ephemeralSK */ + $ephemeralSK = self::box_secretkey($ephemeralKeypair); + + /** @var string $ephemeralPK */ + $ephemeralPK = self::box_publickey($ephemeralKeypair); + + /** @var string $nonce */ + $nonce = self::generichash( + $ephemeralPK . $publicKey, + '', + 24 + ); + + /** @var string $keypair - The combined keypair used in crypto_box() */ + $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); + + /** @var string $ciphertext Ciphertext + MAC from crypto_box */ + $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; + } + + /** + * Opens a message encrypted via box_seal(). + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $keypair + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function box_seal_open($message, $keypair) + { + /** @var string $ephemeralPK */ + $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32); + + /** @var string $ciphertext (ciphertext + MAC) */ + $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32); + + /** @var string $secretKey */ + $secretKey = self::box_secretkey($keypair); + + /** @var string $publicKey */ + $publicKey = self::box_publickey($keypair); + + /** @var string $nonce */ + $nonce = self::generichash( + $ephemeralPK . $publicKey, + '', + 24 + ); + + /** @var string $keypair */ + $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); + + /** @var string $m */ + $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; + } + + /** + * Used by crypto_box() to get the crypto_secretbox() key. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $sk + * @param string $pk + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function box_beforenm($sk, $pk) + { + return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20( + str_repeat("\x00", 16), + self::scalarmult($sk, $pk) + ); + } + + /** + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @return string + * @throws Exception + * @throws SodiumException + * @throws TypeError + */ + public static function box_keypair() + { + $sKey = random_bytes(32); + $pKey = self::scalarmult_base($sKey); + return $sKey . $pKey; + } + + /** + * @param string $seed + * @return string + * @throws SodiumException + * @throws TypeError + */ + 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; + } + + /** + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $sKey + * @param string $pKey + * @return string + * @throws TypeError + */ + 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); + } + + /** + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $keypair + * @return string + * @throws RangeException + * @throws TypeError + */ + 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); + } + + /** + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $keypair + * @return string + * @throws RangeException + * @throws TypeError + */ + 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); + } + + /** + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $sKey + * @return string + * @throws RangeException + * @throws SodiumException + * @throws TypeError + */ + 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); + } + + /** + * Decrypt a message encrypted with box(). + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $ciphertext + * @param string $nonce + * @param string $keypair + * @return string + * @throws SodiumException + * @throws TypeError + */ + 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) + ) + ); + } + + /** + * Calculate a BLAKE2b hash. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string|null $key + * @param int $outlen + * @return string + * @throws RangeException + * @throws SodiumException + * @throws TypeError + */ + public static function generichash($message, $key = '', $outlen = 32) + { + // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized + ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); + + $k = null; + if (!empty($key)) { + /** @var SplFixedArray $k */ + $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); + if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { + throw new RangeException('Invalid key size'); + } + } + + /** @var SplFixedArray $in */ + $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); + + /** @var SplFixedArray $ctx */ + $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen); + ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count()); + + /** @var SplFixedArray $out */ + $out = new SplFixedArray($outlen); + $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out); + + /** @var array<int, int> */ + $outArray = $out->toArray(); + return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); + } + + /** + * Finalize a BLAKE2b hashing context, returning the hash. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $ctx + * @param int $outlen + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function generichash_final($ctx, $outlen = 32) + { + if (!is_string($ctx)) { + throw new TypeError('Context must be a string'); + } + $out = new SplFixedArray($outlen); + + /** @var SplFixedArray $context */ + $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); + + /** @var SplFixedArray $out */ + $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out); + + /** @var array<int, int> */ + $outArray = $out->toArray(); + return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); + } + + /** + * Initialize a hashing context for BLAKE2b. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $key + * @param int $outputLength + * @return string + * @throws RangeException + * @throws SodiumException + * @throws TypeError + */ + public static function generichash_init($key = '', $outputLength = 32) + { + // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized + 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'); + } + } + + /** @var SplFixedArray $ctx */ + $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength); + + return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); + } + + /** + * Initialize a hashing context for BLAKE2b. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $key + * @param int $outputLength + * @param string $salt + * @param string $personal + * @return string + * @throws RangeException + * @throws SodiumException + * @throws TypeError + */ + public static function generichash_init_salt_personal( + $key = '', + $outputLength = 32, + $salt = '', + $personal = '' + ) { + // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized + 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; + } + + /** @var SplFixedArray $ctx */ + $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p); + + return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); + } + + /** + * Update a hashing context for BLAKE2b with $message + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $ctx + * @param string $message + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function generichash_update($ctx, $message) + { + // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized + ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); + + /** @var SplFixedArray $context */ + $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); + + /** @var SplFixedArray $in */ + $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); + + ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count()); + + return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context); + } + + /** + * Libsodium's crypto_kx(). + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $my_sk + * @param string $their_pk + * @param string $client_pk + * @param string $server_pk + * @return string + * @throws SodiumException + * @throws TypeError + */ + 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 + ); + } + + /** + * ECDH over Curve25519 + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $sKey + * @param string $pKey + * @return string + * + * @throws SodiumException + * @throws TypeError + */ + 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; + } + + /** + * ECDH over Curve25519, using the basepoint. + * Used to get a secret key from a public key. + * + * @param string $secret + * @return string + * + * @throws SodiumException + * @throws TypeError + */ + 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; + } + + /** + * This throws an Error if a zero public key was passed to the function. + * + * @param string $q + * @return void + * @throws SodiumException + * @throws TypeError + */ + 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]); + } + + /* branch-free variant of === 0 */ + if (-(1 & (($d - 1) >> 8))) { + throw new SodiumException('Zero public key is not allowed'); + } + } + + /** + * XSalsa20-Poly1305 authenticated symmetric-key encryption. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $plaintext + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function secretbox($plaintext, $nonce, $key) + { + /** @var string $subkey */ + $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); + + /** @var string $block0 */ + $block0 = str_repeat("\x00", 32); + + /** @var int $mlen - Length of the plaintext message */ + $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); + + /** @var string $block0 */ + $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( + $block0, + ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), + $subkey + ); + + /** @var string $c */ + $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); + + /** @var string $c - MAC || ciphertext */ + $c = $state->finish() . $c; + unset($state); + + return $c; + } + + /** + * Decrypt a ciphertext generated via secretbox(). + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $ciphertext + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function secretbox_open($ciphertext, $nonce, $key) + { + /** @var string $mac */ + $mac = ParagonIE_Sodium_Core32_Util::substr( + $ciphertext, + 0, + self::secretbox_xsalsa20poly1305_MACBYTES + ); + + /** @var string $c */ + $c = ParagonIE_Sodium_Core32_Util::substr( + $ciphertext, + self::secretbox_xsalsa20poly1305_MACBYTES + ); + + /** @var int $clen */ + $clen = ParagonIE_Sodium_Core32_Util::strlen($c); + + /** @var string $subkey */ + $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); + + /** @var string $block0 */ + $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'); + } + + /** @var string $m - Decrypted message */ + $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) { + // We had more than 1 block, so let's continue to decrypt the rest. + $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; + } + + /** + * XChaCha20-Poly1305 authenticated symmetric-key encryption. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $plaintext + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) + { + /** @var string $subkey */ + $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( + ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), + $key + ); + $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); + + /** @var string $block0 */ + $block0 = str_repeat("\x00", 32); + + /** @var int $mlen - Length of the plaintext message */ + $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); + + /** @var string $block0 */ + $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( + $block0, + $nonceLast, + $subkey + ); + + /** @var string $c */ + $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); + + /** @var string $c - MAC || ciphertext */ + $c = $state->finish() . $c; + unset($state); + + return $c; + } + + /** + * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $ciphertext + * @param string $nonce + * @param string $key + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) + { + /** @var string $mac */ + $mac = ParagonIE_Sodium_Core32_Util::substr( + $ciphertext, + 0, + self::secretbox_xchacha20poly1305_MACBYTES + ); + + /** @var string $c */ + $c = ParagonIE_Sodium_Core32_Util::substr( + $ciphertext, + self::secretbox_xchacha20poly1305_MACBYTES + ); + + /** @var int $clen */ + $clen = ParagonIE_Sodium_Core32_Util::strlen($c); + + /** @var string $subkey */ + $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key); + + /** @var string $block0 */ + $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'); + } + + /** @var string $m - Decrypted message */ + $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) { + // We had more than 1 block, so let's continue to decrypt the rest. + $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; + } + + /** + * @param string $key + * @return array<int, string> Returns a state and a header. + * @throws Exception + * @throws SodiumException + */ + public static function secretstream_xchacha20poly1305_init_push($key) + { + # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES); + $out = random_bytes(24); + + # crypto_core_hchacha20(state->k, out, k, NULL); + $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) + ); + + # _crypto_secretstream_xchacha20poly1305_counter_reset(state); + $state->counterReset(); + + # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES, + # crypto_secretstream_xchacha20poly1305_INONCEBYTES); + # memset(state->_pad, 0, sizeof state->_pad); + return array( + $state->toString(), + $out + ); + } + + /** + * @param string $key + * @param string $header + * @return string Returns a state. + * @throws Exception + */ + public static function secretstream_xchacha20poly1305_init_pull($key, $header) + { + # crypto_core_hchacha20(state->k, in, k, NULL); + $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(); + # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES, + # crypto_secretstream_xchacha20poly1305_INONCEBYTES); + # memset(state->_pad, 0, sizeof state->_pad); + # return 0; + return $state->toString(); + } + + /** + * @param string $state + * @param string $msg + * @param string $aad + * @param int $tag + * @return string + * @throws SodiumException + */ + public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) + { + $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); + # crypto_onetimeauth_poly1305_state poly1305_state; + # unsigned char block[64U]; + # unsigned char slen[8U]; + # unsigned char *c; + # unsigned char *mac; + + $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' + ); + } + + # if (outlen_p != NULL) { + # *outlen_p = 0U; + # } + # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { + # sodium_misuse(); + # } + + # 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_Core32_Poly1305_State( + ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) + ); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); + $auth->update($aad); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, + # (0x10 - adlen) & 0xf); + $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); + + # memset(block, 0, sizeof block); + # block[0] = tag; + # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, + # state->nonce, 1U, state->k); + $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) + ); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); + $auth->update($block); + + # out[0] = block[0]; + $out = $block[0]; + # c = out + (sizeof tag); + # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k); + $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( + $msg, + $st->getCombinedNonce(), + $st->getKey(), + ParagonIE_Sodium_Core32_Util::store64_le(2) + ); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); + $auth->update($cipher); + + $out .= $cipher; + unset($cipher); + + # 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_Core32_Util::store64_le($aadlen); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); + $auth->update($slen); + + # STORE64_LE(slen, (sizeof block) + mlen); + $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); + $auth->update($slen); + + # mac = c + mlen; + # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); + $mac = $auth->finish(); + $out .= $mac; + + # sodium_memzero(&poly1305_state, sizeof poly1305_state); + unset($auth); + + + # XOR_BUF(STATE_INONCE(state), mac, + # crypto_secretstream_xchacha20poly1305_INONCEBYTES); + $st->xorNonce($mac); + + # sodium_increment(STATE_COUNTER(state), + # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); + $st->incrementCounter(); + // Overwrite by reference: + $state = $st->toString(); + + /** @var bool $rekey */ + $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()) { + // DO REKEY + self::secretstream_xchacha20poly1305_rekey($state); + } + # if (outlen_p != NULL) { + # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen; + # } + return $out; + } + + /** + * @param string $state + * @param string $cipher + * @param string $aad + * @return bool|array{0: string, 1: int} + * @throws SodiumException + */ + public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') + { + $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); + + $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher); + # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES; + $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; + $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad); + + # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { + # sodium_misuse(); + # } + 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_Core32_Poly1305_State( + ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) + ); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); + $auth->update($aad); + + # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, + # (0x10 - adlen) & 0xf); + $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); + + + # memset(block, 0, sizeof block); + # block[0] = in[0]; + # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, + # state->nonce, 1U, state->k); + $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( + $cipher[0] . str_repeat("\0", 63), + $st->getCombinedNonce(), + $st->getKey(), + ParagonIE_Sodium_Core32_Util::store64_le(1) + ); + # tag = block[0]; + # block[0] = in[0]; + # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); + $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]); + $block[0] = $cipher[0]; + $auth->update($block); + + + # c = in + (sizeof tag); + # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); + $auth->update(ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($aadlen); + $auth->update($slen); + + # STORE64_LE(slen, (sizeof block) + mlen); + # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); + $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen); + $auth->update($slen); + + # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); + # sodium_memzero(&poly1305_state, sizeof poly1305_state); + $mac = $auth->finish(); + + # stored_mac = c + mlen; + # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) { + # sodium_memzero(mac, sizeof mac); + # return -1; + # } + + $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16); + if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) { + return false; + } + + # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k); + $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) + ); + + # XOR_BUF(STATE_INONCE(state), mac, + # crypto_secretstream_xchacha20poly1305_INONCEBYTES); + $st->xorNonce($mac); + + # sodium_increment(STATE_COUNTER(state), + # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); + $st->incrementCounter(); + + # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || + # sodium_is_zero(STATE_COUNTER(state), + # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { + # crypto_secretstream_xchacha20poly1305_rekey(state); + # } + + // Overwrite by reference: + $state = $st->toString(); + + /** @var bool $rekey */ + $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; + if ($rekey || $st->needsRekey()) { + // DO REKEY + self::secretstream_xchacha20poly1305_rekey($state); + } + return array($out, $tag); + } + + /** + * @param string $state + * @return void + * @throws SodiumException + */ + public static function secretstream_xchacha20poly1305_rekey(&$state) + { + $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); + # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + + # crypto_secretstream_xchacha20poly1305_INONCEBYTES]; + # size_t i; + # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { + # new_key_and_inonce[i] = state->k[i]; + # } + $new_key_and_inonce = $st->getKey(); + + # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { + # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] = + # STATE_INONCE(state)[i]; + # } + $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8); + + # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce, + # sizeof new_key_and_inonce, + # state->nonce, state->k); + + $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( + $new_key_and_inonce, + $st->getCombinedNonce(), + $st->getKey(), + ParagonIE_Sodium_Core32_Util::store64_le(0) + )); + + # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { + # state->k[i] = new_key_and_inonce[i]; + # } + # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { + # STATE_INONCE(state)[i] = + # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i]; + # } + # _crypto_secretstream_xchacha20poly1305_counter_reset(state); + $st->counterReset(); + + $state = $st->toString(); + } + + /** + * Detached Ed25519 signature. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $sk + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function sign_detached($message, $sk) + { + return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk); + } + + /** + * Attached Ed25519 signature. (Returns a signed message.) + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $message + * @param string $sk + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function sign($message, $sk) + { + return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk); + } + + /** + * Opens a signed message. If valid, returns the message. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $signedMessage + * @param string $pk + * @return string + * @throws SodiumException + * @throws TypeError + */ + public static function sign_open($signedMessage, $pk) + { + return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk); + } + + /** + * Verify a detached signature of a given message and public key. + * + * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. + * + * @param string $signature + * @param string $message + * @param string $pk + * @return bool + * @throws SodiumException + * @throws TypeError + */ + public static function sign_verify_detached($signature, $message, $pk) + { + return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk); + } +} |