encx

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2026 License: MIT Imports: 20 Imported by: 0

README

ENCX - Enterprise Cryptography for Go

Context7 Users: See Quick Integration Guide for structured examples and patterns

A production-ready Go library for field-level encryption, hashing, and key management. ENCX provides struct-based cryptographic operations with support for key rotation, multiple KMS backends, and comprehensive testing utilities.

🚀 Context7 Quick Start

// Install: go get github.com/hengadev/encx

// Define struct with encryption tags (no companion fields needed)
type User struct {
    Email    string `encx:"encrypt,hash_basic"` // Encrypt + searchable
    Password string `encx:"hash_secure"`        // Secure password hash
}

// Generate code using one of 3 options:
// 1. go run ./cmd/encx-gen generate .
// 2. Build first: go build -o bin/encx-gen ./cmd/encx-gen && ./bin/encx-gen generate .
// 3. Add: //go:generate go run ../../cmd/encx-gen generate .  (path must be relative)

// Use generated functions for type-safe encryption
crypto, _ := encx.NewTestCrypto(nil)
user := &User{Email: "user@example.com", Password: "secret123"}

// Process returns separate struct with encrypted/hashed fields
// Note: Function name follows pattern Process<YourStructName>Encx
// For a User struct, it generates ProcessUserEncx
userEncx, err := ProcessUserEncx(ctx, crypto, user)

// userEncx.EmailEncrypted contains encrypted email
// userEncx.EmailHash contains searchable hash
// userEncx.PasswordHashSecure contains secure hash

// Decrypt when needed
// Note: Function name follows pattern Decrypt<YourStructName>Encx
decryptedUser, err := DecryptUserEncx(ctx, crypto, userEncx)

See all patterns and use cases

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

Installation
go get github.com/hengadev/encx
Basic Usage
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/hengadev/encx"
)

// Define your struct with encx tags (no companion fields needed)
type User struct {
    Name     string `encx:"encrypt"`
    Email    string `encx:"hash_basic"`
    Password string `encx:"hash_secure"`
}

// Run code generation using one of 3 options:
// 1. go run ./cmd/encx-gen generate .
// 2. Build first: go build -o bin/encx-gen ./cmd/encx-gen && ./bin/encx-gen generate .
// 3. Add: //go:generate go run ../../cmd/encx-gen generate .  (path must be relative)

