GUN-101
A simple, secure file encryption tool using AES-256-GCM and Argon2id.
Overview
GUN-101 encrypts files with authenticated encryption, providing confidentiality and integrity. It supports two modes:
- Password-only: Encryption key derived from password alone
- Password + keyfile: Two-factor protection requiring both password and a separate keyfile
The design prioritizes correctness and transparency over complexity or marketing claims.
The tool uses a versioned container format (currently v2.1) to allow for future improvements while maintaining backward compatibility with v2.0 encrypted files.
Cryptographic Primitives
- Key Derivation: Argon2id (memory-hard, winner of the Password Hashing Competition 2015)
- Encryption: AES-256-GCM (authenticated encryption with associated data, NIST SP 800-38D)
- Salt: 32 bytes random per encryption
- Nonce: 12 bytes random per encryption (GCM recommended size)
- Key Length: 32 bytes (256 bits)
Security Properties
What GUN-101 does provide:
- Confidentiality: Passive attackers cannot decrypt without the password (and keyfile, if used)
- Integrity: Any tampering with the encrypted container is detected and rejected before returning plaintext
- Brute-force resistance: Argon2id maximizes the cost of guessing passwords (memory-hard, GPU-resistant)
- Two-factor protection: When a keyfile is used and stored separately, an attacker who knows the password still cannot decrypt without the physical keyfile
What GUN-101 does NOT provide:
- Protection against malware on the encryption/decryption machine
- Protection if both the encrypted file and keyfile are stolen (two-factor mode only)
- Protection against side-channel attacks, coercion, or future cryptographic breaks
- Secure deletion of plaintext or temporary files
Usage
Installation
pip install gun101
Generate a keyfile (for two-factor mode)
gun101 generate-keyfile /path/to/keyfile
This creates a 32-byte random keyfile and prints its SHA-256 fingerprint. Store the keyfile on a separate device (e.g., USB drive) and record the fingerprint.
Encrypt a file
gun101 encrypt secrets.pdf --keyfile /path/to/keyfile
Encrypts secrets.pdf to secrets.pdf.gun101. Omit --keyfile for password-only mode.
Password requirements: Must be at least 10 characters long and contain at least one uppercase letter, one lowercase letter, one digit, and one special character.
Password input: Password can be entered via interactive prompt or provided through the GUN101_PASSWORD environment variable (see Security Design for trade-offs).
Decrypt a file
gun101 decrypt secrets.pdf.gun101 --keyfile /path/to/keyfile
Decrypts to secrets.pdf (removes .gun101 extension). Omit --keyfile for password-only mode.
Password requirements: Must be at least 10 characters long and contain at least one uppercase letter, one lowercase letter, one digit, and one special character.
Password input: Password can be entered via interactive prompt or provided through the GUN101_PASSWORD environment variable (see Security Design for trade-offs).
Verify a keyfile fingerprint
gun101 keyfile-fingerprint /path/to/keyfile
Prints the SHA-256 fingerprint to confirm you have the correct keyfile.
Command Reference
gun101 encrypt <file> [--keyfile <path>] [--output <path>]
Encrypts <file>. If --output not given, writes to <file>.gun101
gun101 decrypt <file> [--keyfile <path>] [--output <path>]
Decrypts <file>. If --output not given, strips .gun101 extension or appends .decrypted
gun101 generate-keyfile <path>
Generates a keyfile at <path>. Prints fingerprint after generation.
gun101 keyfile-fingerprint <path>
Prints the SHA-256 fingerprint of a keyfile.
Design Decisions
Why Argon2id?
- Resists GPU/ASIC cracking via high memory usage
- Resists side-channel attacks via data-independent memory access
- Recommended by OWASP and NIST SP 800-63B for password hashing
Password Composition Rules
GUN-101 enforces a password policy requiring at least 10 characters with uppercase, lowercase, digit, and special character. While NIST 800-63B de-emphasizes composition rules for online authentication (where rate limiting applies), GUN-101 retains them for the following reasons:
- Offline attack scenario: Encryption tools face offline brute-force attacks where rate limiting cannot be applied
- Entropy enhancement: Composition rules increase password entropy, making brute-force attacks more expensive
- User familiarity: Many users are accustomed to these rules and they provide a baseline strength guarantee
- Compatibility with Argon2id: Combined with memory-hard Argon2id, this provides strong protection against guessing attacks
Users seeking maximum security should consider using longer passphrases (14+ characters) that meet these requirements, or randomly generated passwords of sufficient length.
Why AES-256-GCM?
- Provides both confidentiality and integrity (authenticated encryption)
- Eliminates padding oracle vulnerabilities present in CBC mode
- Standardized and widely vetted
Key Concatenation
The keyfile bytes are appended to the password UTF-8 bytes before Argon2id input. This ensures the derived key depends on both factors.
Error Handling
All errors produce generic messages (e.g., "Decryption failed") to avoid leaking information about what went wrong.
Limitations
- No perfect memory wiping: Python's garbage collection prevents guaranteed key erasure from memory
- Password strength enforcement: Passwords must be at least 10 characters long and contain uppercase, lowercase, digit, and special character
- No forward secrecy: Compromised key reveals all past messages encrypted with it
- No deniability: Encrypted files are identifiable by their structure
Dependencies
- Python >= 3.9
- argon2-cffi >= 23.1.0
- cryptography >= 42.0.2
Reproducible Builds
For security-critical applications, exact dependency versions should be pinned to prevent supply chain attacks and ensure reproducible builds. A requirements.lock file is provided with exact versions and cryptographic hashes:
pip install --require-hashes -r requirements.lock
This lockfile includes:
- argon2-cffi==25.1.0
- cryptography==50.0.0
Testing
Run the test suite with:
pytest tests/ -v
License
MIT License - see LICENSE file.
Warning
This software is provided as-is without warranty. Use at your own risk. The author is not liable for any data loss or security breach resulting from the use or misuse of this software.
Remember: Encryption is only as strong as your password and your ability to keep the keyfile (if used) secure. No tool can protect against a compromised endpoint or a coerced user.
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 gun101-2.1.0.tar.gz.
File metadata
- Download URL: gun101-2.1.0.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3be8295405b0887ae98f111988085716557b7a9f792d327fa67d444e185ed1af
|
|
| MD5 |
548d42327dec0477d30945266216d7b3
|
|
| BLAKE2b-256 |
5d909e4168d7d95caf6f15eeb18e0c3acdc0d8763e0996c64ad147217498319e
|
Provenance
The following attestation bundles were made for gun101-2.1.0.tar.gz:
Publisher:
publish.yml on dialga-cmd/GUN101
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gun101-2.1.0.tar.gz -
Subject digest:
3be8295405b0887ae98f111988085716557b7a9f792d327fa67d444e185ed1af - Sigstore transparency entry: 2391081589
- Sigstore integration time:
-
Permalink:
dialga-cmd/GUN101@2391eb0e017f612af3bdc0c7af8e24d1016a4a5a -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/dialga-cmd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2391eb0e017f612af3bdc0c7af8e24d1016a4a5a -
Trigger Event:
release
-
Statement type:
File details
Details for the file gun101-2.1.0-py3-none-any.whl.
File metadata
- Download URL: gun101-2.1.0-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
950bdfa10532fa4e7074dccd4b043fcbd7024fc0b7b1d7a2c5552a9566acc8ee
|
|
| MD5 |
162474b1d622b017287031d39f3d9bd9
|
|
| BLAKE2b-256 |
613d213482df2b07ed0be024ddd7416e1bc372f78cb1208a9224cc0df9f09bc4
|
Provenance
The following attestation bundles were made for gun101-2.1.0-py3-none-any.whl:
Publisher:
publish.yml on dialga-cmd/GUN101
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gun101-2.1.0-py3-none-any.whl -
Subject digest:
950bdfa10532fa4e7074dccd4b043fcbd7024fc0b7b1d7a2c5552a9566acc8ee - Sigstore transparency entry: 2391081687
- Sigstore integration time:
-
Permalink:
dialga-cmd/GUN101@2391eb0e017f612af3bdc0c7af8e24d1016a4a5a -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/dialga-cmd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2391eb0e017f612af3bdc0c7af8e24d1016a4a5a -
Trigger Event:
release
-
Statement type: