envelope

package
v0.0.0-...-bd7611f Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package envelope - envelope encryption of secrets at rest.

Secrets are protected with a two-layer scheme:

  • A Data Encryption Key (DEK) is generated per tenant and stored in the database, itself encrypted ("wrapped") by a Key Encryption Key (KEK), so the database never holds a plaintext key.

  • The KEK is managed by a Wrapper and never touches the database. The Wrapper abstracts over how the KEK is stored and used: a local key file for development, or an external service such as AWS KMS or HashiCorp Vault in production.

The application-facing surface is sealing, not raw keys. A TenantEncryptor, built by EncryptorFactory.For once a tenant's DEK is unwrapped, encrypts with Seal (returning the DEK id and ciphertext to store as columns) and decrypts with Open. The ciphertext carries a magic header, so IsSealed lets storage code reject plaintext before persisting it. DEKs are minted at tenant-provisioning time with [EncryptorFactory.Provision], never lazily.

KeyFile is the provided Wrapper: a 32-byte AES-256-GCM key stored as hex on disk, for development and demo use only. In production, use a Wrapper backed by a key management service so the KEK never exists as plaintext in the application process.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Cap_Dek_Create gates minting a tenant's DEK (tenant provisioning). It is a
	// system capability: provisioning runs before the tenant has any grants.
	Cap_Dek_Create = iam.NewCap("Dek_Create")
	// Cap_Dek_Use gates loading and unwrapping a tenant's DEK, i.e. every seal
	// or open on that tenant's secrets.
	Cap_Dek_Use = iam.NewCap("Dek_Use")
)

Functions

func IsSealed

func IsSealed(b []byte) bool

IsSealed reports whether b carries the sealed-data header (magic + known version). Storage code checks this before persisting, so plaintext or a mis-sourced byte slice cannot be written to a ciphertext column.

func Provision

func Provision(ctx context.Context, deks DekStore, wrapper Wrapper, tenant core.ID) error

Provision generates, wraps, and stores a fresh DEK for a tenant. Call once when the tenant is created so an encryptor is available at read time; DEKs are never created lazily on the read/write path.

Types

type DEK

type DEK struct {
	ID         core.ID
	WrappedKey []byte
}

DEK is a data encryption key as stored: an ID and the key wrapped by the KEK. The wrapped key is only usable after being passed through Wrapper.Unwrap.

type DekStore

type DekStore interface {
	// ForTenant returns the tenant's active DEK, or core.ErrNotFound if none exists.
	ForTenant(ctx context.Context, tenant core.ID) (DEK, error)
	// Create stores a new wrapped DEK for the tenant and returns it.
	Create(ctx context.Context, tenant core.ID, wrappedKey []byte) (DEK, error)
}

DekStore stores and retrieves wrapped DEKs. It performs no encryption itself; wrapping and unwrapping are the responsibility of EncryptorFactory.

type EncryptorFactory

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

EncryptorFactory builds tenant-scoped encryptors: For loads and unwraps a tenant's DEK and returns an encryptor bound to it. It holds only the long-lived Wrapper; the per-transaction DekStore is passed to For. DEKs are created separately by Provision at tenant-creation time.

func NewEncryptorFactory

func NewEncryptorFactory(wrapper Wrapper) *EncryptorFactory

func (*EncryptorFactory) For

func (f *EncryptorFactory) For(ctx context.Context, deks DekStore, tenant core.ID) (*TenantEncryptor, error)

For loads and unwraps the tenant's active DEK from deks, returning an encryptor bound to it. The plaintext DEK lives for the lifetime of the returned encryptor, so construct one per operation (e.g. per request) and reuse it across that operation's values — a List then unwraps the DEK once. Returns core.ErrNotFound if the tenant has no DEK (Provision was never called).

type KeyFile

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

KeyFile is a Wrapper backed by a 32-byte AES-256 key loaded from a file. The file should contain a 64-character hex-encoded key. Intended for development and demo use; not suitable for production.

func LoadKeyFile

func LoadKeyFile(path string) (*KeyFile, error)

func (*KeyFile) Unwrap

func (k *KeyFile) Unwrap(ctx context.Context, blob []byte) ([]byte, error)

func (*KeyFile) Wrap

func (k *KeyFile) Wrap(ctx context.Context, dek []byte) ([]byte, error)

type TenantEncryptor

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

TenantEncryptor seals and opens values for one tenant using that tenant's DEK, which EncryptorFactory.For unwraps once when building the encryptor and which is then held in memory for the encryptor's lifetime. Seal returns the DEK id and ciphertext to store as columns; Open takes them back.

func (*TenantEncryptor) Open

func (e *TenantEncryptor) Open(ciphertext []byte) ([]byte, error)

Open reverses Seal, decrypting with the DEK this encryptor holds — the one unwrapped when it was built (see EncryptorFactory.For). Open does no DEK lookup or unwrapping itself. Ciphertext sealed under a different DEK (e.g. a pre-rotation one) fails the GCM authentication check.

func (*TenantEncryptor) Seal

func (e *TenantEncryptor) Seal(plaintext []byte) (core.ID, []byte, error)

Seal encrypts plaintext under the tenant's DEK, returning the DEK id and the ciphertext (carrying the sealed-data header). Store both.

type Wrapper

type Wrapper interface {
	Wrap(ctx context.Context, dek []byte) ([]byte, error)
	Unwrap(ctx context.Context, blob []byte) ([]byte, error)
}

Wrapper wraps and unwraps data encryption keys using a key encryption key. The blobs returned by Wrap are opaque; their format is implementation-defined.

Source Files

  • aesgcm.go
  • dek.go
  • encryptor.go
  • framing.go
  • keyfile.go
  • wrapper.go

Jump to

Keyboard shortcuts

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