Skip to content

Testing Code That Uses a KMS

Edit this page

Three doubles live in the Test\ namespace of the component, for test fixtures only. None of them needs a provider.

Warning

The KeyManagement component is experimental and is not covered by Symfony's Backward Compatibility Promise.

Encrypting Without a Provider

InMemoryKms performs no cryptography: its ciphertext is the plaintext behind a marker holding the instance name, the key id and the AAD. Decrypting under a different key id or AAD fails, and so does feeding a plaintext back into decrypt(), which catches key-routing and AAD-binding bugs.

Each instance carries a name, random unless given, so two instances refuse each other's ciphertexts the way two providers do, and two instances given the same name stand for the same provider. That is what makes it possible to test client routing, a provider migration or a composite client:

1
2
3
4
5
6
use Symfony\Component\KeyManagement\Test\InMemoryKms;

$aws = new InMemoryKms('aws');
$azure = new InMemoryKms('azure');

$azure->decrypt($aws->encrypt('app-key', 'secret'));   // throws

Storing Data Keys Without a Database

InMemoryDataKeyStore is an array-backed RewrappableDataKeyStoreInterface that can wrap its data keys through any KMS client and orders its UUIDv7 references the way a database would:

1
2
3
4
use Symfony\Component\KeyManagement\StoredEnvelopeEncrypter;
use Symfony\Component\KeyManagement\Test\InMemoryDataKeyStore;

$encrypter = new StoredEnvelopeEncrypter(new InMemoryDataKeyStore());

Its $maxAgeSeconds argument is null by default, so a key is never replaced, and 0 replaces it on every call. To check that a key is replaced at its real age, also pass a clock as the next argument: a MockClock that the test moves forward, or a Clock, which follows the global clock of ClockSensitiveTrait. Without a clock, the store uses the system time.

Taking a Provider Down

SwitchableKms answers a different question: what the application does while a provider is down. It wraps a client and throws the exception it was given, instead of answering, while its $down property is true. Its $calls and $keyIds properties record what it was asked:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\KeyManagement\CompositeKms;
use Symfony\Component\KeyManagement\Test\InMemoryKms;
use Symfony\Component\KeyManagement\Test\SwitchableKms;

$aws = new SwitchableKms(new InMemoryKms('aws'));
$kms = new CompositeKms(new ServiceLocator([
    'aws' => fn () => $aws,
    'azure' => fn () => new InMemoryKms('azure'),
]), ['aws' => null, 'azure' => 'backup']);

$ciphertext = $kms->encrypt('app', 'secret');

$aws->down = true;
$kms->decrypt($ciphertext); // still "secret", read through "azure"
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version