Documentation
¶
Index ¶
- Constants
- Variables
- func CategorizeError(err error) string
- func InitCanaryStore(ctx context.Context, store CanaryStore) error
- func InitGlobalEncryption(log logrus.FieldLogger, cfg *config.Config) error
- func InitGlobalEncryptionFull(log logrus.FieldLogger, cfg *config.Config, canaryStore CanaryStore, ...) error
- func InitGlobalEncryptionWithCanary(log logrus.FieldLogger, cfg *config.Config, canaryStore CanaryStore) error
- func IsEncrypted(data []byte) bool
- type ActiveInfo
- type Canary
- type CanaryManager
- type CanaryStore
- type Ciphertext
- type EncryptFunc
- type EncryptionStatus
- type KeyStatus
- type Manager
- func (m *Manager) ActiveStrategyVersion() string
- func (m *Manager) CanaryManager() *CanaryManager
- func (m *Manager) Decrypt(ctx context.Context, data []byte) ([]byte, error)
- func (m *Manager) Encrypt(ctx context.Context, plaintext []byte) ([]byte, error)
- func (m *Manager) EnsureActiveCanary(ctx context.Context) error
- func (m *Manager) GetActiveStrategy() (version string, strategy Strategy)
- func (m *Manager) GetStrategy(version string) (Strategy, bool)
- func (m *Manager) InspectEncrypted(data []byte) (version, keyID string, encrypted bool, err error)
- func (m *Manager) MetricsRecorder() MetricsRecorder
- func (m *Manager) ProcessEncryption(ctx context.Context, data []byte) ([]byte, error)
- func (m *Manager) RegisterStrategy(s Strategy, setActive bool)
- func (m *Manager) SetActiveStrategy(version string) error
- func (m *Manager) SetCanaryStore(store CanaryStore)
- func (m *Manager) SetMetricsRecorder(metrics MetricsRecorder)
- func (m *Manager) StrategyCount() int
- func (m *Manager) ValidateCanaries(ctx context.Context) error
- type MetricsRecorder
- type ModelEncryptHandler
- type ParsedEncrypted
- type Plaintext
- type Plugin
- type Strategy
- type V1Strategy
- func (s *V1Strategy) ActiveKeyID() string
- func (s *V1Strategy) AddKey(keyID string, key []byte, setActive bool) error
- func (s *V1Strategy) Algorithm() string
- func (s *V1Strategy) ConfiguredKeys() []string
- func (s *V1Strategy) DecryptParsed(ctx context.Context, parsed *ParsedEncrypted) ([]byte, error)
- func (s *V1Strategy) EncryptPlaintext(ctx context.Context, plaintext []byte) ([]byte, error)
- func (s *V1Strategy) EncryptWithKey(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
- func (s *V1Strategy) ParseBody(body []byte) (*ParsedEncrypted, error)
- func (s *V1Strategy) SetActiveKey(keyID string) error
- func (s *V1Strategy) String() string
- func (s *V1Strategy) Version() string
- type ValidationResult
Constants ¶
const ( OpEncrypt = "encrypt" OpDecrypt = "decrypt" )
Operation names
Variables ¶
var ( ErrNoActiveStrategy = errors.New("no active encryption strategy set") ErrStrategyNotFound = errors.New("strategy not found") ErrKeyNotFound = errors.New("key not found") ErrEncryptionFailed = errors.New("encryption failed") ErrDecryptionFailed = errors.New("decryption failed") ErrParseFailed = errors.New("parse failed") ErrInvalidFormat = errors.New("invalid format") ErrInvalidKey = errors.New("invalid key") ErrCanaryMismatch = errors.New("canary mismatch") )
Sentinel errors for categorization
Functions ¶
func CategorizeError ¶
CategorizeError returns a safe error category without sensitive data. Returns standardized error types for metrics and tracing.
func InitCanaryStore ¶
func InitCanaryStore(ctx context.Context, store CanaryStore) error
InitCanaryStore sets the canary store on the global manager, creates a canary for the active key, and validates all stored canaries. No-op if encryption is not initialized. Applies a fixed 30 s timeout so callers can't drift.
func InitGlobalEncryption ¶
func InitGlobalEncryption(log logrus.FieldLogger, cfg *config.Config) error
InitGlobalEncryption initializes the global encryption manager from the application config (cfg.Encryption). Must be called once at startup before any concurrent access. Uses sync.Once internally.
func InitGlobalEncryptionFull ¶
func InitGlobalEncryptionFull(log logrus.FieldLogger, cfg *config.Config, canaryStore CanaryStore, metrics MetricsRecorder) error
InitGlobalEncryptionFull initializes encryption from the application config with optional canary store and metrics recorder.
func InitGlobalEncryptionWithCanary ¶
func InitGlobalEncryptionWithCanary(log logrus.FieldLogger, cfg *config.Config, canaryStore CanaryStore) error
InitGlobalEncryptionWithCanary initializes encryption from the application config with an optional canary store for key-health verification.
func IsEncrypted ¶
IsEncrypted checks if data has the encryption version prefix "enc:<version>:..."
Types ¶
type ActiveInfo ¶
type ActiveInfo struct {
Strategy string `json:"strategy"` // "v1"
KeyID string `json:"key_id"` // "default"
Algorithm string `json:"algorithm"` // "AES-256-GCM"
}
ActiveInfo describes the currently active encryption configuration
type Canary ¶
type Canary struct {
Strategy string `json:"strategy"` // e.g., "v1"
KeyID string `json:"key_id"` // e.g., "default"
EncryptedValue []byte `json:"encrypted_value"` // e.g., "enc:v1:default:..."
CreatedAt time.Time `json:"created_at"`
}
Canary represents an encrypted test value used to verify encryption/decryption works. Each canary is specific to a (strategy, keyID) pair.
type CanaryManager ¶
type CanaryManager struct {
// contains filtered or unexported fields
}
CanaryManager manages encryption canaries for verification. It ensures canaries exist for all active keys and validates they can be decrypted.
func NewCanaryManager ¶
func NewCanaryManager(encMgr *Manager, store CanaryStore) *CanaryManager
NewCanaryManager creates a new canary manager.
func (*CanaryManager) EnsureCanary ¶
func (cm *CanaryManager) EnsureCanary(ctx context.Context, strategy, keyID string) error
EnsureCanary ensures a canary exists for the given strategy and keyID. This is called on first encryption with a particular key. Uses do-once pattern: only checks/creates once per session.
func (*CanaryManager) GetActiveCanary ¶
func (cm *CanaryManager) GetActiveCanary(ctx context.Context) (*Canary, error)
GetActiveCanary returns the canary for the currently active strategy/key. Returns nil if not found.
func (*CanaryManager) ValidateAll ¶
func (cm *CanaryManager) ValidateAll(ctx context.Context) ([]ValidationResult, error)
ValidateAll validates all stored canaries. Returns a list of validation results, one per canary. This is used by health/status endpoints to verify encryption is working.
type CanaryStore ¶
type CanaryStore interface {
// Get retrieves a canary for the given strategy and keyID.
// Returns nil if not found.
Get(ctx context.Context, strategy, keyID string) (*Canary, error)
// Save creates or updates a canary.
Save(ctx context.Context, canary *Canary) error
// GetAll retrieves all stored canaries.
GetAll(ctx context.Context) ([]Canary, error)
}
CanaryStore abstracts canary persistence.
type Ciphertext ¶
type Ciphertext []byte
Ciphertext is a type-safe wrapper for encrypted data.
func Encrypt ¶
func Encrypt(ctx context.Context, plaintext Plaintext) (Ciphertext, error)
Encrypt is a type-safe convenience function that encrypts using the global manager. Takes Plaintext, returns Ciphertext - type system prevents swapping arguments.
Thread safety: Safe for concurrent use after InitGlobalEncryption completes.
func (Ciphertext) String ¶
func (c Ciphertext) String() string
String returns the ciphertext as a string (typically starts with "enc:").
type EncryptFunc ¶
EncryptFunc is the function signature for processing field encryption. Call this function for EVERY field that should be encrypted, regardless of current state. It handles: - Plaintext: encrypts - Already encrypted with old version: decrypts and re-encrypts - Already encrypted with current version: returns as-is
type EncryptionStatus ¶
type EncryptionStatus struct {
Enabled bool `json:"enabled"`
Active *ActiveInfo `json:"active,omitempty"`
CanaryChecksEnabled bool `json:"canary_checks_enabled"`
Keys []KeyStatus `json:"keys"`
}
EncryptionStatus represents the complete encryption system state
func Status ¶
func Status(ctx context.Context) (*EncryptionStatus, error)
Status returns the complete encryption system status. Shows the union of: - All configured keys from the active strategy (always shown) - All canaries from any strategy (only if canary exists) Safe to call even if encryption is not initialized (returns Enabled: false).
type KeyStatus ¶
type KeyStatus struct {
Strategy string `json:"strategy"` // "v1"
KeyID string `json:"key_id"` // "default"
Configured bool `json:"configured"` // Is this key currently configured in the strategy?
Active bool `json:"active"` // Is this the active strategy+key?
CanaryStatus string `json:"canary_status"` // "ok", "failed", "not_checked", "key_missing"
}
KeyStatus represents a single encryption key and its health
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager orchestrates multiple encryption strategies and routes operations based on version prefixes in encrypted data.
Thread safety: Manager is safe for concurrent use. All public methods are protected
func GlobalManager ¶
func GlobalManager() *Manager
GlobalManager returns the global encryption manager. Returns nil if InitGlobalEncryption has not been called.
Thread safety: Safe for concurrent access after InitGlobalEncryption completes. The returned Manager is read-only (no RegisterStrategy/SetActiveStrategy calls after init).
func NewManager ¶
func NewManager() *Manager
NewManager creates a new encryption manager with no strategies registered.
func (*Manager) ActiveStrategyVersion ¶
ActiveStrategyVersion returns just the active strategy version string. Returns empty string if no active strategy is set.
func (*Manager) CanaryManager ¶
func (m *Manager) CanaryManager() *CanaryManager
CanaryManager returns the canary manager, or nil if no canary store is set.
func (*Manager) Decrypt ¶
Decrypt decrypts data by detecting the version prefix and routing to the appropriate strategy. Supports plaintext passthrough (no prefix) for backward compatibility during migration.
func (*Manager) Encrypt ¶
Encrypt encrypts plaintext using the active strategy. Returns encrypted data with format: enc:<version>:<strategy-specific-data> Returns error if input is already encrypted (use ProcessEncryption for that).
func (*Manager) EnsureActiveCanary ¶
EnsureActiveCanary creates a canary for the active encryption key if one doesn't exist. Called at startup to surface canary-creation failures immediately rather than on first encrypt.
func (*Manager) GetActiveStrategy ¶
GetActiveStrategy returns the active strategy version and the strategy itself. Returns ("", nil) if no active strategy is set.
func (*Manager) GetStrategy ¶
GetStrategy returns the strategy for the given version. The version is automatically normalized to kebab-case. Returns (nil, false) if the strategy is not registered.
func (*Manager) InspectEncrypted ¶
InspectEncrypted returns the strategy version and key ID when data uses the enc:<version>:<body> envelope. Plaintext returns encrypted=false with empty version/keyID and a nil error. Malformed encrypted envelopes return an error (fail closed — never treated as plaintext).
func (*Manager) MetricsRecorder ¶
func (m *Manager) MetricsRecorder() MetricsRecorder
MetricsRecorder returns the current metrics recorder, or nil if not set.
func (*Manager) ProcessEncryption ¶
ProcessEncryption intelligently handles data that may be plaintext, encrypted with current version/key, or encrypted with old version/key. Behavior: 1. Plaintext: encrypts with active strategy 2. Same version/key: returns unchanged (avoids wasteful re-encryption) 3. Different version: migrates to active version 4. Same version, different key: re-encrypts with active key
func (*Manager) RegisterStrategy ¶
RegisterStrategy adds an encryption strategy to the manager. The version identifier is automatically normalized to kebab-case. If setActive is true, this strategy becomes the active strategy for new encryptions.
func (*Manager) SetActiveStrategy ¶
SetActiveStrategy selects which strategy will be used for new encryptions. The version is automatically normalized to kebab-case.
func (*Manager) SetCanaryStore ¶
func (m *Manager) SetCanaryStore(store CanaryStore)
SetCanaryStore sets the canary store for this manager. This is optional - if not set, no canary verification will be performed.
func (*Manager) SetMetricsRecorder ¶
func (m *Manager) SetMetricsRecorder(metrics MetricsRecorder)
SetMetricsRecorder sets the metrics recorder for this manager. This is optional - if not set, no metrics will be recorded.
func (*Manager) StrategyCount ¶
StrategyCount returns the number of registered strategies.
type MetricsRecorder ¶
type MetricsRecorder interface {
RecordOperation(operation, strategy, keyID, status string, duration time.Duration)
RecordError(operation, strategy, keyID, errorType string)
RecordCanaryValidation(strategy, keyID, status string)
}
MetricsRecorder is an interface for recording encryption metrics. This allows the encryption package to record metrics without depending on the metrics package.
type ModelEncryptHandler ¶
type ModelEncryptHandler func(ctx context.Context, model any, encrypt EncryptFunc) error
ModelEncryptHandler knows how to encrypt a specific model type. It receives the model instance and an encryption function to use.
type ParsedEncrypted ¶
type ParsedEncrypted struct {
// KeyID identifies the key that encrypted this value.
KeyID string
// Payload contains the strategy-specific encrypted payload.
// For v1, this is nonce||ciphertext||tag after base64 decoding.
Payload []byte
// Metadata contains optional non-sensitive strategy-specific metadata.
// It must not contain plaintext, ciphertext, key material, nonces,
// authentication tags, or customer/resource identifiers.
Metadata map[string]string
}
ParsedEncrypted contains the parsed components of a strategy-specific encrypted body.
The Manager parses only the outer Flightctl envelope:
enc:<version>:<body>
The Strategy parses <body>. For v1, the body format is:
<keyID>:<base64(nonce||ciphertext||tag)>
Future strategies may use a different body format while keeping the same Manager-level envelope.
type Plaintext ¶
type Plaintext []byte
Plaintext is a type-safe wrapper for plaintext data.
func Decrypt ¶
Decrypt decrypts ciphertext using the global manager. Returns (plaintext, ok, error) where ok indicates if decryption was performed. - If input has "enc:" prefix: decrypts and returns (plaintext, true, nil) - If input has no "enc:" prefix (backward compatibility): returns (input, false, nil) - On error: returns (nil, false, error)
Thread safety: Safe for concurrent use after InitGlobalEncryption completes.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is a GORM plugin that delegates model-specific encryption to handlers registered from the store/model package.
func NewPlugin ¶
func NewPlugin(manager *Manager, handlers map[string]ModelEncryptHandler) *Plugin
NewPlugin creates a new GORM encryption plugin with model-specific handlers.
func (*Plugin) Initialize ¶
Initialize registers the plugin's callbacks with GORM.
type Strategy ¶
type Strategy interface {
// Version returns the immutable version identifier (e.g., "v1", "v2").
// This should be a simple version string, not include key IDs or key source types.
Version() string
// String returns human-readable status information about this strategy.
// Should include: algorithm, active key ID, configured keys, etc.
// NEVER include actual key material.
// Example: "AES-256-GCM, active_key=default, keys=[default, key2]"
// Used by encryption status endpoints and diagnostics.
String() string
// ActiveKeyID returns the identifier of the currently active encryption key.
// This is the key used for all new encryptions.
ActiveKeyID() string
// Algorithm returns the algorithm name (e.g., "AES-256-GCM").
Algorithm() string
// ConfiguredKeys returns all configured key IDs for this strategy.
ConfiguredKeys() []string
// EncryptPlaintext encrypts plaintext using the strategy's active key.
// Returns the strategy-specific body containing the key ID and encrypted payload,
EncryptPlaintext(ctx context.Context, plaintext []byte) ([]byte, error)
// EncryptWithKey encrypts plaintext using a specific key by ID.
// Same return format as EncryptPlaintext.
EncryptWithKey(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
// ParseBody parses the body returned by EncryptPlaintext/EncryptWithKey
// into a ParsedEncrypted containing the key ID and raw payload.
ParseBody(body []byte) (*ParsedEncrypted, error)
// DecryptParsed decrypts a value previously parsed by ParseBody.
DecryptParsed(ctx context.Context, parsed *ParsedEncrypted) ([]byte, error)
}
Strategy defines one encryption format version.
A Strategy does not decide whether an input value is plaintext or encrypted. The Manager owns encrypted-state detection using the outer "enc:<version>:<body>" envelope and calls the Strategy only with plaintext or with a body already routed to this strategy version.
type V1Strategy ¶
type V1Strategy struct {
// contains filtered or unexported fields
}
V1Strategy implements AES-256-GCM encryption. Format: keyID:base64(nonce||ciphertext||tag) The keyID prefix is always present, enabling key rotation detection.
Thread safety: V1Strategy is safe for concurrent use. All public methods are protected
func NewV1Strategy ¶
func NewV1Strategy(cfg *config.Config) (*V1Strategy, error)
NewV1Strategy creates a V1 (AES-256-GCM) strategy from the application config. Keys are loaded from cfg.Encryption; ActiveKeyID selects the key for new encryptions while the rest remain available for decryption (rotation).
func (*V1Strategy) ActiveKeyID ¶
func (s *V1Strategy) ActiveKeyID() string
ActiveKeyID returns the identifier of the currently active encryption key.
func (*V1Strategy) AddKey ¶
func (s *V1Strategy) AddKey(keyID string, key []byte, setActive bool) error
AddKey registers an encryption key with the given ID. If setActive is true, this key becomes the active key for new encryptions. key must be 32 bytes (AES-256).
func (*V1Strategy) Algorithm ¶
func (s *V1Strategy) Algorithm() string
Algorithm returns the algorithm name.
func (*V1Strategy) ConfiguredKeys ¶
func (s *V1Strategy) ConfiguredKeys() []string
ConfiguredKeys returns all configured key IDs.
func (*V1Strategy) DecryptParsed ¶
func (s *V1Strategy) DecryptParsed(ctx context.Context, parsed *ParsedEncrypted) ([]byte, error)
DecryptParsed decrypts a parsed v1 encrypted value using AES-256-GCM.
func (*V1Strategy) EncryptPlaintext ¶
EncryptPlaintext encrypts plaintext using AES-256-GCM with the active key.
func (*V1Strategy) EncryptWithKey ¶
func (s *V1Strategy) EncryptWithKey(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
EncryptWithKey encrypts plaintext using a specific key by ID.
func (*V1Strategy) ParseBody ¶
func (s *V1Strategy) ParseBody(body []byte) (*ParsedEncrypted, error)
ParseBody parses v1 format: keyID:base64(nonce||ciphertext||tag)
func (*V1Strategy) SetActiveKey ¶
func (s *V1Strategy) SetActiveKey(keyID string) error
SetActiveKey sets which key will be used for new encryptions.
func (*V1Strategy) String ¶
func (s *V1Strategy) String() string
String returns human-readable status information about this strategy.
func (*V1Strategy) Version ¶
func (s *V1Strategy) Version() string
Version returns "v1" (immutable version identifier).