I’ve been working on a P2P messaging implementation focused on mitigating "Harvest Now, Decrypt Later" risks by integrating Post-Quantum Cryptography (PQC) directly into the browser.
Since NIST recently finalized FIPS 203 (ML-KEM), I decided to implement ML-KEM encryption into my cascading. The goal was to ensure that the security of the exchange doesn't rely solely on the relatively new lattice-based assumptions of ML-KEM, but remains anchored by classical ECC (X25519) via the Signal Protocol.
I’m using a application-level cascading-cipher to merge the shared secrets from ML-KEM-768 and X25519. This follows the "composite" approach currently being discussed in IETF drafts to ensure the system is at least as strong as the strongest individual algorithm. The implementation wraps the Signal Protocol's Double Ratchet. Even if a future cryptanalytic breakthrough targets ML-KEM, the classical layer still requires a discrete log break to compromise.
I’ve put together a few resources for the community:
-
Technical Write-up: A deep dive into the "Cascading Cipher" logic and the KDF used for the hybrid secret. https://positive-intentions.com/blog/quantum-resistant-encryption
-
ML-KEM Standalone Demo: A tool to inspect the encapsulation/decapsulation process in the browser console. https://cryptography.positive-intentions.com/?path=%2Fstory%2Fcascading-cipher-ml-kem-demo--mlkem-standalone
-
Messaging app demo: This implementation can be seen working in action in the webapp here https://p2p.positive-intentions.com/iframe.html?globals=&id=demo-p2p-messaging--p-2-p-messaging&viewMode=story
-
GitHub: the implementation is far from finished and not ready to review, but if curious, you can take a look here: https://github.com/positive-intentions/cryptography
(NOTE: We are talking about JavaScript for crypto. So it's important to be clear, that this is for end-to-end P2P browser communication where the environment is already JS-dependent, I'm using Web Crypto API where possible for the classical primitives. The only exception is the signal protocol, which needed primitives not provided by the browser: https://github.com/positive-intentions/signal-protocol.)