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 ¶
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 ¶
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 ¶
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 ¶
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.
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.
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