A high-performance file encryption and compression utility using AES-256 encryption, Zstandard compression, and optional Argon2id password-based key derivation.
# 1. Download dependencies
go mod tidy
# 2. Build the tool
go build -o qcompress qcompressstream_optimized.go
# 3a. Encrypt with a password (easiest)
./qcompress -mode=encrypt -in=myfile.txt -out=myfile.enc -password="my secret passphrase"
# 3b. Or encrypt with a raw key (generated once and saved)
KEY=$(openssl rand -hex 32)
export QCOMPRESS_KEY=$KEY # avoids key appearing in shell history
./qcompress -mode=encrypt -in=myfile.txt -out=myfile.enc
# 4. Decrypt
./qcompress -mode=decrypt -in=myfile.enc -out=myfile_recovered.txt -password="my secret passphrase"
# or with key:
./qcompress -mode=decrypt -in=myfile.enc -out=myfile_recovered.txt| File | Purpose |
|---|---|
qcompressstream_optimized.go |
Main program |
go.mod |
Go module & dependencies |
create_realistic_files.sh |
Generate test files |
benchmark_realistic.sh |
Benchmark performance |
- β AES-256-CTR encryption β Fast, parallelizable, no padding oracle
- β HMAC-SHA256 authentication β Authenticate-then-decrypt; constant-time comparison
- β Zstandard compression β 3β10Γ faster than gzip with similar ratios
- β Multi-threaded β Uses all CPU cores for both compression and decompression
- β Password mode β Argon2id key derivation (OWASP-recommended parameters)
- β
Raw key mode β 32-byte key via flag, key file, or
QCOMPRESS_KEYenv var - β Progress bar β Live byte-rate progress for both encrypt and decrypt
- β Stdin/stdout support β Encrypt from stdin; pipe-friendly output
- β Clean failure β Partial output files are always deleted on error
- β Versioned file format β Magic header enables future format evolution
- β fsync before exit β Output is flushed to disk before the process reports success
There are three ways to supply a key. Choose exactly one per operation.
./qcompress -mode=encrypt -in=file.txt -out=file.enc -password="my passphrase"
./qcompress -mode=decrypt -in=file.enc -out=file.txt -password="my passphrase"- A random 32-byte salt is generated at encryption time and stored in the file header.
- On decryption the salt is read from the header automatically β you only need the passphrase.
- Key derivation takes ~0.5 s (Argon2id, 64 MB memory). Progress is printed to stderr.
- Best for: human-facing use, backups, any situation where you don't want to manage a key file.
KEY=$(openssl rand -hex 32)
./qcompress -mode=encrypt -in=file.txt -out=file.enc -key=$KEY
./qcompress -mode=decrypt -in=file.enc -out=file.txt -key=$KEY
β οΈ The key appears in shell history. Prefer-keyfileorQCOMPRESS_KEY.
export QCOMPRESS_KEY=$(openssl rand -hex 32)
./qcompress -mode=encrypt -in=file.txt -out=file.enc
./qcompress -mode=decrypt -in=file.enc -out=file.txt# Generate a binary key file
openssl rand 32 > mykey.bin
chmod 600 mykey.bin
./qcompress -mode=encrypt -in=file.txt -out=file.enc -keyfile=mykey.bin
./qcompress -mode=decrypt -in=file.enc -out=file.txt -keyfile=mykey.binThe key file may contain either 32 raw bytes or a 64-character hex string.
Usage: qcompress -mode=encrypt|decrypt -in=<file> -out=<file> [key option] [-level=1-4]
Flags:
-mode encrypt or decrypt (required)
-in Input file path; use - for stdin (encrypt only)
-out Output file path; use - for stdout; default: stdout
-key 32-byte key as 64 hex chars
-keyfile Path to key file (32 raw bytes or 64-char hex string)
-password Passphrase β Argon2id key derivation
-level Zstd compression level 1-4 (encrypt only; default 3)
Environment:
QCOMPRESS_KEY Hex key (equivalent to -key but not visible in shell history)| Level | Speed | Ratio | Best For |
|---|---|---|---|
| 1 | Fastest | Good | Large files (>1 GB), real-time use |
| 2 | Fast | Better | General purpose |
| 3 | Good | Very Good | Recommended (default) |
| 4 | Slower | Best | Archival storage, small files |
| File Type | Compression | Example |
|---|---|---|
| XML Config | 99.4% | 10 MB β 58 KB |
| Source Code | 99.8% | 4.6 MB β 9.5 KB |
| HTML Pages | 97.4% | 8.3 MB β 221 KB |
| JSON APIs | 92.3% | 16 MB β 1.2 MB |
| SQL Dumps | 93.1% | 7.8 MB β 552 KB |
| Server Logs | 82.0% | 14 MB β 2.5 MB |
| CSV Data | 69.8% | 9.8 MB β 3.0 MB |
| Plain Text | 71.1% | 2.2 MB β 645 KB |
| Binary/Encrypted | 0% | No compression (expected) |
| File Size | Encryption | Decryption | Total Speedup |
|---|---|---|---|
| 10 MB | 3β4Γ faster | 4β5Γ faster | ~4Γ |
| 100 MB | 4β5Γ faster | 5β7Γ faster | ~5Γ |
| 1 GB | 5β6Γ faster | 7β10Γ faster | ~7Γ |
- Encryption: AES-256-CTR β parallelizable, no padding oracle vulnerability
- Authentication: HMAC-SHA256 β Encrypt-then-MAC construction, constant-time comparison
- Key derivation: Argon2id (1 pass, 64 MB memory, 4 threads, 32-byte output) β OWASP minimums
- IV: Random 16-byte IV generated per file; stored in header
- Salt: Random 32-byte salt generated per encryption (password mode); stored in header
[Magic "QCS1" β 4 bytes]
[Flags β 1 byte: 0x00 = raw key, 0x01 = password/Argon2id]
[Argon2 Salt β 32 bytes, present only when flags = 0x01]
[IV β 16 bytes]
[AES-256-CTR( zstd( plaintext ) ) β variable length]
[HMAC-SHA256 β 32 bytes]The HMAC covers all bytes from the magic header through to the end of the ciphertext, providing full authenticated encryption with associated data (AEAD) semantics. The MAC is verified before any decryption is attempted.
| Method | Shell History | /proc Exposure |
Recommended |
|---|---|---|---|
-password |
β Okay for personal use | ||
-key |
β Exposed | β Exposed | |
QCOMPRESS_KEY |
β Safe | β Good | |
-keyfile |
β Safe | β Safe | β Best for automation |
For production or CI/CD pipelines, use -keyfile or a secrets manager that injects QCOMPRESS_KEY.
- Go 1.21 or higher
- Git
# Download dependencies
go mod tidy
# Build
go build -o qcompress qcompressstream_optimized.go
# Verify it works
echo "hello world" | ./qcompress -mode=encrypt -in=- -out=/tmp/test.enc -password="test"
./qcompress -mode=decrypt -in=/tmp/test.enc -out=- -password="test"| Package | Purpose |
|---|---|
github.com/klauspost/compress/zstd |
High-performance Zstandard compression |
github.com/schollz/progressbar/v3 |
Live progress bar output to stderr |
golang.org/x/crypto/argon2 |
Argon2id key derivation |
./create_realistic_files.shCreates 12 different file types (~10 MB each):
- Highly compressible: server.log, api_response.json, sales_data.csv, config.xml, article.txt, database.sql, source_code.js, webpage.html
- Poorly compressible: binary.bin, encrypted.dat, photo.jpg, archive.zip
../benchmark_realistic.shGreat fits:
- π Log file archival (80β95% compression)
- πΎ Database backups (75β90% compression)
- π API response caching (85β95% compression)
- π Data exports (CSV, JSON, XML)
- π» Source code backups
- π Secure file transfers
Not recommended for:
- β Already compressed files (ZIP, RAR, 7Z, tar.gz)
- β Media files (JPG, PNG, MP4, MP3)
- β Files requiring random access or partial decryption
Encrypt: plaintext β zstd β AES-256-CTR ββ¬βββ output file
ββββ HMAC accumulator β append MAC
Decrypt: Read + verify HMAC first
β AES-256-CTR β zstd β plaintext outputCompression runs before encryption because encryption output is statistically random and incompressible. The HMAC is computed and verified before any decryption output is written, preventing decryption of tampered ciphertext.
| Area | Original | This Version |
|---|---|---|
| Key input | CLI flag only (history risk) | Flag, file, env var, or password |
| Password support | β None | β Argon2id derivation |
| Failed output cleanup | β Partial file left | β Deleted on any error |
| Progress reporting | β Silent | β Live progress bar |
| File format | No header | β Magic + versioned flags |
| Disk flush | β No fsync | β fsync before exit |
| Stdin support | β No | β
-in=- for encrypt |
| Error messages | Generic | β Actionable descriptions |
The file was not encrypted by qcompress, or is corrupted. It may have been created by the old version of this tool (which had no file header) β in that case, re-encrypt using the old binary.
The .enc file was encrypted in password mode. Supply -password= instead of -key=.
The .enc file was encrypted in raw key mode. Supply -key=, -keyfile=, or QCOMPRESS_KEY.
Either the wrong key/password was supplied, or the file has been modified or corrupted since encryption. Do not trust its contents.
The value of -key or QCOMPRESS_KEY must be exactly 64 hexadecimal characters (representing 32 bytes). Generate one with openssl rand -hex 32.
Expected. Random and already-compressed data has no redundancy for zstd to exploit. See the performance table above.
- Verify your Go version:
go version(needs 1.21+) - Ensure internet access to
proxy.golang.org - Try
GOPROXY=direct go mod tidy
- Never lose your key or passphrase β there is no recovery mechanism
- Store keys in a password manager or key vault (e.g. 1Password, HashiCorp Vault, AWS Secrets Manager)
- Each file gets a unique random IV (and unique salt in password mode); encrypted copies of the same file will differ
- For long-term archival, store the key alongside a printed copy of this README so future-you knows the tool and format
Demonstration project. Use at your own risk.
- Zstandard by Facebook/Meta; Go implementation by Klaus Post (
klauspost/compress) - Progress bar by schollz (
schollz/progressbar) - Argon2 by the Password Hashing Competition; Go wrapper by the Go team (
golang.org/x/crypto)