NyxCrypta
A Python cryptography library combining RSA asymmetric encryption and AES symmetric encryption for efficient and secure data protection, with Argon2id-protected private keys and Ed25519 digital signatures.
📑 Table of Contents
- Features
- Security Levels
- Installation
- Usage
- Security Features
- What's New in 3.2.0
- What's New in 3.1.0
- What's New in 3.0.0
- Testing
- Key Format Support
- Python Example
- Dependencies
- Internal Architecture
- FAQ
- Security Considerations
- Development Status
- Contributing
- Bug Reports
- License
- Authors
Features
- 🔐 RSA key pair generation with multiple security levels
- 📄 Multiple key formats support (PEM, DER, SSH, JSON)
- 🧱 Private keys protected with Argon2id + AES-256-GCM
- ✍️ Ed25519 digital signatures (sign and verify files)
- 🔒 File encryption and decryption
- 💾 Raw data encryption and decryption
- 🛡️ Strong encryption using RSA + AES hybrid approach
- 🔄 Key format conversion utilities
- 🖥️ Interactive CLI for beginners
Security Levels
| Security Level | RSA Key Size | Argon2id (time / memory / lanes) | Recommended Use |
|---|---|---|---|
| Standard | 2048-bit | 3 / 64 MiB / 4 | General purpose encryption |
| High | 3072-bit | 4 / 128 MiB / 4 | Sensitive data protection |
| Paranoid | 4096-bit | 5 / 256 MiB / 4 | Maximum security requirements |
The level sets the RSA key size and the cost of the Argon2id key derivation that protects the private key. The Argon2 parameters are stored inside each key, so a key can be used whatever level is selected later. signkeygen (Ed25519) uses the level for the Argon2 cost only.
Installation
From PyPI
pip install nyxcrypta
From Source
git clone https://github.com/Division-of-Cyber-Anarchy/NyxCrypta.git
cd NyxCrypta
pip install -e .
Usage
Interactive CLI
Launch the interactive mode:
nyxcrypta
The interactive CLI provides a beginner-friendly interface with:
- Step-by-step wizards for all operations
- Clear explanations of each option
- Secure password input
- Progress indicators
- Tab completion
Key Generation
# Generate PEM format key pair
nyxcrypta keygen -o ./keys -p "your_strong_password" -f PEM
# Generate DER format key pair
nyxcrypta keygen -o ./keys -p "your_strong_password" -f DER
# Generate JSON format key pair
nyxcrypta keygen -o ./keys -p "your_strong_password" -f JSON
# Generate SSH format public key
nyxcrypta keygen -o ./keys -p "your_strong_password" -f SSH
# (SSH is public-only: the private key is saved as ./keys/private_key.pem)
The private key is always protected with your password through Argon2id (see Security Features).
Key Format Conversion
# Convert PEM to DER
nyxcrypta convert -i ./keys/public_key.pem -o ./keys/key.der --from-format PEM --to-format DER
# Convert DER to SSH (public key only)
nyxcrypta convert -i ./keys/public_key.der -o ./keys/key.ssh --from-format DER --to-format SSH --public
# Re-protect a private key created by 3.1.0 or earlier with Argon2id (same password)
nyxcrypta convert -i ./old_private_key.pem -o ./keys/private_key.pem --from-format PEM --to-format PEM -p "your_password"
File Encryption/Decryption
# Encrypt a file
nyxcrypta encrypt -i file.txt -o file.nyx -k ./keys/public_key.pem
# Decrypt a file
nyxcrypta decrypt -i file.nyx -o file.txt -k ./keys/private_key.pem -p "your_password"
Data Encryption/Decryption
# Encrypt raw data
nyxcrypta encryptdata -d "My secret data" -k ./keys/public_key.pem
# Decrypt raw data
nyxcrypta decryptdata -d "encrypted_hex_string" -k ./keys/private_key.pem -p "your_password"
Digital Signatures
Signatures prove that a file comes from the holder of a signing key and was not modified. They use Ed25519 keys, which are separate from the RSA encryption keys.
# Generate an Ed25519 signing key pair (signing_private_key.* and signing_public_key.*)
nyxcrypta signkeygen -o ./keys -p "your_strong_password" -f PEM
# Sign a file: writes file.txt.sig (use -o to choose another name)
nyxcrypta sign -i file.txt -k ./keys/signing_private_key.pem -p "your_password"
# Verify: exit code 0 if valid, 1 otherwise
nyxcrypta verify -i file.txt -k ./keys/signing_public_key.pem
nyxcrypta verify -i file.txt -s other.sig -k ./keys/signing_public_key.ssh
Encrypting with a public key does not authenticate the sender: to prove who produced a file, sign the plaintext, send the .sig along with the encrypted file, and have the recipient verify it after decryption.
Security Features
| Feature | Description |
|---|---|
| Hybrid Encryption | RSA-OAEP for key exchange, AES-256-GCM for data encryption (chunked, authenticated) |
| Key Derivation | Argon2id turns the password into the 256-bit key that protects each private key (unique 16-byte salt per key, cost set by the security level) |
| Private Key Protection | Private keys are stored encrypted with AES-256-GCM under the Argon2id-derived key; the KDF parameters are authenticated and bounded when a key is read |
| Digital Signatures | Ed25519 detached signatures over the SHA-512 digest of the file, with a domain-separation context |
| Random Generation | Secure random number generation using OS entropy |
| Multi-level Security | Different RSA key sizes and Argon2id costs |
What's New in 3.2.0
Version 3.2.0 makes the code match what the documentation promised: Argon2 is now really used, files can be signed and verified, and dead code was removed. Encrypted files and data (container v3) and public keys are unchanged.
Changes
| Area | Before (3.1.0) | Now (3.2.0) |
|---|---|---|
| Argon2 | Documented, imported but never used; private keys relied on the standard PKCS8 password encryption | Argon2id derives the key protecting every private key (core/security.py) |
| Signatures | "Verify file integrity" was documented but nothing could sign or verify | signkeygen, sign and verify commands and save_signing_keys, sign_file, verify_file, sign_data, verify_data in the Python API (Ed25519) |
core/security.py |
Only the SecurityLevel enum |
Argon2id parameters per level and the private key container ("key derivation and storage") |
| Dead code | NyxCrypta.ph, NyxCrypta.get_hash_algorithm(), core/utils.py (file_exists), VersionCompatibility |
Removed |
| Key type checks | An unsuitable key failed with a cryptic error | Clear errors: RSA keys are required to encrypt/decrypt, Ed25519 keys to sign/verify |
Points to know
- ⚠️ Private key format change. Keys generated or converted with 3.2.0 use the NyxCrypta key container (Argon2id + AES-256-GCM): they cannot be read by NyxCrypta older than 3.2.0, nor by OpenSSL. Public keys are unchanged.
- ♻️ Existing keys keep working. Private keys from 3.1.0 and earlier (and standard encrypted PKCS8 keys) are still loaded. Upgrade them with
nyxcrypta convert -i old.pem -o new.pem --from-format PEM --to-format PEM -p "password"(write to a new file rather than overwriting the old key, and keep the old key until you have checked the new one). - 🧱 DER private keys.
private_key.deris now the binary NyxCrypta container, not an ASN.1 structure. The flag--to-format DERis kept for compatibility. Converting without a password still produces an unencrypted standard PKCS8 key. - 💾 Memory and time. Reading or writing a private key costs 64, 128 or 256 MiB of RAM and a fraction of a second, depending on the level the key was created with. This is the point of Argon2: it makes password guessing expensive.
- 🛡️ Hardened key reading. The Argon2 parameters found in a key file are bounded (at most 1 GiB and 20 passes), so a crafted key cannot exhaust memory. Changing any parameter makes the key unreadable.
- ✍️ Signatures. The file is hashed with SHA-512 (streamed, any file size) and Ed25519 signs a context string plus the digest. The detached
.sigfile is JSON. Signing keys are separate files (signing_private_key.*,signing_public_key.*): an RSA key cannot sign and an Ed25519 key cannot encrypt. The public key can be exported to OpenSSH format (ssh-ed25519). - 🚦 Exit codes.
verifyreturns 1 when the signature is invalid, the key does not match, or the file was modified. - 🧹 Removed API. If your code imported
NyxCrypta.get_hash_algorithm,NyxCrypta.ph,nyxcrypta.core.utils.file_existsorVersionCompatibility, remove those references: the library never used them. - ⚠️ Still open. Passing
-p "password"on the command line exposes the password in your shell history and process list (see Known limitations below).
What's New in 3.1.0
Version 3.1.0 fixes functional bugs in the key handling and the CLI. The encryption format is unchanged (container v3): files encrypted with 3.0.0 and 3.1.0 are interchangeable.
Fixes
| Area | Before (3.0.0) | Now (3.1.0) |
|---|---|---|
--key-format |
Accepted but ignored; the format was only detected from the content | Optional: the format is auto-detected, and when you pass it, it is enforced (a wrong value fails instead of being silently ignored) |
keygen -f JSON |
Offered by the CLI but refused by save_keys |
Generates public_key.json and private_key.json (the private key is always encrypted) |
| Failed encryption | Could leave a partial output file | Output is written to <output>.part and moved into place only on success, for encryption as well as decryption |
decryptdata |
An empty plaintext was reported as a failure; binary data crashed on .decode() |
Empty data is a valid result; binary data is shown as hexadecimal with a warning |
convert |
The key type was guessed from a public substring in the file path (/home/public/key.pem was treated as a public key) |
The type is read from the key content (PEM header, JSON type, DER parsing) |
| Interactive mode | The first error called sys.exit(1) and closed the session; Ctrl+C on a prompt raised an exception |
Errors and cancelled prompts return to the menu; Ctrl+C on the main menu quits cleanly |
Points to know
- 🔑
--key-formatdefault changed. It no longer defaults toPEM: omitting it means auto-detection. Scripts that pass it explicitly keep working, andJSONis now accepted forencrypt,encryptdata,decryptanddecryptdata. - 🧪 Exit codes. In classic CLI mode, a failed operation (wrong password, missing file, failed encryption…) now returns a non-zero exit code, which makes scripting safer. Before, some failures still exited with
0. - 🧩 Python API.
encrypt_file,decrypt_file,encrypt_dataanddecrypt_datagain an optional trailingkey_formatargument (default: auto-detection). Existing calls are unaffected.handle_commandnow returnsTrue/Falseand never exits the process. - 🖥️
convertin interactive mode no longer asks whether the key is public: the type is detected, and the password is requested only for private keys.--publicstays available to force public-key handling.
What's New in 3.0.0
Version 3.0.0 is a major release: it fixes several security flaws in the encryption format and is therefore not backward compatible with older versions of NyxCrypta.
Fixes
| Area | Before (≤ 1.5.0) | Now (3.0.0) |
|---|---|---|
| AES-GCM nonce | One nonce reused for every 1 MB chunk of a file | Unique nonce per chunk (random 8-byte prefix + 4-byte counter) |
| Large files | Files > 1 MB could be encrypted but not decrypted | Files of any size round-trip correctly |
| Integrity | Header and chunk order not authenticated | Header, chunk index and "last chunk" flag are authenticated (AAD): tampering, reordering, duplication and truncation are detected |
| JSON private keys | convert ... --to-format JSON stored the key unencrypted |
The key is always stored encrypted and a password is required |
| SSH key generation | keygen -f SSH discarded the private key |
The private key is saved as private_key.pem next to public_key.ssh |
Points to know
- ⚠️ Format change. Files and data encrypted with 3.0.0 use container format v3 and cannot be decrypted by NyxCrypta 1.x. Upgrade every machine that needs to read them.
- ♻️ Existing files (v2) remain readable. NyxCrypta 3.0.0 still decrypts files and data produced by 1.x and logs a warning. Those files were encrypted with a reused nonce and have no truncation protection, so decrypt and re-encrypt them with 3.0.0.
- 🔑 SSH is a public-key-only format.
keygen -f SSHwritespublic_key.ssh(OpenSSH) and an encryptedprivate_key.pem. Use the.pemfile to decrypt. - 🔐 JSON private keys require a password. Exporting an unencrypted private key to JSON is refused. Existing
.jsonprivate keys created by 1.x may contain an unencrypted key: delete them, treat the key as compromised if the file was shared or stored somewhere untrusted, and regenerate your keys. - 🧩 Key format auto-detection. Keys given to
encrypt,decrypt,encryptdataanddecryptdataare detected from their content (PEM, DER, SSH or JSON). Since 3.1.0,--key-formatcan also force a format. - 🧹 Safer decryption. Output is written to
<output>.partand moved into place only after every chunk is authenticated. A failed decryption never leaves partial plaintext behind. - 📦 Python API unchanged.
NyxCrypta,SecurityLevel,KeyFormatandKeyConverterkeep the same signatures. Failures still returnFalse/None.
Known limitations
- Passing
-p "password"on the command line exposes the password in your shell history and process list. Prefer the interactive mode for sensitive operations.
Testing
Run the comprehensive test suite:
nyxcrypta test
Key Format Support
Public Keys
- PEM format (.pem)
- DER format (.der)
- OpenSSH format (.ssh)
- JSON format (.json)
Private Keys
- PEM format (.pem): PEM armor around the Argon2id key container
- DER format (.der): the binary Argon2id key container
- JSON format (.json): the PEM form wrapped in JSON, always encrypted
Private keys from NyxCrypta 3.1.0 and earlier (standard encrypted PKCS8 in PEM or DER) can still be loaded.
Key Types
- RSA keys (
keygen): encryption and decryption - Ed25519 keys (
signkeygen): signing and verification. Public keys can also be exported as OpenSSH (ssh-ed25519)
Python Example
from nyxcrypta import NyxCrypta, SecurityLevel, KeyFormat
# Initialize NyxCrypta
nx = NyxCrypta() # Uses STANDARD security level by default
# Generate key pair
nx.save_keys("./keys", "your_password", KeyFormat.PEM)
# Encrypt a file
nx.encrypt_file("secret.txt", "secret.nyx", "./keys/public_key.pem")
# Decrypt a file
nx.decrypt_file("secret.nyx", "decrypted.txt", "./keys/private_key.pem", "your_password")
# Encrypt and decrypt data
message = b"Hello, World!"
encrypted = nx.encrypt_data(message, "./keys/public_key.pem")
decrypted = nx.decrypt_data(bytes.fromhex(encrypted), "./keys/private_key.pem", "your_password")
print(decrypted.decode()) # Prints: Hello, World!
# Using higher security level
nx_secure = NyxCrypta(SecurityLevel.PARANOID)
nx_secure.save_keys("./secure_keys", "your_password", KeyFormat.PEM)
# Digital signatures (Ed25519)
nx.save_signing_keys("./keys", "your_password", KeyFormat.PEM)
nx.sign_file("secret.txt", "./keys/signing_private_key.pem", "your_password") # writes secret.txt.sig
print(nx.verify_file("secret.txt", "secret.txt.sig", "./keys/signing_public_key.pem")) # True / False
# Signing raw bytes
signature = nx.sign_data(b"Hello, World!", "./keys/signing_private_key.pem", "your_password")
print(nx.verify_data(b"Hello, World!", signature, "./keys/signing_public_key.pem")) # True
# Key format conversion
from nyxcrypta import KeyConverter
# Convert public key from PEM to SSH format
with open("./keys/public_key.pem", "rb") as f:
pem_data = f.read()
ssh_key = KeyConverter.convert_public_key(pem_data, KeyFormat.PEM, KeyFormat.SSH)
with open("./keys/public_key.ssh", "wb") as f:
f.write(ssh_key)
# Convert private key from PEM to DER format
with open("./keys/private_key.pem", "rb") as f:
pem_data = f.read()
der_key = KeyConverter.convert_private_key(
pem_data,
KeyFormat.PEM,
KeyFormat.DER,
"your_password".encode()
)
with open("./keys/private_key.der", "wb") as f:
f.write(der_key)
Dependencies
| Package | Version | Purpose |
|---|---|---|
| cryptography | >=41.0.5 | Core cryptographic operations |
| argon2-cffi | >=20.1.0 | Argon2id key derivation protecting private keys |
| cffi | >=1.17.1 | C interface for cryptographic operations |
| tqdm | >=4.67 | Progress bars for operations |
| questionary | >=2.0.1 | Interactive prompts for the CLI |
| rich | >=13.7.0 | Rich text and beautiful formatting in the terminal |
Internal Architecture
Core Components
---
config:
layout: fixed
theme: neo-dark
look: handDrawn
---
graph LR
A[Hybrid Encryption]
B[Argon2id Key Derivation]
C[Secure Random Number Generation]
D[Multiple Security Levels]
E[Encrypted Private Key Storage]
S[Digital Signatures]
subgraph "Core Components"
A -->|Uses| F(RSA-OAEP for Key Exchange)
A -->|Uses| G(AES-256-GCM for Data Encryption)
B -->|Based on| H(Argon2id Algorithm)
C -->|Provided by| I(Cryptography Library)
D -->|2048, 3072, 4096-bit| J(RSA Key Sizes)
D -->|64, 128, 256 MiB| M(Argon2id Cost)
E -->|Secured by| B
E -->|Formats| K(PEM, DER, JSON)
S -->|Uses| N(Ed25519 over SHA-512)
end
CLI -->|Triggers| A
CLI -->|Triggers| E
CLI -->|Triggers| S
security.py -->|Implements| B
security.py -->|Implements| E
signing.py -->|Implements| S
Module Structure
-
Core Functions (
core/)crypto.py: Encryption/decryption, key generation and the signing APIsecurity.py: Security levels, Argon2id key derivation and the private key containersigning.py: Ed25519 detached signatures (hashing, signature file, verification)compatibility.py: Key loading, serialization and format conversion
-
CLI Interface (
cli/)commands.py: Command definitionsparser.py: Input parsinginteractive.py: Interactive menus and prompts
-
Testing (
test_runner.py)- Automated testing suite
- Performance metrics
FAQ
What is hybrid encryption?
NyxCrypta uses RSA for secure key exchange and AES for efficient data encryption, combining the strengths of both approaches.
Why use Argon2?
Argon2id is memory-hard: every password guess costs a fixed amount of RAM and time (64 to 256 MiB here), which makes brute-force attacks with GPUs or dedicated hardware far more expensive than with classic key derivation functions.
How secure is the random number generation?
We use os.urandom and the cryptography library's secure random number generators.
What security level should I choose?
- Standard (2048-bit): General use
- High (3072-bit): Sensitive data
- Paranoid (4096-bit): Maximum security
How are private keys protected?
The password is stretched with Argon2id (random 16-byte salt per key) into a 256-bit key, which encrypts the PKCS8 private key with AES-256-GCM. The result is stored as a NyxCrypta key container (PEM-armored, binary for DER, or wrapped in JSON). The Argon2 parameters are stored in the container and authenticated.
Can I use my OpenSSL or ssh-keygen keys?
Public keys: yes (PEM, DER and OpenSSH are all read). Standard password-protected PKCS8 private keys are also accepted; use nyxcrypta convert to re-protect them with Argon2id. Private keys produced by NyxCrypta 3.2.0 cannot be read by OpenSSL.
How do I prove who sent a file?
Generate a signing key pair with signkeygen, sign the file with sign and give the recipient the .sig file and your signing_public_key.*. The recipient runs verify. Encryption alone does not identify the sender.
Security Considerations
- Use strong passwords for private keys
- Keep private keys secure
- Choose appropriate security levels
- Update encryption keys regularly
- Verify the signature of files you receive (
nyxcrypta verify) before trusting them - Keep signing keys and encryption keys separate, and share only the public keys
Development Status
Current status: Active Development
- API may change
- Some experimental features
- Ongoing security audits
Contributing
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
Bug Reports and Feature Requests
Use the GitHub issue tracker
License
MIT License - see LICENSE file
Authors
Division of Cyber Anarchy (DCA)
Contact
GitHub
https://github.com/Division-of-Cyber-Anarchy/
Simplicity is the ultimate sophistication. - Leonardo da Vinci
Metadata
Release files for nyxcrypta 3.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nyxcrypta-3.2.0.tar.gz | 43.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nyxcrypta-3.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.2 kB
Release files / nyxcrypta-3.2.0.tar.gz
| Download URL | nyxcrypta-3.2.0.tar.gz |
|---|---|
| Size | 43.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
275a7a68f7b82b91877642507f69753840291428aea5307d69cbadf4bb5db5fc
|
|
BLAKE2b-256 checksum How to use checksums |
8235586d0702fba0e989775ce8cb632d6fe1a87612f1316b83c7fe44fe7e9776
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / nyxcrypta-3.2.0-py3-none-any.whl
| Download URL | nyxcrypta-3.2.0-py3-none-any.whl |
|---|---|
| Size | 39.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3db65129a1eb315e47c99e3de3afeefb7d1a88ecf5593f6e35c51f39a200462f
|
|
BLAKE2b-256 checksum How to use checksums |
289434164a1835e27e9693768f0a4d54200b872b79245ec9294e3eb31e3881d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|