File manager - Edit - /home/aresglob/public_html/wp/wp-includes/images/smilies/src.zip
Back
PK ];m[l��� � SodiumException.phpnu �[��� <?php if (!class_exists('SodiumException', false)) { /** * Class SodiumException */ class SodiumException extends Exception { } } PK ];m[^���� � Crypto.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Crypto', 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_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; /** * 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_Core_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_Core_Util::strlen($ad); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core_Util::substr( $message, $clen, self::aead_chacha20poly1305_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 32, $nonce, $key ); /* Recalculate the Poly1305 authentication tag (MAC): */ $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(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core_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_Core_ChaCha20::streamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core_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_Core_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $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; } /** @var string $ciphertext - Raw encrypted data */ $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(); } /** * 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_Core_Util::strlen($ad); /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStream( 32, $nonce, $key ); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core_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_Core_Util::substr( $message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES ); /* Recalculate the Poly1305 authentication tag (MAC): */ $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("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", (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(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStreamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core_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_Core_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $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; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core_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_Core_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core_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_Core_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_Core_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) { $c = self::secretbox( $plaintext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); return $c; } /** * 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_Core_Util::substr($message, 0, 32); /** @var string $ciphertext (ciphertext + MAC) */ $ciphertext = ParagonIE_Sodium_Core_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_Core_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_Core_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_Core_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core_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_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); } /** * @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_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); } /** * @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_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); } /** * 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_Core_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { /** @var SplFixedArray $k */ $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen); ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count()); /** @var SplFixedArray $out */ $out = new SplFixedArray($outlen); $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core_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_Core_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $out */ $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core_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_Core_BLAKE2b::$iv is initialized 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'); } } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength); return ParagonIE_Sodium_Core_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_Core_BLAKE2b::$iv is initialized 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; } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p); return ParagonIE_Sodium_Core_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_Core_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count()); return ParagonIE_Sodium_Core_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 ParagonIE_Sodium_Compat::crypto_generichash( ParagonIE_Sodium_Compat::crypto_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_Core_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_Core_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_Core_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_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $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); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( $block0, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); /** @var string $c */ $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); /** @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_Core_Util::substr( $ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core_Util::substr( $ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( 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'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core_Util::xorStrings( ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core_Util::substr( $c, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key ); $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( $block0, $nonceLast, $subkey ); /** @var string $c */ $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); /** @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_Core_Util::substr( $ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core_Util::substr( $ciphertext, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 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'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core_Util::xorStrings( ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( ParagonIE_Sodium_Core_Util::substr( $c, self::secretbox_xchacha20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core_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_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) ); # _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_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(); # 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_Core_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_Core_Util::strlen($msg); $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad); if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # if (outlen_p != NULL) { # *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_Core_Poly1305_State( ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStreamXorIc( $msg, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_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_Core_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_Core_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_Core_SecretStream_State::fromString($state); $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher); # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES; $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad); # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # 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_Core_Poly1305_State( ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStreamXorIc( $cipher[0] . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1) ); # tag = block[0]; # block[0] = in[0]; # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $tag = ParagonIE_Sodium_Core_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_Core_Util::substr($cipher, 1, $msglen)); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core_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_Core_Util::substr($cipher, $msglen + 1, 16); if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) { return false; } # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k); $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) ); # 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_Core_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_Core_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_Core_ChaCha20::ietfStreamXorIc( $new_key_and_inonce, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_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_Core_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_Core_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_Core_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_Core_Ed25519::verify_detached($signature, $message, $pk); } } PK ];m[1��� � Compat.phpnu �[��� <?php /** * Libsodium compatibility layer * * This is the only class you should be interfacing with, as a user of * sodium_compat. * * If the PHP extension for libsodium is installed, it will always use that * instead of our implementations. You get better performance and stronger * guarantees against side-channels that way. * * However, if your users don't have the PHP extension installed, we offer a * compatible interface here. It will give you the correct results as if the * PHP extension was installed. It won't be as fast, of course. * * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * * * * Until audited, this is probably not safe to use! DANGER WILL ROBINSON * * * * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * */ if (class_exists('ParagonIE_Sodium_Compat', false)) { return; } class ParagonIE_Sodium_Compat { /** * This parameter prevents the use of the PECL extension. * It should only be used for unit testing. * * @var bool */ public static $disableFallbackForUnitTests = false; /** * Use fast multiplication rather than our constant-time multiplication * implementation. Can be enabled at runtime. Only enable this if you * are absolutely certain that there is no timing leak on your platform. * * @var bool */ public static $fastMult = false; const LIBRARY_MAJOR_VERSION = 9; const LIBRARY_MINOR_VERSION = 1; const LIBRARY_VERSION_MAJOR = 9; const LIBRARY_VERSION_MINOR = 1; const VERSION_STRING = 'polyfill-1.0.8'; // From libsodium const BASE64_VARIANT_ORIGINAL = 1; const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3; const BASE64_VARIANT_URLSAFE = 5; const BASE64_VARIANT_URLSAFE_NO_PADDING = 7; const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32; const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0; const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12; const CRYPTO_AEAD_AES256GCM_ABYTES = 16; const CRYPTO_AEAD_AEGIS128L_KEYBYTES = 16; const CRYPTO_AEAD_AEGIS128L_NSECBYTES = 0; const CRYPTO_AEAD_AEGIS128L_NPUBBYTES = 16; const CRYPTO_AEAD_AEGIS128L_ABYTES = 32; const CRYPTO_AEAD_AEGIS256_KEYBYTES = 32; const CRYPTO_AEAD_AEGIS256_NSECBYTES = 0; const CRYPTO_AEAD_AEGIS256_NPUBBYTES = 32; const CRYPTO_AEAD_AEGIS256_ABYTES = 32; const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32; const CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0; const CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8; const CRYPTO_AEAD_CHACHA20POLY1305_ABYTES = 16; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES = 32; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES = 0; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES = 16; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES = 32; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES = 0; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES = 24; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES = 16; const CRYPTO_AUTH_BYTES = 32; const CRYPTO_AUTH_KEYBYTES = 32; const CRYPTO_BOX_SEALBYTES = 16; const CRYPTO_BOX_SECRETKEYBYTES = 32; const CRYPTO_BOX_PUBLICKEYBYTES = 32; const CRYPTO_BOX_KEYPAIRBYTES = 64; const CRYPTO_BOX_MACBYTES = 16; const CRYPTO_BOX_NONCEBYTES = 24; const CRYPTO_BOX_SEEDBYTES = 32; const CRYPTO_CORE_RISTRETTO255_BYTES = 32; const CRYPTO_CORE_RISTRETTO255_SCALARBYTES = 32; const CRYPTO_CORE_RISTRETTO255_HASHBYTES = 64; const CRYPTO_CORE_RISTRETTO255_NONREDUCEDSCALARBYTES = 64; const CRYPTO_KDF_BYTES_MIN = 16; const CRYPTO_KDF_BYTES_MAX = 64; const CRYPTO_KDF_CONTEXTBYTES = 8; const CRYPTO_KDF_KEYBYTES = 32; const CRYPTO_KX_BYTES = 32; const CRYPTO_KX_PRIMITIVE = 'x25519blake2b'; const CRYPTO_KX_SEEDBYTES = 32; const CRYPTO_KX_KEYPAIRBYTES = 64; const CRYPTO_KX_PUBLICKEYBYTES = 32; const CRYPTO_KX_SECRETKEYBYTES = 32; const CRYPTO_KX_SESSIONKEYBYTES = 32; const CRYPTO_GENERICHASH_BYTES = 32; const CRYPTO_GENERICHASH_BYTES_MIN = 16; const CRYPTO_GENERICHASH_BYTES_MAX = 64; const CRYPTO_GENERICHASH_KEYBYTES = 32; const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16; const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64; const CRYPTO_PWHASH_SALTBYTES = 16; const CRYPTO_PWHASH_STRPREFIX = '$argon2id$'; const CRYPTO_PWHASH_ALG_ARGON2I13 = 1; const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2; const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432; const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4; const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728; const CRYPTO_PWHASH_OPSLIMIT_MODERATE = 6; const CRYPTO_PWHASH_MEMLIMIT_SENSITIVE = 536870912; const CRYPTO_PWHASH_OPSLIMIT_SENSITIVE = 8; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES = 32; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX = '$7$'; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE = 534288; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE = 16777216; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE = 33554432; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE = 1073741824; const CRYPTO_SCALARMULT_BYTES = 32; const CRYPTO_SCALARMULT_SCALARBYTES = 32; const CRYPTO_SCALARMULT_RISTRETTO255_BYTES = 32; const CRYPTO_SCALARMULT_RISTRETTO255_SCALARBYTES = 32; const CRYPTO_SHORTHASH_BYTES = 8; const CRYPTO_SHORTHASH_KEYBYTES = 16; const CRYPTO_SECRETBOX_KEYBYTES = 32; const CRYPTO_SECRETBOX_MACBYTES = 16; const CRYPTO_SECRETBOX_NONCEBYTES = 24; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80; const CRYPTO_SIGN_BYTES = 64; const CRYPTO_SIGN_SEEDBYTES = 32; const CRYPTO_SIGN_PUBLICKEYBYTES = 32; const CRYPTO_SIGN_SECRETKEYBYTES = 64; const CRYPTO_SIGN_KEYPAIRBYTES = 96; const CRYPTO_STREAM_KEYBYTES = 32; const CRYPTO_STREAM_NONCEBYTES = 24; const CRYPTO_STREAM_XCHACHA20_KEYBYTES = 32; const CRYPTO_STREAM_XCHACHA20_NONCEBYTES = 24; /** * Add two numbers (little-endian unsigned), storing the value in the first * parameter. * * This mutates $val. * * @param string $val * @param string $addv * @return void * @throws SodiumException */ public static function add( #[\SensitiveParameter] &$val, #[\SensitiveParameter] $addv ) { $val_len = ParagonIE_Sodium_Core_Util::strlen($val); $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv); if ($val_len !== $addv_len) { throw new SodiumException('values must have the same length'); } $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val); $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv); $c = 0; for ($i = 0; $i < $val_len; $i++) { $c += ($A[$i] + $B[$i]); $A[$i] = ($c & 0xff); $c >>= 8; } $val = ParagonIE_Sodium_Core_Util::intArrayToString($A); } /** * @param string $encoded * @param int $variant * @param string $ignore * @return string * @throws SodiumException */ public static function base642bin( #[\SensitiveParameter] $encoded, $variant, $ignore = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1); /** @var string $encoded */ $encoded = (string) $encoded; if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) { return ''; } // Just strip before decoding if (!empty($ignore)) { $encoded = str_replace($ignore, '', $encoded); } try { switch ($variant) { case self::BASE64_VARIANT_ORIGINAL: return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true); case self::BASE64_VARIANT_ORIGINAL_NO_PADDING: return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false); case self::BASE64_VARIANT_URLSAFE: return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true); case self::BASE64_VARIANT_URLSAFE_NO_PADDING: return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false); default: throw new SodiumException('invalid base64 variant identifier'); } } catch (Exception $ex) { if ($ex instanceof SodiumException) { throw $ex; } throw new SodiumException('invalid base64 string'); } } /** * @param string $decoded * @param int $variant * @return string * @throws SodiumException */ public static function bin2base64( #[\SensitiveParameter] $decoded, $variant ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1); /** @var string $decoded */ $decoded = (string) $decoded; if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) { return ''; } switch ($variant) { case self::BASE64_VARIANT_ORIGINAL: return ParagonIE_Sodium_Core_Base64_Original::encode($decoded); case self::BASE64_VARIANT_ORIGINAL_NO_PADDING: return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded); case self::BASE64_VARIANT_URLSAFE: return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded); case self::BASE64_VARIANT_URLSAFE_NO_PADDING: return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded); default: throw new SodiumException('invalid base64 variant identifier'); } } /** * Cache-timing-safe implementation of bin2hex(). * * @param string $string A string (probably raw binary) * @return string A hexadecimal-encoded string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function bin2hex( #[\SensitiveParameter] $string ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1); if (self::useNewSodiumAPI()) { return (string) sodium_bin2hex($string); } if (self::use_fallback('bin2hex')) { return (string) call_user_func('\\Sodium\\bin2hex', $string); } return ParagonIE_Sodium_Core_Util::bin2hex($string); } /** * Compare two strings, in constant-time. * Compared to memcmp(), compare() is more useful for sorting. * * @param string $left The left operand; must be a string * @param string $right The right operand; must be a string * @return int If < 0 if the left operand is less than the right * If = 0 if both strings are equal * If > 0 if the right operand is less than the left * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function compare( #[\SensitiveParameter] $left, #[\SensitiveParameter] $right ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2); if (self::useNewSodiumAPI()) { return (int) sodium_compare($left, $right); } if (self::use_fallback('compare')) { return (int) call_user_func('\\Sodium\\compare', $left, $right); } return ParagonIE_Sodium_Core_Util::compare($left, $right); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * AEGIS-128L * * @param string $ciphertext Encrypted message (with MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 32 bytes * @param string $key Encryption key * * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_aegis128l_decrypt( $ciphertext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS128L_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS_128L_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS128L_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long'); } $ct_length = ParagonIE_Sodium_Core_Util::strlen($ciphertext); if ($ct_length < self::CRYPTO_AEAD_AEGIS128L_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_AEGIS128L_ABYTES long'); } $ct = ParagonIE_Sodium_Core_Util::substr( $ciphertext, 0, $ct_length - self::CRYPTO_AEAD_AEGIS128L_ABYTES ); $tag = ParagonIE_Sodium_Core_Util::substr( $ciphertext, $ct_length - self::CRYPTO_AEAD_AEGIS128L_ABYTES, self::CRYPTO_AEAD_AEGIS128L_ABYTES ); return ParagonIE_Sodium_Core_AEGIS128L::decrypt($ct, $tag, $assocData, $key, $nonce); } /** * Authenticated Encryption with Associated Data: Encryption * * Algorithm: * AEGIS-128L * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 32 bytes * @param string $key Encryption key * * @return string Ciphertext with 32-byte authentication tag appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_aegis128l_encrypt( #[\SensitiveParameter] $plaintext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS128L_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS128L_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long'); } list($ct, $tag) = ParagonIE_Sodium_Core_AEGIS128L::encrypt($plaintext, $assocData, $key, $nonce); return $ct . $tag; } /** * Return a secure random key for use with the AEGIS-128L * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_aegis128l_keygen() { return random_bytes(self::CRYPTO_AEAD_AEGIS128L_KEYBYTES); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * AEGIS-256 * * @param string $ciphertext Encrypted message (with MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 32 bytes * @param string $key Encryption key * * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_aegis256_decrypt( $ciphertext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS256_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS256_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS256_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS256_KEYBYTES long'); } $ct_length = ParagonIE_Sodium_Core_Util::strlen($ciphertext); if ($ct_length < self::CRYPTO_AEAD_AEGIS256_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_AEGIS256_ABYTES long'); } $ct = ParagonIE_Sodium_Core_Util::substr( $ciphertext, 0, $ct_length - self::CRYPTO_AEAD_AEGIS256_ABYTES ); $tag = ParagonIE_Sodium_Core_Util::substr( $ciphertext, $ct_length - self::CRYPTO_AEAD_AEGIS256_ABYTES, self::CRYPTO_AEAD_AEGIS256_ABYTES ); return ParagonIE_Sodium_Core_AEGIS256::decrypt($ct, $tag, $assocData, $key, $nonce); } /** * Authenticated Encryption with Associated Data: Encryption * * Algorithm: * AEGIS-256 * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 32 bytes * @param string $key Encryption key * * @return string Ciphertext with 32-byte authentication tag appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_aegis256_encrypt( #[\SensitiveParameter] $plaintext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS256_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS256_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long'); } list($ct, $tag) = ParagonIE_Sodium_Core_AEGIS256::encrypt($plaintext, $assocData, $key, $nonce); return $ct . $tag; } /** * Return a secure random key for use with the AEGIS-256 * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_aegis256_keygen() { return random_bytes(self::CRYPTO_AEAD_AEGIS256_KEYBYTES); } /** * Is AES-256-GCM even available to use? * * @return bool * @psalm-suppress UndefinedFunction * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_aes256gcm_is_available() { if (self::useNewSodiumAPI()) { return sodium_crypto_aead_aes256gcm_is_available(); } if (self::use_fallback('crypto_aead_aes256gcm_is_available')) { return call_user_func('\\Sodium\\crypto_aead_aes256gcm_is_available'); } if (PHP_VERSION_ID < 70100) { // OpenSSL doesn't support AEAD before 7.1.0 return false; } if (!is_callable('openssl_encrypt') || !is_callable('openssl_decrypt')) { // OpenSSL isn't installed return false; } return (bool) in_array('aes-256-gcm', openssl_get_cipher_methods()); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * AES-256-GCM * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string|bool The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_aes256gcm_decrypt( $ciphertext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { if (!self::crypto_aead_aes256gcm_is_available()) { throw new SodiumException('AES-256-GCM is not available'); } ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_AES256GCM_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_AES256GCM_ABYTES long'); } if (!is_callable('openssl_decrypt')) { throw new SodiumException('The OpenSSL extension is not installed, or openssl_decrypt() is not available'); } /** @var string $ctext */ $ctext = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, -self::CRYPTO_AEAD_AES256GCM_ABYTES); /** @var string $authTag */ $authTag = ParagonIE_Sodium_Core_Util::substr($ciphertext, -self::CRYPTO_AEAD_AES256GCM_ABYTES, 16); return openssl_decrypt( $ctext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $nonce, $authTag, $assocData ); } /** * Authenticated Encryption with Associated Data: Encryption * * Algorithm: * AES-256-GCM * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string Ciphertext with a 16-byte GCM message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_aes256gcm_encrypt( #[\SensitiveParameter] $plaintext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { if (!self::crypto_aead_aes256gcm_is_available()) { throw new SodiumException('AES-256-GCM is not available'); } ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long'); } if (!is_callable('openssl_encrypt')) { throw new SodiumException('The OpenSSL extension is not installed, or openssl_encrypt() is not available'); } $authTag = ''; $ciphertext = openssl_encrypt( $plaintext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $nonce, $authTag, $assocData ); return $ciphertext . $authTag; } /** * Return a secure random key for use with the AES-256-GCM * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_aes256gcm_keygen() { return random_bytes(self::CRYPTO_AEAD_AES256GCM_KEYBYTES); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * ChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_chacha20poly1305_decrypt( $ciphertext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_aead_chacha20poly1305_decrypt( $ciphertext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_decrypt')) { return call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_decrypt', $ciphertext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_decrypt( $ciphertext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt( $ciphertext, $assocData, $nonce, $key ); } /** * Authenticated Encryption with Associated Data * * Algorithm: * ChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string Ciphertext with a 16-byte Poly1305 message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_chacha20poly1305_encrypt( #[\SensitiveParameter] $plaintext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_aead_chacha20poly1305_encrypt( $plaintext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_encrypt')) { return (string) call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_encrypt', $plaintext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_encrypt( $plaintext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_encrypt( $plaintext, $assocData, $nonce, $key ); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * ChaCha20-Poly1305 * * IETF mode uses a 96-bit random nonce with a 32-bit counter. * Regular mode uses a 64-bit random nonce with a 64-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 12 bytes * @param string $key Encryption key * * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_chacha20poly1305_ietf_decrypt( $ciphertext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_aead_chacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_decrypt')) { return call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_ietf_decrypt', $ciphertext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } /** * Return a secure random key for use with the ChaCha20-Poly1305 * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_chacha20poly1305_keygen() { return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES); } /** * Authenticated Encryption with Associated Data * * Algorithm: * ChaCha20-Poly1305 * * IETF mode uses a 96-bit random nonce with a 32-bit counter. * Regular mode uses a 64-bit random nonce with a 64-bit counter. * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string Ciphertext with a 16-byte Poly1305 message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_chacha20poly1305_ietf_encrypt( #[\SensitiveParameter] $plaintext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); if (!is_null($assocData)) { ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); } ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_aead_chacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_encrypt')) { return (string) call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_ietf_encrypt', $plaintext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } /** * Return a secure random key for use with the ChaCha20-Poly1305 * symmetric AEAD interface. (IETF version) * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_chacha20poly1305_ietf_keygen() { return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * XChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * @param bool $dontFallback Don't fallback to ext/sodium * * @return string|bool The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_xchacha20poly1305_ietf_decrypt( $ciphertext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '', $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); if (!is_null($assocData)) { ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); } else { $assocData = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES long'); } if (self::useNewSodiumAPI() && !$dontFallback) { if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) { return sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } /** * Authenticated Encryption with Associated Data * * Algorithm: * XChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * @param bool $dontFallback Don't fallback to ext/sodium * * @return string Ciphertext with a 16-byte Poly1305 message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_xchacha20poly1305_ietf_encrypt( #[\SensitiveParameter] $plaintext = '', $assocData = '', $nonce = '', #[\SensitiveParameter] $key = '', $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); if (!is_null($assocData)) { ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); } else { $assocData = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_KEYBYTES long'); } if (self::useNewSodiumAPI() && !$dontFallback) { if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) { return sodium_crypto_aead_xchacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } /** * Return a secure random key for use with the XChaCha20-Poly1305 * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_xchacha20poly1305_ietf_keygen() { return random_bytes(self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES); } /** * Authenticate a message. Uses symmetric-key cryptography. * * Algorithm: * HMAC-SHA512-256. Which is HMAC-SHA-512 truncated to 256 bits. * Not to be confused with HMAC-SHA-512/256 which would use the * SHA-512/256 hash function (uses different initial parameters * but still truncates to 256 bits to sidestep length-extension * attacks). * * @param string $message Message to be authenticated * @param string $key Symmetric authentication key * @return string Message authentication code * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_auth( $message, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_AUTH_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_auth($message, $key); } if (self::use_fallback('crypto_auth')) { return (string) call_user_func('\\Sodium\\crypto_auth', $message, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::auth($message, $key); } return ParagonIE_Sodium_Crypto::auth($message, $key); } /** * @return string * @throws Exception * @throws Error */ public static function crypto_auth_keygen() { return random_bytes(self::CRYPTO_AUTH_KEYBYTES); } /** * Verify the MAC of a message previously authenticated with crypto_auth. * * @param string $mac Message authentication code * @param string $message Message whose authenticity you are attempting to * verify (with a given MAC and key) * @param string $key Symmetric authentication key * @return bool TRUE if authenticated, FALSE otherwise * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_auth_verify( $mac, $message, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($mac, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($mac) !== self::CRYPTO_AUTH_BYTES) { throw new SodiumException('Argument 1 must be CRYPTO_AUTH_BYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_AUTH_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (bool) sodium_crypto_auth_verify($mac, $message, $key); } if (self::use_fallback('crypto_auth_verify')) { return (bool) call_user_func('\\Sodium\\crypto_auth_verify', $mac, $message, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::auth_verify($mac, $message, $key); } return ParagonIE_Sodium_Crypto::auth_verify($mac, $message, $key); } /** * Authenticated asymmetric-key encryption. Both the sender and recipient * may decrypt messages. * * Algorithm: X25519-XSalsa20-Poly1305. * X25519: Elliptic-Curve Diffie Hellman over Curve25519. * XSalsa20: Extended-nonce variant of salsa20. * Poyl1305: Polynomial MAC for one-time message authentication. * * @param string $plaintext The message to be encrypted * @param string $nonce A Number to only be used Once; must be 24 bytes * @param string $keypair Your secret key and your recipient's public key * @return string Ciphertext with 16-byte Poly1305 MAC * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box( $plaintext, $nonce, #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box($plaintext, $nonce, $keypair); } if (self::use_fallback('crypto_box')) { return (string) call_user_func('\\Sodium\\crypto_box', $plaintext, $nonce, $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box($plaintext, $nonce, $keypair); } return ParagonIE_Sodium_Crypto::box($plaintext, $nonce, $keypair); } /** * Anonymous public-key encryption. Only the recipient may decrypt messages. * * Algorithm: X25519-XSalsa20-Poly1305, as with crypto_box. * The sender's X25519 keypair is ephemeral. * Nonce is generated from the BLAKE2b hash of both public keys. * * This provides ciphertext integrity. * * @param string $plaintext Message to be sealed * @param string $publicKey Your recipient's public key * @return string Sealed message that only your recipient can * decrypt * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_seal( #[\SensitiveParameter] $plaintext, $publicKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_seal($plaintext, $publicKey); } if (self::use_fallback('crypto_box_seal')) { return (string) call_user_func('\\Sodium\\crypto_box_seal', $plaintext, $publicKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_seal($plaintext, $publicKey); } return ParagonIE_Sodium_Crypto::box_seal($plaintext, $publicKey); } /** * Opens a message encrypted with crypto_box_seal(). Requires * the recipient's keypair (sk || pk) to decrypt successfully. * * This validates ciphertext integrity. * * @param string $ciphertext Sealed message to be opened * @param string $keypair Your crypto_box keypair * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_box_seal_open( $ciphertext, #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_box_seal_open($ciphertext, $keypair); } if (self::use_fallback('crypto_box_seal_open')) { return call_user_func('\\Sodium\\crypto_box_seal_open', $ciphertext, $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_seal_open($ciphertext, $keypair); } return ParagonIE_Sodium_Crypto::box_seal_open($ciphertext, $keypair); } /** * Generate a new random X25519 keypair. * * @return string A 64-byte string; the first 32 are your secret key, while * the last 32 are your public key. crypto_box_secretkey() * and crypto_box_publickey() exist to separate them so you * don't accidentally get them mixed up! * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_keypair() { if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_keypair(); } if (self::use_fallback('crypto_box_keypair')) { return (string) call_user_func('\\Sodium\\crypto_box_keypair'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_keypair(); } return ParagonIE_Sodium_Crypto::box_keypair(); } /** * Combine two keys into a keypair for use in library methods that expect * a keypair. This doesn't necessarily have to be the same person's keys. * * @param string $secretKey Secret key * @param string $publicKey Public key * @return string Keypair * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_keypair_from_secretkey_and_publickey( #[\SensitiveParameter] $secretKey, $publicKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey); } if (self::use_fallback('crypto_box_keypair_from_secretkey_and_publickey')) { return (string) call_user_func('\\Sodium\\crypto_box_keypair_from_secretkey_and_publickey', $secretKey, $publicKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey); } return ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey); } /** * Decrypt a message previously encrypted with crypto_box(). * * @param string $ciphertext Encrypted message * @param string $nonce Number to only be used Once; must be 24 bytes * @param string $keypair Your secret key and the sender's public key * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_box_open( $ciphertext, $nonce, #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_BOX_MACBYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_BOX_MACBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_box_open($ciphertext, $nonce, $keypair); } if (self::use_fallback('crypto_box_open')) { return call_user_func('\\Sodium\\crypto_box_open', $ciphertext, $nonce, $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_open($ciphertext, $nonce, $keypair); } return ParagonIE_Sodium_Crypto::box_open($ciphertext, $nonce, $keypair); } /** * Extract the public key from a crypto_box keypair. * * @param string $keypair Keypair containing secret and public key * @return string Your crypto_box public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_publickey( #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_publickey($keypair); } if (self::use_fallback('crypto_box_publickey')) { return (string) call_user_func('\\Sodium\\crypto_box_publickey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_publickey($keypair); } return ParagonIE_Sodium_Crypto::box_publickey($keypair); } /** * Calculate the X25519 public key from a given X25519 secret key. * * @param string $secretKey Any X25519 secret key * @return string The corresponding X25519 public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_publickey_from_secretkey( #[\SensitiveParameter] $secretKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_publickey_from_secretkey($secretKey); } if (self::use_fallback('crypto_box_publickey_from_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_box_publickey_from_secretkey', $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_publickey_from_secretkey($secretKey); } return ParagonIE_Sodium_Crypto::box_publickey_from_secretkey($secretKey); } /** * Extract the secret key from a crypto_box keypair. * * @param string $keypair * @return string Your crypto_box secret key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_secretkey( #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_secretkey($keypair); } if (self::use_fallback('crypto_box_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_box_secretkey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_secretkey($keypair); } return ParagonIE_Sodium_Crypto::box_secretkey($keypair); } /** * Generate an X25519 keypair from a seed. * * @param string $seed * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress UndefinedFunction */ public static function crypto_box_seed_keypair( #[\SensitiveParameter] $seed ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_seed_keypair($seed); } if (self::use_fallback('crypto_box_seed_keypair')) { return (string) call_user_func('\\Sodium\\crypto_box_seed_keypair', $seed); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_seed_keypair($seed); } return ParagonIE_Sodium_Crypto::box_seed_keypair($seed); } /** * Calculates a BLAKE2b hash, with an optional key. * * @param string $message The message to be hashed * @param string|null $key If specified, must be a string between 16 * and 64 bytes long * @param int $length Output length in bytes; must be between 16 * and 64 (default = 32) * @return string Raw binary * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_generichash( $message, #[\SensitiveParameter] $key = '', $length = self::CRYPTO_GENERICHASH_BYTES ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); if (is_null($key)) { $key = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 3); /* Input validation: */ if (!empty($key)) { if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); } } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_generichash($message, $key, $length); } if (self::use_fallback('crypto_generichash')) { return (string) call_user_func('\\Sodium\\crypto_generichash', $message, $key, $length); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::generichash($message, $key, $length); } return ParagonIE_Sodium_Crypto::generichash($message, $key, $length); } /** * Get the final BLAKE2b hash output for a given context. * * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init(). * @param int $length Hash output size. * @return string Final BLAKE2b hash. * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress ReferenceConstraintViolation * @psalm-suppress ConflictingReferenceConstraint */ public static function crypto_generichash_final( #[\SensitiveParameter] &$ctx, $length = self::CRYPTO_GENERICHASH_BYTES ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); if (self::useNewSodiumAPI()) { return sodium_crypto_generichash_final($ctx, $length); } if (self::use_fallback('crypto_generichash_final')) { $func = '\\Sodium\\crypto_generichash_final'; return (string) $func($ctx, $length); } if ($length < 1) { try { self::memzero($ctx); } catch (SodiumException $ex) { unset($ctx); } return ''; } if (PHP_INT_SIZE === 4) { $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length); } else { $result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length); } try { self::memzero($ctx); } catch (SodiumException $ex) { unset($ctx); } return $result; } /** * Initialize a BLAKE2b hashing context, for use in a streaming interface. * * @param string|null $key If specified must be a string between 16 and 64 bytes * @param int $length The size of the desired hash output * @return string A BLAKE2 hashing context, encoded as a string * (To be 100% compatible with ext/libsodium) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_generichash_init( #[\SensitiveParameter] $key = '', $length = self::CRYPTO_GENERICHASH_BYTES ) { /* Type checks: */ if (is_null($key)) { $key = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); /* Input validation: */ if (!empty($key)) { if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); } } if (self::useNewSodiumAPI()) { return sodium_crypto_generichash_init($key, $length); } if (self::use_fallback('crypto_generichash_init')) { return (string) call_user_func('\\Sodium\\crypto_generichash_init', $key, $length); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::generichash_init($key, $length); } return ParagonIE_Sodium_Crypto::generichash_init($key, $length); } /** * Initialize a BLAKE2b hashing context, for use in a streaming interface. * * @param string|null $key If specified must be a string between 16 and 64 bytes * @param int $length The size of the desired hash output * @param string $salt Salt (up to 16 bytes) * @param string $personal Personalization string (up to 16 bytes) * @return string A BLAKE2 hashing context, encoded as a string * (To be 100% compatible with ext/libsodium) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_generichash_init_salt_personal( #[\SensitiveParameter] $key = '', $length = self::CRYPTO_GENERICHASH_BYTES, $salt = '', $personal = '' ) { /* Type checks: */ if (is_null($key)) { $key = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4); $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT); $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT); /* Input validation: */ if (!empty($key)) { /* if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); } */ if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); } } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal); } return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal); } /** * Update a BLAKE2b hashing context with additional data. * * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init(). * $ctx is passed by reference and gets updated in-place. * @param-out string $ctx * @param string $message The message to append to the existing hash state. * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress ReferenceConstraintViolation */ public static function crypto_generichash_update( #[\SensitiveParameter] &$ctx, $message ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2); if (self::useNewSodiumAPI()) { sodium_crypto_generichash_update($ctx, $message); return; } if (self::use_fallback('crypto_generichash_update')) { $func = '\\Sodium\\crypto_generichash_update'; $func($ctx, $message); return; } if (PHP_INT_SIZE === 4) { $ctx = ParagonIE_Sodium_Crypto32::generichash_update($ctx, $message); } else { $ctx = ParagonIE_Sodium_Crypto::generichash_update($ctx, $message); } } /** * @return string * @throws Exception * @throws Error */ public static function crypto_generichash_keygen() { return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES); } /** * @param int $subkey_len * @param int $subkey_id * @param string $context * @param string $key * @return string * @throws SodiumException */ public static function crypto_kdf_derive_from_key( $subkey_len, $subkey_id, $context, #[\SensitiveParameter] $key ) { ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); $subkey_id = (int) $subkey_id; $subkey_len = (int) $subkey_len; $context = (string) $context; $key = (string) $key; if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) { throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN'); } if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) { throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX'); } if ($subkey_id < 0) { throw new SodiumException('subkey_id cannot be negative'); } if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) { throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) { throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes'); } $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id); $state = self::crypto_generichash_init_salt_personal( $key, $subkey_len, $salt, $context ); return self::crypto_generichash_final($state, $subkey_len); } /** * @return string * @throws Exception * @throws Error */ public static function crypto_kdf_keygen() { return random_bytes(self::CRYPTO_KDF_KEYBYTES); } /** * Perform a key exchange, between a designated client and a server. * * Typically, you would designate one machine to be the client and the * other to be the server. The first two keys are what you'd expect for * scalarmult() below, but the latter two public keys don't swap places. * * | ALICE | BOB | * | Client | Server | * |--------------------------------|-------------------------------------| * | shared = crypto_kx( | shared = crypto_kx( | * | alice_sk, | bob_sk, | <- contextual * | bob_pk, | alice_pk, | <- contextual * | alice_pk, | alice_pk, | <----- static * | bob_pk | bob_pk | <----- static * | ) | ) | * * They are used along with the scalarmult product to generate a 256-bit * BLAKE2b hash unique to the client and server keys. * * @param string $my_secret * @param string $their_public * @param string $client_public * @param string $server_public * @param bool $dontFallback * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_kx( #[\SensitiveParameter] $my_secret, $their_public, $client_public, $server_public, $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($client_public, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($server_public, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($my_secret) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($their_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($client_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($server_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 4 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI() && !$dontFallback) { if (is_callable('sodium_crypto_kx')) { return (string) sodium_crypto_kx( $my_secret, $their_public, $client_public, $server_public ); } } if (self::use_fallback('crypto_kx')) { return (string) call_user_func( '\\Sodium\\crypto_kx', $my_secret, $their_public, $client_public, $server_public ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::keyExchange( $my_secret, $their_public, $client_public, $server_public ); } return ParagonIE_Sodium_Crypto::keyExchange( $my_secret, $their_public, $client_public, $server_public ); } /** * @param string $seed * @return string * @throws SodiumException */ public static function crypto_kx_seed_keypair( #[\SensitiveParameter] $seed ) { ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); $seed = (string) $seed; if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) { throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes'); } $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES); $pk = self::crypto_scalarmult_base($sk); return $sk . $pk; } /** * @return string * @throws Exception */ public static function crypto_kx_keypair() { $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES); $pk = self::crypto_scalarmult_base($sk); return $sk . $pk; } /** * @param string $keypair * @param string $serverPublicKey * @return array{0: string, 1: string} * @throws SodiumException */ public static function crypto_kx_client_session_keys( #[\SensitiveParameter] $keypair, $serverPublicKey ) { ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2); $keypair = (string) $keypair; $serverPublicKey = (string) $serverPublicKey; if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) { throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) { throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes'); } $sk = self::crypto_kx_secretkey($keypair); $pk = self::crypto_kx_publickey($keypair); $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2); self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey)); self::crypto_generichash_update($h, $pk); self::crypto_generichash_update($h, $serverPublicKey); $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2); return array( ParagonIE_Sodium_Core_Util::substr( $sessionKeys, 0, self::CRYPTO_KX_SESSIONKEYBYTES ), ParagonIE_Sodium_Core_Util::substr( $sessionKeys, self::CRYPTO_KX_SESSIONKEYBYTES, self::CRYPTO_KX_SESSIONKEYBYTES ) ); } /** * @param string $keypair * @param string $clientPublicKey * @return array{0: string, 1: string} * @throws SodiumException */ public static function crypto_kx_server_session_keys( #[\SensitiveParameter] $keypair, $clientPublicKey ) { ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2); $keypair = (string) $keypair; $clientPublicKey = (string) $clientPublicKey; if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) { throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) { throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes'); } $sk = self::crypto_kx_secretkey($keypair); $pk = self::crypto_kx_publickey($keypair); $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2); self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey)); self::crypto_generichash_update($h, $clientPublicKey); self::crypto_generichash_update($h, $pk); $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2); return array( ParagonIE_Sodium_Core_Util::substr( $sessionKeys, self::CRYPTO_KX_SESSIONKEYBYTES, self::CRYPTO_KX_SESSIONKEYBYTES ), ParagonIE_Sodium_Core_Util::substr( $sessionKeys, 0, self::CRYPTO_KX_SESSIONKEYBYTES ) ); } /** * @param string $kp * @return string * @throws SodiumException */ public static function crypto_kx_secretkey( #[\SensitiveParameter] $kp ) { return ParagonIE_Sodium_Core_Util::substr( $kp, 0, self::CRYPTO_KX_SECRETKEYBYTES ); } /** * @param string $kp * @return string * @throws SodiumException */ public static function crypto_kx_publickey($kp) { return ParagonIE_Sodium_Core_Util::substr( $kp, self::CRYPTO_KX_SECRETKEYBYTES, self::CRYPTO_KX_PUBLICKEYBYTES ); } /** * @param int $outlen * @param string $passwd * @param string $salt * @param int $opslimit * @param int $memlimit * @param int|null $alg * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_pwhash( $outlen, #[\SensitiveParameter] $passwd, $salt, $opslimit, $memlimit, $alg = null ) { ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5); if (self::useNewSodiumAPI()) { if (!is_null($alg)) { ParagonIE_Sodium_Core_Util::declareScalarType($alg, 'int', 6); return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg); } return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit); } if (self::use_fallback('crypto_pwhash')) { return (string) call_user_func('\\Sodium\\crypto_pwhash', $outlen, $passwd, $salt, $opslimit, $memlimit); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' ); } /** * !Exclusive to sodium_compat! * * This returns TRUE if the native crypto_pwhash API is available by libsodium. * This returns FALSE if only sodium_compat is available. * * @return bool */ public static function crypto_pwhash_is_available() { if (self::useNewSodiumAPI()) { return true; } if (self::use_fallback('crypto_pwhash')) { return true; } return false; } /** * @param string $passwd * @param int $opslimit * @param int $memlimit * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_pwhash_str( #[\SensitiveParameter] $passwd, $opslimit, $memlimit ) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); if (self::useNewSodiumAPI()) { return sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit); } if (self::use_fallback('crypto_pwhash_str')) { return (string) call_user_func('\\Sodium\\crypto_pwhash_str', $passwd, $opslimit, $memlimit); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' ); } /** * Do we need to rehash this password? * * @param string $hash * @param int $opslimit * @param int $memlimit * @return bool * @throws SodiumException */ public static function crypto_pwhash_str_needs_rehash( #[\SensitiveParameter] $hash, $opslimit, $memlimit ) { ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); // Just grab the first 4 pieces. $pieces = explode('$', (string) $hash); $prefix = implode('$', array_slice($pieces, 0, 4)); // Rebuild the expected header. /** @var int $ops */ $ops = (int) $opslimit; /** @var int $mem */ $mem = (int) $memlimit >> 10; $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1'; // Do they match? If so, we don't need to rehash, so return false. return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix); } /** * @param string $passwd * @param string $hash * @return bool * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_pwhash_str_verify( #[\SensitiveParameter] $passwd, #[\SensitiveParameter] $hash ) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2); if (self::useNewSodiumAPI()) { return (bool) sodium_crypto_pwhash_str_verify($passwd, $hash); } if (self::use_fallback('crypto_pwhash_str_verify')) { return (bool) call_user_func('\\Sodium\\crypto_pwhash_str_verify', $passwd, $hash); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' ); } /** * @param int $outlen * @param string $passwd * @param string $salt * @param int $opslimit * @param int $memlimit * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_pwhash_scryptsalsa208sha256( $outlen, #[\SensitiveParameter] $passwd, $salt, $opslimit, $memlimit ) { ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5); if (self::useNewSodiumAPI()) { return (string) sodium_crypto_pwhash_scryptsalsa208sha256( (int) $outlen, (string) $passwd, (string) $salt, (int) $opslimit, (int) $memlimit ); } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) { return (string) call_user_func( '\\Sodium\\crypto_pwhash_scryptsalsa208sha256', (int) $outlen, (string) $passwd, (string) $salt, (int) $opslimit, (int) $memlimit ); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP' ); } /** * !Exclusive to sodium_compat! * * This returns TRUE if the native crypto_pwhash API is available by libsodium. * This returns FALSE if only sodium_compat is available. * * @return bool */ public static function crypto_pwhash_scryptsalsa208sha256_is_available() { if (self::useNewSodiumAPI()) { return true; } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) { return true; } return false; } /** * @param string $passwd * @param int $opslimit * @param int $memlimit * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_pwhash_scryptsalsa208sha256_str( #[\SensitiveParameter] $passwd, $opslimit, $memlimit ) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); if (self::useNewSodiumAPI()) { return (string) sodium_crypto_pwhash_scryptsalsa208sha256_str( (string) $passwd, (int) $opslimit, (int) $memlimit ); } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str')) { return (string) call_user_func( '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str', (string) $passwd, (int) $opslimit, (int) $memlimit ); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP' ); } /** * @param string $passwd * @param string $hash * @return bool * @throws SodiumException * @throws TypeError */ public static function crypto_pwhash_scryptsalsa208sha256_str_verify( #[\SensitiveParameter] $passwd, #[\SensitiveParameter] $hash ) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2); if (self::useNewSodiumAPI()) { return (bool) sodium_crypto_pwhash_scryptsalsa208sha256_str_verify( (string) $passwd, (string) $hash ); } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str_verify')) { return (bool) call_user_func( '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str_verify', (string) $passwd, (string) $hash ); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP' ); } /** * Calculate the shared secret between your secret key and your * recipient's public key. * * Algorithm: X25519 (ECDH over Curve25519) * * @param string $secretKey * @param string $publicKey * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_scalarmult( #[\SensitiveParameter] $secretKey, $publicKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_scalarmult($secretKey, $publicKey); } if (self::use_fallback('crypto_scalarmult')) { return (string) call_user_func('\\Sodium\\crypto_scalarmult', $secretKey, $publicKey); } /* Output validation: Forbid all-zero keys */ if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) { throw new SodiumException('Zero secret key is not allowed'); } if (ParagonIE_Sodium_Core_Util::hashEquals($publicKey, str_repeat("\0", self::CRYPTO_BOX_PUBLICKEYBYTES))) { throw new SodiumException('Zero public key is not allowed'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::scalarmult($secretKey, $publicKey); } return ParagonIE_Sodium_Crypto::scalarmult($secretKey, $publicKey); } /** * Calculate an X25519 public key from an X25519 secret key. * * @param string $secretKey * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress TooFewArguments * @psalm-suppress MixedArgument */ public static function crypto_scalarmult_base( #[\SensitiveParameter] $secretKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_scalarmult_base($secretKey); } if (self::use_fallback('crypto_scalarmult_base')) { return (string) call_user_func('\\Sodium\\crypto_scalarmult_base', $secretKey); } if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) { throw new SodiumException('Zero secret key is not allowed'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::scalarmult_base($secretKey); } return ParagonIE_Sodium_Crypto::scalarmult_base($secretKey); } /** * Authenticated symmetric-key encryption. * * Algorithm: XSalsa20-Poly1305 * * @param string $plaintext The message you're encrypting * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Ciphertext with Poly1305 MAC * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_secretbox( #[\SensitiveParameter] $plaintext, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_secretbox($plaintext, $nonce, $key); } if (self::use_fallback('crypto_secretbox')) { return (string) call_user_func('\\Sodium\\crypto_secretbox', $plaintext, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox($plaintext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox($plaintext, $nonce, $key); } /** * Decrypts a message previously encrypted with crypto_secretbox(). * * @param string $ciphertext Ciphertext with Poly1305 MAC * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_secretbox_open( $ciphertext, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_secretbox_open($ciphertext, $nonce, $key); } if (self::use_fallback('crypto_secretbox_open')) { return call_user_func('\\Sodium\\crypto_secretbox_open', $ciphertext, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox_open($ciphertext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox_open($ciphertext, $nonce, $key); } /** * Return a secure random key for use with crypto_secretbox * * @return string * @throws Exception * @throws Error */ public static function crypto_secretbox_keygen() { return random_bytes(self::CRYPTO_SECRETBOX_KEYBYTES); } /** * Authenticated symmetric-key encryption. * * Algorithm: XChaCha20-Poly1305 * * @param string $plaintext The message you're encrypting * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Ciphertext with Poly1305 MAC * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_secretbox_xchacha20poly1305($plaintext, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305($plaintext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305($plaintext, $nonce, $key); } /** * Decrypts a message previously encrypted with crypto_secretbox_xchacha20poly1305(). * * @param string $ciphertext Ciphertext with Poly1305 MAC * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_secretbox_xchacha20poly1305_open( $ciphertext, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key); } /** * @param string $key * @return array<int, string> Returns a state and a header. * @throws Exception * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_init_push( #[\SensitiveParameter] $key ) { if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key); } /** * @param string $header * @param string $key * @return string Returns a state. * @throws Exception */ public static function crypto_secretstream_xchacha20poly1305_init_pull( $header, #[\SensitiveParameter] $key ) { if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) { throw new SodiumException( 'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes' ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header); } /** * @param string $state * @param string $msg * @param string $aad * @param int $tag * @return string * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_push( #[\SensitiveParameter] &$state, #[\SensitiveParameter] $msg, $aad = '', $tag = 0 ) { if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push( $state, $msg, $aad, $tag ); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push( $state, $msg, $aad, $tag ); } /** * @param string $state * @param string $msg * @param string $aad * @return bool|array{0: string, 1: int} * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_pull( #[\SensitiveParameter] &$state, $msg, $aad = '' ) { if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull( $state, $msg, $aad ); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull( $state, $msg, $aad ); } /** * @return string * @throws Exception */ public static function crypto_secretstream_xchacha20poly1305_keygen() { return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES); } /** * @param string $state * @return void * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_rekey( #[\SensitiveParameter] &$state ) { if (PHP_INT_SIZE === 4) { ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state); } else { ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state); } } /** * Calculates a SipHash-2-4 hash of a message for a given key. * * @param string $message Input message * @param string $key SipHash-2-4 key * @return string Hash * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_shorthash( $message, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SHORTHASH_KEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SHORTHASH_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_shorthash($message, $key); } if (self::use_fallback('crypto_shorthash')) { return (string) call_user_func('\\Sodium\\crypto_shorthash', $message, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_SipHash::sipHash24($message, $key); } return ParagonIE_Sodium_Core_SipHash::sipHash24($message, $key); } /** * Return a secure random key for use with crypto_shorthash * * @return string * @throws Exception * @throws Error */ public static function crypto_shorthash_keygen() { return random_bytes(self::CRYPTO_SHORTHASH_KEYBYTES); } /** * Returns a signed message. You probably want crypto_sign_detached() * instead, which only returns the signature. * * Algorithm: Ed25519 (EdDSA over Curve25519) * * @param string $message Message to be signed. * @param string $secretKey Secret signing key. * @return string Signed message (signature is prefixed). * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_sign( $message, #[\SensitiveParameter] $secretKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign($message, $secretKey); } if (self::use_fallback('crypto_sign')) { return (string) call_user_func('\\Sodium\\crypto_sign', $message, $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign($message, $secretKey); } return ParagonIE_Sodium_Crypto::sign($message, $secretKey); } /** * Validates a signed message then returns the message. * * @param string $signedMessage A signed message * @param string $publicKey A public key * @return string The original message (if the signature is * valid for this public key) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_sign_open( $signedMessage, $publicKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($signedMessage, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($signedMessage) < self::CRYPTO_SIGN_BYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_BYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SIGN_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_sign_open($signedMessage, $publicKey); } if (self::use_fallback('crypto_sign_open')) { return call_user_func('\\Sodium\\crypto_sign_open', $signedMessage, $publicKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign_open($signedMessage, $publicKey); } return ParagonIE_Sodium_Crypto::sign_open($signedMessage, $publicKey); } /** * Generate a new random Ed25519 keypair. * * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_sign_keypair() { if (self::useNewSodiumAPI()) { return sodium_crypto_sign_keypair(); } if (self::use_fallback('crypto_sign_keypair')) { return (string) call_user_func('\\Sodium\\crypto_sign_keypair'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::keypair(); } return ParagonIE_Sodium_Core_Ed25519::keypair(); } /** * @param string $sk * @param string $pk * @return string * @throws SodiumException */ public static function crypto_sign_keypair_from_secretkey_and_publickey( #[\SensitiveParameter] $sk, $pk ) { ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1); $sk = (string) $sk; $pk = (string) $pk; if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk); } return $sk . $pk; } /** * Generate an Ed25519 keypair from a seed. * * @param string $seed Input seed * @return string Keypair * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_seed_keypair( #[\SensitiveParameter] $seed ) { ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); if (self::useNewSodiumAPI()) { return sodium_crypto_sign_seed_keypair($seed); } if (self::use_fallback('crypto_sign_keypair')) { return (string) call_user_func('\\Sodium\\crypto_sign_seed_keypair', $seed); } $publicKey = ''; $secretKey = ''; if (PHP_INT_SIZE === 4) { ParagonIE_Sodium_Core32_Ed25519::seed_keypair($publicKey, $secretKey, $seed); } else { ParagonIE_Sodium_Core_Ed25519::seed_keypair($publicKey, $secretKey, $seed); } return $secretKey . $publicKey; } /** * Extract an Ed25519 public key from an Ed25519 keypair. * * @param string $keypair Keypair * @return string Public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_publickey( #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_publickey($keypair); } if (self::use_fallback('crypto_sign_publickey')) { return (string) call_user_func('\\Sodium\\crypto_sign_publickey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::publickey($keypair); } return ParagonIE_Sodium_Core_Ed25519::publickey($keypair); } /** * Calculate an Ed25519 public key from an Ed25519 secret key. * * @param string $secretKey Your Ed25519 secret key * @return string The corresponding Ed25519 public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_publickey_from_secretkey( #[\SensitiveParameter] $secretKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_publickey_from_secretkey($secretKey); } if (self::use_fallback('crypto_sign_publickey_from_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_sign_publickey_from_secretkey', $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::publickey_from_secretkey($secretKey); } return ParagonIE_Sodium_Core_Ed25519::publickey_from_secretkey($secretKey); } /** * Extract an Ed25519 secret key from an Ed25519 keypair. * * @param string $keypair Keypair * @return string Secret key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_secretkey( #[\SensitiveParameter] $keypair ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_secretkey($keypair); } if (self::use_fallback('crypto_sign_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_sign_secretkey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::secretkey($keypair); } return ParagonIE_Sodium_Core_Ed25519::secretkey($keypair); } /** * Calculate the Ed25519 signature of a message and return ONLY the signature. * * Algorithm: Ed25519 (EdDSA over Curve25519) * * @param string $message Message to be signed * @param string $secretKey Secret signing key * @return string Digital signature * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_detached( $message, #[\SensitiveParameter] $secretKey ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_detached($message, $secretKey); } if (self::use_fallback('crypto_sign_detached')) { return (string) call_user_func('\\Sodium\\crypto_sign_detached', $message, $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign_detached($message, $secretKey); } return ParagonIE_Sodium_Crypto::sign_detached($message, $secretKey); } /** * Verify the Ed25519 signature of a message. * * @param string $signature Digital sginature * @param string $message Message to be verified * @param string $publicKey Public key * @return bool TRUE if this signature is good for this public key; * FALSE otherwise * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_verify_detached($signature, $message, $publicKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($signature, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($signature) !== self::CRYPTO_SIGN_BYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_BYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_verify_detached($signature, $message, $publicKey); } if (self::use_fallback('crypto_sign_verify_detached')) { return (bool) call_user_func( '\\Sodium\\crypto_sign_verify_detached', $signature, $message, $publicKey ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign_verify_detached($signature, $message, $publicKey); } return ParagonIE_Sodium_Crypto::sign_verify_detached($signature, $message, $publicKey); } /** * Convert an Ed25519 public key to a Curve25519 public key * * @param string $pk * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_ed25519_pk_to_curve25519($pk) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($pk) < self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { if (is_callable('crypto_sign_ed25519_pk_to_curve25519')) { return (string) sodium_crypto_sign_ed25519_pk_to_curve25519($pk); } } if (self::use_fallback('crypto_sign_ed25519_pk_to_curve25519')) { return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_pk_to_curve25519', $pk); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::pk_to_curve25519($pk); } return ParagonIE_Sodium_Core_Ed25519::pk_to_curve25519($pk); } /** * Convert an Ed25519 secret key to a Curve25519 secret key * * @param string $sk * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_ed25519_sk_to_curve25519( #[\SensitiveParameter] $sk ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($sk) < self::CRYPTO_SIGN_SEEDBYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_SEEDBYTES long.'); } if (self::useNewSodiumAPI()) { if (is_callable('crypto_sign_ed25519_sk_to_curve25519')) { return sodium_crypto_sign_ed25519_sk_to_curve25519($sk); } } if (self::use_fallback('crypto_sign_ed25519_sk_to_curve25519')) { return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_sk_to_curve25519', $sk); } $h = hash('sha512', ParagonIE_Sodium_Core_Util::substr($sk, 0, 32), true); $h[0] = ParagonIE_Sodium_Core_Util::intToChr( ParagonIE_Sodium_Core_Util::chrToInt($h[0]) & 248 ); $h[31] = ParagonIE_Sodium_Core_Util::intToChr( (ParagonIE_Sodium_Core_Util::chrToInt($h[31]) & 127) | 64 ); return ParagonIE_Sodium_Core_Util::substr($h, 0, 32); } /** * Expand a key and nonce into a keystream of pseudorandom bytes. * * @param int $len Number of bytes desired * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key XSalsa20 key * @return string Pseudorandom stream that can be XORed with messages * to provide encryption (but not authentication; see * Poly1305 or crypto_auth() for that, which is not * optional for security) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream( $len, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_STREAM_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_stream($len, $nonce, $key); } if (self::use_fallback('crypto_stream')) { return (string) call_user_func('\\Sodium\\crypto_stream', $len, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20($len, $nonce, $key); } return ParagonIE_Sodium_Core_XSalsa20::xsalsa20($len, $nonce, $key); } /** * DANGER! UNAUTHENTICATED ENCRYPTION! * * Unless you are following expert advice, do not use this feature. * * Algorithm: XSalsa20 * * This DOES NOT provide ciphertext integrity. * * @param string $message Plaintext message * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key Encryption key * @return string Encrypted text which is vulnerable to chosen- * ciphertext attacks unless you implement some * other mitigation to the ciphertext (i.e. * Encrypt then MAC) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xor( #[\SensitiveParameter] $message, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_stream_xor($message, $nonce, $key); } if (self::use_fallback('crypto_stream_xor')) { return (string) call_user_func('\\Sodium\\crypto_stream_xor', $message, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20_xor($message, $nonce, $key); } return ParagonIE_Sodium_Core_XSalsa20::xsalsa20_xor($message, $nonce, $key); } /** * Return a secure random key for use with crypto_stream * * @return string * @throws Exception * @throws Error */ public static function crypto_stream_keygen() { return random_bytes(self::CRYPTO_STREAM_KEYBYTES); } /** * Expand a key and nonce into a keystream of pseudorandom bytes. * * @param int $len Number of bytes desired * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key XChaCha20 key * @param bool $dontFallback * @return string Pseudorandom stream that can be XORed with messages * to provide encryption (but not authentication; see * Poly1305 or crypto_auth() for that, which is not * optional for security) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xchacha20( $len, $nonce, #[\SensitiveParameter] $key, $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_STREAM_XCHACHA20_KEYBYTES long.'); } if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_stream_xchacha20($len, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XChaCha20::stream($len, $nonce, $key); } return ParagonIE_Sodium_Core_XChaCha20::stream($len, $nonce, $key); } /** * DANGER! UNAUTHENTICATED ENCRYPTION! * * Unless you are following expert advice, do not use this feature. * * Algorithm: XChaCha20 * * This DOES NOT provide ciphertext integrity. * * @param string $message Plaintext message * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key Encryption key * @return string Encrypted text which is vulnerable to chosen- * ciphertext attacks unless you implement some * other mitigation to the ciphertext (i.e. * Encrypt then MAC) * @param bool $dontFallback * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xchacha20_xor( #[\SensitiveParameter] $message, $nonce, #[\SensitiveParameter] $key, $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.'); } if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_stream_xchacha20_xor($message, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key); } return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key); } /** * DANGER! UNAUTHENTICATED ENCRYPTION! * * Unless you are following expert advice, do not use this feature. * * Algorithm: XChaCha20 * * This DOES NOT provide ciphertext integrity. * * @param string $message Plaintext message * @param string $nonce Number to be used Once; must be 24 bytes * @param int $counter * @param string $key Encryption key * @return string Encrypted text which is vulnerable to chosen- * ciphertext attacks unless you implement some * other mitigation to the ciphertext (i.e. * Encrypt then MAC) * @param bool $dontFallback * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xchacha20_xor_ic( #[\SensitiveParameter] $message, $nonce, $counter, #[\SensitiveParameter] $key, $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($counter, 'int', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.'); } if (is_callable('sodium_crypto_stream_xchacha20_xor_ic') && !$dontFallback) { return sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key); } $ic = ParagonIE_Sodium_Core_Util::store64_le($counter); if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key, $ic); } return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key, $ic); } /** * Return a secure random key for use with crypto_stream_xchacha20 * * @return string * @throws Exception * @throws Error */ public static function crypto_stream_xchacha20_keygen() { return random_bytes(self::CRYPTO_STREAM_XCHACHA20_KEYBYTES); } /** * Cache-timing-safe implementation of hex2bin(). * * @param string $string Hexadecimal string * @param string $ignore List of characters to ignore; useful for whitespace * @return string Raw binary string * @throws SodiumException * @throws TypeError * @psalm-suppress TooFewArguments * @psalm-suppress MixedArgument */ public static function hex2bin( #[\SensitiveParameter] $string, $ignore = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($ignore, 'string', 2); if (self::useNewSodiumAPI()) { if (is_callable('sodium_hex2bin')) { return (string) sodium_hex2bin($string, $ignore); } } if (self::use_fallback('hex2bin')) { return (string) call_user_func('\\Sodium\\hex2bin', $string, $ignore); } return ParagonIE_Sodium_Core_Util::hex2bin($string, $ignore); } /** * Increase a string (little endian) * * @param string $var * * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function increment( #[\SensitiveParameter] &$var ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1); if (self::useNewSodiumAPI()) { sodium_increment($var); return; } if (self::use_fallback('increment')) { $func = '\\Sodium\\increment'; $func($var); return; } $len = ParagonIE_Sodium_Core_Util::strlen($var); $c = 1; $copy = ''; for ($i = 0; $i < $len; ++$i) { $c += ParagonIE_Sodium_Core_Util::chrToInt( ParagonIE_Sodium_Core_Util::substr($var, $i, 1) ); $copy .= ParagonIE_Sodium_Core_Util::intToChr($c); $c >>= 8; } $var = $copy; } /** * @param string $str * @return bool * * @throws SodiumException */ public static function is_zero( #[\SensitiveParameter] $str ) { $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= ParagonIE_Sodium_Core_Util::chrToInt($str[$i]); } return ((($d - 1) >> 31) & 1) === 1; } /** * The equivalent to the libsodium minor version we aim to be compatible * with (sans pwhash and memzero). * * @return int */ public static function library_version_major() { if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MAJOR_VERSION')) { return SODIUM_LIBRARY_MAJOR_VERSION; } if (self::use_fallback('library_version_major')) { /** @psalm-suppress UndefinedFunction */ return (int) call_user_func('\\Sodium\\library_version_major'); } return self::LIBRARY_VERSION_MAJOR; } /** * The equivalent to the libsodium minor version we aim to be compatible * with (sans pwhash and memzero). * * @return int */ public static function library_version_minor() { if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MINOR_VERSION')) { return SODIUM_LIBRARY_MINOR_VERSION; } if (self::use_fallback('library_version_minor')) { /** @psalm-suppress UndefinedFunction */ return (int) call_user_func('\\Sodium\\library_version_minor'); } return self::LIBRARY_VERSION_MINOR; } /** * Compare two strings. * * @param string $left * @param string $right * @return int * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function memcmp( #[\SensitiveParameter] $left, #[\SensitiveParameter] $right ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2); if (self::useNewSodiumAPI()) { return sodium_memcmp($left, $right); } if (self::use_fallback('memcmp')) { return (int) call_user_func('\\Sodium\\memcmp', $left, $right); } /** @var string $left */ /** @var string $right */ return ParagonIE_Sodium_Core_Util::memcmp($left, $right); } /** * It's actually not possible to zero memory buffers in PHP. You need the * native library for that. * * @param string|null $var * @param-out string|null $var * * @return void * @throws SodiumException (Unless libsodium is installed) * @throws TypeError * @psalm-suppress TooFewArguments */ public static function memzero( #[\SensitiveParameter] &$var ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1); if (self::useNewSodiumAPI()) { /** @psalm-suppress MixedArgument */ sodium_memzero($var); return; } if (self::use_fallback('memzero')) { $func = '\\Sodium\\memzero'; $func($var); if ($var === null) { return; } } // This is the best we can do. throw new SodiumException( 'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' . 'To fix this error, make sure libsodium is installed and the PHP extension is enabled.' ); } /** * @param string $unpadded * @param int $blockSize * @param bool $dontFallback * @return string * @throws SodiumException */ public static function pad( #[\SensitiveParameter] $unpadded, $blockSize, $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2); $unpadded = (string) $unpadded; $blockSize = (int) $blockSize; if (self::useNewSodiumAPI() && !$dontFallback) { return (string) sodium_pad($unpadded, $blockSize); } if ($blockSize <= 0) { throw new SodiumException( 'block size cannot be less than 1' ); } $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded); $xpadlen = ($blockSize - 1); if (($blockSize & ($blockSize - 1)) === 0) { $xpadlen -= $unpadded_len & ($blockSize - 1); } else { $xpadlen -= $unpadded_len % $blockSize; } $xpadded_len = $unpadded_len + $xpadlen; $padded = str_repeat("\0", $xpadded_len - 1); if ($unpadded_len > 0) { $st = 1; $i = 0; $k = $unpadded_len; for ($j = 0; $j <= $xpadded_len; ++$j) { $i = (int) $i; $k = (int) $k; $st = (int) $st; if ($j >= $unpadded_len) { $padded[$j] = "\0"; } else { $padded[$j] = $unpadded[$j]; } /** @var int $k */ $k -= $st; $st = (int) (~( ( ( ($k >> 48) | ($k >> 32) | ($k >> 16) | $k ) - 1 ) >> 16 ) ) & 1; $i += $st; } } $mask = 0; $tail = $xpadded_len; for ($i = 0; $i < $blockSize; ++$i) { # barrier_mask = (unsigned char) # (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT)); $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1); # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask); $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr( (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask) | (0x80 & $barrier_mask) ); # mask |= barrier_mask; $mask |= $barrier_mask; } return $padded; } /** * @param string $padded * @param int $blockSize * @param bool $dontFallback * @return string * @throws SodiumException */ public static function unpad( #[\SensitiveParameter] $padded, $blockSize, $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2); $padded = (string) $padded; $blockSize = (int) $blockSize; if (self::useNewSodiumAPI() && !$dontFallback) { return (string) sodium_unpad($padded, $blockSize); } if ($blockSize <= 0) { throw new SodiumException('block size cannot be less than 1'); } $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded); if ($padded_len < $blockSize) { throw new SodiumException('invalid padding'); } # tail = &padded[padded_len - 1U]; $tail = $padded_len - 1; $acc = 0; $valid = 0; $pad_len = 0; $found = 0; for ($i = 0; $i < $blockSize; ++$i) { # c = tail[-i]; $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]); # is_barrier = # (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U; $is_barrier = ( ( ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1) ) >> 7 ) & 1; $is_barrier &= ~$found; $found |= $is_barrier; # acc |= c; $acc |= $c; # pad_len |= i & (1U + ~is_barrier); $pad_len |= $i & (1 + ~$is_barrier); # valid |= (unsigned char) is_barrier; $valid |= ($is_barrier & 0xff); } # unpadded_len = padded_len - 1U - pad_len; $unpadded_len = $padded_len - 1 - $pad_len; if ($valid !== 1) { throw new SodiumException('invalid padding'); } return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len); } /** * Will sodium_compat run fast on the current hardware and PHP configuration? * * @return bool */ public static function polyfill_is_fast() { if (extension_loaded('sodium')) { return true; } if (extension_loaded('libsodium')) { return true; } return PHP_INT_SIZE === 8; } /** * Generate a string of bytes from the kernel's CSPRNG. * Proudly uses /dev/urandom (if getrandom(2) is not available). * * @param int $numBytes * @return string * @throws Exception * @throws TypeError */ public static function randombytes_buf($numBytes) { /* Type checks: */ if (!is_int($numBytes)) { if (is_numeric($numBytes)) { $numBytes = (int) $numBytes; } else { throw new TypeError( 'Argument 1 must be an integer, ' . gettype($numBytes) . ' given.' ); } } /** @var positive-int $numBytes */ if (self::use_fallback('randombytes_buf')) { return (string) call_user_func('\\Sodium\\randombytes_buf', $numBytes); } if ($numBytes < 0) { throw new SodiumException("Number of bytes must be a positive integer"); } return random_bytes($numBytes); } /** * Generate an integer between 0 and $range (non-inclusive). * * @param int $range * @return int * @throws Exception * @throws Error * @throws TypeError */ public static function randombytes_uniform($range) { /* Type checks: */ if (!is_int($range)) { if (is_numeric($range)) { $range = (int) $range; } else { throw new TypeError( 'Argument 1 must be an integer, ' . gettype($range) . ' given.' ); } } if (self::use_fallback('randombytes_uniform')) { return (int) call_user_func('\\Sodium\\randombytes_uniform', $range); } return random_int(0, $range - 1); } /** * Generate a random 16-bit integer. * * @return int * @throws Exception * @throws Error * @throws TypeError */ public static function randombytes_random16() { if (self::use_fallback('randombytes_random16')) { return (int) call_user_func('\\Sodium\\randombytes_random16'); } return random_int(0, 65535); } /** * @param string $p * @param bool $dontFallback * @return bool * @throws SodiumException */ public static function ristretto255_is_valid_point( #[\SensitiveParameter] $p, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_is_valid_point($p); } try { $r = ParagonIE_Sodium_Core_Ristretto255::ristretto255_frombytes($p); return $r['res'] === 0 && ParagonIE_Sodium_Core_Ristretto255::ristretto255_point_is_canonical($p) === 1; } catch (SodiumException $ex) { if ($ex->getMessage() === 'S is not canonical') { return false; } throw $ex; } } /** * @param string $p * @param string $q * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_add( #[\SensitiveParameter] $p, #[\SensitiveParameter] $q, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_add($p, $q); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_add($p, $q); } /** * @param string $p * @param string $q * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_sub( #[\SensitiveParameter] $p, #[\SensitiveParameter] $q, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_sub($p, $q); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_sub($p, $q); } /** * @param string $r * @param bool $dontFallback * @return string * * @throws SodiumException */ public static function ristretto255_from_hash( #[\SensitiveParameter] $r, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_from_hash($r); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_from_hash($r); } /** * @param bool $dontFallback * @return string * * @throws SodiumException */ public static function ristretto255_random($dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_random(); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_random(); } /** * @param bool $dontFallback * @return string * * @throws SodiumException */ public static function ristretto255_scalar_random($dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_random(); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_random(); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_invert( #[\SensitiveParameter] $s, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_invert($s); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_invert($s); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_negate( #[\SensitiveParameter] $s, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_negate($s); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_negate($s); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_complement( #[\SensitiveParameter] $s, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_complement($s); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_complement($s); } /** * @param string $x * @param string $y * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_add( #[\SensitiveParameter] $x, #[\SensitiveParameter] $y, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_add($x, $y); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_add($x, $y); } /** * @param string $x * @param string $y * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_sub( #[\SensitiveParameter] $x, #[\SensitiveParameter] $y, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_sub($x, $y); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_sub($x, $y); } /** * @param string $x * @param string $y * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_mul( #[\SensitiveParameter] $x, #[\SensitiveParameter] $y, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_mul($x, $y); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_mul($x, $y); } /** * @param string $n * @param string $p * @param bool $dontFallback * @return string * @throws SodiumException */ public static function scalarmult_ristretto255( #[\SensitiveParameter] $n, #[\SensitiveParameter] $p, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_scalarmult_ristretto255($n, $p); } return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255($n, $p); } /** * @param string $n * @param string $p * @param bool $dontFallback * @return string * @throws SodiumException */ public static function scalarmult_ristretto255_base( #[\SensitiveParameter] $n, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_scalarmult_ristretto255_base($n); } return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255_base($n); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_reduce( #[\SensitiveParameter] $s, $dontFallback = false ) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_reduce($s); } return ParagonIE_Sodium_Core_Ristretto255::sc_reduce($s); } /** * Runtime testing method for 32-bit platforms. * * Usage: If runtime_speed_test() returns FALSE, then our 32-bit * implementation is to slow to use safely without risking timeouts. * If this happens, install sodium from PECL to get acceptable * performance. * * @param int $iterations Number of multiplications to attempt * @param int $maxTimeout Milliseconds * @return bool TRUE if we're fast enough, FALSE is not * @throws SodiumException */ public static function runtime_speed_test($iterations, $maxTimeout) { if (self::polyfill_is_fast()) { return true; } /** @var float $end */ $end = 0.0; /** @var float $start */ $start = microtime(true); /** @var ParagonIE_Sodium_Core32_Int64 $a */ $a = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16)); for ($i = 0; $i < $iterations; ++$i) { /** @var ParagonIE_Sodium_Core32_Int64 $b */ $b = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16)); $a->mulInt64($b); } /** @var float $end */ $end = microtime(true); /** @var int $diff */ $diff = (int) ceil(($end - $start) * 1000); return $diff < $maxTimeout; } /** * Add two numbers (little-endian unsigned), storing the value in the first * parameter. * * This mutates $val. * * @param string $val * @param string $addv * @return void * @throws SodiumException */ public static function sub( #[\SensitiveParameter] &$val, #[\SensitiveParameter] $addv ) { $val_len = ParagonIE_Sodium_Core_Util::strlen($val); $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv); if ($val_len !== $addv_len) { throw new SodiumException('values must have the same length'); } $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val); $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv); $c = 0; for ($i = 0; $i < $val_len; $i++) { $c = ($A[$i] - $B[$i] - $c); $A[$i] = ($c & 0xff); $c = ($c >> 8) & 1; } $val = ParagonIE_Sodium_Core_Util::intArrayToString($A); } /** * This emulates libsodium's version_string() function, except ours is * prefixed with 'polyfill-'. * * @return string * @psalm-suppress MixedInferredReturnType * @psalm-suppress UndefinedFunction */ public static function version_string() { if (self::useNewSodiumAPI()) { return (string) sodium_version_string(); } if (self::use_fallback('version_string')) { return (string) call_user_func('\\Sodium\\version_string'); } return (string) self::VERSION_STRING; } /** * Should we use the libsodium core function instead? * This is always a good idea, if it's available. (Unless we're in the * middle of running our unit test suite.) * * If ext/libsodium is available, use it. Return TRUE. * Otherwise, we have to use the code provided herein. Return FALSE. * * @param string $sodium_func_name * * @return bool */ protected static function use_fallback($sodium_func_name = '') { static $res = null; if ($res === null) { $res = extension_loaded('libsodium') && PHP_VERSION_ID >= 50300; } if ($res === false) { // No libsodium installed return false; } if (self::$disableFallbackForUnitTests) { // Don't fallback. Use the PHP implementation. return false; } if (!empty($sodium_func_name)) { return is_callable('\\Sodium\\' . $sodium_func_name); } return true; } /** * Libsodium as implemented in PHP 7.2 * and/or ext/sodium (via PECL) * * @ref https://wiki.php.net/rfc/libsodium * @return bool */ protected static function useNewSodiumAPI() { static $res = null; if ($res === null) { $res = PHP_VERSION_ID >= 70000 && extension_loaded('sodium'); } if (self::$disableFallbackForUnitTests) { // Don't fallback. Use the PHP implementation. return false; } return (bool) $res; } } PK ];m[��21� � Core32/HChaCha20.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Core32_HChaCha20', false)) { return; } /** * Class ParagonIE_Sodium_Core_HChaCha20 */ class ParagonIE_Sodium_Core32_HChaCha20 extends ParagonIE_Sodium_Core32_ChaCha20 { /** * @param string $in * @param string $key * @param string|null $c * @return string * @throws SodiumException * @throws TypeError */ public static function hChaCha20($in = '', $key = '', $c = null) { $ctx = array(); if ($c === null) { $ctx[0] = new ParagonIE_Sodium_Core32_Int32(array(0x6170, 0x7865)); $ctx[1] = new ParagonIE_Sodium_Core32_Int32(array(0x3320, 0x646e)); $ctx[2] = new ParagonIE_Sodium_Core32_Int32(array(0x7962, 0x2d32)); $ctx[3] = new ParagonIE_Sodium_Core32_Int32(array(0x6b20, 0x6574)); } else { $ctx[0] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 0, 4)); $ctx[1] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 4, 4)); $ctx[2] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 8, 4)); $ctx[3] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 12, 4)); } $ctx[4] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 0, 4)); $ctx[5] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 4, 4)); $ctx[6] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 8, 4)); $ctx[7] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 12, 4)); $ctx[8] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 16, 4)); $ctx[9] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 20, 4)); $ctx[10] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 24, 4)); $ctx[11] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 28, 4)); $ctx[12] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 0, 4)); $ctx[13] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 4, 4)); $ctx[14] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 8, 4)); $ctx[15] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 12, 4)); return self::hChaCha20Bytes($ctx); } /** * @param array $ctx * @return string * @throws SodiumException * @throws TypeError */ protected static function hChaCha20Bytes(array $ctx) { /** @var ParagonIE_Sodium_Core32_Int32 $x0 */ $x0 = $ctx[0]; /** @var ParagonIE_Sodium_Core32_Int32 $x1 */ $x1 = $ctx[1]; /** @var ParagonIE_Sodium_Core32_Int32 $x2 */ $x2 = $ctx[2]; /** @var ParagonIE_Sodium_Core32_Int32 $x3 */ $x3 = $ctx[3]; /** @var ParagonIE_Sodium_Core32_Int32 $x4 */ $x4 = $ctx[4]; /** @var ParagonIE_Sodium_Core32_Int32 $x5 */ $x5 = $ctx[5]; /** @var ParagonIE_Sodium_Core32_Int32 $x6 */ $x6 = $ctx[6]; /** @var ParagonIE_Sodium_Core32_Int32 $x7 */ $x7 = $ctx[7]; /** @var ParagonIE_Sodium_Core32_Int32 $x8 */ $x8 = $ctx[8]; /** @var ParagonIE_Sodium_Core32_Int32 $x9 */ $x9 = $ctx[9]; /** @var ParagonIE_Sodium_Core32_Int32 $x10 */ $x10 = $ctx[10]; /** @var ParagonIE_Sodium_Core32_Int32 $x11 */ $x11 = $ctx[11]; /** @var ParagonIE_Sodium_Core32_Int32 $x12 */ $x12 = $ctx[12]; /** @var ParagonIE_Sodium_Core32_Int32 $x13 */ $x13 = $ctx[13]; /** @var ParagonIE_Sodium_Core32_Int32 $x14 */ $x14 = $ctx[14]; /** @var ParagonIE_Sodium_Core32_Int32 $x15 */ $x15 = $ctx[15]; for ($i = 0; $i < 10; ++$i) { # QUARTERROUND( x0, x4, x8, x12) list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12); # QUARTERROUND( x1, x5, x9, x13) list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13); # QUARTERROUND( x2, x6, x10, x14) list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14); # QUARTERROUND( x3, x7, x11, x15) list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15); # QUARTERROUND( x0, x5, x10, x15) list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15); # QUARTERROUND( x1, x6, x11, x12) list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12); # QUARTERROUND( x2, x7, x8, x13) list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13); # QUARTERROUND( x3, x4, x9, x14) list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14); } return $x0->toReverseString() . $x1->toReverseString() . $x2->toReverseString() . $x3->toReverseString() . $x12->toReverseString() . $x13->toReverseString() . $x14->toReverseString() . $x15->toReverseString(); } } PK ];m[���`` ` Core32/Int32.phpnu �[��� <?php /** * Class ParagonIE_Sodium_Core32_Int32 * * Encapsulates a 32-bit integer. * * These are immutable. It always returns a new instance. */ class ParagonIE_Sodium_Core32_Int32 { /** * @var array<int, int> - two 16-bit integers * * 0 is the higher 16 bits * 1 is the lower 16 bits */ public $limbs = array(0, 0); /** * @var int */ public $overflow = 0; /** * @var bool */ public $unsignedInt = false; /** * ParagonIE_Sodium_Core32_Int32 constructor. * @param array $array * @param bool $unsignedInt */ public function __construct($array = array(0, 0), $unsignedInt = false) { $this->limbs = array( (int) $array[0], (int) $array[1] ); $this->overflow = 0; $this->unsignedInt = $unsignedInt; } /** * Adds two int32 objects * * @param ParagonIE_Sodium_Core32_Int32 $addend * @return ParagonIE_Sodium_Core32_Int32 */ public function addInt32(ParagonIE_Sodium_Core32_Int32 $addend) { $i0 = $this->limbs[0]; $i1 = $this->limbs[1]; $j0 = $addend->limbs[0]; $j1 = $addend->limbs[1]; $r1 = $i1 + ($j1 & 0xffff); $carry = $r1 >> 16; $r0 = $i0 + ($j0 & 0xffff) + $carry; $carry = $r0 >> 16; $r0 &= 0xffff; $r1 &= 0xffff; $return = new ParagonIE_Sodium_Core32_Int32( array($r0, $r1) ); $return->overflow = $carry; $return->unsignedInt = $this->unsignedInt; return $return; } /** * Adds a normal integer to an int32 object * * @param int $int * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError */ public function addInt($int) { ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1); /** @var int $int */ $int = (int) $int; $int = (int) $int; $i0 = $this->limbs[0]; $i1 = $this->limbs[1]; $r1 = $i1 + ($int & 0xffff); $carry = $r1 >> 16; $r0 = $i0 + (($int >> 16) & 0xffff) + $carry; $carry = $r0 >> 16; $r0 &= 0xffff; $r1 &= 0xffff; $return = new ParagonIE_Sodium_Core32_Int32( array($r0, $r1) ); $return->overflow = $carry; $return->unsignedInt = $this->unsignedInt; return $return; } /** * @param int $b * @return int */ public function compareInt($b = 0) { $gt = 0; $eq = 1; $i = 2; $j = 0; while ($i > 0) { --$i; /** @var int $x1 */ $x1 = $this->limbs[$i]; /** @var int $x2 */ $x2 = ($b >> ($j << 4)) & 0xffff; /** @var int $gt */ $gt |= (($x2 - $x1) >> 8) & $eq; /** @var int $eq */ $eq &= (($x2 ^ $x1) - 1) >> 8; } return ($gt + $gt - $eq) + 1; } /** * @param int $m * @return ParagonIE_Sodium_Core32_Int32 */ public function mask($m = 0) { /** @var int $hi */ $hi = ((int) $m >> 16); $hi &= 0xffff; /** @var int $lo */ $lo = ((int) $m) & 0xffff; return new ParagonIE_Sodium_Core32_Int32( array( (int) ($this->limbs[0] & $hi), (int) ($this->limbs[1] & $lo) ), $this->unsignedInt ); } /** * @param array<int, int> $a * @param array<int, int> $b * @param int $baseLog2 * @return array<int, int> */ public function multiplyLong(array $a, array $b, $baseLog2 = 16) { $a_l = count($a); $b_l = count($b); /** @var array<int, int> $r */ $r = array_fill(0, $a_l + $b_l + 1, 0); $base = 1 << $baseLog2; for ($i = 0; $i < $a_l; ++$i) { $a_i = $a[$i]; for ($j = 0; $j < $a_l; ++$j) { $b_j = $b[$j]; $product = ($a_i * $b_j) + $r[$i + $j]; $carry = ((int) $product >> $baseLog2 & 0xffff); $r[$i + $j] = ((int) $product - (int) ($carry * $base)) & 0xffff; $r[$i + $j + 1] += $carry; } } return array_slice($r, 0, 5); } /** * @param int $int * @return ParagonIE_Sodium_Core32_Int32 */ public function mulIntFast($int) { // Handle negative numbers $aNeg = ($this->limbs[0] >> 15) & 1; $bNeg = ($int >> 31) & 1; $a = array_reverse($this->limbs); $b = array( $int & 0xffff, ($int >> 16) & 0xffff ); if ($aNeg) { for ($i = 0; $i < 2; ++$i) { $a[$i] = ($a[$i] ^ 0xffff) & 0xffff; } ++$a[0]; } if ($bNeg) { for ($i = 0; $i < 2; ++$i) { $b[$i] = ($b[$i] ^ 0xffff) & 0xffff; } ++$b[0]; } // Multiply $res = $this->multiplyLong($a, $b); // Re-apply negation to results if ($aNeg !== $bNeg) { for ($i = 0; $i < 2; ++$i) { $res[$i] = (0xffff ^ $res[$i]) & 0xffff; } // Handle integer overflow $c = 1; for ($i = 0; $i < 2; ++$i) { $res[$i] += $c; $c = $res[$i] >> 16; $res[$i] &= 0xffff; } } // Return our values $return = new ParagonIE_Sodium_Core32_Int32(); $return->limbs = array( $res[1] & 0xffff, $res[0] & 0xffff ); if (count($res) > 2) { $return->overflow = $res[2] & 0xffff; } $return->unsignedInt = $this->unsignedInt; return $return; } /** * @param ParagonIE_Sodium_Core32_Int32 $right * @return ParagonIE_Sodium_Core32_Int32 */ public function mulInt32Fast(ParagonIE_Sodium_Core32_Int32 $right) { $aNeg = ($this->limbs[0] >> 15) & 1; $bNeg = ($right->limbs[0] >> 15) & 1; $a = array_reverse($this->limbs); $b = array_reverse($right->limbs); if ($aNeg) { for ($i = 0; $i < 2; ++$i) { $a[$i] = ($a[$i] ^ 0xffff) & 0xffff; } ++$a[0]; } if ($bNeg) { for ($i = 0; $i < 2; ++$i) { $b[$i] = ($b[$i] ^ 0xffff) & 0xffff; } ++$b[0]; } $res = $this->multiplyLong($a, $b); if ($aNeg !== $bNeg) { if ($aNeg !== $bNeg) { for ($i = 0; $i < 2; ++$i) { $res[$i] = ($res[$i] ^ 0xffff) & 0xffff; } $c = 1; for ($i = 0; $i < 2; ++$i) { $res[$i] += $c; $c = $res[$i] >> 16; $res[$i] &= 0xffff; } } } $return = new ParagonIE_Sodium_Core32_Int32(); $return->limbs = array( $res[1] & 0xffff, $res[0] & 0xffff ); if (count($res) > 2) { $return->overflow = $res[2]; } return $return; } /** * @param int $int * @param int $size * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError */ public function mulInt($int = 0, $size = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1); ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2); if (ParagonIE_Sodium_Compat::$fastMult) { return $this->mulIntFast((int) $int); } /** @var int $int */ $int = (int) $int; /** @var int $size */ $size = (int) $size; if (!$size) { $size = 31; } /** @var int $size */ $a = clone $this; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; // Initialize: $ret0 = 0; $ret1 = 0; $a0 = $a->limbs[0]; $a1 = $a->limbs[1]; /** @var int $size */ /** @var int $i */ for ($i = $size; $i >= 0; --$i) { $m = (int) (-($int & 1)); $x0 = $a0 & $m; $x1 = $a1 & $m; $ret1 += $x1; $c = $ret1 >> 16; $ret0 += $x0 + $c; $ret0 &= 0xffff; $ret1 &= 0xffff; $a1 = ($a1 << 1); $x1 = $a1 >> 16; $a0 = ($a0 << 1) | $x1; $a0 &= 0xffff; $a1 &= 0xffff; $int >>= 1; } $return->limbs[0] = $ret0; $return->limbs[1] = $ret1; return $return; } /** * @param ParagonIE_Sodium_Core32_Int32 $int * @param int $size * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError */ public function mulInt32(ParagonIE_Sodium_Core32_Int32 $int, $size = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2); if (ParagonIE_Sodium_Compat::$fastMult) { return $this->mulInt32Fast($int); } if (!$size) { $size = 31; } /** @var int $size */ $a = clone $this; $b = clone $int; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; // Initialize: $ret0 = 0; $ret1 = 0; $a0 = $a->limbs[0]; $a1 = $a->limbs[1]; $b0 = $b->limbs[0]; $b1 = $b->limbs[1]; /** @var int $size */ /** @var int $i */ for ($i = $size; $i >= 0; --$i) { $m = (int) (-($b1 & 1)); $x0 = $a0 & $m; $x1 = $a1 & $m; $ret1 += $x1; $c = $ret1 >> 16; $ret0 += $x0 + $c; $ret0 &= 0xffff; $ret1 &= 0xffff; $a1 = ($a1 << 1); $x1 = $a1 >> 16; $a0 = ($a0 << 1) | $x1; $a0 &= 0xffff; $a1 &= 0xffff; $x0 = ($b0 & 1) << 16; $b0 = ($b0 >> 1); $b1 = (($b1 | $x0) >> 1); $b0 &= 0xffff; $b1 &= 0xffff; } $return->limbs[0] = $ret0; $return->limbs[1] = $ret1; return $return; } /** * OR this 32-bit integer with another. * * @param ParagonIE_Sodium_Core32_Int32 $b * @return ParagonIE_Sodium_Core32_Int32 */ public function orInt32(ParagonIE_Sodium_Core32_Int32 $b) { $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; $return->limbs = array( (int) ($this->limbs[0] | $b->limbs[0]), (int) ($this->limbs[1] | $b->limbs[1]) ); /** @var int overflow */ $return->overflow = $this->overflow | $b->overflow; return $return; } /** * @param int $b * @return bool */ public function isGreaterThan($b = 0) { return $this->compareInt($b) > 0; } /** * @param int $b * @return bool */ public function isLessThanInt($b = 0) { return $this->compareInt($b) < 0; } /** * @param int $c * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArrayAccess */ public function rotateLeft($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; $c &= 31; if ($c === 0) { // NOP, but we want a copy. $return->limbs = $this->limbs; } else { /** @var int $c */ /** @var int $idx_shift */ $idx_shift = ($c >> 4) & 1; /** @var int $sub_shift */ $sub_shift = $c & 15; /** @var array<int, int> $limbs */ $limbs =& $return->limbs; /** @var array<int, int> $myLimbs */ $myLimbs =& $this->limbs; for ($i = 1; $i >= 0; --$i) { /** @var int $j */ $j = ($i + $idx_shift) & 1; /** @var int $k */ $k = ($i + $idx_shift + 1) & 1; $limbs[$i] = (int) ( ( ((int) ($myLimbs[$j]) << $sub_shift) | ((int) ($myLimbs[$k]) >> (16 - $sub_shift)) ) & 0xffff ); } } return $return; } /** * Rotate to the right * * @param int $c * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArrayAccess */ public function rotateRight($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; $c &= 31; /** @var int $c */ if ($c === 0) { // NOP, but we want a copy. $return->limbs = $this->limbs; } else { /** @var int $c */ /** @var int $idx_shift */ $idx_shift = ($c >> 4) & 1; /** @var int $sub_shift */ $sub_shift = $c & 15; /** @var array<int, int> $limbs */ $limbs =& $return->limbs; /** @var array<int, int> $myLimbs */ $myLimbs =& $this->limbs; for ($i = 1; $i >= 0; --$i) { /** @var int $j */ $j = ($i - $idx_shift) & 1; /** @var int $k */ $k = ($i - $idx_shift - 1) & 1; $limbs[$i] = (int) ( ( ((int) ($myLimbs[$j]) >> (int) ($sub_shift)) | ((int) ($myLimbs[$k]) << (16 - (int) ($sub_shift))) ) & 0xffff ); } } return $return; } /** * @param bool $bool * @return self */ public function setUnsignedInt($bool = false) { $this->unsignedInt = !empty($bool); return $this; } /** * @param int $c * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError */ public function shiftLeft($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; $c &= 63; /** @var int $c */ if ($c === 0) { $return->limbs = $this->limbs; } elseif ($c < 0) { /** @var int $c */ return $this->shiftRight(-$c); } else { /** @var int $c */ /** @var int $tmp */ $tmp = $this->limbs[1] << $c; $return->limbs[1] = (int)($tmp & 0xffff); /** @var int $carry */ $carry = $tmp >> 16; /** @var int $tmp */ $tmp = ($this->limbs[0] << $c) | ($carry & 0xffff); $return->limbs[0] = (int) ($tmp & 0xffff); } return $return; } /** * @param int $c * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedAssignment * @psalm-suppress MixedOperand */ public function shiftRight($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; $c &= 63; /** @var int $c */ if ($c >= 16) { $return->limbs = array( (int) ($this->overflow & 0xffff), (int) ($this->limbs[0]) ); $return->overflow = $this->overflow >> 16; return $return->shiftRight($c & 15); } if ($c === 0) { $return->limbs = $this->limbs; } elseif ($c < 0) { /** @var int $c */ return $this->shiftLeft(-$c); } else { if (!is_int($c)) { throw new TypeError(); } /** @var int $c */ // $return->limbs[0] = (int) (($this->limbs[0] >> $c) & 0xffff); $carryLeft = (int) ($this->overflow & ((1 << ($c + 1)) - 1)); $return->limbs[0] = (int) ((($this->limbs[0] >> $c) | ($carryLeft << (16 - $c))) & 0xffff); $carryRight = (int) ($this->limbs[0] & ((1 << ($c + 1)) - 1)); $return->limbs[1] = (int) ((($this->limbs[1] >> $c) | ($carryRight << (16 - $c))) & 0xffff); $return->overflow >>= $c; } return $return; } /** * Subtract a normal integer from an int32 object. * * @param int $int * @return ParagonIE_Sodium_Core32_Int32 * @throws SodiumException * @throws TypeError */ public function subInt($int) { ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1); /** @var int $int */ $int = (int) $int; $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; /** @var int $tmp */ $tmp = $this->limbs[1] - ($int & 0xffff); /** @var int $carry */ $carry = $tmp >> 16; $return->limbs[1] = (int) ($tmp & 0xffff); /** @var int $tmp */ $tmp = $this->limbs[0] - (($int >> 16) & 0xffff) + $carry; $return->limbs[0] = (int) ($tmp & 0xffff); return $return; } /** * Subtract two int32 objects from each other * * @param ParagonIE_Sodium_Core32_Int32 $b * @return ParagonIE_Sodium_Core32_Int32 */ public function subInt32(ParagonIE_Sodium_Core32_Int32 $b) { $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; /** @var int $tmp */ $tmp = $this->limbs[1] - ($b->limbs[1] & 0xffff); /** @var int $carry */ $carry = $tmp >> 16; $return->limbs[1] = (int) ($tmp & 0xffff); /** @var int $tmp */ $tmp = $this->limbs[0] - ($b->limbs[0] & 0xffff) + $carry; $return->limbs[0] = (int) ($tmp & 0xffff); return $return; } /** * XOR this 32-bit integer with another. * * @param ParagonIE_Sodium_Core32_Int32 $b * @return ParagonIE_Sodium_Core32_Int32 */ public function xorInt32(ParagonIE_Sodium_Core32_Int32 $b) { $return = new ParagonIE_Sodium_Core32_Int32(); $return->unsignedInt = $this->unsignedInt; $return->limbs = array( (int) ($this->limbs[0] ^ $b->limbs[0]), (int) ($this->limbs[1] ^ $b->limbs[1]) ); return $return; } /** * @param int $signed * @return self * @throws SodiumException * @throws TypeError */ public static function fromInt($signed) { ParagonIE_Sodium_Core32_Util::declareScalarType($signed, 'int', 1);; /** @var int $signed */ $signed = (int) $signed; return new ParagonIE_Sodium_Core32_Int32( array( (int) (($signed >> 16) & 0xffff), (int) ($signed & 0xffff) ) ); } /** * @param string $string * @return self * @throws SodiumException * @throws TypeError */ public static function fromString($string) { ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1); $string = (string) $string; if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 4) { throw new RangeException( 'String must be 4 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.' ); } $return = new ParagonIE_Sodium_Core32_Int32(); $return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff) << 8); $return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff); $return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff) << 8); $return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff); return $return; } /** * @param string $string * @return self * @throws SodiumException * @throws TypeError */ public static function fromReverseString($string) { ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1); $string = (string) $string; if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 4) { throw new RangeException( 'String must be 4 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.' ); } $return = new ParagonIE_Sodium_Core32_Int32(); $return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff) << 8); $return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff); $return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff) << 8); $return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff); return $return; } /** * @return array<int, int> */ public function toArray() { return array((int) ($this->limbs[0] << 16 | $this->limbs[1])); } /** * @return string * @throws TypeError */ public function toString() { return ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff); } /** * @return int */ public function toInt() { return (int) ( (($this->limbs[0] & 0xffff) << 16) | ($this->limbs[1] & 0xffff) ); } /** * @return ParagonIE_Sodium_Core32_Int32 */ public function toInt32() { $return = new ParagonIE_Sodium_Core32_Int32(); $return->limbs[0] = (int) ($this->limbs[0] & 0xffff); $return->limbs[1] = (int) ($this->limbs[1] & 0xffff); $return->unsignedInt = $this->unsignedInt; $return->overflow = (int) ($this->overflow & 0x7fffffff); return $return; } /** * @return ParagonIE_Sodium_Core32_Int64 */ public function toInt64() { $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; if ($this->unsignedInt) { $return->limbs[0] += (($this->overflow >> 16) & 0xffff); $return->limbs[1] += (($this->overflow) & 0xffff); } else { $neg = -(($this->limbs[0] >> 15) & 1); $return->limbs[0] = (int)($neg & 0xffff); $return->limbs[1] = (int)($neg & 0xffff); } $return->limbs[2] = (int) ($this->limbs[0] & 0xffff); $return->limbs[3] = (int) ($this->limbs[1] & 0xffff); return $return; } /** * @return string * @throws TypeError */ public function toReverseString() { return ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff); } /** * @return string */ public function __toString() { try { return $this->toString(); } catch (TypeError $ex) { // PHP engine can't handle exceptions from __toString() return ''; } } } PK ];m[[���"+ "+ Core32/X25519.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Core32_X25519', false)) { return; } /** * Class ParagonIE_Sodium_Core32_X25519 */ abstract class ParagonIE_Sodium_Core32_X25519 extends ParagonIE_Sodium_Core32_Curve25519 { /** * Alters the objects passed to this method in place. * * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core32_Curve25519_Fe $f * @param ParagonIE_Sodium_Core32_Curve25519_Fe $g * @param int $b * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedMethodCall */ public static function fe_cswap( ParagonIE_Sodium_Core32_Curve25519_Fe $f, ParagonIE_Sodium_Core32_Curve25519_Fe $g, $b = 0 ) { $f0 = (int) $f[0]->toInt(); $f1 = (int) $f[1]->toInt(); $f2 = (int) $f[2]->toInt(); $f3 = (int) $f[3]->toInt(); $f4 = (int) $f[4]->toInt(); $f5 = (int) $f[5]->toInt(); $f6 = (int) $f[6]->toInt(); $f7 = (int) $f[7]->toInt(); $f8 = (int) $f[8]->toInt(); $f9 = (int) $f[9]->toInt(); $g0 = (int) $g[0]->toInt(); $g1 = (int) $g[1]->toInt(); $g2 = (int) $g[2]->toInt(); $g3 = (int) $g[3]->toInt(); $g4 = (int) $g[4]->toInt(); $g5 = (int) $g[5]->toInt(); $g6 = (int) $g[6]->toInt(); $g7 = (int) $g[7]->toInt(); $g8 = (int) $g[8]->toInt(); $g9 = (int) $g[9]->toInt(); $b = -$b; /** @var int $x0 */ $x0 = ($f0 ^ $g0) & $b; /** @var int $x1 */ $x1 = ($f1 ^ $g1) & $b; /** @var int $x2 */ $x2 = ($f2 ^ $g2) & $b; /** @var int $x3 */ $x3 = ($f3 ^ $g3) & $b; /** @var int $x4 */ $x4 = ($f4 ^ $g4) & $b; /** @var int $x5 */ $x5 = ($f5 ^ $g5) & $b; /** @var int $x6 */ $x6 = ($f6 ^ $g6) & $b; /** @var int $x7 */ $x7 = ($f7 ^ $g7) & $b; /** @var int $x8 */ $x8 = ($f8 ^ $g8) & $b; /** @var int $x9 */ $x9 = ($f9 ^ $g9) & $b; $f[0] = ParagonIE_Sodium_Core32_Int32::fromInt($f0 ^ $x0); $f[1] = ParagonIE_Sodium_Core32_Int32::fromInt($f1 ^ $x1); $f[2] = ParagonIE_Sodium_Core32_Int32::fromInt($f2 ^ $x2); $f[3] = ParagonIE_Sodium_Core32_Int32::fromInt($f3 ^ $x3); $f[4] = ParagonIE_Sodium_Core32_Int32::fromInt($f4 ^ $x4); $f[5] = ParagonIE_Sodium_Core32_Int32::fromInt($f5 ^ $x5); $f[6] = ParagonIE_Sodium_Core32_Int32::fromInt($f6 ^ $x6); $f[7] = ParagonIE_Sodium_Core32_Int32::fromInt($f7 ^ $x7); $f[8] = ParagonIE_Sodium_Core32_Int32::fromInt($f8 ^ $x8); $f[9] = ParagonIE_Sodium_Core32_Int32::fromInt($f9 ^ $x9); $g[0] = ParagonIE_Sodium_Core32_Int32::fromInt($g0 ^ $x0); $g[1] = ParagonIE_Sodium_Core32_Int32::fromInt($g1 ^ $x1); $g[2] = ParagonIE_Sodium_Core32_Int32::fromInt($g2 ^ $x2); $g[3] = ParagonIE_Sodium_Core32_Int32::fromInt($g3 ^ $x3); $g[4] = ParagonIE_Sodium_Core32_Int32::fromInt($g4 ^ $x4); $g[5] = ParagonIE_Sodium_Core32_Int32::fromInt($g5 ^ $x5); $g[6] = ParagonIE_Sodium_Core32_Int32::fromInt($g6 ^ $x6); $g[7] = ParagonIE_Sodium_Core32_Int32::fromInt($g7 ^ $x7); $g[8] = ParagonIE_Sodium_Core32_Int32::fromInt($g8 ^ $x8); $g[9] = ParagonIE_Sodium_Core32_Int32::fromInt($g9 ^ $x9); } /** * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core32_Curve25519_Fe $f * @return ParagonIE_Sodium_Core32_Curve25519_Fe * @throws SodiumException * @throws TypeError * @psalm-suppress MixedAssignment * @psalm-suppress MixedMethodCall */ public static function fe_mul121666(ParagonIE_Sodium_Core32_Curve25519_Fe $f) { /** @var array<int, ParagonIE_Sodium_Core32_Int64> $h */ $h = array(); for ($i = 0; $i < 10; ++$i) { $h[$i] = $f[$i]->toInt64()->mulInt(121666, 17); } $carry9 = $h[9]->addInt(1 << 24)->shiftRight(25); $h[0] = $h[0]->addInt64($carry9->mulInt(19, 5)); $h[9] = $h[9]->subInt64($carry9->shiftLeft(25)); $carry1 = $h[1]->addInt(1 << 24)->shiftRight(25); $h[2] = $h[2]->addInt64($carry1); $h[1] = $h[1]->subInt64($carry1->shiftLeft(25)); $carry3 = $h[3]->addInt(1 << 24)->shiftRight(25); $h[4] = $h[4]->addInt64($carry3); $h[3] = $h[3]->subInt64($carry3->shiftLeft(25)); $carry5 = $h[5]->addInt(1 << 24)->shiftRight(25); $h[6] = $h[6]->addInt64($carry5); $h[5] = $h[5]->subInt64($carry5->shiftLeft(25)); $carry7 = $h[7]->addInt(1 << 24)->shiftRight(25); $h[8] = $h[8]->addInt64($carry7); $h[7] = $h[7]->subInt64($carry7->shiftLeft(25)); $carry0 = $h[0]->addInt(1 << 25)->shiftRight(26); $h[1] = $h[1]->addInt64($carry0); $h[0] = $h[0]->subInt64($carry0->shiftLeft(26)); $carry2 = $h[2]->addInt(1 << 25)->shiftRight(26); $h[3] = $h[3]->addInt64($carry2); $h[2] = $h[2]->subInt64($carry2->shiftLeft(26)); $carry4 = $h[4]->addInt(1 << 25)->shiftRight(26); $h[5] = $h[5]->addInt64($carry4); $h[4] = $h[4]->subInt64($carry4->shiftLeft(26)); $carry6 = $h[6]->addInt(1 << 25)->shiftRight(26); $h[7] = $h[7]->addInt64($carry6); $h[6] = $h[6]->subInt64($carry6->shiftLeft(26)); $carry8 = $h[8]->addInt(1 << 25)->shiftRight(26); $h[9] = $h[9]->addInt64($carry8); $h[8] = $h[8]->subInt64($carry8->shiftLeft(26)); for ($i = 0; $i < 10; ++$i) { $h[$i] = $h[$i]->toInt32(); } /** @var array<int, ParagonIE_Sodium_Core32_Int32> $h2 */ $h2 = $h; return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray($h2); } /** * @internal You should not use this directly from another application * * Inline comments preceded by # are from libsodium's ref10 code. * * @param string $n * @param string $p * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_scalarmult_curve25519_ref10($n, $p) { # for (i = 0;i < 32;++i) e[i] = n[i]; $e = '' . $n; # e[0] &= 248; $e[0] = self::intToChr( self::chrToInt($e[0]) & 248 ); # e[31] &= 127; # e[31] |= 64; $e[31] = self::intToChr( (self::chrToInt($e[31]) & 127) | 64 ); # fe_frombytes(x1,p); $x1 = self::fe_frombytes($p); # fe_1(x2); $x2 = self::fe_1(); # fe_0(z2); $z2 = self::fe_0(); # fe_copy(x3,x1); $x3 = self::fe_copy($x1); # fe_1(z3); $z3 = self::fe_1(); # swap = 0; /** @var int $swap */ $swap = 0; # for (pos = 254;pos >= 0;--pos) { for ($pos = 254; $pos >= 0; --$pos) { # b = e[pos / 8] >> (pos & 7); /** @var int $b */ $b = self::chrToInt( $e[(int) floor($pos / 8)] ) >> ($pos & 7); # b &= 1; $b &= 1; # swap ^= b; $swap ^= $b; # fe_cswap(x2,x3,swap); self::fe_cswap($x2, $x3, $swap); # fe_cswap(z2,z3,swap); self::fe_cswap($z2, $z3, $swap); # swap = b; /** @var int $swap */ $swap = $b; # fe_sub(tmp0,x3,z3); $tmp0 = self::fe_sub($x3, $z3); # fe_sub(tmp1,x2,z2); $tmp1 = self::fe_sub($x2, $z2); # fe_add(x2,x2,z2); $x2 = self::fe_add($x2, $z2); # fe_add(z2,x3,z3); $z2 = self::fe_add($x3, $z3); # fe_mul(z3,tmp0,x2); $z3 = self::fe_mul($tmp0, $x2); # fe_mul(z2,z2,tmp1); $z2 = self::fe_mul($z2, $tmp1); # fe_sq(tmp0,tmp1); $tmp0 = self::fe_sq($tmp1); # fe_sq(tmp1,x2); $tmp1 = self::fe_sq($x2); # fe_add(x3,z3,z2); $x3 = self::fe_add($z3, $z2); # fe_sub(z2,z3,z2); $z2 = self::fe_sub($z3, $z2); # fe_mul(x2,tmp1,tmp0); $x2 = self::fe_mul($tmp1, $tmp0); # fe_sub(tmp1,tmp1,tmp0); $tmp1 = self::fe_sub($tmp1, $tmp0); # fe_sq(z2,z2); $z2 = self::fe_sq($z2); # fe_mul121666(z3,tmp1); $z3 = self::fe_mul121666($tmp1); # fe_sq(x3,x3); $x3 = self::fe_sq($x3); # fe_add(tmp0,tmp0,z3); $tmp0 = self::fe_add($tmp0, $z3); # fe_mul(z3,x1,z2); $z3 = self::fe_mul($x1, $z2); # fe_mul(z2,tmp1,tmp0); $z2 = self::fe_mul($tmp1, $tmp0); } # fe_cswap(x2,x3,swap); self::fe_cswap($x2, $x3, $swap); # fe_cswap(z2,z3,swap); self::fe_cswap($z2, $z3, $swap); # fe_invert(z2,z2); $z2 = self::fe_invert($z2); # fe_mul(x2,x2,z2); $x2 = self::fe_mul($x2, $z2); # fe_tobytes(q,x2); return (string) self::fe_tobytes($x2); } /** * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsY * @param ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsZ * @return ParagonIE_Sodium_Core32_Curve25519_Fe * @throws SodiumException * @throws TypeError */ public static function edwards_to_montgomery( ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsY, ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsZ ) { $tempX = self::fe_add($edwardsZ, $edwardsY); $tempZ = self::fe_sub($edwardsZ, $edwardsY); $tempZ = self::fe_invert($tempZ); return self::fe_mul($tempX, $tempZ); } /** * @internal You should not use this directly from another application * * @param string $n * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_scalarmult_curve25519_ref10_base($n) { # for (i = 0;i < 32;++i) e[i] = n[i]; $e = '' . $n; # e[0] &= 248; $e[0] = self::intToChr( self::chrToInt($e[0]) & 248 ); # e[31] &= 127; # e[31] |= 64; $e[31] = self::intToChr( (self::chrToInt($e[31]) & 127) | 64 ); $A = self::ge_scalarmult_base($e); if ( !($A->Y instanceof ParagonIE_Sodium_Core32_Curve25519_Fe) || !($A->Z instanceof ParagonIE_Sodium_Core32_Curve25519_Fe) ) { throw new TypeError('Null points encountered'); } $pk = self::edwards_to_montgomery($A->Y, $A->Z); return self::fe_tobytes($pk); } } PK ];m[H�w= w= Core32/Ed25519.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Core32_Ed25519', false)) { return; } if (!class_exists('ParagonIE_Sodium_Core32_Curve25519')) { require_once dirname(__FILE__) . '/Curve25519.php'; } /** * Class ParagonIE_Sodium_Core32_Ed25519 */ abstract class ParagonIE_Sodium_Core32_Ed25519 extends ParagonIE_Sodium_Core32_Curve25519 { const KEYPAIR_BYTES = 96; const SEED_BYTES = 32; /** * @internal You should not use this directly from another application * * @return string (96 bytes) * @throws Exception * @throws SodiumException * @throws TypeError */ public static function keypair() { $seed = random_bytes(self::SEED_BYTES); $pk = ''; $sk = ''; self::seed_keypair($pk, $sk, $seed); return $sk . $pk; } /** * @internal You should not use this directly from another application * * @param string $pk * @param string $sk * @param string $seed * @return string * @throws SodiumException * @throws TypeError */ public static function seed_keypair(&$pk, &$sk, $seed) { if (self::strlen($seed) !== self::SEED_BYTES) { throw new RangeException('crypto_sign keypair seed must be 32 bytes long'); } /** @var string $pk */ $pk = self::publickey_from_secretkey($seed); $sk = $seed . $pk; return $sk; } /** * @internal You should not use this directly from another application * * @param string $keypair * @return string * @throws TypeError */ public static function secretkey($keypair) { if (self::strlen($keypair) !== self::KEYPAIR_BYTES) { throw new RangeException('crypto_sign keypair must be 96 bytes long'); } return self::substr($keypair, 0, 64); } /** * @internal You should not use this directly from another application * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function publickey($keypair) { if (self::strlen($keypair) !== self::KEYPAIR_BYTES) { throw new RangeException('crypto_sign keypair must be 96 bytes long'); } return self::substr($keypair, 64, 32); } /** * @internal You should not use this directly from another application * * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function publickey_from_secretkey($sk) { /** @var string $sk */ $sk = hash('sha512', self::substr($sk, 0, 32), true); $sk[0] = self::intToChr( self::chrToInt($sk[0]) & 248 ); $sk[31] = self::intToChr( (self::chrToInt($sk[31]) & 63) | 64 ); return self::sk_to_pk($sk); } /** * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function pk_to_curve25519($pk) { if (self::small_order($pk)) { throw new SodiumException('Public key is on a small order'); } $A = self::ge_frombytes_negate_vartime($pk); $p1 = self::ge_mul_l($A); if (!self::fe_isnonzero($p1->X)) { throw new SodiumException('Unexpected zero result'); } # fe_1(one_minus_y); # fe_sub(one_minus_y, one_minus_y, A.Y); # fe_invert(one_minus_y, one_minus_y); $one_minux_y = self::fe_invert( self::fe_sub( self::fe_1(), $A->Y ) ); # fe_1(x); # fe_add(x, x, A.Y); # fe_mul(x, x, one_minus_y); $x = self::fe_mul( self::fe_add(self::fe_1(), $A->Y), $one_minux_y ); # fe_tobytes(curve25519_pk, x); return self::fe_tobytes($x); } /** * @internal You should not use this directly from another application * * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sk_to_pk($sk) { return self::ge_p3_tobytes( self::ge_scalarmult_base( self::substr($sk, 0, 32) ) ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign($message, $sk) { /** @var string $signature */ $signature = self::sign_detached($message, $sk); return $signature . $message; } /** * @internal You should not use this directly from another application * * @param string $message A signed message * @param string $pk Public key * @return string Message (without signature) * @throws SodiumException * @throws TypeError */ public static function sign_open($message, $pk) { /** @var string $signature */ $signature = self::substr($message, 0, 64); /** @var string $message */ $message = self::substr($message, 64); if (self::verify_detached($signature, $message, $pk)) { return $message; } throw new SodiumException('Invalid signature'); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress PossiblyInvalidArgument */ public static function sign_detached($message, $sk) { # crypto_hash_sha512(az, sk, 32); $az = hash('sha512', self::substr($sk, 0, 32), true); # az[0] &= 248; # az[31] &= 63; # az[31] |= 64; $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); # crypto_hash_sha512_init(&hs); # crypto_hash_sha512_update(&hs, az + 32, 32); # crypto_hash_sha512_update(&hs, m, mlen); # crypto_hash_sha512_final(&hs, nonce); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); self::hash_update($hs, $message); $nonceHash = hash_final($hs, true); # memmove(sig + 32, sk + 32, 32); $pk = self::substr($sk, 32, 32); # sc_reduce(nonce); # ge_scalarmult_base(&R, nonce); # ge_p3_tobytes(sig, &R); $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32); $sig = self::ge_p3_tobytes( self::ge_scalarmult_base($nonce) ); # crypto_hash_sha512_init(&hs); # crypto_hash_sha512_update(&hs, sig, 64); # crypto_hash_sha512_update(&hs, m, mlen); # crypto_hash_sha512_final(&hs, hram); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); self::hash_update($hs, $message); $hramHash = hash_final($hs, true); # sc_reduce(hram); # sc_muladd(sig + 32, hram, az, nonce); $hram = self::sc_reduce($hramHash); $sigAfter = self::sc_muladd($hram, $az, $nonce); $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } return $sig; } /** * @internal You should not use this directly from another application * * @param string $sig * @param string $message * @param string $pk * @return bool * @throws SodiumException * @throws TypeError */ public static function verify_detached($sig, $message, $pk) { if (self::strlen($sig) < 64) { throw new SodiumException('Signature is too short'); } if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) { throw new SodiumException('S < L - Invalid signature'); } if (self::small_order($sig)) { throw new SodiumException('Signature is on too small of an order'); } if ((self::chrToInt($sig[63]) & 224) !== 0) { throw new SodiumException('Invalid signature'); } $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($pk[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ $A = self::ge_frombytes_negate_vartime($pk); /** @var string $hDigest */ $hDigest = hash( 'sha512', self::substr($sig, 0, 32) . self::substr($pk, 0, 32) . $message, true ); /** @var string $h */ $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ $R = self::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = self::ge_tobytes($R); // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; return self::verify_32($rcheck, self::substr($sig, 0, 32)); } /** * @internal You should not use this directly from another application * * @param string $S * @return bool * @throws SodiumException * @throws TypeError */ public static function check_S_lt_L($S) { if (self::strlen($S) < 32) { throw new SodiumException('Signature must be 32 bytes'); } static $L = array( 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ); /** @var array<int, int> $L */ $c = 0; $n = 1; $i = 32; do { --$i; $x = self::chrToInt($S[$i]); $c |= ( (($x - $L[$i]) >> 8) & $n ); $n &= ( (($x ^ $L[$i]) - 1) >> 8 ); } while ($i !== 0); return $c === 0; } /** * @param string $R * @return bool * @throws SodiumException * @throws TypeError */ public static function small_order($R) { static $blocklist = array( /* 0 (order 4) */ array( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), /* 1 (order 1) */ array( 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */ array( 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05 ), /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */ array( 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a ), /* p-1 (order 2) */ array( 0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85 ), /* p (order 4) */ array( 0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa ), /* p+1 (order 1) */ array( 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ), /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */ array( 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ), /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */ array( 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ), /* 2p-1 (order 2) */ array( 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ), /* 2p (order 4) */ array( 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ), /* 2p+1 (order 1) */ array( 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ) ); /** @var array<int, array<int, int>> $blocklist */ $countBlocklist = count($blocklist); for ($i = 0; $i < $countBlocklist; ++$i) { $c = 0; for ($j = 0; $j < 32; ++$j) { $c |= self::chrToInt($R[$j]) ^ $blocklist[$i][$j]; } if ($c === 0) { return true; } } return false; } } PK ];m[]�D�� � Core32/XChaCha20.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Core32_XChaCha20', false)) { return; } /** * Class ParagonIE_Sodium_Core32_XChaCha20 */ class ParagonIE_Sodium_Core32_XChaCha20 extends ParagonIE_Sodium_Core32_HChaCha20 { /** * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function stream($len = 64, $nonce = '', $key = '') { if (self::strlen($nonce) !== 24) { throw new SodiumException('Nonce must be 24 bytes long'); } return self::encryptBytes( new ParagonIE_Sodium_Core32_ChaCha20_Ctx( self::hChaCha20( self::substr($nonce, 0, 16), $key ), self::substr($nonce, 16, 8) ), str_repeat("\x00", $len) ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @param string $ic * @return string * @throws SodiumException * @throws TypeError */ public static function streamXorIc($message, $nonce = '', $key = '', $ic = '') { if (self::strlen($nonce) !== 24) { throw new SodiumException('Nonce must be 24 bytes long'); } return self::encryptBytes( new ParagonIE_Sodium_Core32_ChaCha20_Ctx( self::hChaCha20(self::substr($nonce, 0, 16), $key), self::substr($nonce, 16, 8), $ic ), $message ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @param string $ic * @return string * @throws SodiumException * @throws TypeError */ public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '') { return self::encryptBytes( new ParagonIE_Sodium_Core32_ChaCha20_IetfCtx( self::hChaCha20(self::substr($nonce, 0, 16), $key), "\x00\x00\x00\x00" . self::substr($nonce, 16, 8), $ic ), $message ); } } PK ];m[F&bc c Core32/XSalsa20.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Core32_XSalsa20', false)) { return; } /** * Class ParagonIE_Sodium_Core32_XSalsa20 */ abstract class ParagonIE_Sodium_Core32_XSalsa20 extends ParagonIE_Sodium_Core32_HSalsa20 { /** * Expand a key and nonce into an xsalsa20 keystream. * * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function xsalsa20($len, $nonce, $key) { $ret = self::salsa20( $len, self::substr($nonce, 16, 8), self::hsalsa20($nonce, $key) ); return $ret; } /** * Encrypt a string with XSalsa20. Doesn't provide integrity. * * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function xsalsa20_xor($message, $nonce, $key) { return self::xorStrings( $message, self::xsalsa20( self::strlen($message), $nonce, $key ) ); } } PK ];m[[�(�� � Core32/SipHash.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Core32_SipHash', false)) { return; } /** * Class ParagonIE_SodiumCompat_Core32_SipHash * * Only uses 32-bit arithmetic, while the original SipHash used 64-bit integers */ class ParagonIE_Sodium_Core32_SipHash extends ParagonIE_Sodium_Core32_Util { /** * @internal You should not use this directly from another application * * @param array<int, ParagonIE_Sodium_Core32_Int64> $v * @return array<int, ParagonIE_Sodium_Core32_Int64> */ public static function sipRound(array $v) { # v0 += v1; $v[0] = $v[0]->addInt64($v[1]); # v1 = ROTL(v1, 13); $v[1] = $v[1]->rotateLeft(13); # v1 ^= v0; $v[1] = $v[1]->xorInt64($v[0]); # v0=ROTL(v0,32); $v[0] = $v[0]->rotateLeft(32); # v2 += v3; $v[2] = $v[2]->addInt64($v[3]); # v3=ROTL(v3,16); $v[3] = $v[3]->rotateLeft(16); # v3 ^= v2; $v[3] = $v[3]->xorInt64($v[2]); # v0 += v3; $v[0] = $v[0]->addInt64($v[3]); # v3=ROTL(v3,21); $v[3] = $v[3]->rotateLeft(21); # v3 ^= v0; $v[3] = $v[3]->xorInt64($v[0]); # v2 += v1; $v[2] = $v[2]->addInt64($v[1]); # v1=ROTL(v1,17); $v[1] = $v[1]->rotateLeft(17); # v1 ^= v2; $v[1] = $v[1]->xorInt64($v[2]); # v2=ROTL(v2,32) $v[2] = $v[2]->rotateLeft(32); return $v; } /** * @internal You should not use this directly from another application * * @param string $in * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function sipHash24($in, $key) { $inlen = self::strlen($in); # /* "somepseudorandomlygeneratedbytes" */ # u64 v0 = 0x736f6d6570736575ULL; # u64 v1 = 0x646f72616e646f6dULL; # u64 v2 = 0x6c7967656e657261ULL; # u64 v3 = 0x7465646279746573ULL; $v = array( new ParagonIE_Sodium_Core32_Int64( array(0x736f, 0x6d65, 0x7073, 0x6575) ), new ParagonIE_Sodium_Core32_Int64( array(0x646f, 0x7261, 0x6e64, 0x6f6d) ), new ParagonIE_Sodium_Core32_Int64( array(0x6c79, 0x6765, 0x6e65, 0x7261) ), new ParagonIE_Sodium_Core32_Int64( array(0x7465, 0x6462, 0x7974, 0x6573) ) ); # u64 k0 = LOAD64_LE( k ); # u64 k1 = LOAD64_LE( k + 8 ); $k = array( ParagonIE_Sodium_Core32_Int64::fromReverseString( self::substr($key, 0, 8) ), ParagonIE_Sodium_Core32_Int64::fromReverseString( self::substr($key, 8, 8) ) ); # b = ( ( u64 )inlen ) << 56; $b = new ParagonIE_Sodium_Core32_Int64( array(($inlen << 8) & 0xffff, 0, 0, 0) ); # v3 ^= k1; $v[3] = $v[3]->xorInt64($k[1]); # v2 ^= k0; $v[2] = $v[2]->xorInt64($k[0]); # v1 ^= k1; $v[1] = $v[1]->xorInt64($k[1]); # v0 ^= k0; $v[0] = $v[0]->xorInt64($k[0]); $left = $inlen; # for ( ; in != end; in += 8 ) while ($left >= 8) { # m = LOAD64_LE( in ); $m = ParagonIE_Sodium_Core32_Int64::fromReverseString( self::substr($in, 0, 8) ); # v3 ^= m; $v[3] = $v[3]->xorInt64($m); # SIPROUND; # SIPROUND; $v = self::sipRound($v); $v = self::sipRound($v); # v0 ^= m; $v[0] = $v[0]->xorInt64($m); $in = self::substr($in, 8); $left -= 8; } # switch( left ) # { # case 7: b |= ( ( u64 )in[ 6] ) << 48; # case 6: b |= ( ( u64 )in[ 5] ) << 40; # case 5: b |= ( ( u64 )in[ 4] ) << 32; # case 4: b |= ( ( u64 )in[ 3] ) << 24; # case 3: b |= ( ( u64 )in[ 2] ) << 16; # case 2: b |= ( ( u64 )in[ 1] ) << 8; # case 1: b |= ( ( u64 )in[ 0] ); break; # case 0: break; # } switch ($left) { case 7: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( 0, self::chrToInt($in[6]) << 16 ) ); case 6: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( 0, self::chrToInt($in[5]) << 8 ) ); case 5: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( 0, self::chrToInt($in[4]) ) ); case 4: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( self::chrToInt($in[3]) << 24, 0 ) ); case 3: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( self::chrToInt($in[2]) << 16, 0 ) ); case 2: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( self::chrToInt($in[1]) << 8, 0 ) ); case 1: $b = $b->orInt64( ParagonIE_Sodium_Core32_Int64::fromInts( self::chrToInt($in[0]), 0 ) ); case 0: break; } # v3 ^= b; $v[3] = $v[3]->xorInt64($b); # SIPROUND; # SIPROUND; $v = self::sipRound($v); $v = self::sipRound($v); # v0 ^= b; $v[0] = $v[0]->xorInt64($b); // Flip the lower 8 bits of v2 which is ($v[4], $v[5]) in our implementation # v2 ^= 0xff; $v[2]->limbs[3] ^= 0xff; # SIPROUND; # SIPROUND; # SIPROUND; # SIPROUND; $v = self::sipRound($v); $v = self::sipRound($v); $v = self::sipRound($v); $v = self::sipRound($v); # b = v0 ^ v1 ^ v2 ^ v3; # STORE64_LE( out, b ); return $v[0] ->xorInt64($v[1]) ->xorInt64($v[2]) ->xorInt64($v[3]) ->toReverseString(); } } PK ];m[=��/�y �y Core32/Int64.phpnu �[��� <?php /** * Class ParagonIE_Sodium_Core32_Int64 * * Encapsulates a 64-bit integer. * * These are immutable. It always returns a new instance. */ class ParagonIE_Sodium_Core32_Int64 { /** * @var array<int, int> - four 16-bit integers */ public $limbs = array(0, 0, 0, 0); /** * @var int */ public $overflow = 0; /** * @var bool */ public $unsignedInt = false; /** * ParagonIE_Sodium_Core32_Int64 constructor. * @param array $array * @param bool $unsignedInt */ public function __construct($array = array(0, 0, 0, 0), $unsignedInt = false) { $this->limbs = array( (int) $array[0], (int) $array[1], (int) $array[2], (int) $array[3] ); $this->overflow = 0; $this->unsignedInt = $unsignedInt; } /** * Adds two int64 objects * * @param ParagonIE_Sodium_Core32_Int64 $addend * @return ParagonIE_Sodium_Core32_Int64 */ public function addInt64(ParagonIE_Sodium_Core32_Int64 $addend) { $i0 = $this->limbs[0]; $i1 = $this->limbs[1]; $i2 = $this->limbs[2]; $i3 = $this->limbs[3]; $j0 = $addend->limbs[0]; $j1 = $addend->limbs[1]; $j2 = $addend->limbs[2]; $j3 = $addend->limbs[3]; $r3 = $i3 + ($j3 & 0xffff); $carry = $r3 >> 16; $r2 = $i2 + ($j2 & 0xffff) + $carry; $carry = $r2 >> 16; $r1 = $i1 + ($j1 & 0xffff) + $carry; $carry = $r1 >> 16; $r0 = $i0 + ($j0 & 0xffff) + $carry; $carry = $r0 >> 16; $r0 &= 0xffff; $r1 &= 0xffff; $r2 &= 0xffff; $r3 &= 0xffff; $return = new ParagonIE_Sodium_Core32_Int64( array($r0, $r1, $r2, $r3) ); $return->overflow = $carry; $return->unsignedInt = $this->unsignedInt; return $return; } /** * Adds a normal integer to an int64 object * * @param int $int * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError */ public function addInt($int) { ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1); /** @var int $int */ $int = (int) $int; $i0 = $this->limbs[0]; $i1 = $this->limbs[1]; $i2 = $this->limbs[2]; $i3 = $this->limbs[3]; $r3 = $i3 + ($int & 0xffff); $carry = $r3 >> 16; $r2 = $i2 + (($int >> 16) & 0xffff) + $carry; $carry = $r2 >> 16; $r1 = $i1 + $carry; $carry = $r1 >> 16; $r0 = $i0 + $carry; $carry = $r0 >> 16; $r0 &= 0xffff; $r1 &= 0xffff; $r2 &= 0xffff; $r3 &= 0xffff; $return = new ParagonIE_Sodium_Core32_Int64( array($r0, $r1, $r2, $r3) ); $return->overflow = $carry; $return->unsignedInt = $this->unsignedInt; return $return; } /** * @param int $b * @return int */ public function compareInt($b = 0) { $gt = 0; $eq = 1; $i = 4; $j = 0; while ($i > 0) { --$i; /** @var int $x1 */ $x1 = $this->limbs[$i]; /** @var int $x2 */ $x2 = ($b >> ($j << 4)) & 0xffff; /** int */ $gt |= (($x2 - $x1) >> 8) & $eq; /** int */ $eq &= (($x2 ^ $x1) - 1) >> 8; } return ($gt + $gt - $eq) + 1; } /** * @param int $b * @return bool */ public function isGreaterThan($b = 0) { return $this->compareInt($b) > 0; } /** * @param int $b * @return bool */ public function isLessThanInt($b = 0) { return $this->compareInt($b) < 0; } /** * @param int $hi * @param int $lo * @return ParagonIE_Sodium_Core32_Int64 */ public function mask64($hi = 0, $lo = 0) { /** @var int $a */ $a = ($hi >> 16) & 0xffff; /** @var int $b */ $b = ($hi) & 0xffff; /** @var int $c */ $c = ($lo >> 16) & 0xffff; /** @var int $d */ $d = ($lo & 0xffff); return new ParagonIE_Sodium_Core32_Int64( array( $this->limbs[0] & $a, $this->limbs[1] & $b, $this->limbs[2] & $c, $this->limbs[3] & $d ), $this->unsignedInt ); } /** * @param int $int * @param int $size * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedAssignment */ public function mulInt($int = 0, $size = 0) { if (ParagonIE_Sodium_Compat::$fastMult) { return $this->mulIntFast($int); } ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1); ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2); /** @var int $int */ $int = (int) $int; /** @var int $size */ $size = (int) $size; if (!$size) { $size = 63; } $a = clone $this; $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; // Initialize: $ret0 = 0; $ret1 = 0; $ret2 = 0; $ret3 = 0; $a0 = $a->limbs[0]; $a1 = $a->limbs[1]; $a2 = $a->limbs[2]; $a3 = $a->limbs[3]; /** @var int $size */ /** @var int $i */ for ($i = $size; $i >= 0; --$i) { $mask = -($int & 1); $x0 = $a0 & $mask; $x1 = $a1 & $mask; $x2 = $a2 & $mask; $x3 = $a3 & $mask; $ret3 += $x3; $c = $ret3 >> 16; $ret2 += $x2 + $c; $c = $ret2 >> 16; $ret1 += $x1 + $c; $c = $ret1 >> 16; $ret0 += $x0 + $c; $ret0 &= 0xffff; $ret1 &= 0xffff; $ret2 &= 0xffff; $ret3 &= 0xffff; $a3 = $a3 << 1; $x3 = $a3 >> 16; $a2 = ($a2 << 1) | $x3; $x2 = $a2 >> 16; $a1 = ($a1 << 1) | $x2; $x1 = $a1 >> 16; $a0 = ($a0 << 1) | $x1; $a0 &= 0xffff; $a1 &= 0xffff; $a2 &= 0xffff; $a3 &= 0xffff; $int >>= 1; } $return->limbs[0] = $ret0; $return->limbs[1] = $ret1; $return->limbs[2] = $ret2; $return->limbs[3] = $ret3; return $return; } /** * @param ParagonIE_Sodium_Core32_Int64 $A * @param ParagonIE_Sodium_Core32_Int64 $B * @return array<int, ParagonIE_Sodium_Core32_Int64> * @throws SodiumException * @throws TypeError * @psalm-suppress MixedInferredReturnType */ public static function ctSelect( ParagonIE_Sodium_Core32_Int64 $A, ParagonIE_Sodium_Core32_Int64 $B ) { $a = clone $A; $b = clone $B; /** @var int $aNeg */ $aNeg = ($a->limbs[0] >> 15) & 1; /** @var int $bNeg */ $bNeg = ($b->limbs[0] >> 15) & 1; /** @var int $m */ $m = (-($aNeg & $bNeg)) | 1; /** @var int $swap */ $swap = $bNeg & ~$aNeg; /** @var int $d */ $d = -$swap; /* if ($bNeg && !$aNeg) { $a = clone $int; $b = clone $this; } elseif($bNeg && $aNeg) { $a = $this->mulInt(-1); $b = $int->mulInt(-1); } */ $x = $a->xorInt64($b)->mask64($d, $d); return array( $a->xorInt64($x)->mulInt($m), $b->xorInt64($x)->mulInt($m) ); } /** * @param array<int, int> $a * @param array<int, int> $b * @param int $baseLog2 * @return array<int, int> */ public function multiplyLong(array $a, array $b, $baseLog2 = 16) { $a_l = count($a); $b_l = count($b); /** @var array<int, int> $r */ $r = array_fill(0, $a_l + $b_l + 1, 0); $base = 1 << $baseLog2; for ($i = 0; $i < $a_l; ++$i) { $a_i = $a[$i]; for ($j = 0; $j < $a_l; ++$j) { $b_j = $b[$j]; $product = (($a_i * $b_j) + $r[$i + $j]); $carry = (((int) $product >> $baseLog2) & 0xffff); $r[$i + $j] = ((int) $product - (int) ($carry * $base)) & 0xffff; $r[$i + $j + 1] += $carry; } } return array_slice($r, 0, 5); } /** * @param int $int * @return ParagonIE_Sodium_Core32_Int64 */ public function mulIntFast($int) { // Handle negative numbers $aNeg = ($this->limbs[0] >> 15) & 1; $bNeg = ($int >> 31) & 1; $a = array_reverse($this->limbs); $b = array( $int & 0xffff, ($int >> 16) & 0xffff, -$bNeg & 0xffff, -$bNeg & 0xffff ); if ($aNeg) { for ($i = 0; $i < 4; ++$i) { $a[$i] = ($a[$i] ^ 0xffff) & 0xffff; } ++$a[0]; } if ($bNeg) { for ($i = 0; $i < 4; ++$i) { $b[$i] = ($b[$i] ^ 0xffff) & 0xffff; } ++$b[0]; } // Multiply $res = $this->multiplyLong($a, $b); // Re-apply negation to results if ($aNeg !== $bNeg) { for ($i = 0; $i < 4; ++$i) { $res[$i] = (0xffff ^ $res[$i]) & 0xffff; } // Handle integer overflow $c = 1; for ($i = 0; $i < 4; ++$i) { $res[$i] += $c; $c = $res[$i] >> 16; $res[$i] &= 0xffff; } } // Return our values $return = new ParagonIE_Sodium_Core32_Int64(); $return->limbs = array( $res[3] & 0xffff, $res[2] & 0xffff, $res[1] & 0xffff, $res[0] & 0xffff ); if (count($res) > 4) { $return->overflow = $res[4] & 0xffff; } $return->unsignedInt = $this->unsignedInt; return $return; } /** * @param ParagonIE_Sodium_Core32_Int64 $right * @return ParagonIE_Sodium_Core32_Int64 */ public function mulInt64Fast(ParagonIE_Sodium_Core32_Int64 $right) { $aNeg = ($this->limbs[0] >> 15) & 1; $bNeg = ($right->limbs[0] >> 15) & 1; $a = array_reverse($this->limbs); $b = array_reverse($right->limbs); if ($aNeg) { for ($i = 0; $i < 4; ++$i) { $a[$i] = ($a[$i] ^ 0xffff) & 0xffff; } ++$a[0]; } if ($bNeg) { for ($i = 0; $i < 4; ++$i) { $b[$i] = ($b[$i] ^ 0xffff) & 0xffff; } ++$b[0]; } $res = $this->multiplyLong($a, $b); if ($aNeg !== $bNeg) { if ($aNeg !== $bNeg) { for ($i = 0; $i < 4; ++$i) { $res[$i] = ($res[$i] ^ 0xffff) & 0xffff; } $c = 1; for ($i = 0; $i < 4; ++$i) { $res[$i] += $c; $c = $res[$i] >> 16; $res[$i] &= 0xffff; } } } $return = new ParagonIE_Sodium_Core32_Int64(); $return->limbs = array( $res[3] & 0xffff, $res[2] & 0xffff, $res[1] & 0xffff, $res[0] & 0xffff ); if (count($res) > 4) { $return->overflow = $res[4]; } return $return; } /** * @param ParagonIE_Sodium_Core32_Int64 $int * @param int $size * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedAssignment */ public function mulInt64(ParagonIE_Sodium_Core32_Int64 $int, $size = 0) { if (ParagonIE_Sodium_Compat::$fastMult) { return $this->mulInt64Fast($int); } ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2); if (!$size) { $size = 63; } list($a, $b) = self::ctSelect($this, $int); $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; // Initialize: $ret0 = 0; $ret1 = 0; $ret2 = 0; $ret3 = 0; $a0 = $a->limbs[0]; $a1 = $a->limbs[1]; $a2 = $a->limbs[2]; $a3 = $a->limbs[3]; $b0 = $b->limbs[0]; $b1 = $b->limbs[1]; $b2 = $b->limbs[2]; $b3 = $b->limbs[3]; /** @var int $size */ /** @var int $i */ for ($i = (int) $size; $i >= 0; --$i) { $mask = -($b3 & 1); $x0 = $a0 & $mask; $x1 = $a1 & $mask; $x2 = $a2 & $mask; $x3 = $a3 & $mask; $ret3 += $x3; $c = $ret3 >> 16; $ret2 += $x2 + $c; $c = $ret2 >> 16; $ret1 += $x1 + $c; $c = $ret1 >> 16; $ret0 += $x0 + $c; $ret0 &= 0xffff; $ret1 &= 0xffff; $ret2 &= 0xffff; $ret3 &= 0xffff; $a3 = $a3 << 1; $x3 = $a3 >> 16; $a2 = ($a2 << 1) | $x3; $x2 = $a2 >> 16; $a1 = ($a1 << 1) | $x2; $x1 = $a1 >> 16; $a0 = ($a0 << 1) | $x1; $a0 &= 0xffff; $a1 &= 0xffff; $a2 &= 0xffff; $a3 &= 0xffff; $x0 = ($b0 & 1) << 16; $x1 = ($b1 & 1) << 16; $x2 = ($b2 & 1) << 16; $b0 = ($b0 >> 1); $b1 = (($b1 | $x0) >> 1); $b2 = (($b2 | $x1) >> 1); $b3 = (($b3 | $x2) >> 1); $b0 &= 0xffff; $b1 &= 0xffff; $b2 &= 0xffff; $b3 &= 0xffff; } $return->limbs[0] = $ret0; $return->limbs[1] = $ret1; $return->limbs[2] = $ret2; $return->limbs[3] = $ret3; return $return; } /** * OR this 64-bit integer with another. * * @param ParagonIE_Sodium_Core32_Int64 $b * @return ParagonIE_Sodium_Core32_Int64 */ public function orInt64(ParagonIE_Sodium_Core32_Int64 $b) { $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; $return->limbs = array( (int) ($this->limbs[0] | $b->limbs[0]), (int) ($this->limbs[1] | $b->limbs[1]), (int) ($this->limbs[2] | $b->limbs[2]), (int) ($this->limbs[3] | $b->limbs[3]) ); return $return; } /** * @param int $c * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArrayAccess */ public function rotateLeft($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; $c &= 63; if ($c === 0) { // NOP, but we want a copy. $return->limbs = $this->limbs; } else { /** @var array<int, int> $limbs */ $limbs =& $return->limbs; /** @var array<int, int> $myLimbs */ $myLimbs =& $this->limbs; /** @var int $idx_shift */ $idx_shift = ($c >> 4) & 3; /** @var int $sub_shift */ $sub_shift = $c & 15; for ($i = 3; $i >= 0; --$i) { /** @var int $j */ $j = ($i + $idx_shift) & 3; /** @var int $k */ $k = ($i + $idx_shift + 1) & 3; $limbs[$i] = (int) ( ( ((int) ($myLimbs[$j]) << $sub_shift) | ((int) ($myLimbs[$k]) >> (16 - $sub_shift)) ) & 0xffff ); } } return $return; } /** * Rotate to the right * * @param int $c * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArrayAccess */ public function rotateRight($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; /** @var ParagonIE_Sodium_Core32_Int64 $return */ $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; $c &= 63; /** @var int $c */ if ($c === 0) { // NOP, but we want a copy. $return->limbs = $this->limbs; } else { /** @var array<int, int> $limbs */ $limbs =& $return->limbs; /** @var array<int, int> $myLimbs */ $myLimbs =& $this->limbs; /** @var int $idx_shift */ $idx_shift = ($c >> 4) & 3; /** @var int $sub_shift */ $sub_shift = $c & 15; for ($i = 3; $i >= 0; --$i) { /** @var int $j */ $j = ($i - $idx_shift) & 3; /** @var int $k */ $k = ($i - $idx_shift - 1) & 3; $limbs[$i] = (int) ( ( ((int) ($myLimbs[$j]) >> (int) ($sub_shift)) | ((int) ($myLimbs[$k]) << (16 - (int) ($sub_shift))) ) & 0xffff ); } } return $return; } /** * @param int $c * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError */ public function shiftLeft($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); /** @var int $c */ $c = (int) $c; $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; $c &= 63; if ($c >= 16) { if ($c >= 48) { $return->limbs = array( $this->limbs[3], 0, 0, 0 ); } elseif ($c >= 32) { $return->limbs = array( $this->limbs[2], $this->limbs[3], 0, 0 ); } else { $return->limbs = array( $this->limbs[1], $this->limbs[2], $this->limbs[3], 0 ); } return $return->shiftLeft($c & 15); } if ($c === 0) { $return->limbs = $this->limbs; } elseif ($c < 0) { /** @var int $c */ return $this->shiftRight(-$c); } else { if (!is_int($c)) { throw new TypeError(); } /** @var int $carry */ $carry = 0; for ($i = 3; $i >= 0; --$i) { /** @var int $tmp */ $tmp = ($this->limbs[$i] << $c) | ($carry & 0xffff); $return->limbs[$i] = (int) ($tmp & 0xffff); /** @var int $carry */ $carry = $tmp >> 16; } } return $return; } /** * @param int $c * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError */ public function shiftRight($c = 0) { ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1); $c = (int) $c; /** @var int $c */ $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; $c &= 63; $negative = -(($this->limbs[0] >> 15) & 1); if ($c >= 16) { if ($c >= 48) { $return->limbs = array( (int) ($negative & 0xffff), (int) ($negative & 0xffff), (int) ($negative & 0xffff), (int) $this->limbs[0] ); } elseif ($c >= 32) { $return->limbs = array( (int) ($negative & 0xffff), (int) ($negative & 0xffff), (int) $this->limbs[0], (int) $this->limbs[1] ); } else { $return->limbs = array( (int) ($negative & 0xffff), (int) $this->limbs[0], (int) $this->limbs[1], (int) $this->limbs[2] ); } return $return->shiftRight($c & 15); } if ($c === 0) { $return->limbs = $this->limbs; } elseif ($c < 0) { return $this->shiftLeft(-$c); } else { if (!is_int($c)) { throw new TypeError(); } /** @var int $carryRight */ $carryRight = ($negative & 0xffff); $mask = (int) (((1 << ($c + 1)) - 1) & 0xffff); for ($i = 0; $i < 4; ++$i) { $return->limbs[$i] = (int) ( (($this->limbs[$i] >> $c) | ($carryRight << (16 - $c))) & 0xffff ); $carryRight = (int) ($this->limbs[$i] & $mask); } } return $return; } /** * Subtract a normal integer from an int64 object. * * @param int $int * @return ParagonIE_Sodium_Core32_Int64 * @throws SodiumException * @throws TypeError */ public function subInt($int) { ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1); $int = (int) $int; $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; /** @var int $carry */ $carry = 0; for ($i = 3; $i >= 0; --$i) { /** @var int $tmp */ $tmp = $this->limbs[$i] - (($int >> 16) & 0xffff) + $carry; /** @var int $carry */ $carry = $tmp >> 16; $return->limbs[$i] = (int) ($tmp & 0xffff); } return $return; } /** * The difference between two Int64 objects. * * @param ParagonIE_Sodium_Core32_Int64 $b * @return ParagonIE_Sodium_Core32_Int64 */ public function subInt64(ParagonIE_Sodium_Core32_Int64 $b) { $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; /** @var int $carry */ $carry = 0; for ($i = 3; $i >= 0; --$i) { /** @var int $tmp */ $tmp = $this->limbs[$i] - $b->limbs[$i] + $carry; /** @var int $carry */ $carry = ($tmp >> 16); $return->limbs[$i] = (int) ($tmp & 0xffff); } return $return; } /** * XOR this 64-bit integer with another. * * @param ParagonIE_Sodium_Core32_Int64 $b * @return ParagonIE_Sodium_Core32_Int64 */ public function xorInt64(ParagonIE_Sodium_Core32_Int64 $b) { $return = new ParagonIE_Sodium_Core32_Int64(); $return->unsignedInt = $this->unsignedInt; $return->limbs = array( (int) ($this->limbs[0] ^ $b->limbs[0]), (int) ($this->limbs[1] ^ $b->limbs[1]), (int) ($this->limbs[2] ^ $b->limbs[2]), (int) ($this->limbs[3] ^ $b->limbs[3]) ); return $return; } /** * @param int $low * @param int $high * @return self * @throws SodiumException * @throws TypeError */ public static function fromInts($low, $high) { ParagonIE_Sodium_Core32_Util::declareScalarType($low, 'int', 1); ParagonIE_Sodium_Core32_Util::declareScalarType($high, 'int', 2); $high = (int) $high; $low = (int) $low; return new ParagonIE_Sodium_Core32_Int64( array( (int) (($high >> 16) & 0xffff), (int) ($high & 0xffff), (int) (($low >> 16) & 0xffff), (int) ($low & 0xffff) ) ); } /** * @param int $low * @return self * @throws SodiumException * @throws TypeError */ public static function fromInt($low) { ParagonIE_Sodium_Core32_Util::declareScalarType($low, 'int', 1); $low = (int) $low; return new ParagonIE_Sodium_Core32_Int64( array( 0, 0, (int) (($low >> 16) & 0xffff), (int) ($low & 0xffff) ) ); } /** * @return int */ public function toInt() { return (int) ( (($this->limbs[2] & 0xffff) << 16) | ($this->limbs[3] & 0xffff) ); } /** * @param string $string * @return self * @throws SodiumException * @throws TypeError */ public static function fromString($string) { ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1); $string = (string) $string; if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 8) { throw new RangeException( 'String must be 8 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.' ); } $return = new ParagonIE_Sodium_Core32_Int64(); $return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff) << 8); $return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff); $return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff) << 8); $return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff); $return->limbs[2] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[4]) & 0xff) << 8); $return->limbs[2] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[5]) & 0xff); $return->limbs[3] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[6]) & 0xff) << 8); $return->limbs[3] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[7]) & 0xff); return $return; } /** * @param string $string * @return self * @throws SodiumException * @throws TypeError */ public static function fromReverseString($string) { ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1); $string = (string) $string; if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 8) { throw new RangeException( 'String must be 8 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.' ); } $return = new ParagonIE_Sodium_Core32_Int64(); $return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[7]) & 0xff) << 8); $return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[6]) & 0xff); $return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[5]) & 0xff) << 8); $return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[4]) & 0xff); $return->limbs[2] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff) << 8); $return->limbs[2] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff); $return->limbs[3] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff) << 8); $return->limbs[3] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff); return $return; } /** * @return array<int, int> */ public function toArray() { return array( (int) ((($this->limbs[0] & 0xffff) << 16) | ($this->limbs[1] & 0xffff)), (int) ((($this->limbs[2] & 0xffff) << 16) | ($this->limbs[3] & 0xffff)) ); } /** * @return ParagonIE_Sodium_Core32_Int32 */ public function toInt32() { $return = new ParagonIE_Sodium_Core32_Int32(); $return->limbs[0] = (int) ($this->limbs[2]); $return->limbs[1] = (int) ($this->limbs[3]); $return->unsignedInt = $this->unsignedInt; $return->overflow = (int) (ParagonIE_Sodium_Core32_Util::abs($this->limbs[1], 16) & 0xffff); return $return; } /** * @return ParagonIE_Sodium_Core32_Int64 */ public function toInt64() { $return = new ParagonIE_Sodium_Core32_Int64(); $return->limbs[0] = (int) ($this->limbs[0]); $return->limbs[1] = (int) ($this->limbs[1]); $return->limbs[2] = (int) ($this->limbs[2]); $return->limbs[3] = (int) ($this->limbs[3]); $return->unsignedInt = $this->unsignedInt; $return->overflow = ParagonIE_Sodium_Core32_Util::abs($this->overflow); return $return; } /** * @param bool $bool * @return self */ public function setUnsignedInt($bool = false) { $this->unsignedInt = !empty($bool); return $this; } /** * @return string * @throws TypeError */ public function toString() { return ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[2] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[2] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[3] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[3] & 0xff); } /** * @return string * @throws TypeError */ public function toReverseString() { return ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[3] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[3] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[2] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[2] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) . ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff); } /** * @return string */ public function __toString() { try { return $this->toString(); } catch (TypeError $ex) { // PHP engine can't handle exceptions from __toString() return ''; } } } PK ];m[��xH H Core32/SecretStream/State.phpnu �[��� <?php /** * Class ParagonIE_Sodium_Core32_SecretStream_State */ class ParagonIE_Sodium_Core32_SecretStream_State { /** @var string $key */ protected $key; /** @var int $counter */ protected $counter; /** @var string $nonce */ protected $nonce; /** @var string $_pad */ protected $_pad; /** * ParagonIE_Sodium_Core32_SecretStream_State constructor. * @param string $key * @param string|null $nonce */ public function __construct($key, $nonce = null) { $this->key = $key; $this->counter = 1; if (is_null($nonce)) { $nonce = str_repeat("\0", 12); } $this->nonce = str_pad($nonce, 12, "\0", STR_PAD_RIGHT);; $this->_pad = str_repeat("\0", 4); } /** * @return self */ public function counterReset() { $this->counter = 1; $this->_pad = str_repeat("\0", 4); return $this; } /** * @return string */ public function getKey() { return $this->key; } /** * @return string */ public function getCounter() { return ParagonIE_Sodium_Core32_Util::store32_le($this->counter); } /** * @return string */ public function getNonce() { if (!is_string($this->nonce)) { $this->nonce = str_repeat("\0", 12); } if (ParagonIE_Sodium_Core32_Util::strlen($this->nonce) !== 12) { $this->nonce = str_pad($this->nonce, 12, "\0", STR_PAD_RIGHT); } return $this->nonce; } /** * @return string */ public function getCombinedNonce() { return $this->getCounter() . ParagonIE_Sodium_Core32_Util::substr($this->getNonce(), 0, 8); } /** * @return self */ public function incrementCounter() { ++$this->counter; return $this; } /** * @return bool */ public function needsRekey() { return ($this->counter & 0xffff) === 0; } /** * @param string $newKeyAndNonce * @return self */ public function rekey($newKeyAndNonce) { $this->key = ParagonIE_Sodium_Core32_Util::substr($newKeyAndNonce, 0, 32); $this->nonce = str_pad( ParagonIE_Sodium_Core32_Util::substr($newKeyAndNonce, 32), 12, "\0", STR_PAD_RIGHT ); return $this; } /** * @param string $str * @return self */ public function xorNonce($str) { $this->nonce = ParagonIE_Sodium_Core32_Util::xorStrings( $this->getNonce(), str_pad( ParagonIE_Sodium_Core32_Util::substr($str, 0, 8), 12, "\0", STR_PAD_RIGHT ) ); return $this; } /** * @param string $string * @return self */ public static function fromString($string) { $state = new ParagonIE_Sodium_Core32_SecretStream_State( ParagonIE_Sodium_Core32_Util::substr($string, 0, 32) ); $state->counter = ParagonIE_Sodium_Core32_Util::load_4( ParagonIE_Sodium_Core32_Util::substr($string, 32, 4) ); $state->nonce = ParagonIE_Sodium_Core32_Util::substr($string, 36, 12); $state->_pad = ParagonIE_Sodium_Core32_Util::substr($string, 48, 8); return $state; } /** * @return string */ public function toString() { return $this->key . $this->getCounter() . $this->nonce . $this->_pad; } } PK ];m[�X]>