AwsEncryptionSdk.Keyring.Multi (AWS Encryption SDK v0.7.0)
View SourceMulti-Keyring implementation.
Composes multiple keyrings together, enabling encryption with multiple keys and flexible decryption with any available key.
Use Cases
- Redundancy: Encrypt with multiple keys so any one can decrypt
- Key rotation: Include both old and new keys during transitions
- Multi-party access: Different parties can decrypt with their respective keys
Encryption Behavior
- Generator keyring (if provided) generates and wraps the plaintext data key
- Each child keyring wraps the plaintext data key (adding additional EDKs)
- All keyrings must succeed (fail-fast)
- EDKs accumulate through the pipeline
Decryption Behavior
- Attempts decryption with generator first (if provided), then children
- Each keyring receives the original, unmodified materials
- Returns immediately on first successful decryption
- Fails only if all keyrings fail to decrypt
Security Note
Any keyring in the multi-keyring can decrypt data encrypted with this keyring. Users should understand the security implications of their keyring composition.
Example
# Create keyrings
{:ok, aes_keyring} = RawAes.new("ns", "aes-key", aes_key, :aes_256_gcm)
{:ok, rsa_keyring} = RawRsa.new("ns", "rsa-key", {:oaep, :sha256}, public_key: pub, private_key: priv)
# Create multi-keyring with generator and child
{:ok, multi} = Multi.new(generator: aes_keyring, children: [rsa_keyring])
# Encrypt - AES generates key, both keyrings wrap it
{:ok, enc_materials} = Multi.wrap_key(multi, materials)
# Decrypt - tries AES first, then RSA
{:ok, dec_materials} = Multi.unwrap_key(multi, materials, edks)Spec Reference
https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/multi-keyring.md
Summary
Functions
Returns the list of all keyrings in this multi-keyring.
Creates a new Multi-Keyring.
Creates a Multi-Region Key (MRK) aware Multi-Keyring.
Creates a Multi-Keyring with an AWS KMS keyring as the generator.
Unwraps a data key using the keyrings in the multi-keyring.
Wraps a data key using all keyrings in the multi-keyring.
Types
Functions
Returns the list of all keyrings in this multi-keyring.
Useful for understanding which keyrings will be used for encryption/decryption.
Examples
{:ok, multi} = Multi.new(generator: gen, children: [child1, child2])
Multi.list_keyrings(multi)
# => [gen, child1, child2]
Creates a new Multi-Keyring.
Options
:generator- Optional keyring that generates the plaintext data key during encryption:children- List of keyrings that wrap the data key (default:[])
At least one of generator or children must be provided. If children is empty, generator is required.
Returns
{:ok, multi_keyring}on success{:error, reason}on validation failure
Errors
{:error, :no_keyrings_provided}- Neither generator nor children provided{:error, :generator_required_when_no_children}- Children empty but no generator
Examples
# Generator with children
{:ok, multi} = Multi.new(generator: aes_keyring, children: [rsa_keyring])
# Generator only
{:ok, multi} = Multi.new(generator: aes_keyring)
# Children only (materials must already have plaintext data key for encryption)
{:ok, multi} = Multi.new(children: [rsa_keyring_1, rsa_keyring_2])
@spec new_mrk_aware(String.t(), struct(), [{String.t(), struct()}], keyword()) :: {:ok, t()} | {:error, term()}
Creates a Multi-Region Key (MRK) aware Multi-Keyring.
Creates a multi-keyring optimized for cross-region scenarios using MRK replicas. The primary key is used as the generator (using AwsKmsMrk keyring), and MRK keyrings for each replica region are added as children for cross-region decryption.
Parameters
primary_key_id- Primary MRK key identifier (should be an mrk-* key for cross-region functionality)primary_client- KMS client for the primary regionreplicas- List of{region, kms_client}tuples for replica regionsopts- Optional keyword list::grant_tokens- Grant tokens for all KMS keyrings
Returns
{:ok, multi_keyring}on success{:error, reason}if any keyring creation fails
Examples
# Primary in us-west-2, replicas in us-east-1 and eu-west-1
{:ok, multi} = Multi.new_mrk_aware(
"arn:aws:kms:us-west-2:123:key/mrk-abc",
west_client,
[
{"us-east-1", east_client},
{"eu-west-1", eu_client}
]
)Notes
For true cross-region MRK functionality, the key_id should be an MRK
(key ID starting with mrk-). Non-MRK keys will work but won't provide
cross-region decryption capability.
@spec new_with_kms_generator(String.t(), struct(), [keyring()], keyword()) :: {:ok, t()} | {:error, term()}
Creates a Multi-Keyring with an AWS KMS keyring as the generator.
Convenience function for the common pattern of using a KMS key as the primary generator with additional child keyrings for backup decryption.
Parameters
kms_key_id- AWS KMS key identifier for the generatorkms_client- KMS client structchild_keyrings- List of child keyrings (can be empty)opts- Optional keyword list::grant_tokens- Grant tokens for the KMS generator keyring
Returns
{:ok, multi_keyring}on success{:error, reason}if KMS keyring creation fails or validation fails
Examples
{:ok, multi} = Multi.new_with_kms_generator(
"arn:aws:kms:us-west-2:123:key/abc",
kms_client,
[backup_keyring]
)
@spec unwrap_key(t(), AwsEncryptionSdk.Materials.DecryptionMaterials.t(), [ AwsEncryptionSdk.Materials.EncryptedDataKey.t() ]) :: {:ok, AwsEncryptionSdk.Materials.DecryptionMaterials.t()} | {:error, term()}
Unwraps a data key using the keyrings in the multi-keyring.
Attempts decryption with generator first (if present), then each child keyring in order. Returns immediately when any keyring successfully decrypts.
Each keyring receives the original, unmodified materials (not chained).
Returns
{:ok, materials}- Data key successfully unwrapped by one of the keyrings{:error, :plaintext_data_key_already_set}- Materials already have a key{:error, {:all_keyrings_failed, [reasons]}}- All keyrings failed to decrypt
Examples
{:ok, multi} = Multi.new(generator: keyring1, children: [keyring2])
dec_materials = DecryptionMaterials.new_for_decrypt(suite, ec)
{:ok, result} = Multi.unwrap_key(multi, dec_materials, edks)
@spec wrap_key(t(), AwsEncryptionSdk.Materials.EncryptionMaterials.t()) :: {:ok, AwsEncryptionSdk.Materials.EncryptionMaterials.t()} | {:error, term()}
Wraps a data key using all keyrings in the multi-keyring.
If a generator is present, it generates and wraps the plaintext data key. Each child keyring then wraps the same plaintext data key, adding additional EDKs.
All keyrings must succeed (fail-fast on any error).
Returns
{:ok, materials}- Data key wrapped by all keyrings{:error, :plaintext_data_key_already_set}- Materials already have key (with generator){:error, :no_plaintext_data_key}- No generator and materials have no plaintext key{:error, {:generator_failed, reason}}- Generator keyring failed{:error, {:generator_did_not_produce_key}}- Generator didn't set plaintext key{:error, {:child_keyring_failed, index, reason}}- Child keyring failed
Examples
{:ok, multi} = Multi.new(generator: keyring1, children: [keyring2])
enc_materials = EncryptionMaterials.new_for_encrypt(suite, ec)
{:ok, result} = Multi.wrap_key(multi, enc_materials)