namespace Acme\Algorithm;
use Jose\Component\Core\JWK;
use const Sodium\CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES;
* This algorithm is a custom algorithm that use the ChaCha20 + Poly 1305 with a 192 bits nonce (IETF variant).
final class ChaCha20Poly1305IETF implements KeyEncryption //The algorithm acts as a Key Encryption algorithm
public function name(): string
return 'ChaCha20+Poly1305+IETF'; // The name of our algorithm. This name will be used in our JWE headers
public function allowedKeyTypes(): array
return ['oct']; // Key types for this algorithm are octet keys
public function encryptKey(JWK $key, string $cek, array $completeHeader, array &$additionalHeader): string
$this->checkKey($key); // We check the key
$kek = Base64Url::decode($key->get('k')); // We retrieve the secret
$nonce = random_bytes(CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES); // We create a nonce
$additionalHeader['nonce'] = Base64Url::encode($nonce); // We add the nonce to the header
return sodium_crypto_aead_chacha20poly1305_ietf_encrypt($cek, '', $nonce, $kek); // We return the encrypted CEK
public function decryptKey(JWK $key, string $encrypted_cek, array $header): string
$this->checkKey($key); // We check the key
$this->checkAdditionalParameters($header); // We verify the nonce is in the headers
$nonce = Base64Url::decode($header['nonce']); // We retrieve the nonce
$kek = Base64Url::decode($key->get('k')); // an the secret
$decrypted = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($encrypted_cek, '', $nonce, $kek); // We try to decrypt the CEK
if (false === $decrypted) { // If it fails we throw an exception
throw new \RuntimeException('Unable to decrypt.');
return $decrypted; // Otherwise we return the decrypted CEK
protected function checkKey(JWK $key)
if (!in_array($key->get('kty'), $this->allowedKeyTypes())) {
throw new \InvalidArgumentException('Wrong key type.');
throw new \InvalidArgumentException('The key parameter "k" is missing.');
protected function checkAdditionalParameters(array $header)
foreach (['nonce'] as $k) {
if (!array_key_exists($k, $header)) {
throw new \InvalidArgumentException(sprintf('Parameter "%s" is missing.', $k));
public function getKeyManagementMode(): string
return self::MODE_ENCRYPT; //Key Management Mode is 'enc'.