randomEngine = new RandomEngine(); } public function encrypt(#[\SensitiveParameter] string $plain_text, string $item_name = ""): string { $nonce = $this->randomEngine->get_bytes( SODIUM_CRYPTO_STREAM_NONCEBYTES ); $salt = $this->randomEngine->get_bytes(self::SALT_SIZE_IN_BYTES); list ($enc_key, $auth_key) = $this->splitKeys($item_name, sodium_bin2hex($salt)); $cipher_text = sodium_crypto_stream_xor( $plain_text, $nonce, $enc_key ); sodium_memzero($plain_text); $mac = sodium_crypto_auth($nonce . $cipher_text, $auth_key); sodium_memzero($enc_key); sodium_memzero($auth_key); return sodium_bin2hex($mac . $nonce . $salt . $cipher_text); } public function decrypt(string $cypher_data, string $item_name = ""): string { $bin_data = sodium_hex2bin($cypher_data); $mac = mb_substr( $bin_data, 0, SODIUM_CRYPTO_AUTH_BYTES, '8bit' ); $nonce = mb_substr( $bin_data, SODIUM_CRYPTO_AUTH_BYTES, SODIUM_CRYPTO_STREAM_NONCEBYTES, '8bit' ); $salt = mb_substr( $bin_data, SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES, self::SALT_SIZE_IN_BYTES, '8bit' ); $cipher_text = mb_substr( $bin_data, SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES + self::SALT_SIZE_IN_BYTES, null, '8bit' ); list ($enc_key, $auth_key) = $this->splitKeys($item_name, sodium_bin2hex($salt)); if (sodium_crypto_auth_verify($mac, $nonce . $cipher_text, $auth_key)) { sodium_memzero($auth_key); $plaintext = sodium_crypto_stream_xor($cipher_text, $nonce, $enc_key); sodium_memzero($enc_key); if ($plaintext !== false) { return $plaintext; } } else { sodium_memzero($auth_key); sodium_memzero($enc_key); } throw new \Exception('Decryption failed.'); } /** * @return array(2) [encryption key, authentication key] */ private function splitKeys(string $item_name, #[\SensitiveParameter] string $salt): array { $enc_key = hash_hkdf('sha256', $this->key, SODIUM_CRYPTO_STREAM_KEYBYTES, md5('encryption' . $item_name), $salt); $auth_key = hash_hkdf('sha256', $this->key, SODIUM_CRYPTO_AUTH_KEYBYTES, md5('authentication' . $item_name), $salt); return [$enc_key, $auth_key]; } } /* * Example: $secretkey = "78a5011b9997cd03a28a3412c66565b7c32715b35e055d7abfc228236308d3b2"; $plain_text = "Hello World!"; $sc = new ParagonCrypto\SodiumStorage($secretkey); $index = "sensitive"; $encoded = $sc->encode($index, $plain_text); setcookie($index, $encoded); // On the next page load: try { if (!array_key_exists($index, $_COOKIE)) { throw new \Exception('No Cookie!'); } $data = $_COOKIE[$index]; $plain_text = $sc->decode($index, $data); echo $plain_text; } catch (Exception $ex) { // Handle the exception here echo $ex->getMessage(); } */