Detailed security specifications and cryptographic design of go-file-crypto.
This library uses AES-256-GCM (Galois/Counter Mode) exclusively.
| Property | Value |
|---|---|
| Algorithm | AES-GCM |
| Key Size | 256 bits (32 bytes) |
| IV/Nonce Size | 96 bits (12 bytes) |
| Authentication Tag | 128 bits (16 bytes) |
Why AES-256-GCM?
- Authenticated Encryption - Provides both confidentiality and integrity
- Hardware Acceleration - AES-NI support in modern CPUs
- NIST Recommended - Standardized in NIST SP 800-38D
- Cross-Platform - Identical behavior in Go (
crypto/aes,crypto/cipher) and browsers (Web Crypto API)
For password-based encryption, keys are derived using PBKDF2.
| Property | Value |
|---|---|
| Algorithm | PBKDF2 |
| Hash Function | SHA-256 |
| Iterations | 100,000 |
| Salt Size | 128 bits (16 bytes) |
| Output Key | 256 bits (32 bytes) |
derived_key = PBKDF2(password, salt, 100000, SHA-256, 256)
Why 100,000 iterations?
- Balances security and performance
- Provides approximately 0.1-0.5 seconds of computation
- Resistant to brute-force attacks on modern hardware
- Matches the browser implementation for compatibility
Keyfile-based encryption uses the key directly without derivation.
| Property | Value |
|---|---|
| Key Size | 256 bits (32 bytes) |
| Encoding | Base64 |
| Source | crypto/rand (cryptographically secure) |
┌─────────────┬──────────────┬─────────────┬─────────────────────────┐
│ Marker (1B) │ Salt (16B) │ IV (12B) │ Ciphertext + Tag (16B) │
└─────────────┴──────────────┴─────────────┴─────────────────────────┘
| Field | Size | Description |
|---|---|---|
| Marker | 1 byte | 0x01 - identifies password encryption |
| Salt | 16 bytes | Random salt for PBKDF2 |
| IV | 12 bytes | Random initialization vector |
| Ciphertext | Variable | AES-GCM encrypted data |
| Auth Tag | 16 bytes | GCM authentication tag (appended to ciphertext) |
Minimum Size: 45 bytes (1 + 16 + 12 + 16)
┌─────────────┬─────────────┬─────────────────────────┐
│ Marker (1B) │ IV (12B) │ Ciphertext + Tag (16B) │
└─────────────┴─────────────┴─────────────────────────┘
| Field | Size | Description |
|---|---|---|
| Marker | 1 byte | 0x02 - identifies keyfile encryption |
| IV | 12 bytes | Random initialization vector |
| Ciphertext | Variable | AES-GCM encrypted data |
| Auth Tag | 16 bytes | GCM authentication tag (appended to ciphertext) |
Minimum Size: 29 bytes (1 + 12 + 16)
┌─────────────┬────────────┬──────────────┬──────────────┬──────────────┬─────────┐
│ Marker (1B) │ Version(1B)│ ChunkSize(4B)│ Salt (16B) │ BaseIV (12B) │ Chunks │
└─────────────┴────────────┴──────────────┴──────────────┴──────────────┴─────────┘
| Field | Size | Description |
|---|---|---|
| Marker | 1 byte | 0x11 - streaming password encryption |
| Version | 1 byte | Format version (0x01) |
| ChunkSize | 4 bytes | Chunk size in bytes (little-endian) |
| Salt | 16 bytes | Random salt for PBKDF2 |
| BaseIV | 12 bytes | Base initialization vector |
| Chunks | Variable | Encrypted chunks |
Header Size: 34 bytes
┌─────────────┬────────────┬──────────────┬──────────────┬─────────┐
│ Marker (1B) │ Version(1B)│ ChunkSize(4B)│ BaseIV (12B) │ Chunks │
└─────────────┴────────────┴──────────────┴──────────────┴─────────┘
| Field | Size | Description |
|---|---|---|
| Marker | 1 byte | 0x12 - streaming keyfile encryption |
| Version | 1 byte | Format version (0x01) |
| ChunkSize | 4 bytes | Chunk size in bytes (little-endian) |
| BaseIV | 12 bytes | Base initialization vector |
| Chunks | Variable | Encrypted chunks |
Header Size: 18 bytes
┌───────────────────┬─────────────────────────────────┐
│ ChunkLength (4B) │ EncryptedData + AuthTag (16B) │
└───────────────────┴─────────────────────────────────┘
| Field | Size | Description |
|---|---|---|
| ChunkLength | 4 bytes | Encrypted chunk size (little-endian) |
| EncryptedData | Variable | AES-GCM encrypted chunk data |
| Auth Tag | 16 bytes | Per-chunk authentication tag |
Each chunk uses a unique IV derived from the base IV:
For chunk index i (0-based):
1. Copy baseIV (12 bytes)
2. Read last 4 bytes as little-endian uint32
3. XOR with chunk index i
4. Write back as little-endian uint32
This ensures:
- No IV reuse across chunks
- Deterministic IV generation for each chunk index
- Compatibility with the browser implementation (
DataView getUint32/setUint32with LE)
{
"version": 1,
"algorithm": "AES-256-GCM",
"key": "<base64-encoded-32-bytes>",
"createdAt": "2026-01-01T00:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
version |
int |
Format version (always 1) |
algorithm |
string |
Algorithm identifier ("AES-256-GCM") |
key |
string |
Base64-encoded 256-bit key |
createdAt |
string |
ISO 8601 / RFC3339 timestamp |
hash = hex(SHA-256(base64_decode(key)))
- Base64-decode
keyto raw 32 bytes - Compute SHA-256 hash
- Return lowercase hex string
- AES-256 - 256-bit keys provide 2^256 possible key combinations
- Unique IVs - Random IVs prevent identical plaintexts from producing identical ciphertexts
- Streaming IVs - Per-chunk IV derivation prevents IV reuse
- GCM Authentication - 128-bit authentication tag detects any tampering
- Per-Chunk Authentication - Streaming encryption authenticates each chunk independently
- Early Failure - Corrupted data detected immediately during decryption
- PBKDF2 Hardening - 100,000 iterations slow brute-force attacks
- Random Salt - Unique salt per encryption prevents rainbow table attacks
- Cryptographic RNG - All random values from
crypto/rand
// Good: Long, random passwords
password := "correct-horse-battery-staple-42!"
// Better: Use keyfiles for automated systems
kf, _ := filecrypto.GenerateKeyFile()- Separate Storage - Keep keyfiles separate from encrypted data
- Backup - Maintain secure backups of keyfiles
- Access Control - Set restrictive file permissions (
0600) - Secure Transfer - Use encrypted channels to share keyfiles
// Always handle errors specifically
plain, err := filecrypto.Decrypt(data, opts)
if err != nil {
if filecrypto.IsCryptoErrorCode(err, filecrypto.ErrInvalidPassword) {
// Don't reveal whether password or data is wrong to end users
log.Println("Decryption failed")
}
}
// Clear sensitive data when done
password = ""
keyData = ""- Garbage Collection - No guaranteed secure memory clearing (GC decides when to free)
- String Immutability - Password strings cannot be zeroed after use
- Side Channels - Timing attacks possible in some scenarios
- Key Management - Use dedicated HSMs or vault systems for production key storage
- Multi-Party Encryption - No support for shared keys or threshold encryption
- Forward Secrecy - Same key encrypts all files
This library implements cryptographic primitives following:
- NIST SP 800-38D - AES-GCM specification
- NIST SP 800-132 - PBKDF2 recommendations
- OWASP Guidelines - Secure cryptographic practices
For compliance-critical applications, consult with security professionals to ensure the implementation meets your specific requirements.
The Profile struct allows customizing cryptographic parameters. Zero-value fields default to browser-compatible values.
type Profile struct {
SaltLength int // Default: 16
IVLength int // Default: 12
KeyLengthBytes int // Default: 32
AuthTagLength int // Default: 16
PBKDF2Iterations int // Default: 100,000
MarkerPassword byte // Default: 0x01
MarkerKeyfile byte // Default: 0x02
MarkerPasswordStream byte // Default: 0x11
MarkerKeyfileStream byte // Default: 0x12
StreamVersion byte // Default: 0x01
DefaultChunkSize int // Default: 65,536
}Warning: Changing profile values breaks compatibility with the browser library. Only use custom profiles when you control both encryption and decryption endpoints.