Skip to content

Key management domain (API surface)#

domain, key-management

// Structure of documents
└── src/
    └── KeyManagement/
        └── KeyActivatorException.php
        └── KeyActivatorInterface.php
        └── KeyGeneratorException.php
        └── KeyGeneratorInterface.php
        └── KeyPrunePlan.php
        └── KeyPruneResult.php
        └── KeyPrunerException.php
        └── KeyPrunerInterface.php
        └── KeyRotationException.php
        └── KeyRotationOptions.php
        └── KeyRotationPlan.php
        └── KeyRotationResult.php
        └── KeyRotatorInterface.php
        └── KeyUsageTrackerInterface.php
        └── Observers/
            ├── KeyActivatedObserverInterface.php
            ├── KeyDeletedObserverInterface.php
        └── Port/
            └── KeyRegistryException.php
            └── KeyRegistryInterface.php
            └── KeyUsageMapping.php
            └── KeyUsageProviderInterface.php
            └── MutableKeyRegistryInterface.php

Path: /src/KeyManagement/KeyActivatorException.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Thrown when key activation fails.
 *
 * This exception is intended to be thrown by the KeyActivator application
 * service, so callers do not need to depend on the underlying repository
 * exception hierarchy.
 */
final class KeyActivatorException extends \RuntimeException
{
    /**
     * Creates an exception for a failed encryption key activation.
     *
     * @param string $keyId
     *   The key identifier that could not be activated.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception that caused activation to fail.
     *
     * @return self
     *   The exception instance.
     */
    public static function activationFailed(string $keyId, ?\Throwable $previous = null): self
    {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyActivatorInterface.php#

namespace Drupal\easy_encryption\KeyManagement;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Application service for activating an encryption key.
 *
 * Activation sets which encryption key ID is considered active for new
 * encryption operations on the site.
 */
interface KeyActivatorInterface
{
    /**
     * Activates an existing encryption key by ID.
     *
     * Activation MUST be allowed when private key is missing, as long as a public
     * key exists (encrypt-only environments).
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId $keyId
     *   The encryption key identifier.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\KeyActivatorException
     *   Thrown when activation fails.
     */
    public function activate(EncryptionKeyId $keyId): void;
}

Path: /src/KeyManagement/KeyGeneratorException.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Thrown when key generation or activation fails.
 */
final class KeyGeneratorException extends \RuntimeException
{
    /**
     * Creates an exception for a failed encryption key generation.
     *
     * @param string|null $keyId
     *   The key identifier, if one was known at the time of failure.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception that caused generation to fail.
     *
     * @return self
     *   The exception instance.
     */
    public static function generationFailed(?string $keyId = null, ?\Throwable $previous = null): self
    {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyGeneratorInterface.php#

namespace Drupal\easy_encryption\KeyManagement;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Application service for generating and activating encryption keys.
 *
 * This service returns key identifiers, not key material, to keep the
 * application layer decoupled from the underlying cryptographic library.
 */
interface KeyGeneratorInterface
{
    /**
     * Generates and persists an encryption key.
     *
     * If no ID is provided, the generator MUST create a new identifier.
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId|null $keyId
     *   (optional) The encryption key identifier to use.
     *
     * @return \Drupal\easy_encryption\Encryption\EncryptionKeyId
     *   The encryption key identifier that was generated.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\KeyGeneratorException
     *   Thrown when the encryption key generation fails.
     */
    public function generate(?EncryptionKeyId $keyId = null): EncryptionKeyId;
}

Path: /src/KeyManagement/KeyPrunePlan.php#

namespace Drupal\easy_encryption\KeyManagement;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Describes what would be pruned without performing changes.
 *
 * @immutable
 */
final class KeyPrunePlan
{
    /**
     * Construct a KeyPrunePlan.
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId|null $activeKeyId
     *   The active encryption key ID, if any.
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId[] $toDelete
     *   Encryption key IDs that would be deleted.
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId[] $referenced
     *   Encryption key IDs referenced, in use.
     */
    public function __construct(
        public readonly ?EncryptionKeyId $activeKeyId,
        public readonly array $toDelete = [],
        public readonly array $referenced = [],
    ) {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyPruneResult.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Result of pruning unused encryption keys.
 *
 * @immutable
 */
final class KeyPruneResult
{
    /**
     * Construct a KeyPruneResult.
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId[] $deleted
     *   Deleted encryption key IDs.
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId[] $failed
     *   Encryption key IDs that failed deletion.
     */
    public function __construct(
        public readonly array $deleted = [],
        public readonly array $failed = [],
    ) {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyPrunerException.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Thrown when pruning unused encryption keys cannot be planned or started.
 */
final class KeyPrunerException extends \RuntimeException
{
    /**
     * Creates an exception for failures while building a prune plan.
     *
     * @param \Throwable|null $previous
     *   (optional) The underlying exception.
     */
    public static function planFailed(?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception for failures that prevent pruning from running at all.
     *
     * @param \Throwable|null $previous
     *   (optional) The underlying exception.
     */
    public static function pruneFailed(?\Throwable $previous = null): self
    {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyPrunerInterface.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Application service for pruning unused encryption keys.
 */
interface KeyPrunerInterface
{
    /**
     * Builds a non-mutating plan describing what would be pruned.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\KeyPrunerException
     *   Thrown when the plan cannot be computed.
     */
    public function planPruning(): KeyPrunePlan;


    /**
     * Deletes unused encryption keys.
     *
     * This operation returns partial results: failures are reported in the result
     * object rather than aborting the whole run.
     *
     * @return \Drupal\easy_encryption\KeyManagement\KeyPruneResult
     *   The prune result.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\KeyPrunerException
     *   Thrown when the prune operation cannot start
     *   (for example, planning fails).
     */
    public function pruneUnused(): KeyPruneResult;
}

Path: /src/KeyManagement/KeyRotationException.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Exception is thrown by key rotation workflows.
 */
final class KeyRotationException extends \RuntimeException
{
    /**
     * Creates an exception for failures while building a rotation plan.
     *
     * Planning is a non-mutating operation (used for dry-run previews). This
     * exception indicates that the system could not compute a plan, typically due
     * to storage access errors or unexpected runtime issues.
     *
     * @param \Throwable|null $previous
     *   (optional) The underlying exception that caused planning to fail.
     *
     * @return self
     *   The exception instance.
     */
    public static function planFailed(?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception for failures while rotating the active enc. keys.
     *
     * This exception indicates that generating or activating a new encryption key
     * failed.
     *
     * @param \Throwable|null $previous
     *   (optional) The underlying exception that caused rotation to fail.
     *
     * @return self
     *   The exception instance.
     */
    public static function rotateFailed(?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception when re-encryption cannot be performed.
     *
     * This is used when the operation is not possible in the current environment,
     * for example because the private key is not available and existing encrypted
     * values therefore cannot be decrypted for re-encryption.
     *
     * @param string $reason
     *   A human-readable explanation of why re-encryption cannot be performed.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception, if any.
     *
     * @return self
     *   The exception instance.
     */
    public static function reencryptNotPossible(string $reason, ?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception when one or more credentials fail to re-encrypt.
     *
     * @param int $failedCount
     *   The number of credentials that failed to re-encrypt.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception, if any.
     *
     * @return self
     *   The exception instance.
     */
    public static function reencryptFailed(int $failedCount, ?\Throwable $previous = null): self
    {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyRotationOptions.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Options for the rotation operation.
 */
final class KeyRotationOptions
{
    public function __construct(
        public readonly bool $reencryptKeys = false,
        public readonly bool $failOnReencryptErrors = true,
    ) {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyRotationPlan.php#

namespace Drupal\easy_encryption\KeyManagement;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Describes what would be re-encrypted without performing changes.
 *
 * @immutable
 */
final class KeyRotationPlan
{
    public function __construct(
        public readonly ?EncryptionKeyId $activeKeyId,
        public readonly int $total = 0,
        public readonly int $toUpdate = 0,
        public readonly int $toSkip = 0,
    ) {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyRotationResult.php#

namespace Drupal\easy_encryption\KeyManagement;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Result of rotating the active encryption key and optionally re-encrypting.
 *
 * @immutable
 */
final class KeyRotationResult
{
    public function __construct(
        public readonly ?EncryptionKeyId $oldActiveKeyId,
        public readonly EncryptionKeyId $newActiveKeyId,
        public readonly int $updated = 0,
        public readonly int $skipped = 0,
        public readonly int $failed = 0,
    ) {
        /* ... */
    }
}

Path: /src/KeyManagement/KeyRotatorInterface.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Application service for planning and performing encryption key rotation.
 */
interface KeyRotatorInterface
{
    /**
     * Builds a non-mutating plan describing what would change.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\KeyRotationException
     *   If the plan cannot be computed.
     */
    public function plan(bool $includeReencryptCounts = true): KeyRotationPlan;


    /**
     * Rotates the active encryption key and optionally re-encrypts credentials.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\KeyRotationException
     *   If rotation fails, or re-encryption fails and failOnReencryptErrors
     *   is TRUE.
     */
    public function rotate(KeyRotationOptions $options): KeyRotationResult;
}

Path: /src/KeyManagement/KeyUsageTrackerInterface.php#

namespace Drupal\easy_encryption\KeyManagement;

/**
 * Public API for tracking encryption key usage across the entire system.
 */
interface KeyUsageTrackerInterface
{
    /**
     * Returns the aggregated mapping of all consumers and their encryption keys.
     *
     * @return array{result: array<string, \Drupal\easy_encryption\KeyManagement\Port\KeyUsageMapping>, cacheability: \Drupal\Core\Cache\CacheableDependencyInterface}
     *   The 'result' is keyed by consumer ID.
     *   'cacheability' applies to the entire set.
     */
    public function getKeyUsageMapping(): array;
}

Path: /src/KeyManagement/Observers/KeyActivatedObserverInterface.php#

namespace Drupal\easy_encryption\KeyManagement\Observers;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Contract for encryption key activation observers.
 */
interface KeyActivatedObserverInterface
{
    /**
     * Act when an encryption key gets activated.
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId $activeKeyId
     *   The ID of the activated encryption key.
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId|null $previousKeyId
     *   The ID of the previously active key if there was any.
     */
    public function onKeyActivation(EncryptionKeyId $activeKeyId, ?EncryptionKeyId $previousKeyId): void;
}

Path: /src/KeyManagement/Observers/KeyDeletedObserverInterface.php#

namespace Drupal\easy_encryption\KeyManagement\Observers;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Contract for encryption key deletion observers.
 */
interface KeyDeletedObserverInterface
{
    /**
     * Act when an encryption key gets deleted.
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId $keyId
     *   The ID of the activated encryption key.
     */
    public function onKeyDeletion(EncryptionKeyId $keyId): void;
}

Path: /src/KeyManagement/Port/KeyRegistryException.php#

namespace Drupal\easy_encryption\KeyManagement\Port;

/**
 * Thrown when the encryption key registry cannot be read or written.
 *
 * This exception is intended to be thrown by KeyRegistryInterface
 * implementations, so callers do not need to depend on underlying storage
 * exceptions (config, database, etc.).
 */
final class KeyRegistryException extends \RuntimeException
{
    /**
     * Creates an exception for failures while reading registry data.
     *
     * @param \Throwable|null $previous
     *   (optional) The underlying exception.
     */
    public static function readFailed(?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception for failures while registering a key id.
     *
     * @param string $keyId
     *   The encryption key identifier that could not be registered.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception.
     */
    public static function registerFailed(string $keyId, ?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception for failures while unregistering a key id.
     *
     * @param string $keyId
     *   The encryption key identifier that could not be registered.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception.
     */
    public static function unregisterFailed(string $keyId, ?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception for failures while setting the active key id.
     *
     * @param string $keyId
     *   The encryption key identifier that could not be activated.
     * @param \Throwable|null $previous
     *   (optional) The underlying exception.
     */
    public static function setActiveFailed(string $keyId, ?\Throwable $previous = null): self
    {
        /* ... */
    }


    /**
     * Creates an exception when attempting to set an active key that is unknown.
     *
     * @param string $keyId
     *   The encryption key identifier.
     */
    public static function unknownKey(string $keyId): self
    {
        /* ... */
    }
}

Path: /src/KeyManagement/Port/KeyRegistryInterface.php#

namespace Drupal\easy_encryption\KeyManagement\Port;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Read-only registry of known encryption keys and the active key.
 */
interface KeyRegistryInterface
{
    /**
     * Lists encryption key IDs known to this site.
     *
     * @return array{result: \Drupal\easy_encryption\Encryption\EncryptionKeyId[], cacheability: \Drupal\Core\Cache\CacheableDependencyInterface}
     *   Known key ids plus cacheability metadata.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\Port\KeyRegistryException
     *   When registry storage cannot be read.
     */
    public function listKnownKeyIds(): array;


    /**
     * Returns the active encryption key id, if any.
     *
     * @return array{result: \Drupal\easy_encryption\Encryption\EncryptionKeyId|null, cacheability: \Drupal\Core\Cache\CacheableDependencyInterface}
     *   Active key id plus cacheability metadata.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\Port\KeyRegistryException
     *   When registry storage cannot be read.
     */
    public function getActiveKeyId(): array;


    /**
     * Checks whether a key id is known to the registry.
     *
     * @return array{result: bool, cacheability: \Drupal\Core\Cache\CacheableDependencyInterface}
     *   TRUE if known plus cacheability metadata.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\Port\KeyRegistryException
     *   When registry storage cannot be read.
     */
    public function isKnown(EncryptionKeyId $key_id): array;
}

Path: /src/KeyManagement/Port/KeyUsageMapping.php#

namespace Drupal\easy_encryption\KeyManagement\Port;

use Drupal\Core\Cache\CacheBackendInterface as CacheBackendInterface;
use Drupal\Core\Cache\CacheableDependencyInterface as CacheableDependencyInterface;
use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Represents an encryption key used by a specific consumer with cacheability.
 */
final class KeyUsageMapping implements CacheableDependencyInterface
{
    /**
     * Constructs a new object.
     *
     * @param \Drupal\easy_encryption\Encryption\EncryptionKeyId $keyId
     *   The encryption key being used.
     * @param \Drupal\Core\Cache\CacheableDependencyInterface|null $cacheability
     *   Optional cacheability metadata for this specific mapping.
     */
    public function __construct(
        public readonly EncryptionKeyId $keyId,
        private readonly ?CacheableDependencyInterface $cacheability = null,
    ) {
        /* ... */
    }


    /**
     * {@inheritdoc}
     */
    public function getCacheContexts(): array
    {
        /* ... */
    }


    /**
     * {@inheritdoc}
     */
    public function getCacheTags(): array
    {
        /* ... */
    }


    /**
     * {@inheritdoc}
     */
    public function getCacheMaxAge(): int
    {
        /* ... */
    }
}

Path: /src/KeyManagement/Port/KeyUsageProviderInterface.php#

namespace Drupal\easy_encryption\KeyManagement\Port;

/**
 * Interface for reporters that identify encryption key dependencies.
 */
interface KeyUsageProviderInterface
{
    /**
     * Returns a mapping of consumers.
     *
     * @return array{result: array<string, \Drupal\easy_encryption\KeyManagement\Port\KeyUsageMapping>, cacheability: \Drupal\Core\Cache\CacheableDependencyInterface}
     *   The 'result' is keyed by consumer ID. 'cacheability' applies to the
     *   entire set.
     */
    public function getKeyUsageMapping(): array;
}

Path: /src/KeyManagement/Port/MutableKeyRegistryInterface.php#

namespace Drupal\easy_encryption\KeyManagement\Port;

use Drupal\easy_encryption\Encryption\EncryptionKeyId as EncryptionKeyId;

/**
 * Mutable registry of known encryption keys and the active key.
 */
interface MutableKeyRegistryInterface
{
    /**
     * Registers a key id as known.
     *
     * Implementations must be idempotent.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\Port\KeyRegistryException
     *   When the registry cannot be written.
     */
    public function register(EncryptionKeyId $key_id): void;


    /**
     * Unregisters a key id from the known list.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\Port\KeyRegistryException
     *   When the registry cannot be written.
     */
    public function unregister(EncryptionKeyId $key_id): void;


    /**
     * Sets the active encryption key id.
     *
     * Implementations should either:
     * - implicitly register the key id if missing, or
     * - throw unknownKey() if the key id is not registered.
     *
     * Pick one policy and keep it consistent. For export/import and for
     * predictable behavior, I recommend: throw if unknown.
     *
     * @throws \Drupal\easy_encryption\KeyManagement\Port\KeyRegistryException
     *   When the registry cannot be written or the key is unknown.
     */
    public function setActive(EncryptionKeyId $key_id): void;
}

File Statistics - Size: 20.8 KB - Lines: 829 File: core/domain/key_management.md