encryption

package
v1.3.0-test Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	OpEncrypt = "encrypt"
	OpDecrypt = "decrypt"
)

Operation names

Variables

View Source
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

func CategorizeError(err error) string

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

func IsEncrypted(data []byte) bool

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) Bytes

func (c Ciphertext) Bytes() []byte

Bytes returns the ciphertext as bytes.

func (Ciphertext) String

func (c Ciphertext) String() string

String returns the ciphertext as a string (typically starts with "enc:").

type EncryptFunc

type EncryptFunc func(ctx context.Context, data []byte) ([]byte, error)

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

func (m *Manager) ActiveStrategyVersion() string

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

func (m *Manager) Decrypt(ctx context.Context, data []byte) ([]byte, error)

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

func (m *Manager) Encrypt(ctx context.Context, plaintext []byte) ([]byte, error)

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

func (m *Manager) EnsureActiveCanary(ctx context.Context) error

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

func (m *Manager) GetActiveStrategy() (version string, strategy Strategy)

GetActiveStrategy returns the active strategy version and the strategy itself. Returns ("", nil) if no active strategy is set.

func (*Manager) GetStrategy

func (m *Manager) GetStrategy(version string) (Strategy, bool)

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

func (m *Manager) InspectEncrypted(data []byte) (version, keyID string, encrypted bool, err error)

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

func (m *Manager) ProcessEncryption(ctx context.Context, data []byte) ([]byte, error)

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

func (m *Manager) RegisterStrategy(s Strategy, setActive bool)

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

func (m *Manager) SetActiveStrategy(version string) error

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

func (m *Manager) StrategyCount() int

StrategyCount returns the number of registered strategies.

func (*Manager) ValidateCanaries

func (m *Manager) ValidateCanaries(ctx context.Context) error

ValidateCanaries validates all stored canaries can be decrypted. Returns error if any canary fails.

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

func Decrypt(ctx context.Context, ciphertext Ciphertext) (Plaintext, bool, error)

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.

func (Plaintext) Bytes

func (p Plaintext) Bytes() []byte

Bytes returns the plaintext as bytes.

func (Plaintext) String

func (p Plaintext) String() string

String returns the plaintext as a string.

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

func (p *Plugin) Initialize(db *gorm.DB) error

Initialize registers the plugin's callbacks with GORM.

func (*Plugin) Name

func (p *Plugin) Name() string

Name returns the plugin name for GORM registration.

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

func (s *V1Strategy) EncryptPlaintext(ctx context.Context, plaintext []byte) ([]byte, error)

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

type ValidationResult

type ValidationResult struct {
	Strategy string `json:"strategy"`
	KeyID    string `json:"key_id"`
	Status   string `json:"status"` // "ok", "failed", "mismatch"
	Error    string `json:"error,omitempty"`
}

ValidationResult represents the result of validating a single canary.

Jump to

Keyboard shortcuts

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