# Post-Quantum Hybrid ML-KEM-1024 + AES-256-GCM File Encryption Streaming hybrid file encryption: ML-KEM-1024 handles post-quantum key exchange, and AES-256-GCM handles bulk file encryption with built-in authenticity. The file signature is `KYB`, and the file extension is ".kyb". Simplified flow (ML-KEM-1024 + AES-256 only, HKDF omitted for clarity): ## Features - Post-quantum secure key exchange via liboqs ML-KEM-1024 KEM (~256-bit PQ security) - AES-256-GCM authenticated encryption with a streaming architecture that handles files larger than RAM (64 MB chunks) - AES key derived from the ML-KEM shared secret via HKDF-SHA256 (never stored in the file) - Unforgeable 128-bit GCM tag over the header + ciphertext to detect tampering or corruption - Self-describing CBOR header (RFC 8949; file signature `KYB`, version, algorithm and length fields), so each file identifies its own format and unsupported future versions are rejected cleanly ## Requirements - `oqs` ([liboqs-python](https://github.com/open-quantum-safe/liboqs-python)) - `cryptography` ([pyca/cryptography](https://github.com/pyca/cryptography)) - `cbor2` ([cbor2](https://github.com/agronholm/cbor2)) ```bash python3 -m venv venv && . venv/bin/activate pip install -r requirements.txt ``` ## Usage 1. Generate an ML-KEM-1024 key pair ```bash python3 gen_keys.py ``` Produces: - `mlkem1024.pub` - public key (1568 bytes) - `mlkem1024.sec` - secret key (3168 bytes) 2. Encrypt a file ```bash python3 encrypt.py archive.7z ``` Output: `archive.7z.kyb` The `.kyb` file is self-contained: a compact CBOR header at the start describes the exact format, algorithms, and lengths. See [File formats](#file-formats) for the layout. 3. Decrypt a file ```bash python3 decrypt.py archive.7z.kyb ``` Output: `archive.7z` The script automatically verifies the GCM tag. If the file has been tampered with, you'll see: ``` Error: Integrity check failed. File corrupted or tampered ``` and the temp file is discarded. No plaintext ever appears at the destination path until the tag has been verified. ## File formats **Key files (raw binary)** File|Content|Size (bytes) -|-|- `mlkem1024.pub`|ML-KEM-1024 public key|1568 `mlkem1024.sec`|ML-KEM-1024 secret key|3168 **CBOR header** Layout: 3-byte signature `KYB` + 1-byte version, then a CBOR map (RFC 8949) of field name → value pairs. CBOR is self-delimiting, so the reader knows exactly where the map ends; unknown keys are simply ignored, keeping the format forward-compatible. The `KDF` and `INFO` fields record the key-derivation parameters, making the file fully self-describing: with the secret key alone, a from-scratch decryptor can derive the AES key from the header. Key | Value (v1) -|- `KEM`|`MLKEM1024` `CIPHER`|`AES256GCM` `MLKEMCT`|`1568` `IV`|`12` `TAG`|`16` `KDF`|`HKDF-SHA256` `INFO`|`mlkem-aes-key` **Encrypted file structure** `archive.7z.kyb`: Section | Size | Description -|-|- CBOR header|91 bytes (v1)|Signature `KYB` (3 B) + version (1 B) + CBOR map (87 B) ML-KEM ciphertext|1568 bytes|Encapsulated key (ciphertext from ML-KEM-1024); length read from the header IV (nonce)|12 bytes|GCM nonce, also the HKDF salt; length read from the header File ciphertext|same as plaintext|AES-256-GCM encrypted chunks, streamed in 64 MB blocks GCM auth tag|16 bytes|Tag over the header + ML-KEM CT + IV + all ciphertext; length read from the header ## Security notes - A fresh 32-byte **ML-KEM shared secret** is generated **per file** by `encap_secret()` and recovered only with `mlkem1024.sec` via `decap_secret()`. It is the root key material: neither the AES key nor the ML-KEM private key. - The AES key is **derived from that ML-KEM shared secret** with HKDF-SHA256 (domain-separated `info` label, both recorded in the header), so no key material is ever stored in the file. - Because the AES key is only derivable by the legitimate recipient (ML-KEM decapsulation), the GCM tag is **unforgeable**: an attacker without the secret key cannot modify the file undetectably. - The GCM tag is computed over the **CBOR header + ML-KEM CT + IV + AES ciphertext** (via additional authenticated data), so any tampering with the encrypted data, the nonce, or the format header is detected before decryption. - Decrypted plaintext is written to a temporary file and atomically renamed into place **only after** the GCM tag verifies. - The header carries a format **version**; decrypt checks it up front, so a file written by a newer (or unknown) format is refused with a clear error instead of being mis-parsed. - ML-KEM-1024 is believed to offer security equivalent to ~256-bit classical keys against quantum attackers. - The secret key file (`mlkem1024.sec`) is the single point of failure. Losing it means losing every encrypted file. Treat it like a private SSH key: keep it offline, back it up, and restrict access (e.g. `chmod 600`). For stronger protection, consider encrypting the key file with a passphrase or splitting it into pieces (XOR-splitting or Shamir secret sharing) across separate locations.