Skip to content

Crypto Engine

The crypto engine manages the key lifecycle — creation, retrieval, caching, rotation, and deletion. Users interact with these operations through ITayra; the crypto engine is an internal implementation detail.

User-Facing Operations on ITayra

ITayra MethodWhat Happens Internally
EncryptAsync<T>()Gets or creates a key via the engine, then encrypts
DecryptAsync<T>()Retrieves a key (returns replacements if shredded)
ShredAsync(subjectId)Deletes the key — data becomes permanently unreadable
ShredByPrefixAsync(prefix)Bulk deletes all matching keys
RotateKeyAsync(keyIdBase)Creates a new versioned key, preserves the old one
ReEncryptAsync<T>()Decrypts with old key, re-encrypts with new key
KeyExistsAsync(keyId)Checks if a key is still active
ListKeysAsync(prefix)Lists all key IDs matching a prefix

How It Works

Internal implementation — provided for clarity

ICryptoEngine Interface

csharp
internal interface ICryptoEngine
{
    Task<byte[]> GetOrCreateKeyAsync(string keyId, CancellationToken ct = default);
    Task<byte[]?> GetKeyAsync(string keyId, CancellationToken ct = default);
    Task DeleteKeyAsync(string keyId, CancellationToken ct = default);
    Task<bool> KeyExistsAsync(string keyId, CancellationToken ct = default);
    Task DeleteAllKeysAsync(string subjectPrefix, CancellationToken ct = default);
    Task<string> RotateKeyAsync(string keyIdBase, CancellationToken ct = default);
}

The built-in DefaultCryptoEngine wraps an IKeyStore with an in-memory MemoryCache.

Key Retrieval Flow

GetOrCreateKeyAsync("cust-abc123")

    ├─ Check MemoryCache
    │   ├─ HIT → return cached key
    │   └─ MISS ↓

    ├─ Call IKeyStore.GetAsync("cust-abc123")
    │   ├─ Key exists → cache it, return
    │   └─ Key not found ↓

    ├─ Generate new AES key (RandomNumberGenerator)
    ├─ Call IKeyStore.StoreAsync("cust-abc123", newKey)
    ├─ Cache the new key
    └─ Return new key

Decryption Flow

GetKeyAsync("cust-abc123")

    ├─ Check MemoryCache
    │   ├─ HIT → return cached key
    │   └─ MISS ↓

    ├─ Call IKeyStore.GetAsync("cust-abc123")
    │   ├─ Key exists → cache it, return
    │   └─ Key not found → return null (key was shredded)

    └─ null triggers replacement value logic

Cache Behavior

  • Cache duration is controlled by TayraOptions.KeyCacheDuration (default: 5 minutes).
  • ShredAsync evicts the key from the local cache immediately, then deletes it from the key store.
  • ShredByPrefixAsync delegates to IKeyStore.DeleteByPrefixAsync but does not evict individual keys from the cache. Cached copies expire naturally.

Key Rotation

RotateKeyAsync creates a new versioned key:

  1. Discovers existing versioned keys via IKeyStore.ListKeyIdsAsync.
  2. Determines the current maximum version number.
  3. Generates a new key with the next version (e.g., cust-abc123:v2).
  4. Stores and caches the new key.
  5. Returns a KeyRotationResult with old and new key IDs.

The old key is preserved so that existing data can still be decrypted. Call ReEncryptAsync<T>() to migrate data to the new key.

Telemetry

The engine emits:

  • OpenTelemetry activities via TayraActivitySource for each operation.
  • Metrics via TayraMetrics: cache hits, cache misses, key store latency, keys created, keys deleted.
  • Audit events via ITayraAuditLogger for key creation, access, deletion, and bulk deletion.

Distributed Deployments

In a multi-instance deployment, calling ShredAsync on one instance evicts the key from that instance's cache. Other instances will continue using their cached copy until it expires. Keep KeyCacheDuration short (1–5 minutes) to minimize this window.

See Also

  • Key Store — The persistence layer (public extension point)
  • Field Encryption — How the engine is used during encryption
  • Encryption — AES-256-GCM details and wire format
  • Options — Cache duration and key size settings