Post-quantum cryptography folder encryption tool. Encrypts entire directories into a single .pqc file using NIST-standardized post-quantum algorithms.
https://www.researchgate.net/publication/401719210_Democratizing_Post-Quantum_Cryptography_Design_and_Architecture_of_a_FIPS_203204-Compliant_Folder_Encryption_Tool_for_Non-Technical_Users
March 2026
DOI: 10.13140/RG.2.2.30529.01123
LicenseCC BY 4.0
Paper: Democratizing Post-Quantum Cryptography: Design and Architecture of a FIPS 203/204-Compliant Folder Encryption Tool for Non-Technical Users — March 2026, DOI: 10.13140/RG.2.2.30529.01123, License: CC BY 4.0
Built by TTPSEC SpA — OT/ICS Cybersecurity.
| Layer | Algorithm | Standard |
|---|---|---|
| Key Encapsulation | ML-KEM-768 | FIPS 203 |
| Digital Signature | ML-DSA-65 | FIPS 204 |
| Symmetric Encryption | AES-256-GCM | FIPS 197 / SP 800-38D |
| Password KDF | Argon2id (64 MB, 3 iter) | RFC 9106 |
| Key Derivation | HKDF-SHA256 | RFC 5869 |
- Hardened container format — New v3 binary format with
\x89PQCmagic, big-endian encoding, and length-prefixed fields - Full signature coverage — ML-DSA-65 signature now covers ALL authenticated bytes (header, parameters, ciphertext, public keys, metadata, payload)
- Crypto-agility — Suite registry system allows future algorithm upgrades without breaking the parser
- Fail-closed decryption — 15-step validation chain; no files written to disk until every check passes
- Path traversal protection — NFC normalization,
..rejection, Windows reserved name blocking, resolve-within-target enforcement - Signer identity model — 4 verification modes: integrity-only, fingerprint, public key file, trust store
- Domain-separated key derivation — Unique HKDF labels per derivation purpose prevent key confusion attacks
- Modular architecture — Clean separation into 8 focused Python modules
- No runtime auto-install — Dependencies managed via
pyproject.toml;check_env.pyfor environment verification
- Modular Python package with clean separation of concerns
- GUI (tkinter) and CLI modes
- Encrypts full directory trees preserving structure
- Per-file SHA-256 integrity verification on decrypt
- Canonical JSON manifest with deterministic ordering
- Signer identity verification (
--verify-key,--verify-fp,--trust-store) - Container metadata inspection (
infosubcommand) - Password strength indicator (GUI)
- Standalone
.exebuild for Windows (no Python required)
# Install dependencies
pip install -r requirements.txt
# Verify environment
python check_env.py
# GUI mode
python -m pqc_folder_encryptor.gui
# CLI - Encrypt a folder
python -m pqc_folder_encryptor encrypt my_folder/ output.pqc -p "my-passphrase"
# CLI - Decrypt a .pqc file
python -m pqc_folder_encryptor decrypt output.pqc restored/ -p "my-passphrase"
# CLI - Inspect container metadata (no decryption)
python -m pqc_folder_encryptor info output.pqc
# CLI - Decrypt with signer identity verification
python -m pqc_folder_encryptor decrypt output.pqc restored/ --verify-key signer.pub
# Export signing public key after encryption
python -m pqc_folder_encryptor encrypt my_folder/ output.pqc --export-key signer.pubThe original pqc_encryptor.py is preserved for backward compatibility:
python pqc_encryptor.py encrypt my_folder/ output.pqc -p "my-passphrase"Note: Legacy mode uses the v2 format. Use the new
pqc_folder_encryptorpackage for v3 format with full security hardening.
# Build the executable
build_exe.bat
# The .exe is in dist/PQC-Encryptor.exe
# Distribute it — no Python installation needed- Python 3.10+
- Dependencies (install before use — not auto-installed):
pqcrypto— Post-quantum cryptographic primitives (ML-KEM-768, ML-DSA-65)cryptography— AES-256-GCM and HKDF-SHA256argon2-cffi— Argon2id password hashing
pip install -r requirements.txt
python check_env.py # Verify everything is correctly installedWhy no auto-install? Runtime
pip installis a supply chain risk. Seecheck_env.pyfor a detailed explanation covering dependency confusion, typosquatting, and reproducibility concerns.
Input folder
|
v
[Collect files + validate paths (NFC, no traversal)]
|
v
[Generate canonical manifest (sorted, SHA-256 per file)]
|
v
[Generate ML-KEM-768 keypair] --> [Encapsulate shared secret]
| |
v v
[Argon2id(passphrase)] --> [AES-GCM encrypt KEM secret key]
|
v
[HKDF-SHA256(shared secret, domain label)] --> encryption key
|
v
[Pack manifest + files --> AES-256-GCM encrypt payload]
|
v
[Build authenticated region (all header + payload bytes)]
|
v
[ML-DSA-65 sign ENTIRE authenticated region]
|
v
output.pqc (v3)
input.pqc
|
v
[ 1. Validate magic bytes (\x89PQC)]
[ 2. Check format version (must be 3)]
[ 3. Verify suite ID (must be registered)]
[ 4. Validate field lengths and consistency]
[ 5. Detect truncation]
[ 6. Check signing key fingerprint]
|
v
[ 7. Verify signer identity (if configured)]
[ 8. ML-DSA-65 verify signature over ALL authenticated bytes] --> abort if invalid
|
v
[ 9. Argon2id(passphrase) --> Decrypt KEM secret key] --> abort if wrong passphrase
[10. ML-KEM-768 decapsulate --> shared secret]
[11. HKDF-SHA256(domain label) --> AES key --> AES-256-GCM decrypt] --> abort if tampered
|
v
[12. Parse and validate canonical manifest]
[13. Verify SHA-256 hash + size for every file]
[14. Validate all paths (no traversal, no escape)]
[15. Confirm resolved paths stay within output directory]
|
v
[ALL checks passed --> write files to disk]
|
v
Restored folder
All multi-byte integers are big-endian. The ML-DSA-65 signature covers all bytes from offset 0 through the end of the encrypted payload.
| Section | Field | Size | Content |
|---|---|---|---|
| Header | magic | 4 | \x89PQC (non-ASCII prefix detects text corruption) |
| format_version | 2 | Format version, uint16 BE (currently 3) |
|
| suite_id | 2 | Cryptographic suite identifier, uint16 BE (0x0001) |
|
| KDF Params | argon2_salt | 16 | Argon2id salt |
| argon2_memory | 4 | Memory cost in KiB, uint32 BE | |
| argon2_time | 4 | Time cost (iterations), uint32 BE | |
| argon2_parallel | 4 | Parallelism, uint32 BE | |
| KEM Data | kem_ct_len | 4 | KEM ciphertext length, uint32 BE |
| kem_ciphertext | var | ML-KEM-768 ciphertext (1088 bytes) | |
| Encrypted SK | sk_nonce | 12 | AES-GCM nonce for SK encryption |
| esk_len | 4 | Encrypted secret key length, uint32 BE | |
| encrypted_sk | var | AES-GCM encrypted KEM secret key (2416 bytes) | |
| Public Keys | kem_pk_len | 4 | KEM public key length, uint32 BE |
| kem_public_key | var | ML-KEM-768 public key (1184 bytes) | |
| sig_pk_len | 4 | Signing public key length, uint32 BE | |
| sig_public_key | var | ML-DSA-65 public key (1952 bytes) | |
| Metadata | sig_pk_fingerprint | 32 | SHA-256 of signing public key |
| folder_name_len | 4 | Folder name length, uint32 BE | |
| folder_name | var | Original folder name (UTF-8) | |
| Payload | data_nonce | 12 | AES-GCM nonce for payload encryption |
| payload_len | 8 | Encrypted payload length, uint64 BE | |
| encrypted_payload | var | AES-256-GCM encrypted (manifest + file data) | |
| --- end of authenticated region --- | |||
| Signature | sig_len | 4 | Signature length, uint32 BE |
| signature | var | ML-DSA-65 signature (~3309 bytes) |
| Suite ID | KEM | Signature | AEAD | KDF | Password KDF |
|---|---|---|---|---|---|
0x0001 |
ML-KEM-768 | ML-DSA-65 | AES-256-GCM | HKDF-SHA256 | Argon2id |
0x0002 |
Reserved | ML-DSA-87 | AES-256-GCM | HKDF-SHA512 | Argon2id |
0x0003 |
Reserved | ML-DSA-65 | ChaCha20-Poly1305 | HKDF-SHA256 | Argon2id |
| Derivation | Label |
|---|---|
| Encryption key | pqc-folder-encryptor.v1.encryption-key |
| Manifest binding | pqc-folder-encryptor.v1.manifest-binding |
| Attack | Mitigated | Mechanism |
|---|---|---|
| Ciphertext tampering | Yes | AES-256-GCM auth tag + ML-DSA-65 signature |
| Header tampering | Yes | Signature covers all bytes from magic through payload |
| Algorithm downgrade | Yes | suite_id inside signed region |
| Ciphertext swapping | Yes | Nonce + ciphertext + headers signed as a unit |
| Path traversal | Yes | NFC normalization, .. rejection, resolve-within-target |
| Key confusion | Yes | HKDF domain separation labels |
| Truncation | Yes | Exact-length reads; trailing data rejected |
| Supply chain | Yes | No runtime pip install; pinned dependencies |
| Identity spoofing | Partial | Fingerprint + optional identity verification |
| Mode | Description | CLI Flag |
|---|---|---|
| Integrity only | Self-consistency check (default) | (none) |
| Fingerprint | Verify against known SHA-256 of signing key | --verify-fp HEX |
| Public key file | Verify against a key file | --verify-key PATH |
| Trust store | Verify against directory of .pub files |
--trust-store DIR |
Every release includes a SHA256SUMS.txt file. Verify the .exe integrity:
# PowerShell
certutil -hashfile PQC-Encryptor.exe SHA256# Linux / Git Bash
sha256sum -c SHA256SUMS.txtCompare the output hash with the value in SHA256SUMS.txt.
# Run round-trip tests (encrypt, decrypt, verify, tamper detection)
python tests/test_roundtrip.py
# Run regression / known-answer tests
python tests/test_vectors.py
# Verify environment
python check_env.pypqc-folder-encryptor/
├── pqc_folder_encryptor/ # Main package (v3)
│ ├── __init__.py # Public API: encrypt_folder(), decrypt_folder()
│ ├── __main__.py # python -m entry point
│ ├── config.py # Suite registry, constants, domain separation labels
│ ├── exceptions.py # Typed exception hierarchy (15 exception types)
│ ├── crypto.py # ML-KEM-768, ML-DSA-65, AES-256-GCM, HKDF, Argon2id
│ ├── manifest.py # Canonical JSON manifest, path safety validation
│ ├── container.py # Binary format v3 serialization and parsing
│ ├── signing.py # Signature operations + signer identity model
│ ├── validation.py # 15-step fail-closed decrypt validation chain
│ ├── cli.py # CLI: encrypt, decrypt, info subcommands
│ └── gui.py # Tkinter GUI
├── pqc_encryptor.py # Legacy single-file application (v2 format)
├── pyproject.toml # Project metadata and dependency specification
├── requirements.txt # Python dependencies (pinned ranges)
├── requirements-dev.txt # Build dependencies (PyInstaller)
├── check_env.py # Environment verification script
├── build_exe.bat # Windows .exe builder script
├── tests/
│ ├── test_roundtrip.py # Encrypt/decrypt round-trip + tamper detection
│ └── test_vectors.py # KDF, manifest, format, path safety regression tests
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # CI: test on push/PR
│ │ └── release.yml # Build .exe + checksums on tag
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml # Bug report template
│ │ └── feature_request.yml # Feature request template
│ └── PULL_REQUEST_TEMPLATE.md # PR template
├── LICENSE # MIT License
├── README.md # This file
├── CHANGELOG.md # Version history
├── SECURITY.md # Security policy and crypto details
├── CODE_OF_CONDUCT.md # Contributor Code of Conduct
└── CONTRIBUTING.md # Contribution guidelines
MIT License — see LICENSE for details.
Copyright (c) 2026 TTPSEC SpA