func main() {
    ctx := context.Background()
    crypto, _ := encx.NewTestCrypto(nil)

    // Create user with sensitive data
    user := &User{
        Name:     "John Doe",
        Email:    "john@example.com",
        Password: "secret123",
    }

    // Process returns encrypted struct (generated function)
    // Note: For your struct, replace "User" with your actual struct name
    // Example: ProcessCustomerEncx, ProcessOrderEncx, etc.
    userEncx, err := ProcessUserEncx(ctx, crypto, user)
    if err != nil {
        log.Fatal(err)
    }

    // Store encrypted data in database
    fmt.Printf("NameEncrypted: %d bytes\n", len(userEncx.NameEncrypted))
    fmt.Printf("EmailHash: %s\n", userEncx.EmailHash[:16]+"...")
    fmt.Printf("PasswordHashSecure: %s...\n", userEncx.PasswordHashSecure[:20]+"...")

    // Decrypt when needed (generated function)
    decryptedUser, err := DecryptUserEncx(ctx, crypto, userEncx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Decrypted Name: %s\n", decryptedUser.Name)
}

Struct Tags Reference

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
How It Works

encx-gen discovers structs automatically by parsing Go source files. No special directives required.

Code Generation Methods:

Code Generation: 3 Working Options

# Build the tool once
go build -o bin/encx-gen ./cmd/encx-gen

# Generate code for current directory and all subdirectories recursively
./bin/encx-gen generate .

# Generate code for specific packages
./bin/encx-gen generate ./path/to/package
Option 2: Go Run (RELIABLE)
# No building needed - runs directly from source
go run ./cmd/encx-gen generate ./path/to/package

# Generate code for current directory and all subdirectories recursively
go run ./cmd/encx-gen generate .

# Works from any directory with correct relative path
go run ../../cmd/encx-gen generate .
Option 3: Go Generate with Go Run (ADVANCED)

Add this to your Go source file (path must be relative to your file):

//go:generate go run ../../cmd/encx-gen generate .

Then run: go generate ./...

Note: When using encx-gen generate ., the tool automatically discovers all Go packages in subdirectories recursively, making it ideal for processing entire projects from the root directory.

⚠️ Note: Option 3 requires the correct relative path to cmd/encx-gen. Options 1 & 2 work consistently in all environments.

When you define a struct with encx tags:

// Your source struct - clean and simple
type User struct {
    Email string `encx:"encrypt,hash_basic"`
}

// encx-gen automatically generates a UserEncx struct
type UserEncx struct {
    EmailEncrypted []byte   // Encrypted email data
    EmailHash      string   // Searchable hash
    DEKEncrypted   []byte   // Encrypted data encryption key
    KeyVersion     int      // Key version for rotation
    Metadata       string   // Serialization metadata
}

// And generates these functions:
// - ProcessUserEncx(ctx, crypto, user) (*UserEncx, error)
// - DecryptUserEncx(ctx, crypto, userEncx) (*User, error)
//
// Note: Function names follow the pattern Process<StructName>Encx
// Replace "User" with your actual struct name

Advanced Examples

Combined Tags for Email (Searchable Encryption)

Perfect for user lookup + privacy using code generation:

// Source struct - clean definition
type User struct {
    Email string `encx:"encrypt,hash_basic"`
}

// Run: go run ./cmd/encx-gen generate .

// Usage
user := &User{Email: "user@example.com"}
userEncx, err := ProcessUserEncx(ctx, crypto, user)

// Generated UserEncx has:
// - EmailEncrypted []byte  // For secure storage
// - EmailHash      string  // For fast user lookups

// Database search example
db.Where("email_hash = ?", userEncx.EmailHash).First(&foundUser)

// Decrypt when needed
decrypted, _ := DecryptUserEncx(ctx, crypto, foundUser)
fmt.Println(decrypted.Email) // "user@example.com"
Password with Recovery

Secure authentication + recovery capability:

type User struct {
    Password string `encx:"hash_secure,encrypt"`
}

// Run: go run ./cmd/encx-gen generate .

// Example: Registration
// (Replace "User" with your actual struct name)
user := &User{Password: "secret123"}
userEncx, _ := ProcessUserEncx(ctx, crypto, user)

// Generated UserEncx has:
// - PasswordHashSecure string // For authentication (Argon2id)
// - PasswordEncrypted  []byte // For recovery scenarios

// Login verification
isValid := crypto.CompareSecureHashAndValue(ctx, inputPassword, userEncx.PasswordHashSecure)

// Password recovery (admin function)
recovered, _ := DecryptUserEncx(ctx, crypto, userEncx)
fmt.Println(recovered.Password) // Original password temporarily available
Embedded Structs

Code generation handles embedded structs automatically:

//go:generate go run ../../cmd/encx-gen generate .

type Address struct {
    Street string `encx:"encrypt"`
    City   string `encx:"hash_basic"`
}

type User struct {
    Name    string  `encx:"encrypt"`
    Address Address // Embedded struct, automatically processed
}

// Example: Usage
// (Replace "User" with your actual struct name)
user := &User{
    Name: "John Doe",
    Address: Address{
        Street: "123 Main St",
        City:   "Springfield",
    },
}

userEncx, _ := ProcessUserEncx(ctx, crypto, user)
// Generated UserEncx includes all encrypted/hashed fields from embedded struct

Configuration

ENCX supports two configuration approaches:

  1. Explicit Configuration - Full control over all dependencies (recommended for libraries)
  2. Environment-based Configuration - 12-factor app pattern (recommended for applications)
Production Setup
import (
    "github.com/hengadev/encx"
    awskms "github.com/hengadev/encx/providers/keys/aws"
    awssecrets "github.com/hengadev/encx/providers/secrets/aws"
)

// Initialize KMS for cryptographic operations
kms, err := awskms.NewKMSService(ctx, awskms.Config{
    Region: "us-east-1",
})

// Initialize Secrets Manager for pepper storage
secrets, err := awssecrets.NewSecretsManagerStore(ctx, awssecrets.Config{
    Region: "us-east-1",
})

// Create explicit configuration
cfg := encx.Config{
    KEKAlias:    "my-app-kek",      // KMS key identifier
    PepperAlias: "my-app-service",  // Service identifier for pepper
}

// Initialize crypto with explicit dependencies
crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
import (
    "github.com/hengadev/encx"
    awskms "github.com/hengadev/encx/providers/keys/aws"
    awssecrets "github.com/hengadev/encx/providers/secrets/aws"
)

// Set environment variables:
// export ENCX_KEK_ALIAS="my-app-kek"
// export ENCX_PEPPER_ALIAS="my-app-service"

// Initialize providers
kms, _ := awskms.NewKMSService(ctx, awskms.Config{Region: "us-east-1"})
secrets, _ := awssecrets.NewSecretsManagerStore(ctx, awssecrets.Config{Region: "us-east-1"})

// Load configuration from environment
crypto, err := encx.NewCryptoFromEnv(ctx, kms, secrets)
With HashiCorp Vault
import (
    "github.com/hengadev/encx"
    vaulttransit "github.com/hengadev/encx/providers/keys/hashicorp"
    vaultkv "github.com/hengadev/encx/providers/secrets/hashicorp"
)

// Initialize Transit Engine for cryptographic operations
transit, err := vaulttransit.NewTransitService()

// Initialize KV Store for pepper storage
kvStore, err := vaultkv.NewKVStore()

// Explicit configuration
cfg := encx.Config{
    KEKAlias:    "my-app-kek",
    PepperAlias: "my-app-service",
}

crypto, err := encx.NewCrypto(ctx, transit, kvStore, cfg)
Environment Variables

When using NewCryptoFromEnv(), these environment variables are required:

  • ENCX_KEK_ALIAS - Key encryption key identifier (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)

For AWS configuration:

  • AWS_REGION - AWS region
  • AWS_ACCESS_KEY_ID - AWS credentials
  • AWS_SECRET_ACCESS_KEY - AWS credentials

For Vault configuration:

  • VAULT_ADDR - Vault server address (required)
  • VAULT_TOKEN - Vault token (or use AppRole)
  • VAULT_NAMESPACE - Vault namespace (HCP Vault)
Testing Setup
// For unit tests with code generation
func TestUserEncryption(t *testing.T) {
    crypto, _ := encx.NewTestCrypto(t)

    user := &User{Name: "Test User"}
    userEncx, err := ProcessUserEncx(ctx, crypto, user)

    assert.NoError(t, err)
    assert.NotEmpty(t, userEncx.NameEncrypted)
}

// For integration tests
func TestUserEncryptionIntegration(t *testing.T) {
    crypto, _ := encx.NewTestCrypto(t, &encx.TestCryptoOptions{
        Pepper: []byte("test-pepper-exactly-32-bytes!!"),
    })

    // Test full encrypt/decrypt cycle
    user := &User{Name: "Integration Test"}
    userEncx, err := ProcessUserEncx(ctx, crypto, user)
    assert.NoError(t, err)

    decrypted, err := DecryptUserEncx(ctx, crypto, userEncx)
    assert.NoError(t, err)
    assert.Equal(t, "Integration Test", decrypted.Name)
}
How It Works

ENCX uses a custom compact binary serializer that provides deterministic encryption with minimal overhead. The serialization is handled automatically by the generated code - you don't need to configure anything.

Validation

Validate your struct tags before generating code:

# Validate all Go files in current directory
encx-gen validate -v .

# Validate specific packages
encx-gen validate -v ./models ./api

# Validation is automatically run before generation
go run ./cmd/encx-gen generate -v .

Key Management

Key Rotation
// Rotate the Key Encryption Key (KEK)
if err := crypto.RotateKEK(ctx); err != nil {
    log.Fatalf("Key rotation failed: %v", err)
}

// Data encrypted with old keys can still be decrypted
// New encryptions will use the new key version
Multiple Key Versions

ENCX automatically handles multiple key versions:

// User encrypted with key version 1
oldUser := &User{Name: "Alice"}
oldUserEncx, _ := ProcessUserEncx(ctx, crypto, oldUser) // Uses current key (v1)

// Rotate key
crypto.RotateKEK(ctx)

// New user encrypted with key version 2
newUser := &User{Name: "Bob"}
newUserEncx, _ := ProcessUserEncx(ctx, crypto, newUser) // Uses current key (v2)

// Both can be decrypted regardless of key version
DecryptUserEncx(ctx, crypto, oldUserEncx) // Automatically uses key v1
DecryptUserEncx(ctx, crypto, newUserEncx) // Automatically uses key v2

KMS Providers

ENCX separates key management (cryptographic operations) from secret management (pepper storage). Each provider implements both interfaces.

AWS KMS

AWS provider offers two services:

  • KMSService - Handles encryption/decryption using AWS KMS
  • SecretsManagerStore - Stores pepper in AWS Secrets Manager
import (
    "github.com/hengadev/encx"
    awskms "github.com/hengadev/encx/providers/keys/aws"
    awssecrets "github.com/hengadev/encx/providers/secrets/aws"
)

// Initialize both services
kms, err := awskms.NewKMSService(ctx, awskms.Config{
    Region: "us-east-1",
})
if err != nil {
    log.Fatal(err)
}

secrets, err := awssecrets.NewSecretsManagerStore(ctx, awssecrets.Config{
    Region: "us-east-1",
})
if err != nil {
    log.Fatal(err)
}

// Create crypto instance
cfg := encx.Config{
    KEKAlias:    "alias/my-encryption-key",
    PepperAlias: "my-app-service",
}

crypto, err := encx.NewCrypto(ctx, kms, secrets, cfg)
if err != nil {
    log.Fatal(err)
}

Key Benefits:

  • Pepper automatically stored in AWS Secrets Manager
  • No filesystem dependencies
  • Follows AWS security best practices
  • Supports key rotation

Required IAM Permissions:

  • KMS: kms:Encrypt, kms:Decrypt, kms:DescribeKey
  • Secrets Manager: secretsmanager:GetSecretValue, secretsmanager:CreateSecret, secretsmanager:PutSecretValue

→ Full AWS KMS Documentation

→ Full AWS Secrets Manager Documentation

HashiCorp Vault

HashiCorp provider offers two services:

  • TransitService - Handles encryption/decryption using Vault Transit Engine
  • KVStore - Stores pepper in Vault KV v2 storage
import (
    "github.com/hengadev/encx"
    vaulttransit "github.com/hengadev/encx/providers/keys/hashicorp"
    vaultkv "github.com/hengadev/encx/providers/secrets/hashicorp"
)

// Initialize both services (uses same Vault connection)
transit, err := vaulttransit.NewTransitService()
if err != nil {
    log.Fatal(err)
}

kvStore, err := vaultkv.NewKVStore()
if err != nil {
    log.Fatal(err)
}

// Create crypto instance
cfg := encx.Config{
    KEKAlias:    "my-encryption-key",
    PepperAlias: "my-app-service",
}

crypto, err := encx.NewCrypto(ctx, transit, kvStore, cfg)
if err != nil {
    log.Fatal(err)
}

Key Benefits:

  • Pepper automatically stored in Vault KV v2
  • Leverages Vault's secret versioning
  • Supports multi-region Vault deployments
  • AppRole authentication support

Required Vault Policies:

  • Transit: transit/encrypt/<key-name>, transit/decrypt/<key-name>
  • KV: secret/data/encx/<pepper-alias>/pepper (read/write)

→ Full Vault Transit Documentation

→ Full Vault KV Documentation

Examples

S3 Streaming Upload with Encryption

Example showing how to encrypt files on-the-fly and stream them directly to AWS S3:

import (
    "github.com/hengadev/encx"
    awskms "github.com/hengadev/encx/providers/keys/aws"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

// Encrypt file and stream to S3
err := crypto.EncryptStream(ctx, fileReader, s3Writer, dek)

→ Full S3 Streaming Example

Features:

  • Zero-copy streaming encryption
  • Minimal memory usage (4KB buffer)
  • Production-ready with error handling
  • Includes HTTP upload server example

Best Practices

1. Use Combined Tags Strategically
// Good: Email needs both lookup and privacy
Email string `encx:"encrypt,hash_basic"`

// Good: Password needs auth and recovery
Password string `encx:"hash_secure,encrypt"`

// Avoid: Unnecessary combinations
InternalID string `encx:"encrypt,hash_basic,hash_secure"` // Too much
2. Proper Error Handling
userEncx, err := ProcessUserEncx(ctx, crypto, user)
if err != nil {
    // Log the error for debugging
    log.Printf("Encryption failed: %v", err)

    // Return user-friendly error
    return nil, fmt.Errorf("failed to process user data: %w", err)
}
3. Use Go Generate in Development
//go:generate encx-gen validate -v .
//go:generate go run ./cmd/encx-gen generate -v .

// Run validation and generation during development
// Run: go generate ./...
4. Handle Key Rotation Gracefully
// Schedule regular key rotation
go func() {
    ticker := time.NewTicker(30 * 24 * time.Hour) // 30 days
    defer ticker.Stop()
    
    for range ticker.C {
        if err := crypto.RotateKEK(ctx); err != nil {
            log.Printf("Key rotation failed: %v", err)
        }
    }
}()

Performance Considerations

  • Batch Operations: Process multiple structs in batches when possible
  • Connection Pooling: Use connection pooling for KMS and database
  • Caching: Consider caching decrypted DEKs for frequently accessed data
  • Monitoring: Monitor KMS API calls and database performance

Security Considerations

  • Pepper Management: Peppers are automatically stored in KMS/Vault, never on filesystem
  • Service Isolation: Use unique PepperAlias for each service/environment
  • KMS Permissions: Use least-privilege IAM policies for KMS and Secrets Manager
  • Vault Policies: Restrict access to Transit Engine and KV paths
  • Database Security: Encrypt database at rest and in transit
  • Memory Management: Clear sensitive data from memory when possible
  • Audit Logging: Log all cryptographic operations for compliance
  • Key Rotation: Implement regular KEK rotation schedules (e.g., every 90 days)

Testing

The library includes comprehensive testing utilities:

// Unit testing with generated code
func TestUserEncryption(t *testing.T) {
    crypto, _ := encx.NewTestCrypto(t)

    user := &User{
        Email:    "test@example.com",
        Password: "secret123",
    }

    userEncx, err := ProcessUserEncx(ctx, crypto, user)
    assert.NoError(t, err)
    assert.NotEmpty(t, userEncx.EmailEncrypted)
    assert.NotEmpty(t, userEncx.EmailHash)
}

// Integration testing with full cycle
func TestUserEncryptDecryptCycle(t *testing.T) {
    crypto, _ := encx.NewTestCrypto(t)

    original := &User{Email: "integration@test.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)
}

Important: Version Control (.gitignore)

The .encx/ directory contains the local key metadata database (SQLite). This should be excluded from version control:

.encx/

Note: Peppers are stored in your configured SecretManagementService (AWS Secrets Manager, Vault KV, or in-memory for testing), not in the .encx/ directory. The directory only contains key version metadata for encryption key rotation.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Run the validation utility: go run ./cmd/validate-tags -v
  5. Ensure all tests pass: go test ./...
  6. Submit a pull request

🚧 TODOs

  • implement example for different key management services:
    • HashiCorp Vault
    • AWS KMS
    • Azure Key Vault
    • Google Cloud KMS
    • Thales CipherTrust (formerly Vormetric)
    • AWS CloudHSM
  • explore concurrency for performance improvements
  • comprehensive tests
  • combined tags support
  • compile-time validation
  • enhanced error handling
  • improved documentation

License

[Add your license here]

Support

[Add support information here]

📚 Documentation

Complete Guides
Quick References
  • Use Cases: Data encryption, PII protection, searchable encryption, password management
  • Performance: Code generation provides 10x speed improvement over reflection
  • Security: AES-GCM encryption, Argon2id hashing, automatic key management
  • Integration: Works with PostgreSQL, SQLite, MySQL, AWS KMS, HashiCorp Vault

Context7 Quick Queries

For Context7 users, here are optimized query patterns:

Common Use Cases
Query Pattern Documentation Section
"encrypt user email golang" Quick Start + Context7 Guide
"password hashing with encryption" Advanced Examples + Context7 Guide
"database schema for encrypted fields" Context7 Guide
"performance optimization encryption" Code Generation Guide
"struct tag validation" Validation + API Reference
Implementation Patterns
Pattern Use Case Documentation
encx:"encrypt" Simple data protection Struct Tags Reference
encx:"hash_basic" Fast search/lookup Quick Start
encx:"hash_secure" Password security Advanced Examples
encx:"encrypt,hash_basic" Searchable encryption Combined Tags
Technology Integration
Technology Integration Guide
PostgreSQL Context7 Guide - Database
AWS KMS Configuration
HashiCorp Vault KMS Providers
Docker Complete Web App Example
Per-Struct Serializers Serializer Example

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

View Source
const (
	FieldKeyVersion   = "KeyVersion"
	FieldDEK          = "DEK"
	FieldDEKEncrypted = FieldDEK + SuffixEncrypted
)

Field name constants - exported for public use

View Source
const (
	SuffixEncrypted = "Encrypted"
	SuffixHashed    = "Hash"
)

Suffix constants for generated fields

View Source
const (
	StructTag     = "encx"
	TagEncrypt    = "encrypt"
	TagHashSecure = "hash_secure"
	TagHashBasic  = "hash_basic"
)

Tag constants for struct field annotations

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

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

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

View Source
const (
	Unknown    = types.Unknown
	BasicHash  = types.BasicHash
	SecureHash = types.SecureHash
	Encrypt    = types.Encrypt
	Decrypt    = types.Decrypt
)

Action constants

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

View Source
const (
	// PepperLength defines the required length for pepper values in bytes.
	// Peppers must be exactly 32 bytes for cryptographic operations.
	PepperLength = 32
)

Pepper constants

View Source
const Version = "1.0.0"

Version of the encx library

Variables

View Source
var (
	// High-level service errors
	ErrKMSUnavailable           = errors.New("KMS service unavailable")
	ErrSecretStorageUnavailable = errors.New("secret storage unavailable")
	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")
	ErrDatabaseUnavailable      = errors.New("database unavailable")

	// 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")
)
View Source
var (
	NewInMemoryMetricsCollector   = monitoring.NewInMemoryMetricsCollector
	NewLoggingObservabilityHook   = monitoring.NewLoggingObservabilityHook
	NewMetricsObservabilityHook   = monitoring.NewMetricsObservabilityHook
	NewCompositeObservabilityHook = monitoring.NewCompositeObservabilityHook
)

Constructor functions

View Source
var (
	NoOpMetricsCollector  = &monitoring.NoOpMetricsCollector{}
	NoOpObservabilityHook = &monitoring.NoOpObservabilityHook{}
)

Default implementations

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

View Source
var (
	DefaultConfig = config.DefaultConfig
	ApplyOptions  = config.ApplyOptions
)

Helper functions

View Source
var (
	GitCommit string
	BuildDate string
	BuildUser string
)

Build information (set by ldflags during build)

View Source
var DefaultArgon2Params = &Argon2Params{
	Memory:      64 * 1024,
	Iterations:  3,
	Parallelism: 2,
	SaltLength:  16,
	KeyLength:   32,
}

DefaultArgon2Params provides secure default parameters

Functions

func DeserializeValue

func DeserializeValue(data []byte, target any) error

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

func IsAuthError(err error) bool

IsAuthError returns true if the error represents an authentication problem.

func IsConfigurationError

func IsConfigurationError(err error) bool

IsConfigurationError returns true if the error represents a configuration problem.

func IsOperationError

func IsOperationError(err error) bool

IsOperationError returns true if the error represents a failure during encryption/decryption operations.

func IsRetryableError

func IsRetryableError(err error) bool

IsRetryableError returns true if the error represents a transient failure that might succeed on retry.

func IsValidationError

func IsValidationError(err error) bool

IsValidationError returns true if the error represents a data validation problem.

func NewInvalidFieldTypeError

func NewInvalidFieldTypeError(fieldName string, expectedType, actualType string, action types.Action) error

func NewInvalidFormatError

func NewInvalidFormatError(fieldName string, formatName string, action types.Action) error

func NewMissingFieldError

func NewMissingFieldError(fieldName string, action types.Action) error

func NewMissingTargetFieldError

func NewMissingTargetFieldError(fieldName string, targetFieldName string, action types.Action) error

func NewNilPointerError

func NewNilPointerError(fieldName string, action types.Action) error

func NewOperationFailedError

func NewOperationFailedError(fieldName string, action types.Action, details string) error

func NewSimpleTestKMS

func NewSimpleTestKMS() config.KeyManagementService

NewSimpleTestKMS creates a new simple test KMS with a default key

func NewTypeConversionError

func NewTypeConversionError(fieldName string, typeName string, action types.Action) error

func NewUninitalizedPepperError

func NewUninitalizedPepperError() error

func NewUnsupportedTypeError

func NewUnsupportedTypeError(fieldName string, typeName string, action types.Action) error

func SerializeValue

func SerializeValue(value any) ([]byte, error)

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
}

func VersionInfo

func VersionInfo() string

VersionInfo returns formatted version information

Types

type Action

type Action = types.Action

Type aliases

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

func LoadConfigFromEnvironment() (Config, error)

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

func (c *Config) Validate() error

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

func NewTestCrypto(t interface{}) (*Crypto, error)

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

func NewTestCryptoWithDatabase(db *sql.DB) (*Crypto, error)

NewTestCryptoWithDatabase creates a Crypto instance with a specific database for testing

func (*Crypto) CompareBasicHashAndValue

func (c *Crypto) CompareBasicHashAndValue(ctx context.Context, value any, hashValue string) (bool, error)

func (*Crypto) CompareSecureHashAndValue

func (c *Crypto) CompareSecureHashAndValue(ctx context.Context, value any, hashValue string) (bool, error)

func (*Crypto) DecryptDEKWithVersion

func (c *Crypto) DecryptDEKWithVersion(ctx context.Context, ciphertextDEK []byte, kekVersion int) ([]byte, error)

func (*Crypto) DecryptData

func (c *Crypto) DecryptData(ctx context.Context, ciphertext []byte, dek []byte) ([]byte, error)

func (*Crypto) DecryptStream

func (c *Crypto) DecryptStream(ctx context.Context, reader io.Reader, writer io.Writer, dek []byte) error

func (*Crypto) EncryptDEK

func (c *Crypto) EncryptDEK(ctx context.Context, plaintextDEK []byte) ([]byte, error)

func (*Crypto) EncryptData

func (c *Crypto) EncryptData(ctx context.Context, plaintext []byte, dek []byte) ([]byte, error)

func (*Crypto) EncryptStream

func (c *Crypto) EncryptStream(ctx context.Context, reader io.Reader, writer io.Writer, dek []byte) error

func (*Crypto) GenerateDEK

func (c *Crypto) GenerateDEK() ([]byte, error)

func (*Crypto) GetAlias

func (c *Crypto) GetAlias() string

func (*Crypto) GetArgon2Params

func (c *Crypto) GetArgon2Params() *Argon2Params

func (*Crypto) GetCurrentKEKVersion

func (c *Crypto) GetCurrentKEKVersion(ctx context.Context, alias string) (int, error)

Internal interface implementations

func (*Crypto) GetKMSKeyIDForVersion

func (c *Crypto) GetKMSKeyIDForVersion(ctx context.Context, alias string, version int) (string, error)

func (*Crypto) GetPepper

func (c *Crypto) GetPepper() []byte

Getter methods

func (*Crypto) HashBasic

func (c *Crypto) HashBasic(ctx context.Context, value []byte) string

func (*Crypto) HashSecure

func (c *Crypto) HashSecure(ctx context.Context, value []byte) (string, error)

func (*Crypto) RotateKEK

func (c *Crypto) RotateKEK(ctx context.Context) error

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

func (s *InMemorySecretStore) GetPepper(ctx context.Context, alias string) ([]byte, error)

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

func (s *InMemorySecretStore) PepperExists(ctx context.Context, alias string) (bool, error)

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

func (s *InMemorySecretStore) StorePepper(ctx context.Context, alias string, pepper []byte) error

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 MetricsCollector

type MetricsCollector = monitoring.MetricsCollector

Type aliases

type ObservabilityHook

type ObservabilityHook = monitoring.ObservabilityHook

Type aliases

type Option

type Option = config.Option

Type aliases

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

func (s *SimpleTestKMS) CreateKey(ctx context.Context, description string) (string, error)

CreateKey creates a new test key and returns its ID

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

func (*SimpleTestKMS) GetKeyID

func (s *SimpleTestKMS) GetKeyID(ctx context.Context, alias string) (string, error)

GetKeyID returns a test key ID for the given alias

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

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
error_handling command
go_generate_demo
Package go_generate_demo demonstrates encx-gen integration
Package go_generate_demo demonstrates encx-gen integration
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

Jump to

Keyboard shortcuts

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