AWS KMS encryption provider

AWS Key Management Service provider implementing crypto.EncryptionProvider. Master keys never leave AWS's FIPS 140-2 validated HSMs. Every direct encrypt and decrypt call goes through the KMS API. The provider is a drop-in EncryptionProvider. You write NewProvider plus two piko.With options, and Piko handles the rest.

Overview

The defining property of KMS is that the master key cannot leave the HSM. You hand KMS plaintext and get back ciphertext (or a wrapped data key). The key material itself stays in the HSM, audited through CloudTrail, gated by IAM policies, and rotatable on a schedule AWS manages. The provider supports direct encryption (small payloads, one KMS call per operation), envelope encryption through GenerateDataKey, and streaming encryption for large objects. Envelope mode encrypts a batch of objects with a single data key, then encrypts that data key with KMS. The result is fewer KMS calls and lower per-byte cost on bulk workloads.

Streaming is the typical path for large storage objects. EncryptStream and DecryptStream issue exactly one KMS call per stream (GenerateDataKey to encrypt, Decrypt to read back). They then run local AES-256-GCM at constant memory of about 64 KB regardless of file size. Large uploads stay cheap, and you write no envelope code yourself.

KMS is auditable, slower than local crypto, and billed per request. Direct operations add network latency and show up in CloudTrail with the calling principal. Compliance and audit requirements that demand HSM-backed keys are the standard reason to accept those tradeoffs. KMS rotates the master key on its own schedule, so existing ciphertext keeps decrypting without re-encryption.

The crypto service layer wraps every provider in a circuit breaker (5 consecutive failures opens it for 30 seconds), so a KMS outage fails fast instead of stalling the caller. Bulk workloads still use envelope encryption to reduce KMS pressure. All public methods are safe for concurrent use.

Requirements

  • An AWS account with a customer master key (CMK) created in the chosen region, the provider does not create keys.
  • IAM permissions on the key: kms:Encrypt, kms:Decrypt, kms:GenerateDataKey, kms:DescribeKey. Add kms:ReEncrypt* for key rotation flows.
  • AWS credentials via the standard chain (environment, shared config, EC2/ECS/Lambda role, IAM Identity Centre).
  • Network egress to the region's KMS endpoint (for example kms.eu-west-1.amazonaws.com).

Configuration

import (
    "context"

    "piko.sh/piko/wdk/crypto/crypto_provider_aws_kms"
)

provider, err := crypto_provider_aws_kms.NewProvider(ctx, crypto_provider_aws_kms.Config{
    KeyID:      "alias/myapp-prod", // required: key ID, ARN, alias name, or alias ARN
    Region:     "eu-west-1",        // required
    MaxRetries: 3,                  // optional: SDK retry attempts, default 3
})
if err != nil {
    return err
}

NewProvider validates the config and applies defaults for you, so you only need KeyID and Region. A zero or unset MaxRetries becomes 3, so you cannot set it to 0 to disable retries.

NewProvider is pure Go with no build tags and no CGO. It compiles and runs in every Piko run mode, including the interpreted dev mode and compiled binaries.

NewProvider makes a live DescribeKey call to AWS at construction time. Wiring this provider into piko.New blocks on KMS reachability. It fails at startup if KMS is unreachable, the key does not exist, or the principal lacks kms:DescribeKey. This surfaces a misconfigured key as a boot error, not as a first-request failure.

Bootstrap

ssr := piko.New(
    piko.WithCryptoProvider("aws_kms", provider),
    piko.WithDefaultCryptoProvider("aws_kms"),
)

WithCryptoProvider registers the provider under a name. WithDefaultCryptoProvider selects the active one. When a storage service is also wired, Piko registers the crypto stream transformer against the storage pipeline on its own. The pipeline then encrypts uploads at rest with no extra glue.

The provider implements the same EncryptionProvider port as the local AES-GCM and GCP KMS providers. You can register a local provider for development and KMS for production, then switch between them through WithDefaultCryptoProvider alone, with no application code change.

Tradeoffs

KMS bills per request and adds round-trip latency. A hot path doing a KMS call per object is both expensive and slow. The standard mitigation is envelope encryption. Ask KMS for a data key once per session (or once per batch). Encrypt the bulk data locally with the plaintext data key, and store the wrapped data key alongside the ciphertext. The Piko crypto service supports this pattern without extra configuration. The provider returns each generated data key in secure memory and zeroes the plaintext after use, so it handles key hygiene for you. Reach for envelope encryption before you reach for more KMS quota.

See also

Other crypto providers:

  • GCP KMS, equivalent HSM-backed model on Google Cloud.
  • Local AES-GCM, in-process AES-256-GCM with a local key file. Trades the audit trail and per-op latency of KMS for zero per-op cost.

Storage transformer (the typical consumer):

Framework docs:

External: