Custom Algorithm

Last updated 6 months ago

This framework provides dozens of signature or encryption algorithms. If your application uses a custom algorithm or if another algorithm has been recently approved in the JWT context, it may be interesting to use it with this framework.

Hopefully, this is very easy. In the following example, we will create a class to use the ChaCha20 + Poly 1305 (IETF variant) encryption algorithm as a Key Encryption Algorithm.

<?php
declare(strict_types=1);
namespace Acme\Algorithm;
use Base64Url\Base64Url;
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
{
/**
* {@inheritdoc}
*/
public function name(): string
{
return 'ChaCha20+Poly1305+IETF'; // The name of our algorithm. This name will be used in our JWE headers
}
/**
* {@inheritdoc}
*/
public function allowedKeyTypes(): array
{
return ['oct']; // Key types for this algorithm are octet keys
}
/**
* {@inheritdoc}
*/
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
}
/**
* {@inheritdoc}
*/
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
}
/**
* @param JWK $key
*/
protected function checkKey(JWK $key)
{
if (!in_array($key->get('kty'), $this->allowedKeyTypes())) {
throw new \InvalidArgumentException('Wrong key type.');
}
if (!$key->has('k')) {
throw new \InvalidArgumentException('The key parameter "k" is missing.');
}
}
/**
* @param array $header
*/
protected function checkAdditionalParameters(array $header)
{
foreach (['nonce'] as $k) {
if (!array_key_exists($k, $header)) {
throw new \InvalidArgumentException(sprintf('Parameter "%s" is missing.', $k));
}
}
}
/**
* {@inheritdoc}
*/
public function getKeyManagementMode(): string
{
return self::MODE_ENCRYPT; //Key Management Mode is 'enc'.
}
}