Choosing Components
View SourceThis guide helps you select the right keyring and Cryptographic Materials Manager (CMM) for your use case.
Understanding the Architecture
The AWS Encryption SDK uses two key abstractions:
┌─────────────────────────────────────────────────────────────┐
│ Client │
│ (commitment policy, max EDKs) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Cryptographic Materials Manager (CMM) │
│ (key caching, required context enforcement) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Keyring │
│ (wraps/unwraps data keys using master keys) │
└─────────────────────────────────────────────────────────────┘Keyring: Wraps and unwraps data encryption keys using your master keys. Choose based on where your keys are stored.
CMM: Manages cryptographic materials. The Default CMM is sufficient for most use cases. Use specialized CMMs for caching or context enforcement.
Keyring Selection
Decision Tree
Where are your master keys stored?
│
├─► AWS KMS
│ │
│ ├─► Do you know the key ARN at decrypt time?
│ │ │
│ │ ├─► YES ──► AwsKms Keyring
│ │ │ (Best for most use cases)
│ │ │
│ │ └─► NO ───► AwsKmsDiscovery Keyring
│ │ (Decrypts any KMS-encrypted message)
│ │
│ └─► Do you need multi-region disaster recovery?
│ │
│ ├─► YES ──► AwsKmsMrk or AwsKmsMrkDiscovery Keyring
│ │ (Works with KMS multi-region keys)
│ │
│ └─► NO ───► Standard AwsKms Keyring
│
├─► Local/HSM keys
│ │
│ ├─► Symmetric key (AES)?
│ │ └─► RawAes Keyring
│ │ (256-bit keys recommended)
│ │
│ └─► Asymmetric key (RSA)?
│ └─► RawRsa Keyring
│ (Useful for encrypt-only or decrypt-only scenarios)
│
└─► Multiple keys for redundancy?
└─► Multi Keyring
(Combines multiple keyrings)Keyring Comparison
| Keyring | Use Case | Key Location | Notes |
|---|---|---|---|
AwsKms | Production with AWS | AWS KMS | Recommended for most cases |
AwsKmsDiscovery | Decrypt unknown sources | AWS KMS | Use discovery filter! |
AwsKmsMrk | Multi-region DR | AWS KMS | For MRK keys only |
AwsKmsMrkDiscovery | Multi-region discovery | AWS KMS | Combine with filter |
RawAes | Local/testing | Your app | You manage key storage |
RawRsa | Asymmetric workflows | Your app | Encrypt-only or decrypt-only |
Multi | Redundancy | Multiple | Combines other keyrings |
Keyring Examples
AWS KMS (Recommended for Production)
alias AwsEncryptionSdk.Keyring.AwsKms
alias AwsEncryptionSdk.Keyring.KmsClient.ExAws
{:ok, kms_client} = ExAws.new(region: "us-west-2")
{:ok, keyring} = AwsKms.new(
"arn:aws:kms:us-west-2:123456789012:key/...",
kms_client
)Raw AES (Local Development/Testing)
alias AwsEncryptionSdk.Keyring.RawAes
key = :crypto.strong_rand_bytes(32) # 256-bit key
{:ok, keyring} = RawAes.new("my-namespace", "my-key", key, :aes_256_gcm)Multi-Keyring (Redundancy)
alias AwsEncryptionSdk.Keyring.Multi
# Primary KMS key + backup KMS key
{:ok, primary} = AwsKms.new(primary_arn, kms_client)
{:ok, backup} = AwsKms.new(backup_arn, kms_client)
{:ok, keyring} = Multi.new(generator: primary, children: [backup])CMM Selection
Decision Tree
Do you need special materials handling?
│
├─► No special needs
│ └─► Default CMM
│ (Wraps any keyring, handles materials lifecycle)
│
├─► High-volume encryption (>1000 ops/sec)?
│ └─► Caching CMM
│ (Reduces KMS calls, improves performance)
│
└─► Enforce required encryption context keys?
└─► RequiredEncryptionContext CMM
(Fails if required keys missing)CMM Comparison
| CMM | Use Case | Notes |
|---|---|---|
Default | Standard operations | Use for most cases |
Caching | High-volume workloads | Set max_age, max_messages limits |
RequiredEncryptionContext | Compliance/security | Enforces context key presence |
CMM Examples
Default CMM (Most Common)
alias AwsEncryptionSdk.Cmm.Default
cmm = Default.new(keyring)
client = Client.new(cmm)Caching CMM (High Volume)
Note: Caching CMM currently works with the streaming API.
alias AwsEncryptionSdk.Cmm.Caching
alias AwsEncryptionSdk.Cache.LocalCache
alias AwsEncryptionSdk.Stream
# Start the cache process
{:ok, cache} = LocalCache.start_link([])
# Wrap the keyring with caching
cmm = Caching.new_with_keyring(keyring, cache,
max_age: 300, # 5 minutes
max_messages: 1000 # Re-key after 1000 messages
)
client = Client.new(cmm)
# Use with streaming API
ciphertext =
[plaintext]
|> Stream.encrypt(client)
|> Enum.to_list()
|> IO.iodata_to_binary()Required Encryption Context CMM
alias AwsEncryptionSdk.Cmm.RequiredEncryptionContext
# Require tenant_id in all encryption operations
cmm = RequiredEncryptionContext.new_with_keyring(["tenant_id"], keyring)
client = Client.new(cmm)
# This succeeds
{:ok, _} = Client.encrypt(client, "data",
encryption_context: %{"tenant_id" => "acme"}
)
# This fails - missing required key
{:error, _} = Client.encrypt(client, "data",
encryption_context: %{"other" => "value"}
)Common Configurations
Development/Testing
key = :crypto.strong_rand_bytes(32)
{:ok, keyring} = RawAes.new("test", "key", key, :aes_256_gcm)
cmm = Default.new(keyring)
client = Client.new(cmm)Production with AWS KMS
{:ok, kms_client} = ExAws.new(region: "us-west-2")
{:ok, keyring} = AwsKms.new(kms_key_arn, kms_client)
cmm = Default.new(keyring)
client = Client.new(cmm)High-Volume Production
{:ok, kms_client} = ExAws.new(region: "us-west-2")
{:ok, keyring} = AwsKms.new(kms_key_arn, kms_client)
{:ok, cache} = LocalCache.start_link([])
cmm = Caching.new_with_keyring(keyring, cache, max_age: 300)
client = Client.new(cmm)
# Use with Stream API for caching
ciphertext =
[plaintext]
|> Stream.encrypt(client)
|> Enum.to_list()
|> IO.iodata_to_binary()Multi-Tenant with Required Context
{:ok, keyring} = AwsKms.new(kms_key_arn, kms_client)
cmm = RequiredEncryptionContext.new_with_keyring(["tenant_id"], keyring)
client = Client.new(cmm)