Greeting everyone,
I would like to share a web app I recently developed, and I would really appreciate hearing your opinions, feedback, or any security issues you might encounter.
Thanks for your time, and have a great day ahead!
The system offers "Encrypt-then-Sign" hybrid encryption for Video Call, Screen Sharing, Chat, File Transfer for real-time communications.
Implementing NIST-standardized algorithms at the highest security level:
- ML-KEM-1024 (FIPS 203) • Kyber-1024
- ML-DSA-87 (FIPS 204) • Dilithium-87
- SLH-DSA-256 (FIPS 205) • Sphincs+
- FN-DSA-1024 (FIPS 206) • Falcon (*)
(*) Algorithm is in the process of final standardization by NIST.
https://github.com/AoiAsahina/0x8
Motivation
The transition to post-quantum cryptography (PQC) demands not only standardized algorithms but also practical, deployable systems that integrate PQC primitives into existing communication infrastructures. The “Store-Now-Decrypt-Later” (SNDL) threat makes classical key agreement (ECDH) insufficient: an adversary recording ciphertexts today can decrypt them once a cryptographically relevant quantum computer emerges. Browser-based deployment offers zero install accessibility but introduces unique challenges in trust establishment, key management, and real-time encryption.
Encrypt + Sign
1.The sender uses the recipient's ML-KEM public key to encapsulate a fresh shared secret. This operation produces two outputs: the shared secret itself and a KEM ciphertext that encodes it. Only the holder of the corresponding private key can later recover the shared secret from this ciphertext.
2.A current Unix timestamp and a 16-byte random nonce (bound into the AAD for uniqueness) are generated. Together with the protocol version byte, the signature algorithm identifier, the KEM ciphertext, and the byte lengths of all subsequent fields (IV, ciphertext, signature), they will form part of the Additional Authenticated Data (AAD). To ensure complete cryptographic binding, the digital signature itself is also included in the AAD. This guarantees that any tampering with the signature or ciphertext is detected during decryption.
3.An AES-256-GCM encryption key is deterministically derived from the shared secret using HKDF-SHA256. The KEM ciphertext serves as the salt (ensuring uniqueness per encapsulation) and a fixed info string identifies the protocol context, preventing key reuse across different applications.
4.The sender signs the concatenation of the plaintext and the KEM ciphertext using their own digital signature private key (DSA). This proves authorship and ensures integrity. The generated signature bytes are then added to the AAD, indissolubly linking the signature's integrity to the encrypted payload.
5.The plaintext is encrypted with AES-256-GCM using the derived key, the random nonce, and the full AAD (which now includes the component lengths and the digital signature). AES-GCM provides both confidentiality and authenticity: if any byte of the AAD or ciphertext is altered, decryption will fail outright.
6.Everything is assembled into a single binary packet with a fixed header structure: the PQENC magic bytes, protocol version, signature algorithm ID, timestamp, nonce, then length-prefixed KEM ciphertext, initialization vector, encrypted ciphertext, and digital signature. This format allows the receiver to parse each field unambiguously.
Decrypt + Verify
1.The encrypted binary packet is parsed by reading the PQENC magic header, protocol version, signature algorithm identifier, timestamp, nonce, and then extracting each variable-length field (KEM ciphertext, initialization vector, encrypted ciphertext, digital signature) using the stored length prefixes to locate boundaries precisely.
2.Basic sanity checks are performed: the KEM ciphertext length must be exactly 1568 bytes (as required by ML-KEM-1024), the IV must be exactly 12 bytes (as required by AES-256-GCM), and the embedded timestamp is compared to the current time. If the skew exceeds 180 seconds in either direction, or if lengths are tampered with, the message is rejected as expired, replayed, or malformed.
3.The recipient uses their ML-KEM private key to decapsulate the KEM ciphertext, recovering exactly the same shared secret that the sender obtained during encapsulation. This is the core post-quantum key agreement step.
4.The same AES-256-GCM key is re-derived from the shared secret using HKDF-SHA256, with the identical salt (KEM ciphertext) and info string. Because HKDF is deterministic, both parties arrive at the same key independently.
5.The AAD is reconstructed from the version, algorithm ID, timestamp, nonce, KEM ciphertext, component lengths, and crucially, the digital signature. AES-256-GCM decryption then verifies the authentication tag against this AAD and the ciphertext. This ensures the signature itself was not tampered with in transit. If any byte was modified, the tag check fails and decryption is rejected without revealing any plaintext.
6.Finally, the digital signature is verified against the concatenation of the recovered plaintext and the KEM ciphertext, using the sender's public key. A valid signature confirms that the message was genuinely signed by the claimed sender and that neither the plaintext nor the KEM ciphertext was tampered with in transit.
PQC Encryption OFF
By default, chat and file transfers rely on WebRTC's built-in DTLS (classical, vulnerable to Store-Now-Decrypt-Later).
PQC Encryption ON (Chat and File Transfer)
Add a post-quantum AES-256-GCM layer on top of the DataChannel (chat and file), using session keys derived from ML-KEM encapsulation with the peer's verified FIPS public key. Received file names are automatically sanitized to prevent path traversal attacks.
PQC Encryption ON (Video Call and Screen Sharing)
Real-time media streams use WebRTC Insertable Streams to apply PQC encryption directly on the RTP frames. To ensure zero-latency ratcheting, the Web Worker autonomously advances the AES-256-GCM key every 3 seconds via HKDF, without needing to communicate with the main thread. A clear-text Ratchet ID is prepended to each encrypted frame so the receiver can re-sync its key state in case of packet loss. Any frame that fails cryptographic validation is silently dropped to prevent data leaks.
Stream-to-Disk Transfer (Chat and File Transfer)
If your browser supports File System Access API, click the "Save" button in the chat to stream data directly to your hard drive in real-time. Maximum File Size 2 TB (2,199,023,255,552 bytes)
Stream-to-RAM Transfer (Chat e File Transfer)
If your browser doesn't supports File System Access API, will buffer in RAM and offer a download link in the chat upon completion. Maximum File Size 2 GB (2,147,483,648 bytes)
Quantum-Safe Key Verification
To prevent Man-in-the-Middle attacks on WebRTC signaling, verify your peer's FIPS identity using an Out-of-Band channel (e.g., read the code over a phone call).
VERIFY AUTHENTICATION CODE
WebRTC uses classical DTLS (ECDH) which is vulnerable to Store-Now-Decrypt-Later quantum attacks. If a quantum attacker intercepts the signaling server today, they could substitute FIPS keys. Verifying the SAS ensures the keys you received are authentic, creating a true Post-Quantum secure channel.
PQ Handshake
1.Commit phase: Each peer generates a random 32-byte salt, then computes a SHA-256 hash of the salt combined with their own KEM public key, DSA public key, and signature algorithm identifier. This hash is sent to the other peer as a cryptographic commitment, without revealing the actual keys yet. This prevents a malicious signaling server from adaptively choosing keys based on what the other party sends.
2.Reveal phase: Once both commitments have been exchanged, each peer sends their actual KEM public key, DSA public key, salt, and algorithm identifier, along with a Proof-of-Possession (PoP) signature. The PoP is a digital signature over the concatenation of both public keys, the salt, and the algorithm ID, proving that the sender actually holds the corresponding DSA private key.
3.Commitment verification: Each peer recomputes the SHA-256 hash of the received keys, salt, and algorithm ID, then checks that it matches the commitment they received earlier. A mismatch indicates a potential man-in-the-middle attack. The PoP signature is also verified to confirm that the peer genuinely owns the DSA key they claim to have.
4.SAS computation: Both peers compute a Short Authentication String (SAS) by hashing a deterministically sorted JSON object containing both peers IDs, KEM public keys, DSA public keys, and algorithm identifiers with SHA-256. The first 16 hexadecimal characters of the digest form the SAS code. Because the input is sorted, both peers produce the same SAS independently.
5.Out-of-band verification: The SAS must be compared through a separate, trusted channel, for example by reading it aloud over a phone call. If both peers see the same code, they can be confident that no man-in-the-middle attack has altered the keys during the WebRTC signaling exchange.
6.Trust establishment: Once the SAS is verified, the peer keys are locked as trusted. The Encrypt and Decrypt tabs are automatically populated with the peer KEM public key, DSA public key, and the correct signature algorithm identifier, so subsequent encrypted communication uses the authenticated keys.
7.Optional PQC session encryption: If enabled, the initiator performs ML-KEM encapsulation with the peer verified public key and sends the resulting KEM ciphertext over the DataChannel. The responder decapsulates it with their private key. Both peers then derive an AES-256-GCM session key via HKDF-SHA256 (shared secret as input key material, KEM ciphertext as salt, protocol info string). The responder sends an acknowledgment to confirm the channel is ready. Forward Secrecy is provided via automatic key ratcheting: every sent message or file chunk derives a fresh AES-256-GCM key using HKDF-SHA256 from the previous key, so compromising the current key cannot decrypt past messages. Separate send/receive ratchet keys prevent desync during simultaneous communication. A ratchet counter is included in each encrypted payload. To mitigate packet loss causing permanent desync, the receiver automatically fast-forwards its ratchet state if a gap is detected, while strictly rejecting any rollback attempts (receiving an older counter) to prevent tampering.
Binary Container Format
The PQENC format encodes all cryptographic artifacts into a single binary packet with a fixed header structure and length-prefixed variable fields
Additional Authenticated Data (AAD) Construction
The AAD binds all metadata and the signature itself into the AES-GCM authentication tag, ensuring that any modification to any field is detected.
The digital signature is included in the AAD. This creates a bidirectional integrity binding:
• AES-GCM authentication ensures the signature was not modified in transit.
• The DSA signature covers (plaintext∥KEM ciphertext), ensuring message authenticity.
• Modifying either the signature or the ciphertext causes AES-GCM tag verification to fail, preventing selective tampering.
PQENC Encryption Security Properties
- Confidentiality > AES-256-GCM encryption with ML-KEM-derived key
- Authenticity > AES-GCM authentication tag + DSA signature
- Integrity > AAD binds all metadata + signature into GCM tag
- Non-repudiation > DSA signature on (plaintext || KEM ciphertext)
- Replay resistance > Timestamp validation (±180 seconds)
- Key uniqueness > HKDF with KEM ciphertext as salt
- Algorithm agility > SigAlgoId field supports 6 signature algorithms
Sign-then-Encrypt: The PQENC protocol uses Sign-then-Encrypt (sign the plaintext, then encrypt it), but with a critical enhancement: the signature is also included in the AAD of the encryption. This means:
• The signature is authenticated by AES-GCM (any modification to the signature in transit is detected).
• The signature covers the plaintext and KEM ciphertext (proving authorship and integrity of the plaintext).
• The KEM ciphertext in the signed data provides binding between the encryption key and the signer’s claim.
Why include the signature in AAD? Without this binding, an attacker who compromises the recipient’s KEM private key could:
- Decrypt the ciphertext.
- Replace the original signature with their own (re-signing the plaintext).
- The recipient would not detect this substitution. By including the signature in the AAD, any modification to the signature causes AES-GCM tag verification to fail, creating an indissoluble link between the signature’s integrity and the encrypted payload.
Handshake Security Properties
Commit-reveal prevents adaptive MITM: In a standard key exchange, a malicious signaling server could wait for Alice to send her public key, then send a different key to Bob (and vice versa). The commit-reveal protocol forces the adversary to commit to a key before seeing the other party’s key, making adaptive key selection impossible.
PoP prevents key injection: Even if an adversary obtains a valid DSA public key, they cannot produce a valid PoP signature without the corresponding private key. This prevents an attacker from claiming ownership of a key they don’t control.
- MITM resistance > Commit-reveal prevents adaptive key substitution
- Key ownership proof > Proof-of-Possession (PoP) signature
- Out-of-band verification > SAS comparison via trusted channel
- Signaling server independence > PQ handshake operates over DataChannel after WebRTC establishment.
Session Encryption Security Properties
- Forward secrecy > KDF ratchet (one-way key derivation)
- Post-compromise security > PQC re-keying via fresh ML-KEM encapsulation
- Rollback resistance > Counter comparison with rejection of older values
- Domain separation > Separate send/receive ratchet chains
- Message authentication >AES-GCM with counter-based AAD
Forward secrecy analysis: Each ratchet step applies HKDF-SHA-256 to derive the next key. Since HKDF is a one-way function, knowing Kn does not allow computation of Kn−1. An adversary who compromises a ratchet key cannot decrypt past messages.
Re-keying provides post-compromise security: If an adversary compromises the current ratchet state, they can only decrypt future messages until the next PQC re-key. The re-key uses a fresh ML-KEM encapsulation, which is quantum-safe, establishing a completely new key tree.
Key Regeneration and Handshake Reset
If a key pair is regenerated while a P2P connection is active and a PQ handshake has been performed (or is in progress), the system:
• Aborts any in-progress file transfers.
• Sends a pq-handshake-reset message to the peer.
• Resets the local handshake state (commitments, peer keys, SAS, trust status).
• Disables the PQC session encryption layer.
This prevents key confusion attacks where stale keys could be used after regeneration.
Keyboard Shortcuts
- Ctrl+1 Switch to Key Generation
- Ctrl+2 Switch to Encrypt + Sign
- Ctrl+3 Switch to Decrypt + Verify
- Ctrl+4 Switch to WebRTC P2P
- Ctrl+5 Switch to PQ Handshake
- Ctrl+6 Switch to Video Call
- Enter Send chat message
References
[1] National Institute of Standards and Technology. FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard. 2024.
[2] National Institute of Standards and Technology. FIPS 204: Module-Lattice-Based Digital Signature Standard. 2024.
[3] National Institute of Standards and Technology. FIPS 205: Stateless Hash-Based Digital Signature Standard. 2024.
[4] National Institute of Standards and Technology. FIPS 206: Fast-Fourier Lattice-Based Compact Signature over NTRU Standard. 2024.
[5] Krawczyk, H. Cryptographic Extraction and Key Derivation: The HKDF Scheme. CRYPTO 2010.
[6] Dworkin, M. Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM). NIST SP 800-38D, 2007.
[7] Perrig, A., Song, D. Hash Visualization: A New Technique to Improve Real-World Security. International Workshop on Cryptographic Techniques and E-Commerce, 1999. (SAS concept)
[8] Vaudenay, S. Secure Communications over Insecure Channels Based on Short Authenticated Strings. CRYPTO 2005.
[9] Marlinspike, M. The Double Ratchet Algorithm. Open Whisper Systems, 2016.
[10] Paul Miller, @noble/cryptography. noble-post-quantum: ML-KEM, ML-DSA, SLH-DSA, FN-DSA for JavaScript
Top comments (0)