<?php if (class_exists('ParagonIE_Sodium_Crypto', false)) { return; } abstract class ParagonIE_Sodium_Crypto { 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_Core_Util::strlen($message); $clen = $len - self::aead_chacha20poly1305_ABYTES; $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); $mac = ParagonIE_Sodium_Core_Util::substr($message, $clen, self::aead_chacha20poly1305_ABYTES); $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen); $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(32, $nonce, $key); $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); $computed_mac = $state->finish(); if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } return ParagonIE_Sodium_Core_ChaCha20::streamXorIc($ciphertext, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1)); } public static function aead_chacha20poly1305_encrypt($message = '', $ad = '', $nonce = '', $key = '') { $len = ParagonIE_Sodium_Core_Util::strlen($message); $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(32, $nonce, $key); $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc($message, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1)); $state->update($ad); $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); return $ciphertext . $state->finish(); } public static function aead_chacha20poly1305_ietf_decrypt($message = '', $ad = '', $nonce = '', $key = '') { $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); $len = ParagonIE_Sodium_Core_Util::strlen($message); $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $nonce, $key); $mac = ParagonIE_Sodium_Core_Util::substr($message, $len - self::aead_chacha20poly1305_IETF_ABYTES, self::aead_chacha20poly1305_IETF_ABYTES); $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES); $state = new ParagonIE_Sodium_Core_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_Core_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); $computed_mac = $state->finish(); if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($ciphertext, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1)); } public static function aead_chacha20poly1305_ietf_encrypt($message = '', $ad = '', $nonce = '', $key = '') { $len = ParagonIE_Sodium_Core_Util::strlen($message); $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $nonce, $key); $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($message, $nonce, $key, ParagonIE_Sodium_Core_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_Core_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); return $ciphertext . $state->finish(); } public static function aead_xchacha20poly1305_ietf_decrypt($message = '', $ad = '', $nonce = '', $key = '') { $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key); $nonceLast = "\0\0\0\0" . ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key); $nonceLast = "\0\0\0\0" . ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); } public static function auth($message, $key) { return ParagonIE_Sodium_Core_Util::substr(hash_hmac('sha512', $message, $key, true), 0, 32); } public static function auth_verify($mac, $message, $key) { return ParagonIE_Sodium_Core_Util::hashEquals($mac, self::auth($message, $key)); } public static function box($plaintext, $nonce, $keypair) { $c = self::secretbox($plaintext, $nonce, self::box_beforenm(self::box_secretkey($keypair), self::box_publickey($keypair))); return $c; } 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_Core_Util::substr($message, 0, 32); $ciphertext = ParagonIE_Sodium_Core_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_Core_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_Core_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_Core_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32); } public static function box_secretkey($keypair) { if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) { throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'); } return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32); } public static function box_publickey($keypair) { if (ParagonIE_Sodium_Core_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_Core_Util::substr($keypair, 32, 32); } public static function box_publickey_from_secretkey($sKey) { if (ParagonIE_Sodium_Core_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_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen); ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count()); $out = new SplFixedArray($outlen); $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out); $outArray = $out->toArray(); return ParagonIE_Sodium_Core_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_Core_BLAKE2b::stringToContext($ctx); $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out); $outArray = $out->toArray(); return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); } public static function generichash_init($key = '', $outputLength = 32) { ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength); return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); } public static function generichash_init_salt_personal($key = '', $outputLength = 32, $salt = '', $personal = '') { ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } if (!empty($salt)) { $s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt); } else { $s = null; } if (!empty($salt)) { $p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal); } else { $p = null; } $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p); return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); } public static function generichash_update($ctx, $message) { ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count()); return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context); } public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) { return ParagonIE_Sodium_Compat::crypto_generichash(ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) . $client_pk . $server_pk); } public static function scalarmult($sKey, $pKey) { $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); self::scalarmult_throw_if_zero($q); return $q; } public static function scalarmult_base($secret) { $q = ParagonIE_Sodium_Core_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_Core_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_Core_HSalsa20::hsalsa20($nonce, $key); $block0 = str_repeat("\0", 32); $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor($block0, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey); $c = ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(ParagonIE_Sodium_Core_Util::substr($plaintext, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1, $subkey); } $state = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_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_Core_Util::substr($ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES); $c = ParagonIE_Sodium_Core_Util::substr($ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES); $clen = ParagonIE_Sodium_Core_Util::strlen($c); $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey); $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify($mac, $c, ParagonIE_Sodium_Core_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_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) { $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(ParagonIE_Sodium_Core_Util::substr($c, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1, (string) $subkey); } return $m; } public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) { $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key); $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); $block0 = str_repeat("\0", 32); $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc($block0, $nonceLast, $subkey); $c = ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(ParagonIE_Sodium_Core_Util::substr($plaintext, self::secretbox_xchacha20poly1305_ZEROBYTES), $nonceLast, $subkey, ParagonIE_Sodium_Core_Util::store64_le(1)); } $state = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_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_Core_Util::substr($ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES); $c = ParagonIE_Sodium_Core_Util::substr($ciphertext, self::secretbox_xchacha20poly1305_MACBYTES); $clen = ParagonIE_Sodium_Core_Util::strlen($c); $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key); $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey); $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify($mac, $c, ParagonIE_Sodium_Core_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_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) { $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(ParagonIE_Sodium_Core_Util::substr($c, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core_Util::store64_le(1)); } return $m; } public static function secretstream_xchacha20poly1305_init_push($key) { $out = random_bytes(24); $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key); $state = new ParagonIE_Sodium_Core_SecretStream_State($subkey, ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($header, 0, 16), $key); $state = new ParagonIE_Sodium_Core_SecretStream_State($subkey, ParagonIE_Sodium_Core_Util::substr($header, 16)); $state->counterReset(); return $state->toString(); } public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) { $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); $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'); } $auth = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())); $auth->update($aad); $auth->update(str_repeat("\0", 0x10 - $aadlen & 0xf)); $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1)); $auth->update($block); $out = $block[0]; $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($msg, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(2)); $auth->update($cipher); $out .= $cipher; unset($cipher); $auth->update(str_repeat("\0", 0x10 - 64 + $msglen & 0xf)); $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); $auth->update($slen); $slen = ParagonIE_Sodium_Core_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_Core_SecretStream_State::fromString($state); $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher); $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; $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'); } $auth = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())); $auth->update($aad); $auth->update(str_repeat("\0", 0x10 - $aadlen & 0xf)); $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($cipher[0] . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1)); $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]); $block[0] = $cipher[0]; $auth->update($block); $auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen)); $auth->update(str_repeat("\0", 0x10 - 64 + $msglen & 0xf)); $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); $auth->update($slen); $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); $auth->update($slen); $mac = $auth->finish(); $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16); if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) { return false; } $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_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_Core_SecretStream_State::fromString($state); $new_key_and_inonce = $st->getKey(); $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8); $st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($new_key_and_inonce, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(0))); $st->counterReset(); $state = $st->toString(); } public static function sign_detached($message, $sk) { return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk); } public static function sign($message, $sk) { return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk); } public static function sign_open($signedMessage, $pk) { return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk); } public static function sign_verify_detached($signature, $message, $pk) { return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk); } }