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 Method | What 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 keyDecryption 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 logicCache Behavior
- Cache duration is controlled by
TayraOptions.KeyCacheDuration(default: 5 minutes). ShredAsyncevicts the key from the local cache immediately, then deletes it from the key store.ShredByPrefixAsyncdelegates toIKeyStore.DeleteByPrefixAsyncbut does not evict individual keys from the cache. Cached copies expire naturally.
Key Rotation
RotateKeyAsync creates a new versioned key:
- Discovers existing versioned keys via
IKeyStore.ListKeyIdsAsync. - Determines the current maximum version number.
- Generates a new key with the next version (e.g.,
cust-abc123:v2). - Stores and caches the new key.
- Returns a
KeyRotationResultwith 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
TayraActivitySourcefor each operation. - Metrics via
TayraMetrics: cache hits, cache misses, key store latency, keys created, keys deleted. - Audit events via
ITayraAuditLoggerfor 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
