AwsEncryptionSdk.Keyring.AwsKms (AWS Encryption SDK v0.7.0)

View Source

AWS KMS Keyring implementation.

Encrypts and decrypts data keys using AWS Key Management Service (KMS). This is the primary keyring for AWS-based encryption workflows.

Use Cases

  • Server-side encryption: Encrypt data at rest with KMS-managed keys
  • Multi-party encryption: Use with Multi-keyring for redundant key access
  • Compliance: Leverage KMS audit trails and key policies

Key Identifier Formats

The keyring accepts various KMS key identifier formats:

FormatExampleRecommended
Key ARNarn:aws:kms:us-west-2:123:key/abcYes
Alias ARNarn:aws:kms:us-west-2:123:alias/my-keyYes
Key ID12345678-1234-1234-1234-123456789012No*
Alias Namealias/my-keyNo*

*Non-ARN formats work but limit portability and explicit region control.

Operations

Encryption (wrap_key)

When no plaintext data key exists:

  1. Calls KMS GenerateDataKey to create a new data key
  2. Returns both plaintext and encrypted data key

When plaintext data key already exists (multi-keyring scenario):

  1. Calls KMS Encrypt to wrap the existing key
  2. Returns additional encrypted data key (EDK)

Decryption (unwrap_key)

  1. Filters EDKs to find those with provider ID "aws-kms"
  2. Validates EDK key ARN matches configured key (supports MRK matching)
  3. Calls KMS Decrypt with the first matching EDK
  4. Returns decrypted plaintext data key

IAM Permissions Required

The IAM principal must have these KMS permissions:

For Encryption

{
  "Effect": "Allow",
  "Action": [
    "kms:GenerateDataKey",
    "kms:Encrypt"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT:key/KEY-ID"
}

For Decryption

{
  "Effect": "Allow",
  "Action": "kms:Decrypt",
  "Resource": "arn:aws:kms:REGION:ACCOUNT:key/KEY-ID"
}
{
  "Effect": "Allow",
  "Action": [
    "kms:GenerateDataKey",
    "kms:Encrypt",
    "kms:Decrypt"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT:key/KEY-ID"
}

Security Considerations

  • Key Access: Anyone with KMS Decrypt permission for the key can decrypt data
  • Encryption Context: Use encryption context to bind ciphertext to specific contexts
  • Audit Trail: All KMS operations are logged to CloudTrail
  • Key Rotation: Enable automatic key rotation in KMS for long-lived keys
  • Grant Tokens: Use for temporary, fine-grained access control

Examples

Basic Usage

alias AwsEncryptionSdk.Keyring.AwsKms
alias AwsEncryptionSdk.Keyring.KmsClient.ExAws
alias AwsEncryptionSdk.Cmm.Default
alias AwsEncryptionSdk.Client

# Create KMS client for your region
{:ok, kms_client} = ExAws.new(region: "us-west-2")

# Create keyring with KMS key ARN
{:ok, keyring} = AwsKms.new(
  "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012",
  kms_client
)

# Create CMM and client
cmm = Default.new(keyring)
client = Client.new(cmm)

# Encrypt
{:ok, ciphertext} = Client.encrypt(client, "sensitive data",
  encryption_context: %{"tenant" => "acme", "purpose" => "storage"}
)

# Decrypt
{:ok, {plaintext, context}} = Client.decrypt(client, ciphertext)

With Grant Tokens

{:ok, keyring} = AwsKms.new(
  "arn:aws:kms:us-west-2:123:key/abc",
  kms_client,
  grant_tokens: ["grant-token-from-create-grant-api"]
)

With Multi-Keyring for Redundancy

alias AwsEncryptionSdk.Keyring.Multi

# Primary KMS key (generator)
{:ok, primary} = AwsKms.new("arn:aws:kms:us-west-2:123:key/primary", west_client)

# Backup KMS key (child)
{:ok, backup} = AwsKms.new("arn:aws:kms:us-east-1:123:key/backup", east_client)

# Multi-keyring: encrypts with both, can decrypt with either
{:ok, multi} = Multi.new(generator: primary, children: [backup])

Spec Reference

https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/aws-kms/aws-kms-keyring.md

Summary

Functions

Creates a new AWS KMS Keyring.

Unwraps a data key using AWS KMS.

Wraps a data key using AWS KMS.

Types

t()

@type t() :: %AwsEncryptionSdk.Keyring.AwsKms{
  grant_tokens: [String.t()],
  kms_client: struct(),
  kms_key_id: String.t()
}

Functions

new(kms_key_id, kms_client, opts \\ [])

@spec new(String.t(), struct(), keyword()) :: {:ok, t()} | {:error, term()}

Creates a new AWS KMS Keyring.

Parameters

  • kms_key_id - AWS KMS key identifier (ARN, alias ARN, alias name, or key ID)
  • kms_client - KMS client struct implementing KmsClient behaviour
  • opts - Optional keyword list:
    • :grant_tokens - List of grant tokens for KMS API calls

Returns

  • {:ok, keyring} on success
  • {:error, reason} on validation failure

Errors

  • {:error, :key_id_required} - kms_key_id is nil
  • {:error, :key_id_empty} - kms_key_id is empty string
  • {:error, :invalid_key_id_type} - kms_key_id is not a string
  • {:error, :client_required} - kms_client is nil
  • {:error, :invalid_client_type} - kms_client is not a struct

Examples

{:ok, client} = KmsClient.Mock.new(%{})
{:ok, keyring} = AwsKms.new("arn:aws:kms:us-west-2:123:key/abc", client)

# With grant tokens
{:ok, keyring} = AwsKms.new("arn:aws:kms:us-west-2:123:key/abc", client,
  grant_tokens: ["token1", "token2"]
)

unwrap_key(keyring, materials, edks)

Unwraps a data key using AWS KMS.

Filters EDKs to find those encrypted with KMS, then attempts decryption with the configured KMS key. Returns on first successful decryption.

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} = AwsKms.unwrap_key(keyring, materials, edks)

wrap_key(keyring, materials)

Wraps a data key using AWS KMS.

If materials don't have a plaintext data key, generates one using KMS GenerateDataKey. If materials already have a plaintext data key, encrypts it using KMS Encrypt.

Returns

  • {:ok, materials} - Data key generated/encrypted and EDK added
  • {:error, reason} - KMS operation failed or validation error

Examples

{:ok, result} = AwsKms.wrap_key(keyring, materials)