Documentation
¶
Overview ¶
Package kek implements KEK (Key Encryption Key) providers that wrap and unwrap DEKs (Data Encryption Keys) per §5.1 of the data-at-rest encryption design.
The KEK never appears in elastickv's data dir. It is held externally — in a KMS, a sealed file, or HashiCorp Vault — and only exercised at process boot and at DEK rotation.
Stage 0 ships only the FileWrapper for tests / single-host clusters. AWS KMS, GCP KMS, and Vault providers are added in Stage 9.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrInsecureKEKFile = errors.New("kek: file is group/world-accessible; require owner-only mode")
ErrInsecureKEKFile is returned by NewFileWrapper when the KEK file permission bits permit group or other access. Loading such a file would silently weaken the at-rest encryption boundary on a multi-user host (any local user could read the master key bytes), so the wrapper fails closed rather than warning. Owner-only modes (0o400 / 0o600) are accepted; anything with bits in 0o077 is not.
var ErrNilFileWrapper = errors.New("kek: FileWrapper is nil or uninitialised; construct with NewFileWrapper")
ErrNilFileWrapper is returned by Wrap/Unwrap when called on a nil receiver or a zero-value FileWrapper (i.e. one whose internal AEAD was never initialised by NewFileWrapper). Surfaced as a typed error rather than a nil-deref panic so a wiring mistake during bootstrap or rotation surfaces as a recoverable failure instead of a process crash. Mirrors the encryption.Cipher / encryption.Keystore zero-value contract.
Functions ¶
This section is empty.
Types ¶
type FileWrapper ¶
type FileWrapper struct {
// contains filtered or unexported fields
}
FileWrapper wraps DEKs using AES-256-GCM under a KEK read from a file at construction time.
Suitable for tests, single-host clusters, and deployments that store the KEK on a sealed tmpfs volume. Production deployments should prefer a KMS-backed Wrapper (Stage 9: aws_kms.go, gcp_kms.go, vault.go); see §5.1 for the recommended provider ordering.
The zero value is NOT safe to use: Wrap/Unwrap return ErrNilFileWrapper for a nil pointer or a FileWrapper whose internal AEAD was never initialised. Always construct via NewFileWrapper.
func NewFileWrapper ¶
func NewFileWrapper(path string) (*FileWrapper, error)
NewFileWrapper reads a KEK from path. The file must be a regular file containing exactly 32 bytes (an AES-256 key). Any other length returns an error rather than silently padding or truncating.
On unix, the file's permission bits MUST be owner-only (no group or other access bits set, i.e. mode & 0o077 == 0). A misconfigured 0o644 or 0o666 KEK file would let any local user read the master key on a multi-user host, defeating the entire at-rest encryption boundary — NewFileWrapper fails closed with ErrInsecureKEKFile rather than logging a warning. Windows has a fundamentally different permission model and is not gated.
The mode check and the key-bytes read share a single os.File so a path swap (or symlink retarget) between checks cannot race the load. f.Stat resolves the inode behind the open fd, not the path, so what is validated is exactly what is read.
The read is bounded to fileKEKSize+1 bytes and is preceded by a "regular file" stat check, so a misconfigured path pointing at a FIFO, device, or huge file (a misaligned cluster bootstrap could otherwise hang or OOM on /dev/zero) fails fast.
func (*FileWrapper) Name ¶
func (w *FileWrapper) Name() string
Name returns the provider id plus the file path so log lines and the EncryptionAdmin status RPC let an operator distinguish multiple configured KEKs without grepping config.
func (*FileWrapper) Unwrap ¶
func (w *FileWrapper) Unwrap(wrapped []byte) ([]byte, error)
Unwrap reverses Wrap. It returns ErrIntegrity-equivalent errors via the AEAD library (the parent encryption package's ErrIntegrity is the caller's responsibility to wrap, since this package must stay dependency-free of the parent).
The post-Open length check that earlier drafts had was unreachable — the strict-length input check above guarantees Open returns exactly fileKEKSize bytes on success — and has been removed.
Returns ErrNilFileWrapper for a nil receiver or zero-value FileWrapper, symmetric with Wrap.
func (*FileWrapper) Wrap ¶
func (w *FileWrapper) Wrap(dek []byte) ([]byte, error)
Wrap returns AES-GCM(KEK, dek) prefixed by a freshly-drawn random nonce. Output layout:
[nonce 12 bytes] [ciphertext 32 bytes] [tag 16 bytes]
Total wrapped size: 60 bytes for a 32-byte DEK.
Returns ErrNilFileWrapper if w is nil or the embedded AEAD was never initialised by NewFileWrapper. A wiring/configuration mistake during bootstrap or rotation surfaces as a typed error rather than a nil-deref panic.
type Wrapper ¶
type Wrapper interface {
Wrap(dek []byte) ([]byte, error)
Unwrap(wrapped []byte) ([]byte, error)
// Name returns a short identifier of the KEK source ("file",
// "aws-kms", "gcp-kms", "vault", "env"). Surfaces in logs and the
// EncryptionAdmin status RPC.
Name() string
}
Wrapper wraps and unwraps DEK bytes under an externally-held KEK.
Wrap input is always exactly encryption.KeySize (32) bytes; the wrapped output's exact size depends on the provider but is at least the input size plus an AEAD nonce and tag (or KMS protocol overhead).
Implementations MUST be safe for concurrent use by multiple goroutines because the encryption Keystore may issue Wrap/Unwrap from rotation and resync paths simultaneously.