File manager - Edit - /home/thefreecuj/www/sodium_compat.zip
Back
PK �}[� �H H composer.jsonnu �[��� { "name": "paragonie/sodium_compat", "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", "keywords": [ "PHP", "cryptography", "elliptic curve", "elliptic curve cryptography", "Pure-PHP cryptography", "side-channel resistant", "Curve25519", "X25519", "ECDH", "Elliptic Curve Diffie-Hellman", "Ed25519", "RFC 7748", "RFC 8032", "EdDSA", "Edwards-curve Digital Signature Algorithm", "ChaCha20", "Salsa20", "Xchacha20", "Xsalsa20", "Poly1305", "BLAKE2b", "public-key cryptography", "secret-key cryptography", "AEAD", "Chapoly", "Salpoly", "ChaCha20-Poly1305", "XSalsa20-Poly1305", "XChaCha20-Poly1305", "encryption", "authentication", "libsodium" ], "license": "ISC", "authors": [ { "name": "Paragon Initiative Enterprises", "email": "security@paragonie.com" }, { "name": "Frank Denis", "email": "jedisct1@pureftpd.org" } ], "autoload": { "files": ["autoload.php"] }, "require": { "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8", "paragonie/random_compat": ">=1" }, "require-dev": { "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" }, "scripts": { "test": "phpunit" }, "suggest": { "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." } } PK �}[���- - autoload.phpnu �[��� <?php if (PHP_VERSION_ID < 70000) { if (!is_callable('sodiumCompatAutoloader')) { /** * Sodium_Compat autoloader. * * @param string $class Class name to be autoloaded. * * @return bool Stop autoloading? */ function sodiumCompatAutoloader($class) { $namespace = 'ParagonIE_Sodium_'; // Does the class use the namespace prefix? $len = strlen($namespace); if (strncmp($namespace, $class, $len) !== 0) { // no, move to the next registered autoloader return false; } // Get the relative class name $relative_class = substr($class, $len); // Replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php $file = dirname(__FILE__) . '/src/' . str_replace('_', '/', $relative_class) . '.php'; // if the file exists, require it if (file_exists($file)) { require_once $file; return true; } return false; } // Now that we have an autoloader, let's register it! spl_autoload_register('sodiumCompatAutoloader'); } } else { require_once dirname(__FILE__) . '/autoload-php7.php'; } /* Explicitly, always load the Compat class: */ if (!class_exists('ParagonIE_Sodium_Compat', false)) { require_once dirname(__FILE__) . '/src/Compat.php'; } if (!class_exists('SodiumException', false)) { require_once dirname(__FILE__) . '/src/SodiumException.php'; } if (PHP_VERSION_ID >= 50300) { // Namespaces didn't exist before 5.3.0, so don't even try to use this // unless PHP >= 5.3.0 require_once dirname(__FILE__) . '/lib/namespaced.php'; require_once dirname(__FILE__) . '/lib/sodium_compat.php'; if (!defined('SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES')) { require_once dirname(__FILE__) . '/lib/php84compat_const.php'; } } else { require_once dirname(__FILE__) . '/src/PHP52/SplFixedArray.php'; } if (PHP_VERSION_ID < 70200 || !extension_loaded('sodium')) { if (PHP_VERSION_ID >= 50300 && !defined('SODIUM_CRYPTO_SCALARMULT_BYTES')) { require_once dirname(__FILE__) . '/lib/php72compat_const.php'; } if (PHP_VERSION_ID >= 70000) { assert(class_exists('ParagonIE_Sodium_Compat'), 'Possible filesystem/autoloader bug?'); } else { assert(class_exists('ParagonIE_Sodium_Compat')); } require_once(dirname(__FILE__) . '/lib/php72compat.php'); } elseif (!function_exists('sodium_crypto_stream_xchacha20_xor')) { // Older versions of {PHP, ext/sodium} will not define these require_once(dirname(__FILE__) . '/lib/php72compat.php'); } if (PHP_VERSION_ID < 80400 || !extension_loaded('sodium')) { require_once dirname(__FILE__) . '/lib/php84compat.php'; } require_once(dirname(__FILE__) . '/lib/stream-xchacha20.php'); require_once(dirname(__FILE__) . '/lib/ristretto255.php'); PK �}[��ٷ;� ;� src/File.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_File', false)) { return; } /** * Class ParagonIE_Sodium_File */ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util { /* PHP's default buffer size is 8192 for fread()/fwrite(). */ const BUFFER_SIZE = 8192; /** * Box a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $nonce Number to be used only once * @param string $keyPair ECDH secret key and ECDH public key concatenated * * @return bool * @throws SodiumException * @throws TypeError */ public static function box( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $keyPair ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } /* Input validation: */ if (!is_string($keyPair)) { throw new TypeError('Argument 4 must be a string, ' . gettype($keyPair) . ' given.'); } if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_NONCEBYTES bytes'); } if (self::strlen($keyPair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $keyPair); fclose($ifp); fclose($ofp); return $res; } /** * Open a boxed file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_box_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $nonce * @param string $keypair * @return bool * @throws SodiumException * @throws TypeError */ public static function box_open( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $keypair ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } if (!is_string($keypair)) { throw new TypeError('Argument 4 must be a string, ' . gettype($keypair) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_NONCEBYTES bytes'); } if (self::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { if (isset($ephKeypair)) { unset($ephKeypair); } } return $res; } /** * Seal a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_seal(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $publicKey ECDH public key * * @return bool * @throws SodiumException * @throws TypeError */ public static function box_seal( $inputFile, $outputFile, #[\SensitiveParameter] $publicKey ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($publicKey)) { throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.'); } /* Input validation: */ if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } /** @var string $ephKeypair */ $ephKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair(); /** @var string $msgKeypair */ $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( ParagonIE_Sodium_Compat::crypto_box_secretkey($ephKeypair), $publicKey ); /** @var string $ephemeralPK */ $ephemeralPK = ParagonIE_Sodium_Compat::crypto_box_publickey($ephKeypair); /** @var string $nonce */ $nonce = ParagonIE_Sodium_Compat::crypto_generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var int $firstWrite */ $firstWrite = fwrite( $ofp, $ephemeralPK, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES ); if (!is_int($firstWrite)) { fclose($ifp); fclose($ofp); ParagonIE_Sodium_Compat::memzero($ephKeypair); throw new SodiumException('Could not write to output file'); } if ($firstWrite !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { ParagonIE_Sodium_Compat::memzero($ephKeypair); fclose($ifp); fclose($ofp); throw new SodiumException('Error writing public key to output file'); } $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $msgKeypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { /** @psalm-suppress PossiblyUndefinedVariable */ unset($ephKeypair); } return $res; } /** * Open a sealed file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_seal_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_box_seal_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $ecdhKeypair * @return bool * @throws SodiumException * @throws TypeError */ public static function box_seal_open( $inputFile, $outputFile, #[\SensitiveParameter] $ecdhKeypair ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($ecdhKeypair)) { throw new TypeError('Argument 3 must be a string, ' . gettype($ecdhKeypair) . ' given.'); } /* Input validation: */ if (self::strlen($ecdhKeypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } $publicKey = ParagonIE_Sodium_Compat::crypto_box_publickey($ecdhKeypair); /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $ephemeralPK = fread($ifp, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES); if (!is_string($ephemeralPK)) { throw new SodiumException('Could not read input file'); } if (self::strlen($ephemeralPK) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { fclose($ifp); fclose($ofp); throw new SodiumException('Could not read public key from sealed file'); } $nonce = ParagonIE_Sodium_Compat::crypto_generichash( $ephemeralPK . $publicKey, '', 24 ); $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( ParagonIE_Sodium_Compat::crypto_box_secretkey($ecdhKeypair), $ephemeralPK ); $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { if (isset($ephKeypair)) { unset($ephKeypair); } } return $res; } /** * Calculate the BLAKE2b hash of a file. * * @param string $filePath Absolute path to a file on the filesystem * @param string|null $key BLAKE2b key * @param int $outputLength Length of hash output * * @return string BLAKE2b hash * @throws SodiumException * @throws TypeError * @psalm-suppress FailedTypeResolution */ public static function generichash( $filePath, #[\SensitiveParameter] $key = '', $outputLength = 32 ) { /* Type checks: */ if (!is_string($filePath)) { throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($key)) { if (is_null($key)) { $key = ''; } else { throw new TypeError('Argument 2 must be a string, ' . gettype($key) . ' given.'); } } if (!is_int($outputLength)) { if (!is_numeric($outputLength)) { throw new TypeError('Argument 3 must be an integer, ' . gettype($outputLength) . ' given.'); } $outputLength = (int) $outputLength; } /* Input validation: */ if (!empty($key)) { if (self::strlen($key) < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new TypeError('Argument 2 must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes'); } if (self::strlen($key) > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new TypeError('Argument 2 must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes'); } } if ($outputLength < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MIN) { throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MIN'); } if ($outputLength > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MAX) { throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MAX'); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } $ctx = ParagonIE_Sodium_Compat::crypto_generichash_init($key, $outputLength); while ($size > 0) { $blockSize = $size > 64 ? 64 : $size; $read = fread($fp, $blockSize); if (!is_string($read)) { throw new SodiumException('Could not read input file'); } ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, $read); $size -= $blockSize; } fclose($fp); return ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, $outputLength); } /** * Encrypt a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_secretbox(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $nonce Number to be used only once * @param string $key Encryption key * * @return bool * @throws SodiumException * @throws TypeError */ public static function secretbox( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given..'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) { throw new TypeError('Argument 3 must be CRYPTO_SECRETBOX_NONCEBYTES bytes'); } if (!is_string($key)) { throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.'); } if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_KEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::secretbox_encrypt($ifp, $ofp, $size, $nonce, $key); fclose($ifp); fclose($ofp); return $res; } /** * Seal a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_secretbox_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_secretbox_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function secretbox_open( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } if (!is_string($key)) { throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_NONCEBYTES bytes'); } if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOXBOX_KEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($key); } catch (SodiumException $ex) { /** @psalm-suppress PossiblyUndefinedVariable */ unset($key); } return $res; } /** * Sign a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces * the same result. * * @param string $filePath Absolute path to a file on the filesystem * @param string $secretKey Secret signing key * * @return string Ed25519 signature * @throws SodiumException * @throws TypeError */ public static function sign( $filePath, #[\SensitiveParameter] $secretKey ) { /* Type checks: */ if (!is_string($filePath)) { throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($secretKey)) { throw new TypeError('Argument 2 must be a string, ' . gettype($secretKey) . ' given.'); } /* Input validation: */ if (self::strlen($secretKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_SECRETKEYBYTES) { throw new TypeError('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES bytes'); } if (PHP_INT_SIZE === 4) { return self::sign_core32($filePath, $secretKey); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var string $az */ $az = hash('sha512', self::substr($secretKey, 0, 32), true); $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $nonceHash */ $nonceHash = hash_final($hs, true); /** @var string $pk */ $pk = self::substr($secretKey, 32, 32); /** @var string $nonce */ $nonce = ParagonIE_Sodium_Core_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32); /** @var string $sig */ $sig = ParagonIE_Sodium_Core_Ed25519::ge_p3_tobytes( ParagonIE_Sodium_Core_Ed25519::ge_scalarmult_base($nonce) ); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hramHash */ $hramHash = hash_final($hs, true); /** @var string $hram */ $hram = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hramHash); /** @var string $sigAfter */ $sigAfter = ParagonIE_Sodium_Core_Ed25519::sc_muladd($hram, $az, $nonce); /** @var string $sig */ $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } fclose($fp); return $sig; } /** * Verify a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but * produces the same result. * * @param string $sig Ed25519 signature * @param string $filePath Absolute path to a file on the filesystem * @param string $publicKey Signing public key * * @return bool * @throws SodiumException * @throws TypeError * @throws Exception */ public static function verify( $sig, $filePath, $publicKey ) { /* Type checks: */ if (!is_string($sig)) { throw new TypeError('Argument 1 must be a string, ' . gettype($sig) . ' given.'); } if (!is_string($filePath)) { throw new TypeError('Argument 2 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($publicKey)) { throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.'); } /* Input validation: */ if (self::strlen($sig) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_BYTES) { throw new TypeError('Argument 1 must be CRYPTO_SIGN_BYTES bytes'); } if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new TypeError('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES bytes'); } if (self::strlen($sig) < 64) { throw new SodiumException('Signature is too short'); } if (PHP_INT_SIZE === 4) { return self::verify_core32($sig, $filePath, $publicKey); } /* Security checks */ if ( (ParagonIE_Sodium_Core_Ed25519::chrToInt($sig[63]) & 240) && ParagonIE_Sodium_Core_Ed25519::check_S_lt_L(self::substr($sig, 32, 32)) ) { throw new SodiumException('S < L - Invalid signature'); } if (ParagonIE_Sodium_Core_Ed25519::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($publicKey[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @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_Core_Curve25519_Ge_P3 $A */ $A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($publicKey, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hDigest */ $hDigest = hash_final($hs, true); /** @var string $h */ $h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ $R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R); // Close the file handle fclose($fp); // 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)); } /** * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $boxKeypair * @return bool * @throws SodiumException * @throws TypeError */ protected static function box_encrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair) { if (PHP_INT_SIZE === 4) { return self::secretbox_encrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto32::box_beforenm( ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair) ) ); } return self::secretbox_encrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto::box_beforenm( ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto::box_publickey($boxKeypair) ) ); } /** * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $boxKeypair * @return bool * @throws SodiumException * @throws TypeError */ protected static function box_decrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair) { if (PHP_INT_SIZE === 4) { return self::secretbox_decrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto32::box_beforenm( ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair) ) ); } return self::secretbox_decrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto::box_beforenm( ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto::box_publickey($boxKeypair) ) ); } /** * Encrypt a file * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_encrypt($ifp, $ofp, $mlen, $nonce, $key) { if (PHP_INT_SIZE === 4) { return self::secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key); } $plaintext = fread($ifp, 32); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $first32 = self::ftell($ifp); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen0 = $mlen; if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( $block0, $realNonce, $subkey ); $state = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_Util::substr( $block0, 0, ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES ) ); // Pre-write 16 blank bytes for the Poly1305 tag $start = self::ftell($ofp); fwrite($ofp, str_repeat("\x00", 16)); /** @var string $c */ $cBlock = ParagonIE_Sodium_Core_Util::substr( $block0, ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES ); $state->update($cBlock); fwrite($ofp, $cBlock); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ fseek($ifp, $first32, SEEK_SET); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $plaintext = fread($ifp, $blockSize); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $cBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( $plaintext, $realNonce, $iter, $subkey ); fwrite($ofp, $cBlock, $blockSize); $state->update($cBlock); $mlen -= $blockSize; $iter += $incr; } try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $end = self::ftell($ofp); /* * Write the Poly1305 authentication tag that provides integrity * over the ciphertext (encrypt-then-MAC) */ fseek($ofp, $start, SEEK_SET); fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES); fseek($ofp, $end, SEEK_SET); unset($state); return true; } /** * Decrypt a file * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_decrypt($ifp, $ofp, $mlen, $nonce, $key) { if (PHP_INT_SIZE === 4) { return self::secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key); } $tag = fread($ifp, 16); if (!is_string($tag)) { throw new SodiumException('Could not read input file'); } /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( 64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); /* Verify the Poly1305 MAC -before- attempting to decrypt! */ $state = new ParagonIE_Sodium_Core_Poly1305_State(self::substr($block0, 0, 32)); if (!self::onetimeauth_verify($state, $ifp, $tag, $mlen)) { throw new SodiumException('Invalid MAC'); } /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ $first32 = fread($ifp, 32); if (!is_string($first32)) { throw new SodiumException('Could not read input file'); } $first32len = self::strlen($first32); fwrite( $ofp, self::xorStrings( self::substr($block0, 32, $first32len), self::substr($first32, 0, $first32len) ) ); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* Decrypts ciphertext, writes to output file. */ while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $pBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( $ciphertext, $realNonce, $iter, $subkey ); fwrite($ofp, $pBlock, $blockSize); $mlen -= $blockSize; $iter += $incr; } return true; } /** * @param ParagonIE_Sodium_Core_Poly1305_State $state * @param resource $ifp * @param string $tag * @param int $mlen * @return bool * @throws SodiumException * @throws TypeError */ protected static function onetimeauth_verify( ParagonIE_Sodium_Core_Poly1305_State $state, $ifp, $tag = '', $mlen = 0 ) { /** @var int $pos */ $pos = self::ftell($ifp); /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $state->update($ciphertext); $mlen -= $blockSize; $iter += $incr; } $res = ParagonIE_Sodium_Core_Util::verify_16($tag, $state->finish()); fseek($ifp, $pos, SEEK_SET); return $res; } /** * Update a hash context with the contents of a file, without * loading the entire file into memory. * * @param resource|HashContext $hash * @param resource $fp * @param int $size * @return resource|object Resource on PHP < 7.2, HashContext object on PHP >= 7.2 * @throws SodiumException * @throws TypeError * @psalm-suppress PossiblyInvalidArgument * PHP 7.2 changes from a resource to an object, * which causes Psalm to complain about an error. * @psalm-suppress TypeCoercion * Ditto. */ public static function updateHashWithFile($hash, $fp, $size = 0) { /* Type checks: */ if (PHP_VERSION_ID < 70200) { if (!is_resource($hash)) { throw new TypeError('Argument 1 must be a resource, ' . gettype($hash) . ' given.'); } } else { if (!is_object($hash)) { throw new TypeError('Argument 1 must be an object (PHP 7.2+), ' . gettype($hash) . ' given.'); } } if (!is_resource($fp)) { throw new TypeError('Argument 2 must be a resource, ' . gettype($fp) . ' given.'); } if (!is_int($size)) { throw new TypeError('Argument 3 must be an integer, ' . gettype($size) . ' given.'); } /** @var int $originalPosition */ $originalPosition = self::ftell($fp); // Move file pointer to beginning of file fseek($fp, 0, SEEK_SET); for ($i = 0; $i < $size; $i += self::BUFFER_SIZE) { /** @var string|bool $message */ $message = fread( $fp, ($size - $i) > self::BUFFER_SIZE ? $size - $i : self::BUFFER_SIZE ); if (!is_string($message)) { throw new SodiumException('Unexpected error reading from file.'); } /** @var string $message */ /** @psalm-suppress InvalidArgument */ self::hash_update($hash, $message); } // Reset file pointer's position fseek($fp, $originalPosition, SEEK_SET); return $hash; } /** * Sign a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces * the same result. (32-bit) * * @param string $filePath Absolute path to a file on the filesystem * @param string $secretKey Secret signing key * * @return string Ed25519 signature * @throws SodiumException * @throws TypeError */ private static function sign_core32($filePath, $secretKey) { $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var string $az */ $az = hash('sha512', self::substr($secretKey, 0, 32), true); $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); $nonceHash = hash_final($hs, true); $pk = self::substr($secretKey, 32, 32); $nonce = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32); $sig = ParagonIE_Sodium_Core32_Ed25519::ge_p3_tobytes( ParagonIE_Sodium_Core32_Ed25519::ge_scalarmult_base($nonce) ); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); $hramHash = hash_final($hs, true); $hram = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hramHash); $sigAfter = ParagonIE_Sodium_Core32_Ed25519::sc_muladd($hram, $az, $nonce); /** @var string $sig */ $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } fclose($fp); return $sig; } /** * * Verify a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but * produces the same result. (32-bit) * * @param string $sig Ed25519 signature * @param string $filePath Absolute path to a file on the filesystem * @param string $publicKey Signing public key * * @return bool * @throws SodiumException * @throws Exception */ public static function verify_core32($sig, $filePath, $publicKey) { /* Security checks */ if (ParagonIE_Sodium_Core32_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))) { throw new SodiumException('S < L - Invalid signature'); } if (ParagonIE_Sodium_Core32_Ed25519::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($publicKey[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var int|bool $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var int $size */ /** @var resource|bool $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $fp */ /** @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 = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($publicKey, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hDigest */ $hDigest = hash_final($hs, true); /** @var string $h */ $h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ $R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R); // Close the file handle fclose($fp); // 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)); } /** * Encrypt a file (32-bit) * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key) { $plaintext = fread($ifp, 32); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $first32 = self::ftell($ifp); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen0 = $mlen; if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( $block0, $realNonce, $subkey ); $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES ) ); // Pre-write 16 blank bytes for the Poly1305 tag $start = self::ftell($ofp); fwrite($ofp, str_repeat("\x00", 16)); /** @var string $c */ $cBlock = ParagonIE_Sodium_Core32_Util::substr( $block0, ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES ); $state->update($cBlock); fwrite($ofp, $cBlock); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ fseek($ifp, $first32, SEEK_SET); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $plaintext = fread($ifp, $blockSize); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $cBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( $plaintext, $realNonce, $iter, $subkey ); fwrite($ofp, $cBlock, $blockSize); $state->update($cBlock); $mlen -= $blockSize; $iter += $incr; } try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $end = self::ftell($ofp); /* * Write the Poly1305 authentication tag that provides integrity * over the ciphertext (encrypt-then-MAC) */ fseek($ofp, $start, SEEK_SET); fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES); fseek($ofp, $end, SEEK_SET); unset($state); return true; } /** * Decrypt a file (32-bit) * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key) { $tag = fread($ifp, 16); if (!is_string($tag)) { throw new SodiumException('Could not read input file'); } /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); /* Verify the Poly1305 MAC -before- attempting to decrypt! */ $state = new ParagonIE_Sodium_Core32_Poly1305_State(self::substr($block0, 0, 32)); if (!self::onetimeauth_verify_core32($state, $ifp, $tag, $mlen)) { throw new SodiumException('Invalid MAC'); } /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ $first32 = fread($ifp, 32); if (!is_string($first32)) { throw new SodiumException('Could not read input file'); } $first32len = self::strlen($first32); fwrite( $ofp, self::xorStrings( self::substr($block0, 32, $first32len), self::substr($first32, 0, $first32len) ) ); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* Decrypts ciphertext, writes to output file. */ while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $pBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( $ciphertext, $realNonce, $iter, $subkey ); fwrite($ofp, $pBlock, $blockSize); $mlen -= $blockSize; $iter += $incr; } return true; } /** * One-time message authentication for 32-bit systems * * @param ParagonIE_Sodium_Core32_Poly1305_State $state * @param resource $ifp * @param string $tag * @param int $mlen * @return bool * @throws SodiumException * @throws TypeError */ protected static function onetimeauth_verify_core32( ParagonIE_Sodium_Core32_Poly1305_State $state, $ifp, $tag = '', $mlen = 0 ) { /** @var int $pos */ $pos = self::ftell($ifp); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $state->update($ciphertext); $mlen -= $blockSize; } $res = ParagonIE_Sodium_Core32_Util::verify_16($tag, $state->finish()); fseek($ifp, $pos, SEEK_SET); return $res; } /** * @param resource $resource * @return int * @throws SodiumException */ private static function ftell($resource) { $return = ftell($resource); if (!is_int($return)) { throw new SodiumException('ftell() returned false'); } return (int) $return; } } PK �}[^���� � src/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 �}[��ӈO� O� src/Crypto32.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_Crypto32', false)) { return; } /** * Class ParagonIE_Sodium_Crypto * * ATTENTION! * * If you are using this library, you should be using * ParagonIE_Sodium_Compat in your code, not this class. */ abstract class ParagonIE_Sodium_Crypto32 { const aead_chacha20poly1305_KEYBYTES = 32; const aead_chacha20poly1305_NSECBYTES = 0; const aead_chacha20poly1305_NPUBBYTES = 8; const aead_chacha20poly1305_ABYTES = 16; const aead_chacha20poly1305_IETF_KEYBYTES = 32; const aead_chacha20poly1305_IETF_NSECBYTES = 0; const aead_chacha20poly1305_IETF_NPUBBYTES = 12; const aead_chacha20poly1305_IETF_ABYTES = 16; const aead_xchacha20poly1305_IETF_KEYBYTES = 32; const aead_xchacha20poly1305_IETF_NSECBYTES = 0; const aead_xchacha20poly1305_IETF_NPUBBYTES = 24; const aead_xchacha20poly1305_IETF_ABYTES = 16; const box_curve25519xsalsa20poly1305_SEEDBYTES = 32; const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32; const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32; const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32; const box_curve25519xsalsa20poly1305_NONCEBYTES = 24; const box_curve25519xsalsa20poly1305_MACBYTES = 16; const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16; const box_curve25519xsalsa20poly1305_ZEROBYTES = 32; const onetimeauth_poly1305_BYTES = 16; const onetimeauth_poly1305_KEYBYTES = 32; const secretbox_xsalsa20poly1305_KEYBYTES = 32; const secretbox_xsalsa20poly1305_NONCEBYTES = 24; const secretbox_xsalsa20poly1305_MACBYTES = 16; const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16; const secretbox_xsalsa20poly1305_ZEROBYTES = 32; const secretbox_xchacha20poly1305_KEYBYTES = 32; const secretbox_xchacha20poly1305_NONCEBYTES = 24; const secretbox_xchacha20poly1305_MACBYTES = 16; const secretbox_xchacha20poly1305_BOXZEROBYTES = 16; const secretbox_xchacha20poly1305_ZEROBYTES = 32; const stream_salsa20_KEYBYTES = 32; /** * AEAD Decryption with ChaCha20-Poly1305 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $clen - Length of ciphertext */ $clen = $len - self::aead_chacha20poly1305_ABYTES; /** @var int $adlen - Length of associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core32_Util::substr( $message, $clen, self::aead_chacha20poly1305_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $clen); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( 32, $nonce, $key ); /* Recalculate the Poly1305 authentication tag (MAC): */ $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen)); $computed_mac = $state->finish(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } // Here, we know that the MAC is valid, so we decrypt and return the plaintext return ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } /** * AEAD Encryption with ChaCha20-Poly1305 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of the plaintext message */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( 32, $nonce, $key ); $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); $state->update($ad); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len)); return $ciphertext . $state->finish(); } /** * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_ietf_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $adlen - Length of associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $clen - Length of ciphertext */ $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( 32, $nonce, $key ); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core32_Util::substr( $message, $len - self::aead_chacha20poly1305_IETF_ABYTES, self::aead_chacha20poly1305_IETF_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core32_Util::substr( $message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES ); /* Recalculate the Poly1305 authentication tag (MAC): */ $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf)); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen)); $computed_mac = $state->finish(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } // Here, we know that the MAC is valid, so we decrypt and return the plaintext return ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } /** * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_ietf_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of the plaintext message */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( 32, $nonce, $key ); $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); $state->update($ad); $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf))); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len)); return $ciphertext . $state->finish(); } /** * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_xchacha20poly1305_ietf_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); } /** * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_xchacha20poly1305_ietf_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); } /** * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $key * @return string * @throws TypeError */ public static function auth($message, $key) { return ParagonIE_Sodium_Core32_Util::substr( hash_hmac('sha512', $message, $key, true), 0, 32 ); } /** * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $mac * @param string $message * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function auth_verify($mac, $message, $key) { return ParagonIE_Sodium_Core32_Util::hashEquals( $mac, self::auth($message, $key) ); } /** * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box($plaintext, $nonce, $keypair) { return self::secretbox( $plaintext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); } /** * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $publicKey * @return string * @throws SodiumException * @throws TypeError */ public static function box_seal($message, $publicKey) { /** @var string $ephemeralKeypair */ $ephemeralKeypair = self::box_keypair(); /** @var string $ephemeralSK */ $ephemeralSK = self::box_secretkey($ephemeralKeypair); /** @var string $ephemeralPK */ $ephemeralPK = self::box_publickey($ephemeralKeypair); /** @var string $nonce */ $nonce = self::generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var string $keypair - The combined keypair used in crypto_box() */ $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); /** @var string $ciphertext Ciphertext + MAC from crypto_box */ $ciphertext = self::box($message, $nonce, $keypair); try { ParagonIE_Sodium_Compat::memzero($ephemeralKeypair); ParagonIE_Sodium_Compat::memzero($ephemeralSK); ParagonIE_Sodium_Compat::memzero($nonce); } catch (SodiumException $ex) { $ephemeralKeypair = null; $ephemeralSK = null; $nonce = null; } return $ephemeralPK . $ciphertext; } /** * Opens a message encrypted via box_seal(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box_seal_open($message, $keypair) { /** @var string $ephemeralPK */ $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32); /** @var string $ciphertext (ciphertext + MAC) */ $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32); /** @var string $secretKey */ $secretKey = self::box_secretkey($keypair); /** @var string $publicKey */ $publicKey = self::box_publickey($keypair); /** @var string $nonce */ $nonce = self::generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var string $keypair */ $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); /** @var string $m */ $m = self::box_open($ciphertext, $nonce, $keypair); try { ParagonIE_Sodium_Compat::memzero($secretKey); ParagonIE_Sodium_Compat::memzero($ephemeralPK); ParagonIE_Sodium_Compat::memzero($nonce); } catch (SodiumException $ex) { $secretKey = null; $ephemeralPK = null; $nonce = null; } return $m; } /** * Used by crypto_box() to get the crypto_secretbox() key. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sk * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function box_beforenm($sk, $pk) { return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20( str_repeat("\x00", 16), self::scalarmult($sk, $pk) ); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @return string * @throws Exception * @throws SodiumException * @throws TypeError */ public static function box_keypair() { $sKey = random_bytes(32); $pKey = self::scalarmult_base($sKey); return $sKey . $pKey; } /** * @param string $seed * @return string * @throws SodiumException * @throws TypeError */ public static function box_seed_keypair($seed) { $sKey = ParagonIE_Sodium_Core32_Util::substr( hash('sha512', $seed, true), 0, 32 ); $pKey = self::scalarmult_base($sKey); return $sKey . $pKey; } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @param string $pKey * @return string * @throws TypeError */ public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) { return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function box_secretkey($keypair) { if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' ); } return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function box_publickey($keypair) { if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' ); } return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function box_publickey_from_secretkey($sKey) { if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.' ); } return self::scalarmult_base($sKey); } /** * Decrypt a message encrypted with box(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box_open($ciphertext, $nonce, $keypair) { return self::secretbox_open( $ciphertext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); } /** * Calculate a BLAKE2b hash. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string|null $key * @param int $outlen * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash($message, $key = '', $outlen = 32) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { /** @var SplFixedArray $k */ $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen); ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count()); /** @var SplFixedArray $out */ $out = new SplFixedArray($outlen); $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); } /** * Finalize a BLAKE2b hashing context, returning the hash. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ctx * @param int $outlen * @return string * @throws SodiumException * @throws TypeError */ public static function generichash_final($ctx, $outlen = 32) { if (!is_string($ctx)) { throw new TypeError('Context must be a string'); } $out = new SplFixedArray($outlen); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $out */ $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); } /** * Initialize a hashing context for BLAKE2b. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $key * @param int $outputLength * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash_init($key = '', $outputLength = 32) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength); return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); } /** * Initialize a hashing context for BLAKE2b. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $key * @param int $outputLength * @param string $salt * @param string $personal * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash_init_salt_personal( $key = '', $outputLength = 32, $salt = '', $personal = '' ) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } if (!empty($salt)) { $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt); } else { $s = null; } if (!empty($salt)) { $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal); } else { $p = null; } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p); return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); } /** * Update a hashing context for BLAKE2b with $message * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ctx * @param string $message * @return string * @throws SodiumException * @throws TypeError */ public static function generichash_update($ctx, $message) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count()); return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context); } /** * Libsodium's crypto_kx(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $my_sk * @param string $their_pk * @param string $client_pk * @param string $server_pk * @return string * @throws SodiumException * @throws TypeError */ public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) { return self::generichash( self::scalarmult($my_sk, $their_pk) . $client_pk . $server_pk ); } /** * ECDH over Curve25519 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @param string $pKey * @return string * * @throws SodiumException * @throws TypeError */ public static function scalarmult($sKey, $pKey) { $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); self::scalarmult_throw_if_zero($q); return $q; } /** * ECDH over Curve25519, using the basepoint. * Used to get a secret key from a public key. * * @param string $secret * @return string * * @throws SodiumException * @throws TypeError */ public static function scalarmult_base($secret) { $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret); self::scalarmult_throw_if_zero($q); return $q; } /** * This throws an Error if a zero public key was passed to the function. * * @param string $q * @return void * @throws SodiumException * @throws TypeError */ protected static function scalarmult_throw_if_zero($q) { $d = 0; for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) { $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]); } /* branch-free variant of === 0 */ if (-(1 & (($d - 1) >> 8))) { throw new SodiumException('Zero public key is not allowed'); } } /** * XSalsa20-Poly1305 authenticated symmetric-key encryption. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox($plaintext, $nonce, $key) { /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( $block0, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $block0, self::secretbox_xsalsa20poly1305_ZEROBYTES ); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core32_Util::substr( $plaintext, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), 1, $subkey ); } $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, self::onetimeauth_poly1305_KEYBYTES ) ); try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $state->update($c); /** @var string $c - MAC || ciphertext */ $c = $state->finish() . $c; unset($state); return $c; } /** * Decrypt a ciphertext generated via secretbox(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_open($ciphertext, $nonce, $key) { /** @var string $mac */ $mac = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core32_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify( $mac, $c, ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32) ); if (!$verified) { try { ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $subkey = null; } throw new SodiumException('Invalid MAC'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core32_Util::xorStrings( ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core32_Util::substr( $c, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), 1, (string) $subkey ); } return $m; } /** * XChaCha20-Poly1305 authenticated symmetric-key encryption. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) { /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key ); $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( $block0, $nonceLast, $subkey ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $block0, self::secretbox_xchacha20poly1305_ZEROBYTES ); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( ParagonIE_Sodium_Core32_Util::substr( $plaintext, self::secretbox_xchacha20poly1305_ZEROBYTES ), $nonceLast, $subkey, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, self::onetimeauth_poly1305_KEYBYTES ) ); try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $state->update($c); /** @var string $c - MAC || ciphertext */ $c = $state->finish() . $c; unset($state); return $c; } /** * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) { /** @var string $mac */ $mac = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core32_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify( $mac, $c, ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32) ); if (!$verified) { try { ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $subkey = null; } throw new SodiumException('Invalid MAC'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core32_Util::xorStrings( ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( ParagonIE_Sodium_Core32_Util::substr( $c, self::secretbox_xchacha20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } return $m; } /** * @param string $key * @return array<int, string> Returns a state and a header. * @throws Exception * @throws SodiumException */ public static function secretstream_xchacha20poly1305_init_push($key) { # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES); $out = random_bytes(24); # crypto_core_hchacha20(state->k, out, k, NULL); $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key); $state = new ParagonIE_Sodium_Core32_SecretStream_State( $subkey, ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4) ); # _crypto_secretstream_xchacha20poly1305_counter_reset(state); $state->counterReset(); # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); # memset(state->_pad, 0, sizeof state->_pad); return array( $state->toString(), $out ); } /** * @param string $key * @param string $header * @return string Returns a state. * @throws Exception */ public static function secretstream_xchacha20poly1305_init_pull($key, $header) { # crypto_core_hchacha20(state->k, in, k, NULL); $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($header, 0, 16), $key ); $state = new ParagonIE_Sodium_Core32_SecretStream_State( $subkey, ParagonIE_Sodium_Core32_Util::substr($header, 16) ); $state->counterReset(); # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); # memset(state->_pad, 0, sizeof state->_pad); # return 0; return $state->toString(); } /** * @param string $state * @param string $msg * @param string $aad * @param int $tag * @return string * @throws SodiumException */ public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) { $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); # crypto_onetimeauth_poly1305_state poly1305_state; # unsigned char block[64U]; # unsigned char slen[8U]; # unsigned char *c; # unsigned char *mac; $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg); $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad); if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # if (outlen_p != NULL) { # *outlen_p = 0U; # } # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # sodium_misuse(); # } # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); # crypto_onetimeauth_poly1305_init(&poly1305_state, block); # sodium_memzero(block, sizeof block); $auth = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); $auth->update($aad); # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, # (0x10 - adlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); # memset(block, 0, sizeof block); # block[0] = tag; # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, # state->nonce, 1U, state->k); $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(1) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $auth->update($block); # out[0] = block[0]; $out = $block[0]; # c = out + (sizeof tag); # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k); $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $msg, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(2) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); $auth->update($cipher); $out .= $cipher; unset($cipher); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $auth->update($slen); # mac = c + mlen; # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); $mac = $auth->finish(); $out .= $mac; # sodium_memzero(&poly1305_state, sizeof poly1305_state); unset($auth); # XOR_BUF(STATE_INONCE(state), mac, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); $st->xorNonce($mac); # sodium_increment(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); $st->incrementCounter(); // Overwrite by reference: $state = $st->toString(); /** @var bool $rekey */ $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || # sodium_is_zero(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { # crypto_secretstream_xchacha20poly1305_rekey(state); # } if ($rekey || $st->needsRekey()) { // DO REKEY self::secretstream_xchacha20poly1305_rekey($state); } # if (outlen_p != NULL) { # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen; # } return $out; } /** * @param string $state * @param string $cipher * @param string $aad * @return bool|array{0: string, 1: int} * @throws SodiumException */ public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') { $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher); # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES; $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad); # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # sodium_misuse(); # } if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); # crypto_onetimeauth_poly1305_init(&poly1305_state, block); # sodium_memzero(block, sizeof block); $auth = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); $auth->update($aad); # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, # (0x10 - adlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); # memset(block, 0, sizeof block); # block[0] = in[0]; # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, # state->nonce, 1U, state->k); $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $cipher[0] . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(1) ); # tag = block[0]; # block[0] = in[0]; # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]); $block[0] = $cipher[0]; $auth->update($block); # c = in + (sizeof tag); # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen)); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen); $auth->update($slen); # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); # sodium_memzero(&poly1305_state, sizeof poly1305_state); $mac = $auth->finish(); # stored_mac = c + mlen; # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) { # sodium_memzero(mac, sizeof mac); # return -1; # } $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16); if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) { return false; } # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k); $out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(2) ); # XOR_BUF(STATE_INONCE(state), mac, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); $st->xorNonce($mac); # sodium_increment(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); $st->incrementCounter(); # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || # sodium_is_zero(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { # crypto_secretstream_xchacha20poly1305_rekey(state); # } // Overwrite by reference: $state = $st->toString(); /** @var bool $rekey */ $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; if ($rekey || $st->needsRekey()) { // DO REKEY self::secretstream_xchacha20poly1305_rekey($state); } return array($out, $tag); } /** * @param string $state * @return void * @throws SodiumException */ public static function secretstream_xchacha20poly1305_rekey(&$state) { $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + # crypto_secretstream_xchacha20poly1305_INONCEBYTES]; # size_t i; # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { # new_key_and_inonce[i] = state->k[i]; # } $new_key_and_inonce = $st->getKey(); # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] = # STATE_INONCE(state)[i]; # } $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8); # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce, # sizeof new_key_and_inonce, # state->nonce, state->k); $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $new_key_and_inonce, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(0) )); # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { # state->k[i] = new_key_and_inonce[i]; # } # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { # STATE_INONCE(state)[i] = # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i]; # } # _crypto_secretstream_xchacha20poly1305_counter_reset(state); $st->counterReset(); $state = $st->toString(); } /** * Detached Ed25519 signature. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_detached($message, $sk) { return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk); } /** * Attached Ed25519 signature. (Returns a signed message.) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign($message, $sk) { return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk); } /** * Opens a signed message. If valid, returns the message. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $signedMessage * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_open($signedMessage, $pk) { return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk); } /** * Verify a detached signature of a given message and public key. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $signature * @param string $message * @param string $pk * @return bool * @throws SodiumException * @throws TypeError */ public static function sign_verify_detached($signature, $message, $pk) { return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk); } } PK �}[1��� � src/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 �}[�o��|U |U src/Core/Ristretto255.phpnu �[��� <?php /** * Class ParagonIE_Sodium_Core_Ristretto255 */ class ParagonIE_Sodium_Core_Ristretto255 extends ParagonIE_Sodium_Core_Ed25519 { const crypto_core_ristretto255_HASHBYTES = 64; const HASH_SC_L = 48; const CORE_H2C_SHA256 = 1; const CORE_H2C_SHA512 = 2; /** * @param ParagonIE_Sodium_Core_Curve25519_Fe $f * @param int $b * @return ParagonIE_Sodium_Core_Curve25519_Fe */ public static function fe_cneg(ParagonIE_Sodium_Core_Curve25519_Fe $f, $b) { $negf = self::fe_neg($f); return self::fe_cmov($f, $negf, $b); } /** * @param ParagonIE_Sodium_Core_Curve25519_Fe $f * @return ParagonIE_Sodium_Core_Curve25519_Fe * @throws SodiumException */ public static function fe_abs(ParagonIE_Sodium_Core_Curve25519_Fe $f) { return self::fe_cneg($f, self::fe_isnegative($f)); } /** * Returns 0 if this field element results in all NUL bytes. * * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core_Curve25519_Fe $f * @return int * @throws SodiumException */ public static function fe_iszero(ParagonIE_Sodium_Core_Curve25519_Fe $f) { static $zero; if ($zero === null) { $zero = str_repeat("\x00", 32); } /** @var string $zero */ $str = self::fe_tobytes($f); $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($str[$i]); } return (($d - 1) >> 31) & 1; } /** * @param ParagonIE_Sodium_Core_Curve25519_Fe $u * @param ParagonIE_Sodium_Core_Curve25519_Fe $v * @return array{x: ParagonIE_Sodium_Core_Curve25519_Fe, nonsquare: int} * * @throws SodiumException */ public static function ristretto255_sqrt_ratio_m1( ParagonIE_Sodium_Core_Curve25519_Fe $u, ParagonIE_Sodium_Core_Curve25519_Fe $v ) { $sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1); $v3 = self::fe_mul( self::fe_sq($v), $v ); /* v3 = v^3 */ $x = self::fe_mul( self::fe_mul( self::fe_sq($v3), $u ), $v ); /* x = uv^7 */ $x = self::fe_mul( self::fe_mul( self::fe_pow22523($x), /* x = (uv^7)^((q-5)/8) */ $v3 ), $u ); /* x = uv^3(uv^7)^((q-5)/8) */ $vxx = self::fe_mul( self::fe_sq($x), $v ); /* vx^2 */ $m_root_check = self::fe_sub($vxx, $u); /* vx^2-u */ $p_root_check = self::fe_add($vxx, $u); /* vx^2+u */ $f_root_check = self::fe_mul($u, $sqrtm1); /* u*sqrt(-1) */ $f_root_check = self::fe_add($vxx, $f_root_check); /* vx^2+u*sqrt(-1) */ $has_m_root = self::fe_iszero($m_root_check); $has_p_root = self::fe_iszero($p_root_check); $has_f_root = self::fe_iszero($f_root_check); $x_sqrtm1 = self::fe_mul($x, $sqrtm1); /* x*sqrt(-1) */ $x = self::fe_abs( self::fe_cmov($x, $x_sqrtm1, $has_p_root | $has_f_root) ); return array( 'x' => $x, 'nonsquare' => $has_m_root | $has_p_root ); } /** * @param string $s * @return int * @throws SodiumException */ public static function ristretto255_point_is_canonical($s) { $c = (self::chrToInt($s[31]) & 0x7f) ^ 0x7f; for ($i = 30; $i > 0; --$i) { $c |= self::chrToInt($s[$i]) ^ 0xff; } $c = ($c - 1) >> 8; $d = (0xed - 1 - self::chrToInt($s[0])) >> 8; $e = self::chrToInt($s[31]) >> 7; return 1 - ((($c & $d) | $e | self::chrToInt($s[0])) & 1); } /** * @param string $s * @param bool $skipCanonicalCheck * @return array{h: ParagonIE_Sodium_Core_Curve25519_Ge_P3, res: int} * @throws SodiumException */ public static function ristretto255_frombytes($s, $skipCanonicalCheck = false) { if (!$skipCanonicalCheck) { if (!self::ristretto255_point_is_canonical($s)) { throw new SodiumException('S is not canonical'); } } $s_ = self::fe_frombytes($s); $ss = self::fe_sq($s_); /* ss = s^2 */ $u1 = self::fe_sub(self::fe_1(), $ss); /* u1 = 1-ss */ $u1u1 = self::fe_sq($u1); /* u1u1 = u1^2 */ $u2 = self::fe_add(self::fe_1(), $ss); /* u2 = 1+ss */ $u2u2 = self::fe_sq($u2); /* u2u2 = u2^2 */ $v = self::fe_mul( ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d), $u1u1 ); /* v = d*u1^2 */ $v = self::fe_neg($v); /* v = -d*u1^2 */ $v = self::fe_sub($v, $u2u2); /* v = -(d*u1^2)-u2^2 */ $v_u2u2 = self::fe_mul($v, $u2u2); /* v_u2u2 = v*u2^2 */ // fe25519_1(one); // notsquare = ristretto255_sqrt_ratio_m1(inv_sqrt, one, v_u2u2); $one = self::fe_1(); $result = self::ristretto255_sqrt_ratio_m1($one, $v_u2u2); $inv_sqrt = $result['x']; $notsquare = $result['nonsquare']; $h = new ParagonIE_Sodium_Core_Curve25519_Ge_P3(); $h->X = self::fe_mul($inv_sqrt, $u2); $h->Y = self::fe_mul(self::fe_mul($inv_sqrt, $h->X), $v); $h->X = self::fe_mul($h->X, $s_); $h->X = self::fe_abs( self::fe_add($h->X, $h->X) ); $h->Y = self::fe_mul($u1, $h->Y); $h->Z = self::fe_1(); $h->T = self::fe_mul($h->X, $h->Y); $res = - ((1 - $notsquare) | self::fe_isnegative($h->T) | self::fe_iszero($h->Y)); return array('h' => $h, 'res' => $res); } /** * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h * @return string * @throws SodiumException */ public static function ristretto255_p3_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h) { $sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1); $invsqrtamd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$invsqrtamd); $u1 = self::fe_add($h->Z, $h->Y); /* u1 = Z+Y */ $zmy = self::fe_sub($h->Z, $h->Y); /* zmy = Z-Y */ $u1 = self::fe_mul($u1, $zmy); /* u1 = (Z+Y)*(Z-Y) */ $u2 = self::fe_mul($h->X, $h->Y); /* u2 = X*Y */ $u1_u2u2 = self::fe_mul(self::fe_sq($u2), $u1); /* u1_u2u2 = u1*u2^2 */ $one = self::fe_1(); // fe25519_1(one); // (void) ristretto255_sqrt_ratio_m1(inv_sqrt, one, u1_u2u2); $result = self::ristretto255_sqrt_ratio_m1($one, $u1_u2u2); $inv_sqrt = $result['x']; $den1 = self::fe_mul($inv_sqrt, $u1); /* den1 = inv_sqrt*u1 */ $den2 = self::fe_mul($inv_sqrt, $u2); /* den2 = inv_sqrt*u2 */ $z_inv = self::fe_mul($h->T, self::fe_mul($den1, $den2)); /* z_inv = den1*den2*T */ $ix = self::fe_mul($h->X, $sqrtm1); /* ix = X*sqrt(-1) */ $iy = self::fe_mul($h->Y, $sqrtm1); /* iy = Y*sqrt(-1) */ $eden = self::fe_mul($den1, $invsqrtamd); $t_z_inv = self::fe_mul($h->T, $z_inv); /* t_z_inv = T*z_inv */ $rotate = self::fe_isnegative($t_z_inv); $x_ = self::fe_copy($h->X); $y_ = self::fe_copy($h->Y); $den_inv = self::fe_copy($den2); $x_ = self::fe_cmov($x_, $iy, $rotate); $y_ = self::fe_cmov($y_, $ix, $rotate); $den_inv = self::fe_cmov($den_inv, $eden, $rotate); $x_z_inv = self::fe_mul($x_, $z_inv); $y_ = self::fe_cneg($y_, self::fe_isnegative($x_z_inv)); // fe25519_sub(s_, h->Z, y_); // fe25519_mul(s_, den_inv, s_); // fe25519_abs(s_, s_); // fe25519_tobytes(s, s_); return self::fe_tobytes( self::fe_abs( self::fe_mul( $den_inv, self::fe_sub($h->Z, $y_) ) ) ); } /** * @param ParagonIE_Sodium_Core_Curve25519_Fe $t * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3 * * @throws SodiumException */ public static function ristretto255_elligator(ParagonIE_Sodium_Core_Curve25519_Fe $t) { $sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1); $onemsqd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$onemsqd); $d = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d); $sqdmone = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqdmone); $sqrtadm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtadm1); $one = self::fe_1(); $r = self::fe_mul($sqrtm1, self::fe_sq($t)); /* r = sqrt(-1)*t^2 */ $u = self::fe_mul(self::fe_add($r, $one), $onemsqd); /* u = (r+1)*(1-d^2) */ $c = self::fe_neg(self::fe_1()); /* c = -1 */ $rpd = self::fe_add($r, $d); /* rpd = r+d */ $v = self::fe_mul( self::fe_sub( $c, self::fe_mul($r, $d) ), $rpd ); /* v = (c-r*d)*(r+d) */ $result = self::ristretto255_sqrt_ratio_m1($u, $v); $s = $result['x']; $wasnt_square = 1 - $result['nonsquare']; $s_prime = self::fe_neg( self::fe_abs( self::fe_mul($s, $t) ) ); /* s_prime = -|s*t| */ $s = self::fe_cmov($s, $s_prime, $wasnt_square); $c = self::fe_cmov($c, $r, $wasnt_square); // fe25519_sub(n, r, one); /* n = r-1 */ // fe25519_mul(n, n, c); /* n = c*(r-1) */ // fe25519_mul(n, n, ed25519_sqdmone); /* n = c*(r-1)*(d-1)^2 */ // fe25519_sub(n, n, v); /* n = c*(r-1)*(d-1)^2-v */ $n = self::fe_sub( self::fe_mul( self::fe_mul( self::fe_sub($r, $one), $c ), $sqdmone ), $v ); /* n = c*(r-1)*(d-1)^2-v */ $w0 = self::fe_mul( self::fe_add($s, $s), $v ); /* w0 = 2s*v */ $w1 = self::fe_mul($n, $sqrtadm1); /* w1 = n*sqrt(ad-1) */ $ss = self::fe_sq($s); /* ss = s^2 */ $w2 = self::fe_sub($one, $ss); /* w2 = 1-s^2 */ $w3 = self::fe_add($one, $ss); /* w3 = 1+s^2 */ return new ParagonIE_Sodium_Core_Curve25519_Ge_P3( self::fe_mul($w0, $w3), self::fe_mul($w2, $w1), self::fe_mul($w1, $w3), self::fe_mul($w0, $w2) ); } /** * @param string $h * @return string * @throws SodiumException */ public static function ristretto255_from_hash($h) { if (self::strlen($h) !== 64) { throw new SodiumException('Hash must be 64 bytes'); } //fe25519_frombytes(r0, h); //fe25519_frombytes(r1, h + 32); $r0 = self::fe_frombytes(self::substr($h, 0, 32)); $r1 = self::fe_frombytes(self::substr($h, 32, 32)); //ristretto255_elligator(&p0, r0); //ristretto255_elligator(&p1, r1); $p0 = self::ristretto255_elligator($r0); $p1 = self::ristretto255_elligator($r1); //ge25519_p3_to_cached(&p1_cached, &p1); //ge25519_add_cached(&p_p1p1, &p0, &p1_cached); $p_p1p1 = self::ge_add( $p0, self::ge_p3_to_cached($p1) ); //ge25519_p1p1_to_p3(&p, &p_p1p1); //ristretto255_p3_tobytes(s, &p); return self::ristretto255_p3_tobytes( self::ge_p1p1_to_p3($p_p1p1) ); } /** * @param string $p * @return int * @throws SodiumException */ public static function is_valid_point($p) { $result = self::ristretto255_frombytes($p); if ($result['res'] !== 0) { return 0; } return 1; } /** * @param string $p * @param string $q * @return string * @throws SodiumException */ public static function ristretto255_add($p, $q) { $p_res = self::ristretto255_frombytes($p); $q_res = self::ristretto255_frombytes($q); if ($p_res['res'] !== 0 || $q_res['res'] !== 0) { throw new SodiumException('Could not add points'); } $p_p3 = $p_res['h']; $q_p3 = $q_res['h']; $q_cached = self::ge_p3_to_cached($q_p3); $r_p1p1 = self::ge_add($p_p3, $q_cached); $r_p3 = self::ge_p1p1_to_p3($r_p1p1); return self::ristretto255_p3_tobytes($r_p3); } /** * @param string $p * @param string $q * @return string * @throws SodiumException */ public static function ristretto255_sub($p, $q) { $p_res = self::ristretto255_frombytes($p); $q_res = self::ristretto255_frombytes($q); if ($p_res['res'] !== 0 || $q_res['res'] !== 0) { throw new SodiumException('Could not add points'); } $p_p3 = $p_res['h']; $q_p3 = $q_res['h']; $q_cached = self::ge_p3_to_cached($q_p3); $r_p1p1 = self::ge_sub($p_p3, $q_cached); $r_p3 = self::ge_p1p1_to_p3($r_p1p1); return self::ristretto255_p3_tobytes($r_p3); } /** * @param int $hLen * @param ?string $ctx * @param string $msg * @return string * @throws SodiumException * @psalm-suppress PossiblyInvalidArgument hash API */ protected static function h2c_string_to_hash_sha256($hLen, $ctx, $msg) { $h = array_fill(0, $hLen, 0); $ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0; if ($hLen > 0xff) { throw new SodiumException('Hash must be less than 256 bytes'); } if ($ctx_len > 0xff) { $st = hash_init('sha256'); self::hash_update($st, "H2C-OVERSIZE-DST-"); self::hash_update($st, $ctx); $ctx = hash_final($st, true); $ctx_len = 32; } $t = array(0, $hLen, 0); $ux = str_repeat("\0", 64); $st = hash_init('sha256'); self::hash_update($st, $ux); self::hash_update($st, $msg); self::hash_update($st, self::intArrayToString($t)); self::hash_update($st, $ctx); self::hash_update($st, self::intToChr($ctx_len)); $u0 = hash_final($st, true); for ($i = 0; $i < $hLen; $i += 64) { $ux = self::xorStrings($ux, $u0); ++$t[2]; $st = hash_init('sha256'); self::hash_update($st, $ux); self::hash_update($st, self::intToChr($t[2])); self::hash_update($st, $ctx); self::hash_update($st, self::intToChr($ctx_len)); $ux = hash_final($st, true); $amount = min($hLen - $i, 64); for ($j = 0; $j < $amount; ++$j) { $h[$i + $j] = self::chrToInt($ux[$i]); } } return self::intArrayToString(array_slice($h, 0, $hLen)); } /** * @param int $hLen * @param ?string $ctx * @param string $msg * @return string * @throws SodiumException * @psalm-suppress PossiblyInvalidArgument hash API */ protected static function h2c_string_to_hash_sha512($hLen, $ctx, $msg) { $h = array_fill(0, $hLen, 0); $ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0; if ($hLen > 0xff) { throw new SodiumException('Hash must be less than 256 bytes'); } if ($ctx_len > 0xff) { $st = hash_init('sha256'); self::hash_update($st, "H2C-OVERSIZE-DST-"); self::hash_update($st, $ctx); $ctx = hash_final($st, true); $ctx_len = 32; } $t = array(0, $hLen, 0); $ux = str_repeat("\0", 128); $st = hash_init('sha512'); self::hash_update($st, $ux); self::hash_update($st, $msg); self::hash_update($st, self::intArrayToString($t)); self::hash_update($st, $ctx); self::hash_update($st, self::intToChr($ctx_len)); $u0 = hash_final($st, true); for ($i = 0; $i < $hLen; $i += 128) { $ux = self::xorStrings($ux, $u0); ++$t[2]; $st = hash_init('sha512'); self::hash_update($st, $ux); self::hash_update($st, self::intToChr($t[2])); self::hash_update($st, $ctx); self::hash_update($st, self::intToChr($ctx_len)); $ux = hash_final($st, true); $amount = min($hLen - $i, 128); for ($j = 0; $j < $amount; ++$j) { $h[$i + $j] = self::chrToInt($ux[$i]); } } return self::intArrayToString(array_slice($h, 0, $hLen)); } /** * @param int $hLen * @param ?string $ctx * @param string $msg * @param int $hash_alg * @return string * @throws SodiumException */ public static function h2c_string_to_hash($hLen, $ctx, $msg, $hash_alg) { switch ($hash_alg) { case self::CORE_H2C_SHA256: return self::h2c_string_to_hash_sha256($hLen, $ctx, $msg); case self::CORE_H2C_SHA512: return self::h2c_string_to_hash_sha512($hLen, $ctx, $msg); default: throw new SodiumException('Invalid H2C hash algorithm'); } } /** * @param ?string $ctx * @param string $msg * @param int $hash_alg * @return string * @throws SodiumException */ protected static function _string_to_element($ctx, $msg, $hash_alg) { return self::ristretto255_from_hash( self::h2c_string_to_hash(self::crypto_core_ristretto255_HASHBYTES, $ctx, $msg, $hash_alg) ); } /** * @return string * @throws SodiumException * @throws Exception */ public static function ristretto255_random() { return self::ristretto255_from_hash( ParagonIE_Sodium_Compat::randombytes_buf(self::crypto_core_ristretto255_HASHBYTES) ); } /** * @return string * @throws SodiumException */ public static function ristretto255_scalar_random() { return self::scalar_random(); } /** * @param string $s * @return string * @throws SodiumException */ public static function ristretto255_scalar_complement($s) { return self::scalar_complement($s); } /** * @param string $s * @return string */ public static function ristretto255_scalar_invert($s) { return self::sc25519_invert($s); } /** * @param string $s * @return string * @throws SodiumException */ public static function ristretto255_scalar_negate($s) { return self::scalar_negate($s); } /** * @param string $x * @param string $y * @return string */ public static function ristretto255_scalar_add($x, $y) { return self::scalar_add($x, $y); } /** * @param string $x * @param string $y * @return string */ public static function ristretto255_scalar_sub($x, $y) { return self::scalar_sub($x, $y); } /** * @param string $x * @param string $y * @return string */ public static function ristretto255_scalar_mul($x, $y) { return self::sc25519_mul($x, $y); } /** * @param string $ctx * @param string $msg * @param int $hash_alg * @return string * @throws SodiumException */ public static function ristretto255_scalar_from_string($ctx, $msg, $hash_alg) { $h = array_fill(0, 64, 0); $h_be = self::stringToIntArray( self::h2c_string_to_hash( self::HASH_SC_L, $ctx, $msg, $hash_alg ) ); for ($i = 0; $i < self::HASH_SC_L; ++$i) { $h[$i] = $h_be[self::HASH_SC_L - 1 - $i]; } return self::ristretto255_scalar_reduce(self::intArrayToString($h)); } /** * @param string $s * @return string */ public static function ristretto255_scalar_reduce($s) { return self::sc_reduce($s); } /** * @param string $n * @param string $p * @return string * @throws SodiumException */ public static function scalarmult_ristretto255($n, $p) { if (self::strlen($n) !== 32) { throw new SodiumException('Scalar must be 32 bytes, ' . self::strlen($p) . ' given.'); } if (self::strlen($p) !== 32) { throw new SodiumException('Point must be 32 bytes, ' . self::strlen($p) . ' given.'); } $result = self::ristretto255_frombytes($p); if ($result['res'] !== 0) { throw new SodiumException('Could not multiply points'); } $P = $result['h']; $t = self::stringToIntArray($n); $t[31] &= 0x7f; $Q = self::ge_scalarmult(self::intArrayToString($t), $P); $q = self::ristretto255_p3_tobytes($Q); if (ParagonIE_Sodium_Compat::is_zero($q)) { throw new SodiumException('An unknown error has occurred'); } return $q; } /** * @param string $n * @return string * @throws SodiumException */ public static function scalarmult_ristretto255_base($n) { $t = self::stringToIntArray($n); $t[31] &= 0x7f; $Q = self::ge_scalarmult_base(self::intArrayToString($t)); $q = self::ristretto255_p3_tobytes($Q); if (ParagonIE_Sodium_Compat::is_zero($q)) { throw new SodiumException('An unknown error has occurred'); } return $q; } } PK �}[7� A src/Core/AEGIS256.phpnu �[��� <?php if (!defined('SODIUM_COMPAT_AEGIS_C0')) { define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62"); } if (!defined('SODIUM_COMPAT_AEGIS_C1')) { define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd"); } class ParagonIE_Sodium_Core_AEGIS256 extends ParagonIE_Sodium_Core_AES { /** * @param string $ct * @param string $tag * @param string $ad * @param string $key * @param string $nonce * @return string * @throws SodiumException */ public static function decrypt($ct, $tag, $ad, $key, $nonce) { $state = self::init($key, $nonce); // ad_blocks = Split(ZeroPad(ad, 128), 128) $ad_blocks = (self::strlen($ad) + 15) >> 4; // for ai in ad_blocks: // Absorb(ai) for ($i = 0; $i < $ad_blocks; ++$i) { $ai = self::substr($ad, $i << 4, 16); if (self::strlen($ai) < 16) { $ai = str_pad($ai, 16, "\0", STR_PAD_RIGHT); } $state->absorb($ai); } $msg = ''; $cn = self::strlen($ct) & 15; $ct_blocks = self::strlen($ct) >> 4; // ct_blocks = Split(ZeroPad(ct, 128), 128) // cn = Tail(ct, |ct| mod 128) for ($i = 0; $i < $ct_blocks; ++$i) { $msg .= $state->dec(self::substr($ct, $i << 4, 16)); } // if cn is not empty: // msg = msg || DecPartial(cn) if ($cn) { $start = $ct_blocks << 4; $msg .= $state->decPartial(self::substr($ct, $start, $cn)); } $expected_tag = $state->finalize( self::strlen($ad) << 3, self::strlen($msg) << 3 ); if (!self::hashEquals($expected_tag, $tag)) { try { // The RFC says to erase msg, so we shall try: ParagonIE_Sodium_Compat::memzero($msg); } catch (SodiumException $ex) { // Do nothing if we cannot memzero } throw new SodiumException('verification failed'); } return $msg; } /** * @param string $msg * @param string $ad * @param string $key * @param string $nonce * @return array * @throws SodiumException */ public static function encrypt($msg, $ad, $key, $nonce) { $state = self::init($key, $nonce); $ad_len = self::strlen($ad); $msg_len = self::strlen($msg); $ad_blocks = ($ad_len + 15) >> 4; for ($i = 0; $i < $ad_blocks; ++$i) { $ai = self::substr($ad, $i << 4, 16); if (self::strlen($ai) < 16) { $ai = str_pad($ai, 16, "\0", STR_PAD_RIGHT); } $state->absorb($ai); } $ct = ''; $msg_blocks = ($msg_len + 15) >> 4; for ($i = 0; $i < $msg_blocks; ++$i) { $xi = self::substr($msg, $i << 4, 16); if (self::strlen($xi) < 16) { $xi = str_pad($xi, 16, "\0", STR_PAD_RIGHT); } $ct .= $state->enc($xi); } $tag = $state->finalize( $ad_len << 3, $msg_len << 3 ); return array( self::substr($ct, 0, $msg_len), $tag ); } /** * @param string $key * @param string $nonce * @return ParagonIE_Sodium_Core_AEGIS_State256 */ public static function init($key, $nonce) { return ParagonIE_Sodium_Core_AEGIS_State256::init($key, $nonce); } } PK �}[��A���B ��B src/Core/AES/hcidumpnu ��� ELF >