awskms

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 15 Imported by: 0

README

AWS KMS

AWS KMS using envelope encryption with 256-bit AES in Galois/Counter Mode (GCM).

Quick Start

Sample Terraform code is available in testing/terraform/awskms to try this provider with AWS KMS.

Example
package main

import (
	"context"
	"time"

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/kms"
	"github.com/dgraph-io/ristretto"

	"github.com/bincyber/go-sqlcrypter"
	"github.com/bincyber/go-sqlcrypter/providers/awskms"
)

func main() {
	cfg, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		// handle error
	}

	client := kms.NewFromConfig(cfg)

	kmsCrypter, err := awskms.New(
		context.Background(),
		client,
		"alias/sqlcrypter",
		// Optioanlly configure request timeout, rate limits, and cache
		awskms.WithRequestTimeout(2*time.Second),
		awskms.WithKMSDecryptRateLimit(5, 10)
		awskms.WithDEKCacheConfig(ristretto.Config{
			MaxCost:     int64(100_000),
			NumCounters: int64(500_000),
			BufferItems: int64(64),
		}),
	)
	if err != nil {
		//handle error
	}

	sqlcrypter.Init(kmsCrypter)
}
Envelope Encryption

KMSCrypter uses envelope encryption. When awskms.New() is called, a request is made to the the KMS GenerateDataKey API to retrieve a 256-bit symmetric data encryption key (DEK). This DEK is used to encrypt data using AES GCM instead of calling the KMS Encrypt and Decrypt APIs every time. The encrypted DEK is stored alongside the ciphertext. To decrypt previous DEKs stored alongside ciphertext, a request is made to the KMS Decrypt API. The decrypted DEK is then cached in memory to avoid repetitive API calls to KMS.

Request Deadlines

Each call to KMS (GenerateDataKey during New, and Decrypt when decrypting an unknown encrypted DEK) runs under a deadline. The default is 2 seconds. This can be overridden with WithRequestTimeout() option.

Cache

Previous DEKs decrypted via KMS are cached in a Ristretto in-memory cache.

Defaults:

  • MaxCost 1,000,000 (1MB)
  • NumCounters 50,000
  • BufferItems 64.

This can be overridden with WithDEKCacheConfig(ristretto.Config{...}).

Mitigating Denial of Wallet

If an attacker can invoke Decrypt with arbitrary ciphertext, they can force cache misses by supplying unique encrypted DEK blobs. Each miss triggers a billable KMS Decrypt until the in-memory cache fills or entries expire.

Mitigations in this provider:

  • In-memory cache of decrypted DEKs (TTL 60 minutes) so repeated blobs do not re-hit KMS.
  • Optional rate limit on the KMS Decrypt path only: WithKMSDecryptRateLimit(rps, burst) using a token bucket. When the limit is exceeded, Decrypt returns awskms.ErrKMSDecryptRateLimited before calling AWS (map to HTTP 429 or similar at the application layer).

You should still enforce authentication, least-privilege IAM on the CMK, AWS Budgets / billing alarms, and CloudWatch throttling alarms for KMS. Edge rate limiting (API gateway, WAF) is recommended for internet-facing services.

Testing

nsmith/local-kms is used to help with testing. The seed file used is located in testing/seed.yaml.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrKMSDecryptRateLimited = errors.New("rate limit exceeded for KMS Decrypt")

ErrKMSDecryptRateLimited is returned when optional KMS Decrypt rate limiting rejects a cache-miss decrypt before calling AWS KMS.

Functions

func New

func New(ctx context.Context, client *kms.Client, keyID string, opts ...Option) (sqlcrypter.Crypterer, error)

New creates a new AWS KMS crypter given a KMS client and the ID/Alias/ARN of a KMS key. A new data encryption key (DEK) is obtained from KMS which will be stored alongside the ciphertext. 256-bit AES GCM is used to perform the encryption.

By default each KMS request uses a 2 second deadline. This can be overridden using WithRequestTimeout option. The decrypted-DEK Ristretto cache uses built-in defaults; override with WithDEKCacheConfig.

Types

type KMSCrypter

type KMSCrypter struct {
	// contains filtered or unexported fields
}

KMSCrypter is an implementation of the Crypterer interface using AWS KMS with envelope encryption.

func (*KMSCrypter) Decrypt

func (k *KMSCrypter) Decrypt(w io.Writer, r io.Reader) error

Decrypt decrypts ciphertext to plaintext. It first attempts to decrypt using the current DEK if it matches the encrypted key stored alongside the ciphertext. Otherwise, a request is made to KMS to decrypt the encrypted key and then the DEK is used to decrypt the ciphertext.

func (*KMSCrypter) Encrypt

func (k *KMSCrypter) Encrypt(w io.Writer, r io.Reader) error

Encrypt encrypts plaintext to ciphertext using the current DEK. The encrypted DEK is stored alongside the ciphertext.

type Option added in v0.3.0

type Option func(*KMSCrypter) error

Option configures a KMSCrypter during New.

func WithDEKCacheConfig added in v0.3.0

func WithDEKCacheConfig(cfg ristretto.Config) Option

WithDEKCacheConfig sets the Ristretto configuration for the decrypted-DEK cache. NumCounters, MaxCost, and BufferItems must be greater than zero, and NumCounters must be at least MaxCost (see github.com/dgraph-io/ristretto documentation).

func WithKMSDecryptRateLimit added in v0.3.0

func WithKMSDecryptRateLimit(rps float64, burst int) Option

WithKMSDecryptRateLimit sets a token-bucket limit on KMS Decrypt calls (cache-miss path only). rps and burst must be greater than zero.

func WithRequestTimeout added in v0.3.0

func WithRequestTimeout(timeout time.Duration) Option

WithRequestTimeout sets the per-KMS-call deadline for GenerateDataKey and Decrypt (encrypted DEK path). Values must be greater than zero. The default is 2 second.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL