π Encryptor

A lightweight, secure, and fast encryption service for Go applications using AES-256-GCM encryption.
β¨ Features
- π AES-256-GCM Encryption - Industry-standard authenticated encryption
- π Flexible Key Management - Support for random keys, user passwords, exported keys, and file-based keys
- π‘οΈ PBKDF2 Key Derivation - Secure password-to-key transformation
- π Secure File Storage - Keys stored with restrictive permissions (0600)
- π Key Export/Import - Safely share keys across services or persist for later use
- β‘ High Performance - Optimized for speed with minimal overhead
- π§΅ Thread-Safe - 100% safe for concurrent operations with comprehensive race testing
- β
Comprehensive Testing - 95%+ test coverage with benchmarks and race detection
- π¦ Zero Dependencies - Uses only Go standard library +
golang.org/x/crypto
π¦ Installation
go get github.com/AlexanderEl/encryptor
π Quick Start
The easiest way to get started is using the CLI tool for encrypting and decrypting files:
# Clone the repository
git clone https://github.com/AlexanderEl/encryptor.git
cd encryptor
# Build the CLI tool
go build -o encryptor cmd/encryptor/main.go
# Encrypt a file (automatically generates passkey.txt)
./encryptor -op encrypt -file document.txt
# Decrypt the file (uses existing passkey.txt)
./encryptor -op decrypt -file document.txt.enc
See the CLI Usage section for complete documentation.
Library Usage
package main
import (
"fmt"
"log"
"github.com/AlexanderEl/encryptor"
)
func main() {
// Create a new encryption service
service := encryptor.NewService()
// Set a passkey (will be automatically derived to 32 bytes using PBKDF2)
if err := service.SetNewPassKey([]byte("my-secret-key")); err != nil {
log.Fatal(err)
}
// Encrypt data
plaintext := []byte("super secret message")
encrypted, err := service.Encrypt(plaintext)
if err != nil {
log.Fatal(err)
}
// Decrypt data
decrypted, err := service.Decrypt(encrypted)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Original: %s\n", plaintext)
fmt.Printf("Decrypted: %s\n", decrypted)
}
Auto-Generated Keys
// Let the service generate a secure random key
service := encryptor.NewService()
encrypted, err := service.Encrypt([]byte("secret data"))
// Key is automatically generated on first encryption
Password-Based Encryption
service := encryptor.NewService()
// Derive a secure key from a password using PBKDF2
password := "user-password-123"
if err := service.SetNewPassKeyFromPassword(password, nil); err != nil {
log.Fatal(err)
}
encrypted, err := service.Encrypt([]byte("sensitive data"))
Key Export and Import
// Export a key from one service
service1 := encryptor.NewService()
service1.GeneratePassKey()
exportedKey, err := service1.ExportPassKey()
if err != nil {
log.Fatal(err)
}
// Import the key into another service
service2 := encryptor.NewService()
if err := service2.SetExportedKey(exportedKey); err != nil {
log.Fatal(err)
}
// Both services can now encrypt/decrypt each other's data
message := []byte("shared secret")
encrypted, _ := service1.Encrypt(message)
decrypted, _ := service2.Decrypt(encrypted)
// decrypted == message
Key Export/Import Use Cases
-
Distributed Systems - Share keys across multiple servers
// Central key management
masterKey, _ := masterService.ExportPassKey()
// Distribute to worker nodes
for _, worker := range workers {
worker.SetExportedKey(masterKey)
}
-
Key Persistence - Store derived keys for reuse
// Derive once from password
service.SetNewPassKeyFromPassword("user-password", nil)
derivedKey, _ := service.ExportPassKey()
// Store in secure storage (e.g., encrypted database)
db.StoreKey(userID, derivedKey)
// Later: Load and use without re-deriving
storedKey := db.LoadKey(userID)
newService.SetExportedKey(storedKey)
-
Key Migration - Move between different storage mechanisms
// Load from file
oldService, _ := encryptor.LoadEncryptionServiceFromFile("key.txt")
key, _ := oldService.ExportPassKey()
// Store in database
database.SaveKey(key)
Persistent Key Storage
// Generate and save key to file
service := encryptor.NewService()
service.SetWriteKeyToFile(true)
service.SetKeyFilePath("my-secret-key.txt")
if err := service.GeneratePassKey(); err != nil {
log.Fatal(err)
}
// Later, load the key from file
loadedService, err := encryptor.LoadEncryptionServiceFromFile("my-secret-key.txt")
if err != nil {
log.Fatal(err)
}
π₯οΈ CLI Usage
The encryptor comes with a command-line interface for easy file encryption and decryption.
Building the CLI
# Clone the repository
git clone https://github.com/AlexanderEl/encryptor.git
cd encryptor
# Build the CLI tool
go build -o encryptor cmd/encryptor/main.go
# Optionally, install it to your PATH
go install github.com/AlexanderEl/encryptor/cmd/encryptor@latest
Command Syntax
encryptor -op <operation> -file <path> [options]
Available Flags
| Flag |
Description |
Required |
Default |
-op |
Operation: encrypt or decrypt |
Yes |
- |
-file |
Path to input file |
Yes |
- |
-out |
Path to output file |
No |
Auto-generated |
-key |
Path to passkey file |
No |
passkey.txt |
-v |
Verbose output |
No |
false |
-version |
Show version and exit |
No |
false |
Encryption Examples
# Basic encryption (creates passkey.txt automatically)
./encryptor -op encrypt -file document.txt
# Output: document.txt.enc
# Encrypt with verbose output
./encryptor -op encrypt -file confidential.pdf -v
# Shows detailed progress and warnings
# Encrypt with custom output path
./encryptor -op encrypt -file data.json -out encrypted_data.bin
# Encrypt using a specific key file
./encryptor -op encrypt -file report.docx -key my-secret-key.txt
# Encrypt multiple files (bash example)
for file in *.txt; do
./encryptor -op encrypt -file "$file"
done
Decryption Examples
# Basic decryption (uses existing passkey.txt)
./encryptor -op decrypt -file document.txt.enc
# Output: document.txt
# Decrypt with verbose output
./encryptor -op decrypt -file confidential.pdf.enc -v
# Decrypt to a specific location
./encryptor -op decrypt -file encrypted_data.bin -out original_data.json
# Decrypt using a specific key file
./encryptor -op decrypt -file report.docx.enc -key my-secret-key.txt
# Decrypt multiple files (bash example)
for file in *.enc; do
./encryptor -op decrypt -file "$file"
done
CLI Output Behavior
Default Output Paths:
- Encryption: Adds
.enc extension
document.txt β document.txt.enc
- Decryption: Removes
.enc extension or adds .dec
document.txt.enc β document.txt
document.bin β document.bin.dec
Normal Mode (default):
$ ./encryptor -op encrypt -file test.txt
Encrypted: test.txt β test.txt.enc
Verbose Mode (-v flag):
$ ./encryptor -op encrypt -file test.txt -v
=========================================================
ββββββββββββ βββ ββββββββββββββ βββ ββββββββββ βββββββββ βββββββ βββββββ
βββββββββββββ βββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ
ββββββ ββββββ ββββββ ββββββββ βββββββ ββββββββ βββ βββ βββββββββββ
ββββββ βββββββββββββ ββββββββ βββββ βββββββ βββ βββ βββββββββββ
βββββββββββ βββββββββββββββββ βββ βββ βββ βββ ββββββββββββ βββ
βββββββββββ βββββ ββββββββββ βββ βββ βββ βββ βββββββ βββ βββ
π Secure File Encryption Tool v1.1.0
=========================================================
π Encrypting file: test.txt
File size: 1234 bytes
β New encryption key generated and saved to: passkey.txt
β οΈ Keep this key file secure - you'll need it for decryption!
Encrypted size: 1262 bytes
β Encrypted file saved to: test.txt.enc
β Operation completed successfully!
CLI Error Messages
The CLI provides clear error messages for common issues:
# Missing input file
$ ./encryptor -op encrypt -file nonexistent.txt
Error: input file does not exist: nonexistent.txt
# Missing key file for decryption
$ ./encryptor -op decrypt -file document.txt.enc
Error: passkey file not found: passkey.txt (needed for decryption)
# Wrong key or corrupted file
$ ./encryptor -op decrypt -file document.txt.enc
Error: decryption failed: cipher: message authentication failed (wrong key or corrupted file?)
# Invalid operation
$ ./encryptor -op invalid -file test.txt
Error: invalid operation 'invalid'. Use 'encrypt' or 'decrypt'
CLI Best Practices
-
Keep Your Key File Safe
# After encrypting, backup your key file
cp passkey.txt ~/secure-backup/passkey-backup.txt
# Set restrictive permissions
chmod 600 passkey.txt
-
Batch Processing
# Encrypt all text files in a directory
find . -name "*.txt" -exec ./encryptor -op encrypt -file {} \;
# Decrypt all encrypted files
find . -name "*.enc" -exec ./encryptor -op decrypt -file {} \;
-
Use Different Keys for Different Projects
# Project A
./encryptor -op encrypt -file project-a-data.txt -key keys/project-a.txt
# Project B
./encryptor -op encrypt -file project-b-data.txt -key keys/project-b.txt
-
Verify Encryption Worked
# Encrypt a file
./encryptor -op encrypt -file important.txt
# Try to view encrypted file (should be unreadable)
cat important.txt.enc
# Decrypt and verify
./encryptor -op decrypt -file important.txt.enc -out verified.txt
diff important.txt verified.txt
Integration with Scripts
#!/bin/bash
# backup-and-encrypt.sh
BACKUP_DIR="/backup"
KEY_FILE="$HOME/.secrets/backup-key.txt"
# Create backup
tar -czf backup.tar.gz /important/data
# Encrypt backup
./encryptor -op encrypt -file backup.tar.gz -key "$KEY_FILE"
# Remove unencrypted backup
rm backup.tar.gz
# Move to backup location
mv backup.tar.gz.enc "$BACKUP_DIR/backup-$(date +%Y%m%d).tar.gz.enc"
echo "Backup completed and encrypted successfully!"
π API Documentation
Core Methods
NewService() *Service
Creates a new encryption service with default settings.
SetNewPassKey(key []byte) error
Sets the encryption key with automatic PBKDF2 derivation. Keys are securely derived to 32 bytes.
Parameters:
key - Encryption key (max 32 bytes)
Returns: Error if key is empty or exceeds 32 bytes
Note: This method derives the key using PBKDF2. To set a pre-derived or exported key without derivation, use SetExportedKey().
SetExportedKey(key []byte) error
Sets a pre-derived encryption key without additional derivation. Use this for importing previously exported keys.
Parameters:
key - Pre-derived encryption key (16, 24, or 32 bytes for AES-128, AES-192, or AES-256)
Returns:
ErrEmptyPassKey if key is empty
ErrKeyTooShort if key is less than 16 bytes
ErrPassKeyTooLong if key exceeds 32 bytes
Important: Unlike SetNewPassKey(), this method does NOT derive the key. Use it for:
- Importing keys exported via
ExportPassKey()
- Loading pre-derived keys from secure storage
- Sharing keys across multiple service instances
// Example: Don't mix SetNewPassKey and SetExportedKey
password := []byte("my-password-1234")
s1 := encryptor.NewService()
s1.SetNewPassKey(password) // Derives key using PBKDF2
encrypted, _ := s1.Encrypt(data)
s2 := encryptor.NewService()
s2.SetExportedKey(password) // Uses password directly (NO derivation)
// s2.Decrypt(encrypted) will FAIL - keys don't match!
// Correct approach: Export the derived key
derivedKey, _ := s1.ExportPassKey()
s3 := encryptor.NewService()
s3.SetExportedKey(derivedKey) // Now keys match
decrypted, _ := s3.Decrypt(encrypted) // SUCCESS
SetNewPassKeyFromPassword(password string, salt []byte) error
Derives a secure 32-byte key from a password using PBKDF2 with 100,000 iterations.
Parameters:
password - User password
salt - Optional salt (auto-generated if nil)
Returns: Error if password is empty
Encrypt(data []byte) ([]byte, error)
Encrypts data using AES-256-GCM. Auto-generates a key if not set.
Parameters:
data - Plaintext to encrypt
Returns: Encrypted data with prepended nonce, or error
Decrypt(data []byte) ([]byte, error)
Decrypts AES-256-GCM encrypted data.
Parameters:
data - Ciphertext to decrypt
Returns: Decrypted plaintext, or error
GeneratePassKey() error
Generates a cryptographically secure random 32-byte key.
Returns: Error on failure
ExportPassKey() ([]byte, error)
Returns a copy of the current encryption key. Use with caution.
Returns: Key copy, or ErrPassKeyNotSet if key not set
Security Note: The exported key is a copy, so modifying it won't affect the service's internal key. However, treat exported keys with the same security as passwords.
ClearPassKey()
Securely zeros out the key from memory.
Configuration Methods
SetWriteKeyToFile(write bool)
Thread-safe setter to enable/disable writing keys to file.
GetWriteKeyToFile() bool
Thread-safe getter for the write-to-file setting.
SetKeyFilePath(path string)
Thread-safe setter for the key file path.
GetKeyFilePath() string
Thread-safe getter for the key file path.
File Operations
LoadEncryptionServiceFromFile(filePath string) (*Service, error)
Convenience function to load an encryption service from a key file.
Parameters:
filePath - Path to key file
Returns: Service instance, or error
π§ Configuration
Service Options
service := encryptor.NewService()
// Write generated keys to file
service.SetWriteKeyToFile(true)
// Custom key file location
service.SetKeyFilePath("/secure/path/encryption.key")
Constants
const (
KeyByteLength = 32 // AES-256 key size
MinKeyByteLength = 16 // Minimum key size (AES-128)
DefaultPassKeyFileName = "passkey.txt"
FilePermissions = 0600 // Owner read/write only
PBKDF2Iterations = 100000 // Key derivation iterations
SaltLength = 16 // Salt size in bytes
)
Error Constants
var (
ErrEmptyPassKey = errors.New("passkey cannot be empty")
ErrPassKeyTooLong = errors.New("passkey exceeds maximum length of 32 bytes")
ErrKeyTooShort = errors.New("key must be at least 16 bytes for AES-128")
ErrEmptyData = errors.New("data cannot be empty")
ErrInvalidCiphertext = errors.New("ciphertext too short")
ErrPassKeyNotSet = errors.New("passkey not set")
)
π Security Features
Encryption Algorithm
- AES-256-GCM - Authenticated encryption with associated data (AEAD)
- Unique Nonces - Random nonce for each encryption operation
- Authentication Tags - Prevents tampering and ensures data integrity
Key Management
- PBKDF2 Key Derivation - SHA-256 with 100,000 iterations
- Secure Random Generation - Uses
crypto/rand for key generation
- Memory Protection -
ClearPassKey() zeros memory before cleanup
- File Permissions - Keys stored with 0600 (owner-only access)
- Key Export/Import - Safely share keys with proper copying to prevent external modification
Thread Safety
- Full Concurrency Support - All operations are thread-safe with optimized locking
- Race Condition Free - Extensively tested with Go's race detector
- Minimal Lock Duration - Expensive operations performed outside critical sections
- Memory-Safe Key Management - Protected copying prevents concurrent modification issues
Best Practices
β
Never reuse keys across different applications
β
Store key files in secure locations with restricted permissions
β
Use password-based keys with strong, unique passwords
β
Call ClearPassKey() when done with sensitive operations
β
Never log or transmit raw encryption keys
β
Safe for concurrent use in multi-threaded applications
β
Use SetExportedKey() only for pre-derived keys, never for passwords
β
Export keys only when necessary and handle them as securely as passwords
Quick Reference:
| Operation |
Speed |
Throughput |
| Encrypt |
557 ns/op |
~1.8M ops/sec |
| Decrypt |
488 ns/op |
~2.0M ops/sec |
| Key Generation |
92 ns/op |
~10.9M ops/sec |
| Password Derivation |
11.8 ms/op |
Security feature |
Benchmarks on Intel Core i9-14900HX (with thread-safe locking):
TEST_NAME ITERATIONS AVG_ITER_DURATION MEMORY_USED NUM_MEMORY_ALLOCATIONS
BenchmarkEncrypt-32 2168189 556.7 ns/op 1376 B/op 4 allocs/op
BenchmarkDecrypt-32 2448208 487.6 ns/op 1328 B/op 3 allocs/op
BenchmarkGeneratePassKey-32 12290386 92.06 ns/op 32 B/op 1 allocs/op
BenchmarkSetPassKeyFromPassword-32 99 11848985 ns/op 788 B/op 11 allocs/op
Performance Highlights:
- β‘ Encryption: ~557 ns/op (~1.8 million ops/sec) - Thread-safe with minimal overhead
- β‘ Decryption: ~488 ns/op (~2.0 million ops/sec) - Optimized concurrent access
- β‘ Key Generation: ~92 ns/op (~10.9 million ops/sec) - Lock-free random generation
- π Password Derivation: ~11.8 ms/op (intentionally slow for security - 100k PBKDF2 iterations)
Thread-Safety Impact:
The implementation uses optimized read-write locking (sync.RWMutex) for safe concurrent access:
- Read operations (encryption/decryption) can run concurrently
- Write operations (key setting) have minimal lock duration
- Expensive cryptographic operations performed outside critical sections
- The baseline implementation was highly optimized (~420ns per encryption), operating near the theoretical limits of AES-GCM performance. Even minimal synchronization overhead becomes proportionally significant at this performance tier
- Despite the relative overhead, absolute performance remains exceptional at ~1.8-2.0 million operations per second, making this trade-off worthwhile for production applications requiring concurrent access
Note: Password-based key derivation is intentionally slow to protect against brute-force attacks. This is a security feature, not a performance issue.
π§ͺ Testing
The encryptor package includes comprehensive testing with multiple test categories.
Basic Testing
# Run all tests
go test -v
# Run tests with coverage
go test -cover
# Generate detailed coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
# Run specific test
go test -v -run TestEncryptDecrypt
# Run benchmarks
go test -bench=. -benchmem
Race Condition Testing
The package includes extensive race condition tests to ensure 100% thread-safety. These tests use Go's built-in race detector to identify data races.
# Run ALL tests with race detection (RECOMMENDED)
go test -race
# Run tests with race detection and verbose output
go test -race -v
# Run only race-specific tests
go test -race -v -run Race
# Run race tests with coverage
go test -race -cover
# Run intensive stress test (takes ~1 second)
go test -race -v -run TestRaceStressTest
Race Test Categories:
-
Concurrent Key Operations
TestRaceConcurrentSetPassKey - Multiple goroutines setting different keys
TestRaceConcurrentGeneratePassKey - Concurrent key generation
TestRaceSetPassKeyFromPasswordConcurrent - Password-based key derivation races
-
Key Operations During Encryption/Decryption
TestRaceSetPassKeyWhileEncrypting - Key changes during encryption
TestRaceGeneratePassKeyWhileEncrypting - Key generation during encryption
TestRaceClearPassKeyWhileEncrypting - Clearing key during encryption
TestRaceClearPassKeyWhileDecrypting - Clearing key during decryption
-
Auto-Generation Races
TestRaceEncryptWithAutoGenerateRace - Multiple encryptions triggering auto-generation
-
Configuration Field Races
TestRaceConfigFieldsWhileOperating - Concurrent config changes during operations
TestRaceExportPassKeyWhileModifying - Exporting key while modifying
-
Export/Import Operations
TestRaceExportPassKeyWhileModifying - Exporting key while modifying
-
Mixed Operations
TestRaceMixedOperations - Realistic concurrent usage patterns
TestRaceStressTest - Intensive continuous operations for 1 second
-
Different Keys Scenario
TestRaceEncryptDecryptDifferentKeys - Encryption/decryption with concurrent key changes
Continuous Integration Testing
# Complete CI test suite (what should run in CI/CD)
go test -race -cover -v -timeout 30s
# With JSON output for CI tools
go test -race -cover -json > test-results.json
Test Coverage
The package maintains 95%+ test coverage across:
- β
All public API methods
- β
Error conditions and edge cases
- β
Concurrent operations
- β
File I/O operations
- β
Race conditions
- β
Memory safety
- β
Key export/import workflows
# Run all benchmarks
go test -bench=.
# Run specific benchmark
go test -bench=BenchmarkEncrypt
# Run benchmarks with memory allocation stats
go test -bench=. -benchmem
# Run benchmarks multiple times for accuracy
go test -bench=. -benchtime=10s -count=5
# Compare benchmarks (requires benchstat tool)
go test -bench=. -benchmem > old.txt
# Make changes...
go test -bench=. -benchmem > new.txt
benchstat old.txt new.txt
Testing Best Practices
-
Always Run Race Detection Locally
# Before committing code
go test -race -v
-
Test Concurrent Scenarios
# Run stress tests multiple times
for i in {1..10}; do go test -race -run TestRaceStressTest; done
-
Check for Memory Leaks
go test -bench=. -benchmem -memprofile=mem.out
go tool pprof mem.out
-
Verify Coverage
go test -cover -coverprofile=coverage.out
go tool cover -func=coverage.out | grep total
Expected Test Results
When all tests pass, you should see:
PASS
coverage: 95%+ of statements
ok github.com/AlexanderEl/encryptor 12.456s
Warning Signs:
- β οΈ
WARNING: DATA RACE - Indicates a race condition (should not occur)
- β οΈ Coverage below 90% - May indicate untested code paths
- β οΈ Benchmark performance degradation >10% - May indicate performance regression
π Examples
Example 1: Secure File Encryption
package main
import (
"io/ioutil"
"log"
"github.com/AlexanderEl/encryptor"
)
func encryptFile(inputPath, outputPath, keyPath string) error {
// Read file
data, err := ioutil.ReadFile(inputPath)
if err != nil {
return err
}
// Load or create encryption service
var service *encryptor.Service
service, err = encryptor.LoadEncryptionServiceFromFile(keyPath)
if err != nil {
// Create new service if key doesn't exist
service = encryptor.NewService()
service.SetWriteKeyToFile(true)
service.SetKeyFilePath(keyPath)
if err := service.GeneratePassKey(); err != nil {
return err
}
}
// Encrypt
encrypted, err := service.Encrypt(data)
if err != nil {
return err
}
// Write encrypted file
return ioutil.WriteFile(outputPath, encrypted, 0644)
}
Example 2: Distributed System with Shared Keys
package main
import (
"github.com/AlexanderEl/encryptor"
)
// Central key management service
type KeyManager struct {
masterService *encryptor.Service
masterKey []byte
}
func NewKeyManager() (*KeyManager, error) {
service := encryptor.NewService()
if err := service.GeneratePassKey(); err != nil {
return nil, err
}
key, err := service.ExportPassKey()
if err != nil {
return nil, err
}
return &KeyManager{
masterService: service,
masterKey: key,
}, nil
}
// Distribute key to worker nodes
func (km *KeyManager) CreateWorkerService() (*encryptor.Service, error) {
worker := encryptor.NewService()
if err := worker.SetExportedKey(km.masterKey); err != nil {
return nil, err
}
return worker, nil
}
// Usage
func main() {
manager, _ := NewKeyManager()
// Create multiple workers with same key
worker1, _ := manager.CreateWorkerService()
worker2, _ := manager.CreateWorkerService()
// All can encrypt/decrypt each other's data
data := []byte("shared message")
encrypted, _ := worker1.Encrypt(data)
decrypted, _ := worker2.Decrypt(encrypted)
// decrypted == data
}
Example 3: Environment-Based Keys
package main
import (
"encoding/hex"
"errors"
"os"
"github.com/AlexanderEl/encryptor"
)
func getServiceFromEnv() (*encryptor.Service, error) {
keyHex := os.Getenv("ENCRYPTION_KEY")
if keyHex == "" {
return nil, errors.New("ENCRYPTION_KEY not set")
}
key, err := hex.DecodeString(keyHex)
if err != nil {
return nil, err
}
service := encryptor.NewService()
// Use SetExportedKey for pre-derived keys
if err := service.SetExportedKey(key); err != nil {
return nil, err
}
return service, nil
}
Example 4: Concurrent Processing
package main
import (
"io/ioutil"
"sync"
"github.com/AlexanderEl/encryptor"
)
func encryptMultipleFiles(files []string, service *encryptor.Service) error {
var wg sync.WaitGroup
errChan := make(chan error, len(files))
// Process files concurrently - service is thread-safe
for _, file := range files {
wg.Add(1)
go func(f string) {
defer wg.Done()
data, err := ioutil.ReadFile(f)
if err != nil {
errChan <- err
return
}
encrypted, err := service.Encrypt(data)
if err != nil {
errChan <- err
return
}
if err := ioutil.WriteFile(f+".enc", encrypted, 0644); err != nil {
errChan <- err
}
}(file)
}
wg.Wait()
close(errChan)
// Check for errors
if err := <-errChan; err != nil {
return err
}
return nil
}
Example 5: Key Rotation Strategy
package main
import (
"github.com/AlexanderEl/encryptor"
)
type EncryptionService struct {
current *encryptor.Service
old *encryptor.Service
}
// Rotate to a new key while keeping old key for decryption
func (es *EncryptionService) RotateKey() error {
newService := encryptor.NewService()
if err := newService.GeneratePassKey(); err != nil {
return err
}
// Keep old service for decrypting old data
es.old = es.current
es.current = newService
return nil
}
// Encrypt always uses current key
func (es *EncryptionService) Encrypt(data []byte) ([]byte, error) {
return es.current.Encrypt(data)
}
// Decrypt tries current key, falls back to old key
func (es *EncryptionService) Decrypt(data []byte) ([]byte, error) {
decrypted, err := es.current.Decrypt(data)
if err != nil && es.old != nil {
// Try old key if current fails
return es.old.Decrypt(data)
}
return decrypted, err
}
β Error Handling
The service defines several sentinel errors for clear error handling:
var (
ErrEmptyPassKey = errors.New("passkey cannot be empty")
ErrPassKeyTooLong = errors.New("passkey exceeds maximum length of 32 bytes")
ErrKeyTooShort = errors.New("key must be at least 16 bytes for AES-128")
ErrEmptyData = errors.New("data cannot be empty")
ErrInvalidCiphertext = errors.New("ciphertext too short")
ErrPassKeyNotSet = errors.New("passkey not set")
)
Usage
service := encryptor.NewService()
_, err := service.Decrypt(data)
if errors.Is(err, encryptor.ErrPassKeyNotSet) {
// Handle missing key
service.GeneratePassKey()
} else if errors.Is(err, encryptor.ErrInvalidCiphertext) {
// Handle corrupted data
log.Println("Data appears to be corrupted")
} else if errors.Is(err, encryptor.ErrKeyTooShort) {
// Handle key size validation errors
log.Println("Key must be at least 16 bytes")
}
π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/encryptor.git
- Create a feature branch:
git checkout -b feature/amazing-feature
- Make your changes and add tests
- Run tests with race detection:
go test -race -v
- Ensure coverage stays above 90%:
go test -cover
- Validate performance with justification for any reduction:
go test -bench=. -benchmem
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- Open a Pull Request
Code Standards
- Follow Effective Go guidelines
- Add tests for new functionality (including race tests if concurrent)
- Maintain test coverage above 90%
- All new code must pass
go test -race
- Update documentation for API changes
- Run
go fmt and go vet before committing
- Optimize for minimal lock duration in concurrent code
Testing Requirements
All pull requests must:
- β
Pass
go test -race without any race conditions
- β
Maintain or improve test coverage
- β
Include benchmarks for performance-critical changes
- β
Pass all existing tests
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Acknowledgments
π Support
β οΈ Security Considerations
When to Use This Library
β
Encrypting data at rest
β
Protecting sensitive configuration
β
Secure file storage
β
Application-level encryption
β
Multi-threaded/concurrent applications
β
Distributed systems requiring shared encryption keys
When NOT to Use This Library
β TLS/SSL connections (use crypto/tls)
β Password hashing (use bcrypt or argon2)
β Digital signatures (use crypto/rsa or crypto/ecdsa)
β End-to-end messaging (use specialized protocols)
Important Security Notes
- Key Management: The security of your encrypted data depends entirely on the security of your keys. Never commit keys to version control.
- Key Rotation: Implement regular key rotation for long-lived applications.
- Compliance: Ensure your use case complies with relevant regulations (GDPR, HIPAA, etc.).
- Threat Model: This library protects data at rest. It does not protect against attacks on the running process.
- Thread Safety: All operations are safe for concurrent use, but key management operations will briefly block other operations.
- Key Export: Exported keys should be handled with the same security precautions as passwords. Use secure channels for transmission and encrypted storage for persistence.
- SetExportedKey vs SetNewPassKey: Never use
SetExportedKey() with raw passwords. It's designed for pre-derived or exported keys only. For passwords, always use SetNewPassKeyFromPassword().
πΊοΈ Roadmap
- Add Argon2 support for password-based key derivation
- Implement key rotation mechanism
- Add streaming encryption for large files
- Support for multiple encryption algorithms
- Built-in key management service integration (AWS KMS, Vault)
- Add context support for cancellable operations
- Key export/import functionality for distributed systems
- Provide CLI tool for file encryption
- 100% thread-safe implementation with comprehensive race testing
π Version History
v1.1.0 (Current)
- β¨ NEW: Key export/import functionality via
ExportPassKey() and SetExportedKey()
- π Support for sharing keys across distributed systems
- π Distinguish between derived keys (
SetNewPassKey) and raw keys (SetExportedKey)
- π Added comprehensive examples for key export/import workflows
- π§ͺ Extended test coverage for key export/import scenarios
- π Updated documentation with key management best practices
- π Added validation for minimum key size (16 bytes for AES-128)
v1.0.1
- β¨ 100% thread-safe code with optimized locking strategy
- π§ͺ Comprehensive race condition test suite (15+ race-specific tests)
- π Thread-safe configuration getters/setters
- π Maintains high performance (~1.6-1.9M ops/sec) despite thread-safety overhead
- π Updated documentation with testing guidelines
- π Fixed race condition in key generation and file writing
- β
All operations verified safe under Go race detector
v1.0.0
- Initial release with AES-256-GCM encryption
- PBKDF2 key derivation
- File-based key management
- Comprehensive test suite
Made with β€οΈ by the Encryptor Team
If you find this library useful, please consider giving it a β on GitHub!