Documentation
¶
Overview ¶
Package encx provides production-ready field-level encryption, hashing, and key management for Go applications.
Context7 Metadata: - Library Type: Encryption & Security - Use Cases: Data protection, PII encryption, password hashing, searchable encryption - Complexity: Intermediate to Advanced - Performance: High (10x improvement with code generation) - Compliance: HIPAA, GDPR, SOX ready - Integration: PostgreSQL, MySQL, SQLite, AWS KMS, HashiCorp Vault
ENCX enables you to encrypt and hash struct fields using simple struct tags, with automatic key management through a DEK/KEK architecture. It supports multiple KMS backends, key rotation, combined operations, and comprehensive testing utilities.
Key Features ¶
- Field-level encryption with AES-GCM
- Secure hashing with Argon2id and basic SHA-256
- Combined operations - encrypt AND hash the same field
- Automatic key management with DEK/KEK architecture
- Key rotation support with version tracking
- Multiple KMS backends (AWS KMS, HashiCorp Vault, etc.)
- Comprehensive testing utilities and mocks
- Compile-time validation for struct tags
Quick Start ¶
Define your struct with encx tags:
type User struct {
Name string `encx:"encrypt"`
Email string `encx:"hash_basic"`
Password string `encx:"hash_secure"`
// No companion fields needed! Code generation creates separate output struct
}
Generate type-safe functions (recommended approach):
//go:generate encx-gen generate .
Create crypto instance and process with generated functions:
crypto, _ := encx.NewTestCrypto(nil)
user := &User{
Name: "John Doe",
Email: "john@example.com",
Password: "secret123",
}
// Use generated type-safe functions
userEncx, err := ProcessUserEncx(ctx, crypto, user)
orderEncx, err := ProcessOrderEncx(ctx, crypto, order)
// Pattern: Process{YourStructName}Encx
Struct Tags ¶
Single operation tags:
- encx:"encrypt" - Encrypts field value
- encx:"hash_basic" - Creates SHA-256 hash for searchable indexing
- encx:"hash_secure" - Creates Argon2id hash with pepper (for passwords)
Combined operation tags:
- encx:"encrypt,hash_basic" - Both encrypts AND hashes the field (searchable encryption)
- encx:"hash_secure,encrypt" - Secure hash for auth + encryption for recovery
Code Generation ¶
Code generation creates a separate {StructName}Encx struct with all encrypted/hashed fields:
// Your source struct
type User struct {
Email string `encx:"encrypt,hash_basic"`
}
// Generated UserEncx struct (automatic)
type UserEncx struct {
EmailEncrypted []byte
EmailHash string
DEKEncrypted []byte
KeyVersion int
Metadata string
}
Advanced Example: Combined Tags ¶
Perfect for user lookup with privacy protection:
type User struct {
Email string `encx:"encrypt,hash_basic"`
// No companion fields needed! Code generation creates:
// - UserEncx.EmailEncrypted []byte (for secure storage)
// - UserEncx.EmailHash string (for fast lookups)
}
// Usage
user := &User{Email: "user@example.com"}
userEncx, err := ProcessUserEncx(ctx, crypto, user)
// Now you can:
// 1. Store userEncx.EmailEncrypted securely in database
// 2. Use userEncx.EmailHash for fast user lookups
// 3. Decrypt with DecryptUserEncx when needed for display
Production Configuration ¶
// With AWS KMS
crypto, err := encx.NewCrypto(ctx,
encx.WithKMSService(awsKMS),
encx.WithDatabase(db),
encx.WithPepper(pepper),
encx.WithKEKAlias("myapp-kek"),
)
// With HashiCorp Vault
crypto, err := encx.NewCrypto(ctx,
encx.WithKMSService(vaultKMS),
encx.WithDatabase(db),
encx.WithPepper(pepper),
encx.WithKEKAlias("myapp-kek"),
)
Validation ¶
Validate struct tags before generating code:
encx-gen validate -v . encx-gen validate -v ./models ./api
Validation runs automatically before generation:
encx-gen generate -v .
Error Handling ¶
ENCX provides structured error handling with sentinel errors for precise error classification:
user := &User{Name: "John", Email: "john@example.com"}
userEncx, err := ProcessUserEncx(ctx, crypto, user)
if err != nil {
switch {
case encx.IsRetryableError(err):
// KMS or database temporarily unavailable - retry with backoff
log.Warn("Retryable error: %v", err)
return handleRetry(err)
case encx.IsConfigurationError(err):
// Invalid configuration - fix setup
log.Error("Configuration error: %v", err)
return handleConfigError(err)
case encx.IsAuthError(err):
// Authentication failed - check credentials
log.Error("Authentication failed: %v", err)
return handleAuthError(err)
case encx.IsOperationError(err):
// Encryption/decryption failed - check data/keys
log.Error("Operation failed: %v", err)
return handleOperationError(err)
case encx.IsValidationError(err):
// Data validation failed - check input
log.Error("Validation error: %v", err)
return handleValidationError(err)
default:
log.Error("Unknown error: %v", err)
return err
}
}
Checking specific errors:
if errors.Is(err, encx.ErrKMSUnavailable) {
// Implement retry logic
return retryWithBackoff(operation)
}
if errors.Is(err, encx.ErrAuthenticationFailed) {
// Refresh credentials and retry
return refreshAuthAndRetry(operation)
}
Testing ¶
Unit testing with generated functions:
func TestUserEncryption(t *testing.T) {
crypto, _ := encx.NewTestCrypto(t)
user := &User{Email: "test@example.com"}
userEncx, err := ProcessUserEncx(ctx, crypto, user)
assert.NoError(t, err)
assert.NotEmpty(t, userEncx.EmailEncrypted)
}
Integration testing with full cycle:
func TestUserEncryptDecryptCycle(t *testing.T) {
crypto, _ := encx.NewTestCrypto(t)
original := &User{Email: "test@example.com"}
userEncx, err := ProcessUserEncx(ctx, crypto, original)
assert.NoError(t, err)
decrypted, err := DecryptUserEncx(ctx, crypto, userEncx)
assert.NoError(t, err)
assert.Equal(t, original.Email, decrypted.Email)
}
Documentation ¶
For comprehensive documentation, examples, and advanced usage:
- README.md - Complete getting started guide
- docs/EXAMPLES.md - Detailed examples for all use cases
- docs/API.md - Complete API reference
- docs/MIGRATION.md - Version upgrade guide
- docs/TROUBLESHOOTING.md - Common issues and solutions
Important: Version Control ¶
Add to your .gitignore:
.encx/
Index ¶
- Constants
- Variables
- func DeserializeValue(data []byte, target any) error
- func IsAuthError(err error) bool
- func IsConfigurationError(err error) bool
- func IsOperationError(err error) bool
- func IsRetryableError(err error) bool
- func IsValidationError(err error) bool
- func NewInvalidFieldTypeError(fieldName string, expectedType, actualType string, action types.Action) error
- func NewInvalidFormatError(fieldName string, formatName string, action types.Action) error
- func NewMissingFieldError(fieldName string, action types.Action) error
- func NewMissingTargetFieldError(fieldName string, targetFieldName string, action types.Action) error
- func NewNilPointerError(fieldName string, action types.Action) error
- func NewOperationFailedError(fieldName string, action types.Action, details string) error
- func NewSimpleTestKMS() config.KeyManagementService
- func NewTypeConversionError(fieldName string, typeName string, action types.Action) error
- func NewUninitalizedPepperError() error
- func NewUnsupportedTypeError(fieldName string, typeName string, action types.Action) error
- func SerializeValue(value any) ([]byte, error)
- func VersionInfo() string
- type Action
- type Argon2Params
- type Config
- type Crypto
- func NewCrypto(ctx context.Context, kms KeyManagementService, secrets SecretManagementService, ...) (*Crypto, error)
- func NewCryptoFromEnv(ctx context.Context, kms KeyManagementService, secrets SecretManagementService, ...) (*Crypto, error)
- func NewTestCrypto(t interface{}) (*Crypto, error)
- func NewTestCryptoWithDatabase(db *sql.DB) (*Crypto, error)
- func (c *Crypto) CompareBasicHashAndValue(ctx context.Context, value any, hashValue string) (bool, error)
- func (c *Crypto) CompareSecureHashAndValue(ctx context.Context, value any, hashValue string) (bool, error)
- func (c *Crypto) DecryptDEKWithVersion(ctx context.Context, ciphertextDEK []byte, kekVersion int) ([]byte, error)
- func (c *Crypto) DecryptData(ctx context.Context, ciphertext []byte, dek []byte) ([]byte, error)
- func (c *Crypto) DecryptStream(ctx context.Context, reader io.Reader, writer io.Writer, dek []byte) error
- func (c *Crypto) EncryptDEK(ctx context.Context, plaintextDEK []byte) ([]byte, error)
- func (c *Crypto) EncryptData(ctx context.Context, plaintext []byte, dek []byte) ([]byte, error)
- func (c *Crypto) EncryptStream(ctx context.Context, reader io.Reader, writer io.Writer, dek []byte) error
- func (c *Crypto) GenerateDEK() ([]byte, error)
- func (c *Crypto) GetAlias() string
- func (c *Crypto) GetArgon2Params() *Argon2Params
- func (c *Crypto) GetCurrentKEKVersion(ctx context.Context, alias string) (int, error)
- func (c *Crypto) GetKMSKeyIDForVersion(ctx context.Context, alias string, version int) (string, error)
- func (c *Crypto) GetPepper() []byte
- func (c *Crypto) HashBasic(ctx context.Context, value []byte) string
- func (c *Crypto) HashSecure(ctx context.Context, value []byte) (string, error)
- func (c *Crypto) RotateKEK(ctx context.Context) error
- type CryptoService
- type EncryptionMetadata
- type InMemorySecretStore
- func (s *InMemorySecretStore) GetPepper(ctx context.Context, alias string) ([]byte, error)
- func (s *InMemorySecretStore) GetStoragePath(alias string) string
- func (s *InMemorySecretStore) PepperExists(ctx context.Context, alias string) (bool, error)
- func (s *InMemorySecretStore) StorePepper(ctx context.Context, alias string, pepper []byte) error
- type KeyManagementService
- type MetricsCollector
- type ObservabilityHook
- type Option
- type SecretManagementService
- type SimpleTestKMS
- func (s *SimpleTestKMS) CreateKey(ctx context.Context, description string) (string, error)
- func (s *SimpleTestKMS) DecryptDEK(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
- func (s *SimpleTestKMS) EncryptDEK(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
- func (s *SimpleTestKMS) GetKeyID(ctx context.Context, alias string) (string, error)
- type VersionDetails
Constants ¶
const ( FieldKeyVersion = "KeyVersion" FieldDEK = "DEK" FieldDEKEncrypted = FieldDEK + SuffixEncrypted )
Field name constants - exported for public use
const ( SuffixEncrypted = "Encrypted" SuffixHashed = "Hash" )
Suffix constants for generated fields
const ( StructTag = "encx" TagEncrypt = "encrypt" TagHashSecure = "hash_secure" TagHashBasic = "hash_basic" )
Tag constants for struct field annotations
const ( // EnvKEKAlias is the environment variable name for the KEK (Key Encryption Key) alias. // This identifies which key to use in the KMS for encrypting/decrypting DEKs. // Example: "user-service-kek" or "alias/myapp-kek" EnvKEKAlias = "ENCX_KEK_ALIAS" // EnvPepperAlias is the environment variable name for the pepper storage alias. // This identifies which pepper to use for secure hashing operations. // The alias is used to construct the storage path in the SecretManagementService. // Example: "user-service" or "myapp" EnvPepperAlias = "ENCX_PEPPER_ALIAS" // EnvDBPath is the environment variable name for the database directory path. // This specifies where the SQLite database for key metadata is stored. // Default: .encx EnvDBPath = "ENCX_DB_PATH" // EnvDBFilename is the environment variable name for the database filename. // This specifies the filename of the SQLite database for key metadata. // Default: keys.db EnvDBFilename = "ENCX_DB_FILENAME" )
Environment variable names
const ( // DefaultDBPath is the default directory for the key metadata database. DefaultDBPath = ".encx" // DefaultDBFilename is the default filename for the key metadata database. DefaultDBFilename = "keys.db" )
Default values
const ( // AWSPepperPathTemplate is the path template for storing peppers in AWS Secrets Manager. // The %s placeholder is replaced with the pepper alias (service identifier). // Example: "encx/user-service/pepper" AWSPepperPathTemplate = "encx/%s/pepper" // VaultPepperPathTemplate is the path template for storing peppers in HashiCorp Vault KV v2. // The %s placeholder is replaced with the pepper alias (service identifier). // Example: "secret/data/encx/user-service/pepper" // Note: This follows the KV v2 API path convention where "secret/data/" is the mount point. VaultPepperPathTemplate = "secret/data/encx/%s/pepper" )
Storage path templates for different secret management providers
const ( Unknown = types.Unknown BasicHash = types.BasicHash SecureHash = types.SecureHash Encrypt = types.Encrypt Decrypt = types.Decrypt )
Action constants
const ( // MaxKEKAliasLength is the maximum allowed length for a KEK alias. // This prevents excessively long identifiers that could cause issues with storage or KMS APIs. MaxKEKAliasLength = 256 )
KEK constraints
const ( // PepperLength defines the required length for pepper values in bytes. // Peppers must be exactly 32 bytes for cryptographic operations. PepperLength = 32 )
Pepper constants
const Version = "1.0.0"
Version of the encx library
Variables ¶
var ( // High-level service errors ErrKeyRotationRequired = errors.New("key rotation required") ErrInvalidConfiguration = errors.New("invalid configuration") ErrAuthenticationFailed = errors.New("authentication failed") ErrEncryptionFailed = errors.New("encryption failed") ErrDecryptionFailed = errors.New("decryption failed") // Crypto errors ErrUninitializedPepper = errors.New("pepper value appears to be uninitialized (all zeros)") // Field errors ErrMissingField = errors.New("missing required field") ErrMissingTargetField = errors.New("missing required target field") ErrInvalidFieldType = errors.New("invalid field type") ErrUnsupportedType = errors.New("unsupported type") // Conversion errors ErrTypeConversion = errors.New("type conversion failed") ErrNilPointer = errors.New("nil pointer encountered") // Operation errors ErrOperationFailed = errors.New("operation failed") ErrInvalidFormat = errors.New("invalid format") // Metadata validation errors ErrMissingKEKAlias = errors.New("KEK alias is required") ErrMissingGeneratorVersion = errors.New("generator version is required") )
var ( NewInMemoryMetricsCollector = monitoring.NewInMemoryMetricsCollector NewLoggingObservabilityHook = monitoring.NewLoggingObservabilityHook NewMetricsObservabilityHook = monitoring.NewMetricsObservabilityHook NewCompositeObservabilityHook = monitoring.NewCompositeObservabilityHook )
Constructor functions
var ( NoOpMetricsCollector = &monitoring.NoOpMetricsCollector{} NoOpObservabilityHook = &monitoring.NoOpObservabilityHook{} )
Default implementations
var ( // Available optional options: WithArgon2Params = config.WithArgon2Params WithDBPath = config.WithDBPath WithDBFilename = config.WithDBFilename WithKeyMetadataDBPath = config.WithKeyMetadataDBPath WithKeyMetadataDBFilename = config.WithKeyMetadataDBFilename WithMetricsCollector = config.WithMetricsCollector WithObservabilityHook = config.WithObservabilityHook )
Configuration option functions Note: KMS service, KEK alias, and pepper are now handled automatically via required parameters and environment variables for better security
var ( DefaultConfig = config.DefaultConfig ApplyOptions = config.ApplyOptions )
Helper functions
var ( GitCommit string BuildDate string BuildUser string )
Build information (set by ldflags during build)
var DefaultArgon2Params = &Argon2Params{
Memory: 64 * 1024,
Iterations: 3,
Parallelism: 2,
SaltLength: 16,
KeyLength: 32,
}
DefaultArgon2Params provides secure default parameters
Functions ¶
func DeserializeValue ¶
DeserializeValue converts bytes back to a value using ENCX's compact binary format. The target parameter must be a pointer to the type you want to deserialize into.
Example:
var result string
err := encx.DeserializeValue(data, &result)
if err != nil {
// handle error
}
func IsAuthError ¶
IsAuthError returns true if the error represents an authentication problem.
func IsConfigurationError ¶
IsConfigurationError returns true if the error represents a configuration problem.
func IsOperationError ¶
IsOperationError returns true if the error represents a failure during encryption/decryption operations.
func IsRetryableError ¶
IsRetryableError returns true if the error represents a transient failure that might succeed on retry.
func IsValidationError ¶
IsValidationError returns true if the error represents a data validation problem.
func NewInvalidFormatError ¶
func NewOperationFailedError ¶
func NewSimpleTestKMS ¶
func NewSimpleTestKMS() config.KeyManagementService
NewSimpleTestKMS creates a new simple test KMS with a default key
func NewTypeConversionError ¶
func NewUninitalizedPepperError ¶
func NewUninitalizedPepperError() error
func NewUnsupportedTypeError ¶
func SerializeValue ¶
SerializeValue converts a value to bytes using ENCX's compact binary format. This is the same serialization used internally by ENCX for field encryption. Applications can use this function to create consistent hash values for database queries.
Supported types: string, int, int32, int64, uint, uint32, uint64, bool, time.Time, []byte, float32, float64
Example:
data, err := encx.SerializeValue("hello world")
if err != nil {
// handle error
}
Types ¶
type Argon2Params ¶
type Argon2Params = config.Argon2Params
Argon2Params defines the parameters for Argon2id (re-exported from internal)
func NewArgon2Params ¶
func NewArgon2Params( memory uint32, iterations uint32, parallelism uint8, saltLength uint32, keyLength uint32, ) (*Argon2Params, error)
NewArgon2Params creates a new Argon2Params instance with validation
type Config ¶
type Config struct {
// KEKAlias is the Key Encryption Key identifier in the KMS.
//
// This identifies which key to use for encrypting/decrypting DEKs.
// The format depends on the KMS provider:
// - AWS KMS: "alias/my-key" or full ARN
// - HashiCorp Vault: key name (e.g., "my-service-kek")
//
// Required field. Maximum length: 256 characters.
KEKAlias string
// PepperAlias is the service identifier for pepper storage.
//
// This is used to construct the storage path in the SecretManagementService.
// Each service should use a unique alias to isolate peppers:
// - Microservice: use service name (e.g., "user-service", "payment-service")
// - Monolith: use application name (e.g., "myapp")
//
// The SecretManagementService implementation uses this to create the full path:
// - AWS: "encx/{PepperAlias}/pepper"
// - Vault: "secret/data/encx/{PepperAlias}/pepper"
//
// Required field.
PepperAlias string
// DBPath is the directory where the key metadata database is stored.
//
// This SQLite database stores KEK version information for key rotation.
// If empty, the default ".encx" is used.
//
// Optional field. Default: .encx
DBPath string
// DBFilename is the filename of the key metadata database.
//
// If empty, the default "keys.db" is used.
//
// Optional field. Default: keys.db
DBFilename string
}
Config holds the configuration for creating a Crypto instance.
This struct contains only data, no behavior. Configuration can be loaded from any source (environment variables, files, code, etc.) and passed explicitly to NewCrypto.
Required fields:
- KEKAlias: The KMS key identifier for encrypting/decrypting DEKs
- PepperAlias: The service identifier for pepper storage
Optional fields (defaults are applied if empty):
- DBPath: Database directory (default: .encx)
- DBFilename: Database filename (default: keys.db)
Example usage:
// Explicit configuration
cfg := encx.Config{
KEKAlias: "user-service-kek",
PepperAlias: "user-service",
DBPath: "/var/lib/encx",
DBFilename: "production.db",
}
// Validate and apply defaults
if err := cfg.Validate(); err != nil {
log.Fatal(err)
}
crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
func LoadConfigFromEnvironment ¶ added in v0.6.0
LoadConfigFromEnvironment loads configuration from environment variables.
This function reads configuration from standard environment variables and returns a validated Config struct. It follows the 12-factor app methodology where configuration is read from the environment.
Required environment variables:
- ENCX_KEK_ALIAS: KMS key identifier for encrypting/decrypting DEKs
- ENCX_PEPPER_ALIAS: Service identifier for pepper storage
Optional environment variables (defaults are applied if not set):
- ENCX_DB_PATH: Database directory (default: .encx)
- ENCX_DB_FILENAME: Database filename (default: keys.db)
Returns an error if required variables are missing or validation fails.
Example usage (12-factor app):
// Set environment variables (typically in deployment config):
// export ENCX_KEK_ALIAS="user-service-kek"
// export ENCX_PEPPER_ALIAS="user-service"
// export ENCX_DB_PATH="/var/lib/encx" # optional
cfg, err := encx.LoadConfigFromEnvironment()
if err != nil {
log.Fatal(err)
}
crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
Example usage (convenience with NewCryptoFromEnv):
// NewCryptoFromEnv calls this function internally crypto, err := encx.NewCryptoFromEnv(ctx, kms, secrets)
func (*Config) Validate ¶ added in v0.6.0
Validate checks that the configuration is valid and applies defaults to optional fields.
This method:
- Ensures required fields (KEKAlias, PepperAlias) are not empty
- Validates KEKAlias length (must be <= MaxKEKAliasLength)
- Applies defaults to optional fields (DBPath, DBFilename) if empty
Returns an error if validation fails.
Example:
cfg := encx.Config{
KEKAlias: "my-service-kek",
PepperAlias: "my-service",
// DBPath and DBFilename will be set to defaults
}
if err := cfg.Validate(); err != nil {
log.Fatal(err)
}
// cfg.DBPath is now ".encx"
// cfg.DBFilename is now "keys.db"
type Crypto ¶
type Crypto struct {
// contains filtered or unexported fields
}
Crypto provides cryptographic operations with Key Management Service (KMS) integration and secret storage capabilities.
This struct holds both:
- KeyManagementService: for cryptographic operations (encrypting/decrypting DEKs)
- SecretManagementService: for secure storage of secrets (like peppers)
func NewCrypto ¶
func NewCrypto( ctx context.Context, kms KeyManagementService, secrets SecretManagementService, cfg Config, options ...Option, ) (*Crypto, error)
NewCrypto creates a new Crypto instance with explicit configuration and dependencies.
This is the primary constructor for production use. It provides explicit dependency injection for both the KeyManagementService (cryptographic operations) and SecretManagementService (pepper storage), along with explicit configuration.
Architecture:
- Separates cryptographic operations (KMS) from secret storage (SecretManagementService)
- Automatic pepper lifecycle management (load or generate)
- Validates configuration and applies defaults
- Initializes key metadata database with proper schema
- Sets up all internal cryptographic components
**Parameters:**
ctx: Context for initialization operations (pepper loading, database setup, etc.) kms: KeyManagementService for encrypting/decrypting DEKs (required) secrets: SecretManagementService for pepper storage (required) cfg: Configuration struct with KEKAlias, PepperAlias, database settings (required) options: Optional runtime configuration (metrics, observability, custom database, etc.)
**Returns:**
*Crypto: Fully initialized crypto instance error: Initialization error
**Example Usage (AWS):**
import (
"github.com/hengadev/encx"
"github.com/hengadev/encx/providers/aws"
)
// Create AWS providers
kms, err := aws.NewKMSService()
secrets, err := aws.NewSecretsManagerStore()
// Configure
cfg := encx.Config{
KEKAlias: "alias/my-service-kek",
PepperAlias: "my-service",
}
// Create crypto instance
crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
**Example Usage (HashiCorp Vault):**
import (
"github.com/hengadev/encx"
"github.com/hengadev/encx/providers/hashicorp"
)
// Create Vault providers
transit, err := hashicorp.NewTransitService()
kv, err := hashicorp.NewKVStore()
// Configure
cfg := encx.Config{
KEKAlias: "my-service-kek",
PepperAlias: "my-service",
}
// Create crypto instance
crypto, err := encx.NewCrypto(ctx, transit, kv, cfg)
**Example Usage (Testing):**
// Use in-memory implementations for testing
kms := encx.NewSimpleTestKMS()
secrets := encx.NewInMemorySecretStore()
cfg := encx.Config{
KEKAlias: "test-key",
PepperAlias: "test-service",
}
crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
**Configuration Loading:**
For 12-factor apps, use LoadConfigFromEnvironment():
cfg, err := encx.LoadConfigFromEnvironment() crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
Or for a convenience function that does both, use NewCryptoFromEnv():
crypto, err := encx.NewCryptoFromEnv(ctx, kms, secrets)
func NewCryptoFromEnv ¶ added in v0.6.0
func NewCryptoFromEnv( ctx context.Context, kms KeyManagementService, secrets SecretManagementService, options ...Option, ) (*Crypto, error)
NewCryptoFromEnv is a convenience constructor that loads configuration from environment variables.
This function combines LoadConfigFromEnvironment() and NewCrypto() into a single call, making it ideal for 12-factor applications that use environment-based configuration.
**Environment Variables:**
ENCX_KEK_ALIAS: KMS key alias for this service (required) ENCX_PEPPER_ALIAS: Service identifier for pepper storage (required) ENCX_DB_PATH: Database directory (optional, default: .encx) ENCX_DB_FILENAME: Database filename (optional, default: keys.db)
**Parameters:**
ctx: Context for initialization operations kms: KeyManagementService for encrypting/decrypting DEKs (required) secrets: SecretManagementService for pepper storage (required) options: Optional runtime configuration (metrics, observability, etc.)
**Returns:**
*Crypto: Fully initialized crypto instance error: Initialization or configuration error
**Example Usage:**
// Set environment variables // export ENCX_KEK_ALIAS="alias/my-service-kek" // export ENCX_PEPPER_ALIAS="my-service" kms, err := aws.NewKMSService() secrets, err := aws.NewSecretsManagerStore() crypto, err := encx.NewCryptoFromEnv(ctx, kms, secrets)
This is equivalent to:
cfg, err := encx.LoadConfigFromEnvironment() crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
func NewTestCrypto ¶
NewTestCrypto creates a simple Crypto instance for testing and examples If t is nil, creates a basic test crypto for examples/demos
func NewTestCryptoWithDatabase ¶ added in v0.6.0
NewTestCryptoWithDatabase creates a Crypto instance with a specific database for testing
func (*Crypto) CompareBasicHashAndValue ¶
func (*Crypto) CompareSecureHashAndValue ¶
func (*Crypto) DecryptDEKWithVersion ¶
func (*Crypto) DecryptData ¶
func (*Crypto) DecryptStream ¶
func (*Crypto) EncryptDEK ¶
func (*Crypto) EncryptData ¶
func (*Crypto) EncryptStream ¶
func (*Crypto) GenerateDEK ¶
func (*Crypto) GetArgon2Params ¶
func (c *Crypto) GetArgon2Params() *Argon2Params
func (*Crypto) GetCurrentKEKVersion ¶
Internal interface implementations
func (*Crypto) GetKMSKeyIDForVersion ¶
func (*Crypto) HashSecure ¶
type CryptoService ¶
type CryptoService interface {
GetPepper() []byte
GetArgon2Params() *Argon2Params
GetAlias() string
GenerateDEK() ([]byte, error)
EncryptData(ctx context.Context, plaintext []byte, dek []byte) ([]byte, error)
DecryptData(ctx context.Context, ciphertext []byte, dek []byte) ([]byte, error)
EncryptDEK(ctx context.Context, plaintextDEK []byte) ([]byte, error)
DecryptDEKWithVersion(ctx context.Context, ciphertextDEK []byte, kekVersion int) ([]byte, error)
RotateKEK(ctx context.Context) error
HashBasic(ctx context.Context, value []byte) string
HashSecure(ctx context.Context, value []byte) (string, error)
CompareSecureHashAndValue(ctx context.Context, value any, hashValue string) (bool, error)
CompareBasicHashAndValue(ctx context.Context, value any, hashValue string) (bool, error)
EncryptStream(ctx context.Context, reader io.Reader, writer io.Writer, dek []byte) error
DecryptStream(ctx context.Context, reader io.Reader, writer io.Writer, dek []byte) error
GetCurrentKEKVersion(ctx context.Context, alias string) (int, error)
GetKMSKeyIDForVersion(ctx context.Context, alias string, version int) (string, error)
}
type EncryptionMetadata ¶
type EncryptionMetadata struct {
PepperVersion int `json:"pepper_version"`
KEKAlias string `json:"kek_alias"`
EncryptionTime int64 `json:"encryption_time"`
GeneratorVersion string `json:"generator_version"`
}
EncryptionMetadata contains metadata about how a struct was encrypted
func NewEncryptionMetadata ¶
func NewEncryptionMetadata(kekAlias, generatorVersion string, pepperVersion int) *EncryptionMetadata
NewEncryptionMetadata creates a new EncryptionMetadata instance
func (*EncryptionMetadata) FromJSON ¶
func (em *EncryptionMetadata) FromJSON(data []byte) error
FromJSON deserializes metadata from JSON bytes
func (*EncryptionMetadata) ToJSON ¶
func (em *EncryptionMetadata) ToJSON() ([]byte, error)
ToJSON serializes the metadata to JSON bytes
func (*EncryptionMetadata) Validate ¶
func (em *EncryptionMetadata) Validate() error
Validate checks if the metadata is valid
type InMemorySecretStore ¶ added in v0.6.0
type InMemorySecretStore struct {
// contains filtered or unexported fields
}
InMemorySecretStore implements SecretManagementService for testing
This store keeps all secrets in memory and is suitable for unit tests and examples. All data is lost when the process terminates.
Usage:
store := NewInMemorySecretStore() err := store.StorePepper(ctx, "my-service", pepper)
func (*InMemorySecretStore) GetPepper ¶ added in v0.6.0
GetPepper retrieves a pepper from memory
Returns an error if the pepper doesn't exist or has invalid length.
func (*InMemorySecretStore) GetStoragePath ¶ added in v0.6.0
func (s *InMemorySecretStore) GetStoragePath(alias string) string
GetStoragePath returns the storage path for a given alias
For in-memory store, this is just a virtual path for consistency.
func (*InMemorySecretStore) PepperExists ¶ added in v0.6.0
PepperExists checks if a pepper exists in memory
Returns true if the pepper exists, false if it doesn't.
func (*InMemorySecretStore) StorePepper ¶ added in v0.6.0
StorePepper stores a pepper in memory
The pepper must be exactly 32 bytes (PepperLength).
type KeyManagementService ¶
type KeyManagementService interface {
// GetKeyID resolves a key alias to a key ID.
//
// For AWS KMS, this resolves an alias like "alias/my-key" to the underlying key ID.
// For HashiCorp Vault, this returns the key name directly.
//
// Parameters:
// - ctx: Context for the operation
// - alias: The key alias to resolve (e.g., "alias/myapp-kek", "transit/keys/myapp")
//
// Returns:
// - The resolved key ID
// - Error if the key cannot be found or accessed
GetKeyID(ctx context.Context, alias string) (string, error)
// CreateKey creates a new encryption key in the KMS.
//
// This creates a symmetric encryption key suitable for encrypting DEKs.
// The key remains in the KMS and is never exposed.
//
// Parameters:
// - ctx: Context for the operation
// - description: Human-readable description for the key
//
// Returns:
// - The created key ID
// - Error if key creation fails
CreateKey(ctx context.Context, description string) (string, error)
// EncryptDEK encrypts a Data Encryption Key (DEK) using the specified KMS key.
//
// The DEK is encrypted with the KEK identified by keyID. The encrypted DEK can
// be safely stored and later decrypted using DecryptDEK.
//
// Parameters:
// - ctx: Context for the operation
// - keyID: The KMS key ID to use for encryption
// - plaintext: The plaintext DEK to encrypt (typically 32 bytes)
//
// Returns:
// - The encrypted DEK (ciphertext)
// - Error if encryption fails
EncryptDEK(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
// DecryptDEK decrypts a Data Encryption Key (DEK) that was encrypted by EncryptDEK.
//
// The ciphertext DEK is decrypted using the KEK identified by keyID. The KMS
// performs the decryption operation without exposing the KEK.
//
// Parameters:
// - ctx: Context for the operation
// - keyID: The KMS key ID to use for decryption
// - ciphertext: The encrypted DEK to decrypt
//
// Returns:
// - The plaintext DEK
// - Error if decryption fails
DecryptDEK(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
}
KeyManagementService defines the contract for cryptographic key operations.
This interface is implemented by KMS providers (AWS KMS, HashiCorp Vault Transit Engine, etc.) and handles cryptographic operations with Key Encryption Keys (KEKs). It is responsible for:
- Managing KEK lifecycle (creation, retrieval)
- Encrypting Data Encryption Keys (DEKs) with KEKs
- Decrypting DEKs that were encrypted with KEKs
This interface is separate from SecretManagementService, which handles secret storage. KeyManagementService performs cryptographic operations, while SecretManagementService stores and retrieves secret values.
Implementations:
- AWS KMS: github.com/hengadev/encx/providers/aws.KMSService
- HashiCorp Vault Transit: github.com/hengadev/encx/providers/hashicorp.TransitService
Example usage:
import "github.com/hengadev/encx/providers/aws"
kms, err := aws.NewKMSService(ctx, aws.Config{Region: "us-east-1"})
if err != nil {
log.Fatal(err)
}
// Use with Crypto
crypto, err := encx.NewCrypto(ctx, kms, secretStore, cfg)
type SecretManagementService ¶ added in v0.6.0
type SecretManagementService interface {
// StorePepper stores a pepper secret for the specified alias.
//
// The pepper must be exactly 32 bytes (PepperLength constant). If a pepper
// already exists for this alias, it will be updated.
//
// Parameters:
// - ctx: Context for the operation
// - alias: The service identifier (e.g., "user-service", "myapp")
// - pepper: The pepper bytes to store (must be 32 bytes)
//
// Returns:
// - Error if storage fails or pepper length is invalid
//
// Example:
//
// pepper := make([]byte, encx.PepperLength)
// rand.Read(pepper)
// err := secretStore.StorePepper(ctx, "user-service", pepper)
StorePepper(ctx context.Context, alias string, pepper []byte) error
// GetPepper retrieves the pepper secret for the specified alias.
//
// Parameters:
// - ctx: Context for the operation
// - alias: The service identifier (e.g., "user-service", "myapp")
//
// Returns:
// - The pepper bytes (always 32 bytes)
// - Error if the pepper doesn't exist or retrieval fails
//
// Example:
//
// pepper, err := secretStore.GetPepper(ctx, "user-service")
// if err != nil {
// log.Fatal(err)
// }
GetPepper(ctx context.Context, alias string) ([]byte, error)
// PepperExists checks if a pepper exists for the specified alias.
//
// This is useful for determining whether to generate a new pepper or load
// an existing one during initialization.
//
// Parameters:
// - ctx: Context for the operation
// - alias: The service identifier (e.g., "user-service", "myapp")
//
// Returns:
// - true if the pepper exists, false otherwise
// - Error only if the check itself fails (not if pepper doesn't exist)
//
// Example:
//
// exists, err := secretStore.PepperExists(ctx, "user-service")
// if err != nil {
// log.Fatal(err)
// }
// if !exists {
// // Generate and store new pepper
// }
PepperExists(ctx context.Context, alias string) (bool, error)
// GetStoragePath returns the full storage path for a given alias.
//
// This method is primarily for debugging and logging purposes, allowing
// users to see exactly where their secrets are stored in the underlying
// secret management system.
//
// The returned path format is implementation-specific:
// - AWS Secrets Manager: "encx/{alias}/pepper"
// - Vault KV v2: "secret/data/encx/{alias}/pepper"
// - In-Memory: "memory://{alias}/pepper"
//
// Parameters:
// - alias: The service identifier (e.g., "user-service", "myapp")
//
// Returns:
// - The full storage path as a string
//
// Example:
//
// path := secretStore.GetStoragePath("user-service")
// log.Printf("Pepper will be stored at: %s", path)
GetStoragePath(alias string) string
}
SecretManagementService defines the contract for secret storage and retrieval operations.
This interface is implemented by secret storage providers (AWS Secrets Manager, HashiCorp Vault KV Engine, in-memory store for testing, etc.) and handles the storage and retrieval of sensitive configuration values such as peppers.
This interface is separate from KeyManagementService, which handles cryptographic operations. SecretManagementService stores secret values, while KeyManagementService performs cryptographic operations on keys.
Implementations:
- AWS Secrets Manager: github.com/hengadev/encx/providers/aws.SecretsManagerStore
- HashiCorp Vault KV v2: github.com/hengadev/encx/providers/hashicorp.KVStore
- In-Memory (testing): encx.InMemorySecretStore
Example usage:
import "github.com/hengadev/encx/providers/aws"
secrets, err := aws.NewSecretsManagerStore(ctx, aws.Config{Region: "us-east-1"})
if err != nil {
log.Fatal(err)
}
// Use with Crypto
crypto, err := encx.NewCrypto(ctx, kmsService, secrets, cfg)
Storage Path Convention:
Each implementation determines its own storage path based on the alias. For example:
- AWS Secrets Manager: "encx/{alias}/pepper"
- Vault KV v2: "secret/data/encx/{alias}/pepper"
- In-Memory: "memory://{alias}/pepper"
This allows for service isolation in microservices architectures where each service uses a unique alias (e.g., "user-service", "payment-service").
func NewInMemorySecretStore ¶ added in v0.6.0
func NewInMemorySecretStore() SecretManagementService
NewInMemorySecretStore creates a new in-memory secret store
type SimpleTestKMS ¶
type SimpleTestKMS struct {
// contains filtered or unexported fields
}
SimpleTestKMS implements a basic in-memory KMS for testing and examples
func (*SimpleTestKMS) DecryptDEK ¶
func (s *SimpleTestKMS) DecryptDEK(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
DecryptDEK decrypts the DEK using AES-GCM
func (*SimpleTestKMS) EncryptDEK ¶
func (s *SimpleTestKMS) EncryptDEK(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
EncryptDEK encrypts the DEK using AES-GCM
type VersionDetails ¶
type VersionDetails struct {
Version string `json:"version"`
GitCommit string `json:"git_commit,omitempty"`
BuildDate string `json:"build_date,omitempty"`
BuildUser string `json:"build_user,omitempty"`
}
VersionDetails contains detailed version information
func FullVersionInfo ¶
func FullVersionInfo() VersionDetails
FullVersionInfo returns complete version information including build user
func (VersionDetails) String ¶
func (v VersionDetails) String() string
String returns a formatted version string
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
encx-gen
command
|
|
|
Package examples demonstrates various use cases for the encx library.
|
Package examples demonstrates various use cases for the encx library. |
|
basic_demo
command
|
|
|
combined_tags_demo
command
|
|
|
combined_tags_simple
command
|
|
|
context7/01-basic/basic_hashing
command
|
|
|
context7/02-intermediate
command
|
|
|
context7/03-advanced
command
|
|
|
context7/04-industry
command
|
|
|
enhanced_validation
command
|
|
|
error_handling
command
|
|
|
go_generate_demo
Package go_generate_demo demonstrates encx-gen integration
|
Package go_generate_demo demonstrates encx-gen integration |
|
per_struct_serializers
command
|
|
|
internal
|
|
|
serialization
Package serialization provides compact binary serialization for deterministic encryption.
|
Package serialization provides compact binary serialization for deterministic encryption. |
|
providers
|
|
|
keys/aws
Package aws provides AWS Key Management Service (KMS) integration for encx.
|
Package aws provides AWS Key Management Service (KMS) integration for encx. |
|
keys/hashicorp
Package hashicorp provides HashiCorp Vault Transit Engine integration for encx.
|
Package hashicorp provides HashiCorp Vault Transit Engine integration for encx. |
|
secrets/aws
Package aws provides AWS Secrets Manager integration for encx.
|
Package aws provides AWS Secrets Manager integration for encx. |
|
secrets/hashicorp
Package hashicorp provides HashiCorp Vault KV v2 Engine integration for encx.
|
Package hashicorp provides HashiCorp Vault KV v2 Engine integration for encx. |
|
test
|
|