AwsEncryptionSdk.Keyring.AwsKmsDiscovery (AWS Encryption SDK v0.7.0)
View SourceAWS KMS Discovery Keyring implementation.
A decrypt-only keyring that can decrypt data encrypted with ANY KMS key
the caller has access to. Unlike the standard AwsKms keyring, this keyring
does not require knowing the key ARN in advance.
Use Cases
- Decryption services: Services that decrypt data from multiple sources
- Migration: Decrypt data while transitioning between KMS keys
- Flexible decryption: When the encrypting key is not known at decrypt time
Security Warning
Discovery keyrings will attempt to decrypt using ANY KMS key ARN found in the encrypted data keys. Use a discovery filter to restrict which keys can be used:
{:ok, keyring} = AwsKmsDiscovery.new(client,
discovery_filter: %{
partition: "aws",
accounts: ["123456789012"] # Only allow keys from this account
}
)Operations
Encryption
Discovery keyrings cannot encrypt. wrap_key/2 always returns
{:error, :discovery_keyring_cannot_encrypt}.
For encryption, use:
AwsKmskeyring if you know the key ARNMultikeyring with anAwsKmsgenerator for encryption + discovery for decryption
Decryption (unwrap_key)
- Filters EDKs by provider ID "aws-kms"
- Validates each EDK's key ARN format
- Applies discovery filter (if configured)
- Attempts KMS Decrypt using the ARN from each EDK
- Returns on first successful decryption
Discovery Filter
Restrict which KMS keys can be used for decryption:
| Field | Description | Required |
|---|---|---|
partition | AWS partition ("aws", "aws-cn", "aws-us-gov") | Yes |
accounts | List of allowed AWS account IDs | Yes |
IAM Permissions Required
The principal needs kms:Decrypt on ALL keys that might be encountered:
{
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": [
"arn:aws:kms:*:123456789012:key/*",
"arn:aws:kms:*:987654321098:key/*"
]
}Or use a condition to limit to specific accounts:
{
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": ["123456789012", "987654321098"]
}
}
}Examples
Basic Discovery Decryption
alias AwsEncryptionSdk.Keyring.AwsKmsDiscovery
alias AwsEncryptionSdk.Keyring.KmsClient.ExAws
alias AwsEncryptionSdk.Cmm.Default
alias AwsEncryptionSdk.Client
# Create discovery keyring
{:ok, kms_client} = ExAws.new(region: "us-west-2")
{:ok, keyring} = AwsKmsDiscovery.new(kms_client)
# Create client
cmm = Default.new(keyring)
client = Client.new(cmm)
# Decrypt data (encrypted with any accessible KMS key)
{:ok, {plaintext, context}} = Client.decrypt(client, ciphertext)With Discovery Filter (Recommended)
{:ok, keyring} = AwsKmsDiscovery.new(kms_client,
discovery_filter: %{
partition: "aws",
accounts: ["123456789012", "987654321098"]
}
)Encrypt with KMS, Decrypt with Discovery
alias AwsEncryptionSdk.Keyring.{AwsKms, AwsKmsDiscovery, Multi}
# Encryption keyring - knows the key
{:ok, encrypt_keyring} = AwsKms.new("arn:aws:kms:us-west-2:123:key/abc", kms_client)
# Decryption keyring - discovery mode
{:ok, decrypt_keyring} = AwsKmsDiscovery.new(kms_client,
discovery_filter: %{partition: "aws", accounts: ["123456789012"]}
)
# Use different clients for encrypt vs decrypt
encrypt_client = Client.new(Default.new(encrypt_keyring))
decrypt_client = Client.new(Default.new(decrypt_keyring))Spec Reference
Summary
Functions
Creates a new AWS KMS Discovery Keyring.
Unwraps a data key using AWS KMS Discovery.
Discovery keyrings cannot encrypt - this always fails.
Types
@type t() :: %AwsEncryptionSdk.Keyring.AwsKmsDiscovery{ discovery_filter: discovery_filter() | nil, grant_tokens: [String.t()], kms_client: struct() }
Functions
Creates a new AWS KMS Discovery Keyring.
Parameters
kms_client- KMS client struct implementing KmsClient behaviouropts- Optional keyword list::discovery_filter- Map with:partition(string) and:accounts(list of strings):grant_tokens- List of grant tokens for KMS API calls
Returns
{:ok, keyring}on success{:error, reason}on validation failure
Errors
{:error, :client_required}- kms_client is nil{:error, :invalid_client_type}- kms_client is not a struct{:error, :invalid_discovery_filter}- filter missing partition or accounts{:error, :discovery_filter_accounts_empty}- accounts list is empty{:error, :invalid_account_ids}- accounts contains non-string values
Examples
{:ok, client} = KmsClient.Mock.new(%{})
{:ok, keyring} = AwsKmsDiscovery.new(client)
# With discovery filter
{:ok, keyring} = AwsKmsDiscovery.new(client,
discovery_filter: %{partition: "aws", accounts: ["123456789012"]}
)
@spec unwrap_key(t(), AwsEncryptionSdk.Materials.DecryptionMaterials.t(), [ AwsEncryptionSdk.Materials.EncryptedDataKey.t() ]) :: {:ok, AwsEncryptionSdk.Materials.DecryptionMaterials.t()} | {:error, term()}
Unwraps a data key using AWS KMS Discovery.
Iterates through EDKs, filtering by provider ID and ARN validity. For each matching EDK, extracts the key ARN from provider info and attempts decryption with KMS.
Returns
{:ok, materials}- Data key successfully decrypted{:error, :plaintext_data_key_already_set}- Materials already have key{:error, {:unable_to_decrypt_any_data_key, errors}}- All decryption attempts failed
Examples
{:ok, result} = AwsKmsDiscovery.unwrap_key(keyring, materials, edks)
@spec wrap_key(t(), AwsEncryptionSdk.Materials.EncryptionMaterials.t()) :: {:error, :discovery_keyring_cannot_encrypt}
Discovery keyrings cannot encrypt - this always fails.
Returns
Always returns {:error, :discovery_keyring_cannot_encrypt}