Secure, streaming file encryption and decryption library for Go using AES-256-GCM. Designed for cross-platform use with memory-safe key handling, chunked processing for large files, and support for external libraries to enhance functionality.
Warning
This library is provided "as-is" under the Mozilla Public License 2.0 (see LICENSE for details). While it implements industry-standard cryptographic primitives (AES-256-GCM), it has not undergone independent security audits. For production use, especially in security-critical applications, you should:
- Conduct your own security review or hire a professional security auditor
- Follow the security best practices outlined in this documentation
- Stay updated on security advisories and updates
The author makes no warranties regarding the library's security or fitness for any particular purpose.
- Strong Encryption: AES-256-GCM with authenticated encryption
- Streaming Support: Encrypt/decrypt files of any size without loading into memory
- Cross-Platform: Works on Linux, macOS, and Windows with platform-specific memory locking
- Memory Safety: Secure key handling with explicit memory zeroing
- Progress Tracking: Built-in progress callbacks for long operations
- Context Support: Cancellation and timeout support for all operations
- Modern Key Derivation: Argon2id (recommended) and PBKDF2-HMAC-SHA256 support
- GPU-Resistant: Argon2id provides superior protection against GPU/ASIC attacks
- Installation
- Supported Platforms
- Quick Start
- Usage Examples
- API Reference
- Security Considerations
- Performance
- Documentation
- FAQ
- Contributing
- License
go get github.com/gitrgoliveira/go-fileencryptRequirements:
- Go 1.25 or later
This library works across all major operating systems:
- Linux: Full support with memory locking via
mlock(2) - macOS: Full support with memory locking via
mlock(2) - Windows: Full support (memory locking is no-op)
Memory Locking:
- On Unix-based systems (Linux, macOS), the library uses
mlock()to prevent sensitive data from being swapped to disk - On Windows, memory locking is currently a no-op (not implemented)
- All platforms support secure memory zeroing via
secure.Zero()
File Permissions:
- Unix/macOS: Use
0600permissions for encrypted files (owner read/write only) - Windows: NTFS ACLs apply; consider restricting access to the current user
Performance:
- Performance is consistent across platforms
- Benchmarks shown in this README were conducted on Apple M1 Pro (ARM64)
package main
import (
"context"
"crypto/rand"
"log"
"github.com/gitrgoliveira/go-fileencrypt"
"github.com/gitrgoliveira/go-fileencrypt/secure" // Always import for key zeroing
)
func main() {
// Generate a random 32-byte key
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
log.Fatal(err)
}
defer secure.Zero(key) // Always zero sensitive data
ctx := context.Background()
// Encrypt
err := fileencrypt.EncryptFile(ctx, "document.pdf", "document.pdf.enc", key)
if err != nil {
log.Fatal(err)
}
// Decrypt
err = fileencrypt.DecryptFile(ctx, "document.pdf.enc", "document.pdf", key)
if err != nil {
log.Fatal(err)
}
}package main
import (
"context"
"log"
"github.com/gitrgoliveira/go-fileencrypt"
"github.com/gitrgoliveira/go-fileencrypt/secure"
)
func main() {
password := []byte("your-secure-password")
// Generate salt (store this with your encrypted file!)
salt, err := fileencrypt.GenerateSalt(fileencrypt.DefaultSaltSize)
if err != nil {
log.Fatal(err)
}
// Derive key from password using PBKDF2
key, err := fileencrypt.DeriveKeyPBKDF2(
password,
salt,
fileencrypt.DefaultPBKDF2Iterations, // 600,000 iterations
fileencrypt.DefaultKeySize, // 32 bytes
)
if err != nil {
log.Fatal(err)
}
defer secure.Zero(key) // Always zero sensitive data
ctx := context.Background()
err = fileencrypt.EncryptFile(ctx, "secret.txt", "secret.enc", key)
if err != nil {
log.Fatal(err)
}
}// Encrypt from io.Reader to io.Writer
src := bytes.NewReader(plaintext)
var dst bytes.Buffer
err := fileencrypt.EncryptStream(ctx, src, &dst, key)
if err != nil {
log.Fatal(err)
}
// Decrypt back
encReader := bytes.NewReader(dst.Bytes())
var plainDst bytes.Buffer
err = fileencrypt.DecryptStream(ctx, encReader, &plainDst, key)
if err != nil {
log.Fatal(err)
}chunkOpt, err := fileencrypt.WithChunkSize(1*1024*1024) // 1MB chunks
if err != nil {
// handle invalid chunk size (very large or environment-limited)
// fallback to default chunk size or return error
// For examples we abort on error
log.Fatalf("invalid chunk size: %v", err)
}
err = fileencrypt.EncryptFile(ctx, "large_video.mp4", "large_video.enc", key,
fileencrypt.WithProgress(func(progress float64) {
// progress is a fraction between 0.0 and 1.0
fmt.Printf("\rEncrypting: %.1f%%", progress*100)
}),
chunkOpt,
)ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
err := fileencrypt.EncryptFile(ctx, "source.bin", "encrypted.bin", key)
if err == context.DeadlineExceeded {
log.Println("Encryption timed out")
}For more examples, see the examples/ directory and run them locally:
examples/basic/— Basic encryption/decryptionexamples/with-password/— Password-based encryption (PBKDF2)examples/with-argon2/— Password-based encryption with Argon2idexamples/large-files/— Large files with progress tracking (showsWithChunkSizeand fractional progress usage)
func EncryptFile(ctx context.Context, srcPath, dstPath string, key []byte, opts ...Option) errorEncrypts a file from srcPath to dstPath using the provided 32-byte key.
Options:
WithChunkSize(size int)- Set chunk size (default:DefaultChunkSize= 1MB, allowed range: 1 byte toMaxChunkSize= 10MB).WithProgress(callback func(float64))- Progress callback (receives a fraction between0.0and1.0).
func DecryptFile(ctx context.Context, srcPath, dstPath string, key []byte, opts ...Option) errorDecrypts a file from srcPath to dstPath using the provided key.
func EncryptStream(ctx context.Context, src io.Reader, dst io.Writer, key []byte, opts ...Option) errorEncrypts data from an io.Reader to an io.Writer.
func DecryptStream(ctx context.Context, src io.Reader, dst io.Writer, key []byte, opts ...Option) errorDecrypts data from an io.Reader to an io.Writer.
func DeriveKeyPBKDF2(password, salt []byte, iterations, keyLen int) ([]byte, error)Derives an encryption key from a password using PBKDF2-HMAC-SHA256.
Recommended values:
iterations: 600,000 (OWASP 2023) or minimum 210,000keyLen: 32 bytes for AES-256
func GenerateSalt(size int) ([]byte, error)Generates a cryptographically secure random salt. Recommended size: 32 bytes.
func Zero(b []byte)Securely zeros a byte slice to prevent key material from remaining in memory.
func LockMemory(b []byte) error
func UnlockMemory(b []byte) errorLock/unlock memory pages (uses mlock on Unix/macOS, no-op on Windows).
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Size: 256 bits (32 bytes)
- Nonce: 96 bits (12 bytes), randomly generated per file
- Authentication: 128-bit GCM tag per chunk
- Key Derivation: PBKDF2-HMAC-SHA256 (600,000 iterations default)
Key Management:
- Generate keys using
crypto/rand(cryptographically secure) - Never hardcode keys in source code
- Use
defer secure.Zero(key)to clear key material from memory - Store keys securely (HSM, KMS, or encrypted key storage)
- Use unique keys for different contexts
Password-Based Encryption:
- Use strong passwords (minimum 12 characters, mixed complexity)
- Always generate a unique, random salt per encryption
- Store the salt alongside the encrypted file
- Use at least 600,000 PBKDF2 iterations (OWASP 2023)
File Handling:
- Validate decrypted data integrity before use
- Use secure file permissions (0600 for sensitive files)
- Delete plaintext securely after encryption (consider
shredorsrm) - Handle authentication failures as potential tampering
Production Deployment:
- Run security audits before production use
- Implement proper error handling without leaking sensitive data
- Use context cancellation for long-running operations
- Monitor for security advisories and updates
Tested on Apple M1 Pro:
| Operation | File Size | Throughput | Time |
|---|---|---|---|
| Encryption | 1 MB | ~949 MB/s | ~1.1 ms |
| Encryption | 10 MB | ~1361 MB/s | ~7.7 ms |
| Encryption | 100 MB | ~1039 MB/s | ~101 ms |
| Encryption | 1 GB | ~235 MB/s | ~4.6 s |
| Decryption | 1 MB | ~1260 MB/s | ~0.8 ms |
| Decryption | 10 MB | ~1362 MB/s | ~7.7 ms |
| Decryption | 100 MB | ~1338 MB/s | ~78 ms |
| Decryption | 1 GB | ~800 MB/s | ~1.3 s |
| PBKDF2 (600k iter) | - | - | ~76 ms |
Chunk Size Impact (10MB file):
- 64KB chunks: ~1376 MB/s
- 256KB chunks: ~1478 MB/s
- 1MB chunks: ~1483 MB/s (default, recommended)
- 4MB chunks: ~1409 MB/s
Run benchmarks yourself:
go test -bench=. ./benchmark -benchtime=10s- Header: 20 bytes (12-byte nonce + 8-byte file size)
- Per-chunk: 20 bytes (4-byte size + 16-byte GCM tag)
- Example: 1GB file with 1MB chunks = ~20KB overhead (~0.002%)
- GoDoc - API documentation
- File Format Specification - Detailed file format description
Keys should never be stored in plaintext. Options include:
- Hardware Security Modules (HSM): For production environments
- Key Management Services (KMS): Cloud providers (AWS KMS, Azure Key Vault, etc.)
- Environment Variables: For development (not recommended for production)
- Password-based: Derive from user password with PBKDF2
This library is designed for data at rest (file encryption). For data in transit, use TLS/HTTPS.
The salt must be stored alongside the encrypted file. Common approaches:
- Prepend salt to encrypted file:
[32 bytes salt][encrypted data] - Store in separate metadata file:
file.encandfile.enc.salt - Include in file header (requires custom format)
Yes. Each encryption/decryption operation is independent and can run concurrently. However, do not share keys across goroutines without proper synchronization (use separate key copies).
Decryption failures typically indicate:
- Wrong key (authentication failed)
- File corruption or tampering
- Truncated file
Always treat authentication failures as potential security issues.
Yes. Each encryption uses a unique random nonce, so the output will be different each time. However, for better security, consider using different keys for different files.
Post-quantum cryptography support may be considered in future versions
You can override the default chunk size limit (10MB) by setting the FILEENCRYPT_CHUNKSIZE_LIMIT environment variable. This variable accepts human-readable file sizes, such as 10MB, 1GB, etc.
Example:
export FILEENCRYPT_CHUNKSIZE_LIMIT=50MBContributions are welcome! Please:
- Open an issue to discuss proposed changes
- Follow existing code style and conventions
- Add tests for new functionality
- Update documentation as needed
- Run
make validate-allbefore submitting
See CONTRIBUTING.md for details.
Mozilla Public License 2.0 - see LICENSE for details.