Encrypt files with envelope encryption, key rotation, and audit logging — no server required
Project description
encryptbox
Encrypt files with key rotation — no server required
Envelope encryption + key rotation + audit log in a single CLI
Why This Exists
You need to encrypt config files, backups, or secrets. Your options are:
- age — simple, but no key rotation. Rotate the key = re-encrypt everything.
- AWS KMS / HashiCorp Vault — powerful, but you need infrastructure, IAM policies, network access.
- GPG — works, but the UX is from 1999 and key management is painful.
encryptbox gives you envelope encryption (the same pattern AWS KMS uses) in a local CLI. Rotate your master key without touching encrypted files. Every operation gets logged in a tamper-evident audit trail. No server, no cloud, no subscriptions.
How It Works
Envelope Encryption
┌─────────────────────────────────────────────────┐
│ Your File │
│ │
│ plaintext ──► AES-256-GCM ──► ciphertext │
│ ▲ │
│ │ │
│ data key (random) │
│ │ │
│ X25519 wrap ──► wrapped data key │
│ ▲ │
│ │ │
│ master key (your keypair) │
└─────────────────────────────────────────────────┘
- A random data key encrypts your file with AES-256-GCM
- The data key is wrapped (encrypted) with your X25519 public key
- Both the wrapped key and ciphertext go into a
.eboxfile
To rotate: unwrap the data key with the old master, re-wrap with the new master. The encrypted file data stays untouched.
Audit Chain
Every operation (encrypt, decrypt, rotate, keygen) is logged with an HMAC chain:
entry[0].hmac ──► entry[1].prev_hmac ──► entry[1].hmac ──► entry[2].prev_hmac ──► ...
Tamper with any entry and the chain breaks. Delete an entry and the chain breaks. The verification is O(n) and deterministic.
Installation
pip install encryptbox
Or from source:
git clone https://github.com/JSLEEKR/encryptbox.git
cd encryptbox
pip install -e ".[dev]"
Requirements
- Python 3.10+
- cryptography (pyca/cryptography)
- click (CLI framework)
Quick Start
1. Initialize
encryptbox init
# [OK] Keystore initialized
# Keystore created at: .encryptbox
# Run 'encryptbox keygen' to generate your first keypair.
2. Generate a keypair
encryptbox keygen --label "my-laptop"
# [OK] Generated key: a1b2c3d4e5f6a7b8
# Label: my-laptop
# Key ID: a1b2c3d4e5f6a7b8
3. Encrypt a file
encryptbox encrypt secrets.env
# [OK] Encrypted: secrets.env
# Output: secrets.env.ebox
4. Decrypt it back
encryptbox decrypt secrets.env.ebox
# [OK] Decrypted: secrets.env.ebox
# Output: secrets.env
5. Rotate keys
encryptbox rotate
# [OK] Key rotated
# Old key: a1b2c3d4e5f6a7b8 (deactivated)
# New key: f8e7d6c5b4a39281 (active)
# Files re-wrapped: 3
6. Check the audit log
encryptbox audit
# [2026-03-28 14:00:01] INIT (by alice)
# [2026-03-28 14:00:02] KEYGEN key=a1b2c3d4e5f6a7b8 (by alice)
# [2026-03-28 14:00:05] ENCRYPT key=a1b2c3d4e5f6a7b8 secrets.env (by alice)
# [2026-03-28 14:01:00] ROTATE key=f8e7d6c5b4a39281 (by alice)
CLI Reference
Core Commands
| Command | Description |
|---|---|
encryptbox init |
Initialize the keystore in the current directory |
encryptbox keygen |
Generate a new X25519 keypair |
encryptbox encrypt <file> |
Encrypt a file with envelope encryption |
encryptbox decrypt <file> |
Decrypt a .ebox file |
encryptbox rotate |
Rotate master key and re-wrap all .ebox files |
encryptbox audit |
Show the audit log |
encryptbox status |
Show keystore status and health |
Batch Commands
| Command | Description |
|---|---|
encryptbox encrypt-dir <dir> |
Encrypt all files in a directory |
encryptbox decrypt-dir <dir> |
Decrypt all .ebox files in a directory |
Key Management
| Command | Description |
|---|---|
encryptbox export-key |
Export a public key as hex |
encryptbox import-key <hex> --id <name> |
Import a public key |
Options
# Encrypt with custom output path
encryptbox encrypt secret.txt -o /backup/secret.enc
# Encrypt for multiple recipients
encryptbox encrypt secret.txt -k key1 -k key2
# Remove original after encryption
encryptbox encrypt secret.txt --remove
# Encrypt only .env files in a directory
encryptbox encrypt-dir ./configs -p "*.env"
# Verify audit log integrity
encryptbox audit --verify
# Show last 5 audit entries
encryptbox audit --last 5
# Output audit as JSON
encryptbox audit --json-output
# Use a specific key for decryption
encryptbox decrypt secret.txt.ebox -k a1b2c3d4e5f6a7b8
# Specify project root
encryptbox --root /path/to/project encrypt file.txt
Multi-Recipient Encryption
Encrypt a file so multiple people can decrypt it:
# Alice generates a key and shares her public key
encryptbox keygen --label alice
encryptbox export-key
# Public key: 7a3f...
# Bob imports Alice's public key
encryptbox import-key 7a3f... --id alice-pub
# Bob encrypts for both himself and Alice
encryptbox encrypt shared-secret.txt -k bob-key-id -k alice-pub
Both Alice and Bob can decrypt the file independently with their own private keys.
Key Rotation
Key rotation re-wraps data keys without re-encrypting file data:
encryptbox rotate
What happens:
- A new X25519 keypair is generated
- All
.eboxfiles in the project tree are found - Each file's data key is unwrapped with the old key and re-wrapped with the new key
- The old key is marked as inactive
- The operation is logged in the audit trail
The encrypted data itself is never touched during rotation. This means:
- Rotation is fast (no I/O proportional to file sizes)
- File integrity is preserved
- You can verify by decrypting after rotation
Audit Log
Every operation is logged with a tamper-evident HMAC chain:
# View all entries
encryptbox audit
# Verify chain integrity
encryptbox audit --verify
# [OK] All 42 entries verified
# Export as JSON for analysis
encryptbox audit --json-output > audit.json
What Gets Logged
| Field | Description |
|---|---|
timestamp |
Unix timestamp |
action |
init, keygen, encrypt, decrypt, rotate |
key_id |
The key used |
file_path |
The file involved |
file_hash |
SHA-256 of the file |
user |
System username |
prev_hmac |
HMAC of the previous entry |
entry_hmac |
HMAC of this entry |
Tamper Detection
The audit log uses HMAC-SHA256 chaining. Each entry's HMAC includes the previous entry's HMAC, forming a hash chain. If any entry is modified, deleted, or inserted, audit --verify will detect it.
File Format
Encrypted files use the .ebox binary format:
EBOX (4 bytes) — magic bytes
VERSION (1 byte) — format version (currently 1)
NUM_RECIPIENTS (2 bytes) — number of recipient key entries
For each recipient:
KEY_ID_LEN (1 byte) — length of key ID
KEY_ID (variable) — key ID string
WRAPPED_LEN (4 bytes) — length of wrapped key data
WRAPPED_KEY (variable) — ephemeral public key + encrypted data key
ORIGINAL_HASH (32 bytes) — SHA-256 of original plaintext
ENCRYPTED_DATA (variable) — AES-256-GCM nonce + ciphertext
Architecture
encryptbox/
├── crypto.py # AES-256-GCM encryption, X25519 key wrapping
├── keystore.py # On-disk key management, metadata
├── envelope.py # Envelope encryption, .ebox format, key rotation
├── audit.py # HMAC-chained append-only audit log
├── operations.py # High-level orchestration (encrypt/decrypt/rotate)
└── cli.py # Click CLI interface
Module Responsibilities
| Module | Responsibility |
|---|---|
crypto |
Low-level crypto: AES-256-GCM encrypt/decrypt, X25519 key wrap/unwrap, HKDF key derivation |
keystore |
Key lifecycle: generate, store, load, list, deactivate, import/export keypairs |
envelope |
Envelope encryption: combine crypto + multi-recipient + serialization into .ebox format |
audit |
Append-only log with HMAC chain for tamper detection |
operations |
Orchestrate all modules: file I/O, batch ops, rotation, status |
cli |
User-facing CLI commands via Click |
Security Properties
- AES-256-GCM — authenticated encryption with 96-bit nonces (CSPRNG)
- X25519 — Curve25519 key exchange for key wrapping
- HKDF-SHA256 — key derivation from shared secrets
- CSPRNG — all random bytes from
os.urandom - No custom crypto — everything from pyca/cryptography
Development
git clone https://github.com/JSLEEKR/encryptbox.git
cd encryptbox
pip install -e ".[dev]"
pytest
Running Tests
# All tests
pytest
# With verbose output
pytest -v
# With coverage
pytest --cov=encryptbox --cov-report=term-missing
# Specific module
pytest tests/test_crypto.py
Test Coverage
| Module | Tests | Coverage |
|---|---|---|
crypto |
27 | encrypt/decrypt, key wrapping, serialization, edge cases |
keystore |
28 | init, keygen, retrieval, metadata, import/export |
envelope |
17 | envelope encrypt/decrypt, multi-recipient, rotation, serialization |
audit |
22 | logging, chain verification, tamper detection, persistence |
operations |
33 | file encrypt/decrypt, rotation, batch ops, status, audit integration |
cli |
20 | all CLI commands end-to-end |
| Total | 157 |
Comparison
| Feature | encryptbox | age | GPG | AWS KMS |
|---|---|---|---|---|
| Envelope encryption | Yes | No | No | Yes |
| Key rotation (no re-encrypt) | Yes | No | No | Yes |
| Audit log | Yes | No | No | CloudTrail |
| Multi-recipient | Yes | Yes | Yes | Via policies |
| No server required | Yes | Yes | Yes | No |
| Tamper-evident logging | Yes | No | No | No |
| Binary format | .ebox |
.age |
.gpg |
N/A |
| CLI UX | Modern (Click) | Good | Legacy | AWS CLI |
Security Considerations
- Key storage: Private keys are stored as raw bytes on disk. Use appropriate file permissions.
- Memory: Data keys exist in memory during encrypt/decrypt operations.
- Not a replacement for KMS: For production secrets management at scale, use a proper KMS. encryptbox is for developer workflows, local encryption, and backup protection.
- Audit log secret: The HMAC secret for the audit chain is stored in
.encryptbox/audit.secret. Protect this file.
License
MIT — JSLEEKR
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file encryptbox-1.0.0.tar.gz.
File metadata
- Download URL: encryptbox-1.0.0.tar.gz
- Upload date:
- Size: 33.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c655fcb11e9216a974be784180456bbf7f7c79e7b383449220fbf6d266faaa8
|
|
| MD5 |
f9ce4d27042258b2b9361d9e65e2be47
|
|
| BLAKE2b-256 |
7516cb5663b5aeca19b09244b1326cbee80165d9efee4fa4afc768d55c370dea
|
File details
Details for the file encryptbox-1.0.0-py3-none-any.whl.
File metadata
- Download URL: encryptbox-1.0.0-py3-none-any.whl
- Upload date:
- Size: 21.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96e68d220e99b6e673d442061c4c6d8724444409bad0369504f4fee0fae2325d
|
|
| MD5 |
43384c7280a0e0e2b84a32afc86a4fbc
|
|
| BLAKE2b-256 |
4af34ea2b8465dc9fd3222339d25e07a73c27027be86da9e5bde250423000650
|