Skip to main content

8 posts tagged with "cryptography"

View All Tags

Building Defense-in-Depth Encryption: A Cascading Cipher System

· 49 min read
xoron
positive-intentions

⚠️ NOTE: This document and related project is not finished. The details in this document are subject to change.

What if you could combine multiple encryption algorithms like layers of an onion, where each layer provides independent protection? What if breaking one encryption layer still left your data protected by two or three more?

In this article, we'll explore how to build a cascading cipher system that chains multiple encryption algorithms together for defense-in-depth security. We'll walk through a browser-based JavaScript implementation that combines MLS (Message Layer Security), Signal Protocol's Double Ratchet, Diffie-Hellman key exchange, and AES-GCM encryption—all working together to create a robust, multi-layered encryption solution.

Decentralized Microfrontend Architecture

· 15 min read
xoron
positive-intentions

In the ever-evolving landscape of digital communication, decentralization has emerged as a powerful concept with diverse interpretations and applications. This article explores our unique approach to decentralization in the context of a chat application, pushing the boundaries of what it means to be truly self-hosted and user-centric.

Our decentralized chat application reimagines the traditional centralized model by leveraging cutting-edge web technologies. Built as a Progressive Web App (PWA), it combines the best of both worlds - the accessibility of web applications and the rich functionality of native apps. What sets our approach apart is its commitment to operating independently of central servers, placing control firmly in the hands of users.

By prioritizing user privacy and data security, we've created an architecture that challenges conventional notions of chat applications. This article will take you on a deep dive into the innovative components and architectural decisions that form the backbone of our truly decentralized chat app, showcasing how we're redefining digital communication for the privacy-conscious era.

ML-KEM for Beginners: A Complete Guide to Quantum-Resistant Encryption

· 11 min read
xoron
positive-intentions

Have you ever wondered how to protect your data from future quantum computers? What if I told you there's a way to encrypt messages that even quantum computers can't break?

In this beginner-friendly tutorial, we'll explore ML-KEM (Module-Lattice Key Encapsulation Mechanism), a quantum-resistant encryption algorithm that's becoming the new standard for secure communication. By the end of this guide, you'll understand what ML-KEM is, how it works, and how to use it in your own applications.

What is ML-KEM?

ML-KEM (formerly known as CRYSTALS-Kyber) is a post-quantum cryptographic algorithm standardized by NIST (National Institute of Standards and Technology). It's designed to be secure against attacks from both classical computers and quantum computers.

Why Do We Need Quantum-Resistant Encryption?

Most encryption algorithms used today (like RSA and ECC) rely on mathematical problems that are hard for classical computers but easy for quantum computers to solve. When quantum computers become powerful enough, they could break current encryption methods.

ML-KEM solves this problem by using mathematical structures (lattices) that are believed to be hard for both classical and quantum computers to break.

Key Concepts for Beginners

Before we dive into code, let's understand a few key concepts:

  1. Key Encapsulation Mechanism (KEM): Instead of directly encrypting data, ML-KEM creates a shared secret key that can then be used with symmetric encryption (like AES).

  2. Public Key vs Private Key:

    • Public key: Anyone can use this to encrypt messages to you (like a mailbox address)
    • Private key: Only you have this, and you use it to decrypt messages (like a mailbox key)
  3. Encapsulation: The process of creating an encrypted "package" containing a shared secret key

  4. Decapsulation: The process of extracting the shared secret key from the encrypted package

How ML-KEM Works: The Big Picture

Here's a simplified overview of how ML-KEM encryption works:

Getting Started: Installation

To use ML-KEM in your JavaScript/TypeScript project, you'll need the ML-KEM library. In our implementation, we use @hpke/ml-kem:

npm install @hpke/ml-kem

Step-by-Step Tutorial

Let's walk through a complete example of using ML-KEM, step by step.

Step 1: Generate a Key Pair

First, we need to generate a public/private key pair. The public key can be shared with anyone, while the private key must be kept secret.

import { MlKem768 } from "@hpke/ml-kem";

// Create an ML-KEM instance
const kem = new MlKem768();

// Generate a key pair
const keyPair = await kem.generateKeyPair();

console.log("Public key:", keyPair.publicKey);
console.log("Private key:", keyPair.privateKey);

What's happening here?

  • MlKem768 creates an ML-KEM instance using the 768-bit security level (equivalent to AES-192)
  • generateKeyPair() creates two keys:
    • Public key: 1184 bytes - safe to share publicly
    • Private key: 64 bytes - must be kept secret

Step 2: Encrypt a Message

Now let's encrypt a message using the public key. In ML-KEM, we don't encrypt the message directly. Instead, we:

  1. Create a shared secret using the public key (encapsulation)
  2. Use that shared secret to encrypt the message with AES-GCM
import { MLKEMCipherLayer } from "cryptography/CascadingCipher";

// Create the ML-KEM cipher layer
const layer = new MLKEMCipherLayer();

// The message we want to encrypt
const message = "Hello, Quantum-Resistant World!";
const plaintext = new TextEncoder().encode(message);

// Encrypt using the public key
const encrypted = await layer.encrypt(plaintext, {
publicKey: keyPair.publicKey
});

console.log("Encrypted:", encrypted);

What's in the encrypted result?

  • ciphertext: The encrypted message (AES-GCM encrypted)
  • parameters.encapsulated: The ML-KEM encapsulated key (1088 bytes)
  • parameters.iv: Initialization vector for AES (12 bytes)
  • parameters.salt: Salt for key derivation (16 bytes)

Step 3: Decrypt the Message

To decrypt, we use the private key to extract the shared secret, then use it to decrypt the message:

// Decrypt using the private key
const decrypted = await layer.decrypt(encrypted, {
privateKey: keyPair.privateKey
});

// Convert back to text
const decryptedMessage = new TextDecoder().decode(decrypted);
console.log("Decrypted:", decryptedMessage);
// Output: "Hello, Quantum-Resistant World!"

What's happening during decryption?

  1. ML-KEM extracts the shared secret from the encapsulated key using the private key (decapsulation)
  2. The shared secret is used to derive the AES key
  3. AES-GCM decrypts the ciphertext
  4. We get back the original message!

Complete Working Example

Here's a complete example that puts it all together:

import { MlKem768 } from "@hpke/ml-kem";
import { MLKEMCipherLayer } from "cryptography/CascadingCipher";

async function mlkemExample() {
// Step 1: Generate key pair
console.log("🔑 Generating ML-KEM key pair...");
const kem = new MlKem768();
const keyPair = await kem.generateKeyPair();
console.log("✅ Key pair generated!");
console.log(` Public key size: ${keyPair.publicKey.key.length} bytes`);
console.log(` Private key size: ${keyPair.privateKey.key.length} bytes`);

// Step 2: Encrypt a message
console.log("\n🔒 Encrypting message...");
const layer = new MLKEMCipherLayer();
const message = "This is a secret message!";
const plaintext = new TextEncoder().encode(message);

const encrypted = await layer.encrypt(plaintext, {
publicKey: keyPair.publicKey
});

console.log("✅ Message encrypted!");
console.log(` Encapsulated key size: ${encrypted.parameters.encapsulated.length} bytes`);
console.log(` Ciphertext size: ${encrypted.ciphertext.length} bytes`);

// Step 3: Decrypt the message
console.log("\n🔓 Decrypting message...");
const decrypted = await layer.decrypt(encrypted, {
privateKey: keyPair.privateKey
});

const decryptedMessage = new TextDecoder().decode(decrypted);
console.log("✅ Message decrypted!");
console.log(` Original: "${message}"`);
console.log(` Decrypted: "${decryptedMessage}"`);

// Verify they match
if (decryptedMessage === message) {
console.log("\n🎉 Success! Round-trip encryption/decryption works perfectly!");
}
}

// Run the example
mlkemExample().catch(console.error);

Output:

🔑 Generating ML-KEM key pair...
✅ Key pair generated!
Public key size: 1184 bytes
Private key size: 64 bytes

🔒 Encrypting message...
✅ Message encrypted!
Encapsulated key size: 1088 bytes
Ciphertext size: 48 bytes

🔓 Decrypting message...
✅ Message decrypted!
Original: "This is a secret message!"
Decrypted: "This is a secret message!"

🎉 Success! Round-trip encryption/decryption works perfectly!

Understanding the Encryption Process in Detail

Let's break down what happens under the hood when you encrypt with ML-KEM:

1. Key Encapsulation (Encryption Side)

// When you call layer.encrypt():
// Step 1: ML-KEM encapsulation
const { sharedSecret, enc } = await kem.encap({
recipientPublicKey: publicKey
});
// Result: sharedSecret (64 bytes) + enc (1088 bytes)

// Step 2: Derive AES key from shared secret
const salt = crypto.getRandomValues(new Uint8Array(16));
const aesKey = await deriveAESKey(sharedSecret, salt);

// Step 3: Encrypt message with AES-GCM
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await aesGcmEncrypt(message, aesKey, iv);

// Final result:
// - ciphertext: encrypted message
// - encapsulated: enc (1088 bytes)
// - salt: for key derivation
// - iv: for AES-GCM

2. Key Decapsulation (Decryption Side)

// When you call layer.decrypt():
// Step 1: ML-KEM decapsulation
const sharedSecret = await kem.decap({
recipientKey: privateKey,
enc: encrypted.parameters.encapsulated
});
// Result: sharedSecret (64 bytes) - same as encryption!

// Step 2: Derive AES key (same process as encryption)
const aesKey = await deriveAESKey(sharedSecret, encrypted.parameters.salt);

// Step 3: Decrypt message with AES-GCM
const plaintext = await aesGcmDecrypt(
encrypted.ciphertext,
aesKey,
encrypted.parameters.iv
);

Key Sizes and Performance

Understanding the sizes and performance characteristics helps you plan your application:

ComponentSizeNotes
Public Key1184 bytes (~1.16 KB)Safe to share publicly
Private Key64 bytesMust be kept secret
Encapsulated Key1088 bytes (~1.06 KB)Sent with each encrypted message
Shared Secret64 bytesUsed to derive AES key

Performance (typical values on modern hardware):

  • Key generation: ~5-15ms
  • Encryption (encapsulation): ~10-25ms
  • Decryption (decapsulation): ~10-25ms

Real-World Use Cases

ML-KEM is perfect for:

  1. Long-term data storage: Encrypt data that needs to stay secure for years
  2. Secure messaging: Protect messages from future quantum attacks
  3. File encryption: Encrypt files with quantum-resistant security
  4. API security: Secure API communications with post-quantum cryptography
  5. IoT devices: Protect devices that will be in use for many years

Common Questions

Q: Why is ML-KEM called a "Key Encapsulation Mechanism"?

A: ML-KEM doesn't encrypt your data directly. Instead, it creates an encrypted "package" (encapsulation) containing a shared secret key. This shared secret is then used with symmetric encryption (AES) to actually encrypt your data. This two-step process is more efficient than encrypting large amounts of data directly with public-key cryptography.

Q: Is ML-KEM slower than RSA or ECC?

A: ML-KEM operations are generally faster than RSA but slightly slower than ECC. However, the security benefit against quantum computers makes it worth the small performance cost. For most applications, the difference is negligible.

Q: Can I use ML-KEM with existing encryption?

A: Yes! ML-KEM works great alongside traditional encryption. Many systems use "hybrid" encryption: ML-KEM for key exchange + AES for data encryption. This provides defense-in-depth security.

Q: How do I store the keys securely?

A:

  • Public key: Can be stored anywhere (it's public!)
  • Private key: Should be stored encrypted, ideally in a hardware security module (HSM) or secure key storage
// Example: Store private key encrypted
const encryptedPrivateKey = await encryptKey(
privateKey.key,
userPassword
);
localStorage.setItem('mlkem_private_key', encryptedPrivateKey);

Security Best Practices

  1. Never share your private key: Keep it secret!
  2. Use secure random number generation: Always use crypto.getRandomValues() for salts and IVs
  3. Validate inputs: Check that keys and messages are the correct size
  4. Handle errors securely: Don't leak information about why decryption failed
  5. Rotate keys periodically: Generate new key pairs periodically for long-term security

Interactive Demo: Try It Yourself

Try encrypting and decrypting your own messages with this interactive demo:

What's Next?

Now that you understand the basics of ML-KEM, you can:

  1. Explore the implementation: Check out the ML-KEM cipher layer source code

  2. Learn about cascading ciphers: ML-KEM can be combined with other encryption layers for defense-in-depth security. Read our cascading cipher tutorial

  3. Try it in your project: Use ML-KEM in your own applications for quantum-resistant security

  4. Read the NIST standard: Learn more about the official ML-KEM specification (FIPS 203)

Summary

In this tutorial, we've learned:

  • What ML-KEM is: A quantum-resistant key encapsulation mechanism
  • Why it's important: Protects against future quantum computer attacks
  • How to use it: Generate keys, encrypt, and decrypt messages
  • How it works: Key encapsulation + AES-GCM encryption
  • Best practices: Security considerations and key management

ML-KEM is the future of secure encryption, and now you know how to use it! Whether you're building a messaging app, encrypting files, or securing API communications, ML-KEM provides the quantum-resistant security you need.

Have questions or want to learn more? Check out our cryptography repository or join our Discord community!


This tutorial is part of our ongoing research into post-quantum cryptography. For more technical deep-dives, check out our ML-KEM security audit documentation.

Building Scalable Group Messaging with MLS (Message Layer Security)

· 41 min read
xoron
positive-intentions

⚠️ WARNING: This document is not finished. The details in this document are subject to change.

End-to-end encrypted messaging for two people is a solved problem—Signal Protocol has set the gold standard. But what happens when you want to scale that security to group chats with dozens or hundreds of participants? Traditional pairwise encryption becomes a nightmare: N participants require N(N-1)/2 encrypted channels, each with its own key management overhead.

Enter MLS (Message Layer Security), the IETF's RFC 9420 standard designed specifically for scalable group messaging. MLS provides the same strong security guarantees as Signal Protocol—forward secrecy, post-compromise security, authentication—but does so efficiently for groups of any size.

In this article, we'll explore how MLS works, why it's a game-changer for group messaging, and walk through a complete browser-based implementation using the ts-mls library. We'll cover everything from the TreeKEM algorithm to practical P2P integration with WebRTC.

P2P Messaging: Technical Breakdown and Roadmap

· 10 min read
xoron
positive-intentions

⚠️ NOTE: This is still a work-in-progress and far from finished. It is free to use and not sold or monetized in any way. It has NOT been audited or reviewed. For testing purposes only, not a replacement for your current messaging app. I have open source examples of various parts of the app and I'm sure more investigation needs to be done for all details of this project. USE RESPONSIBLY!

This post serves as a technical update and roadmap for the P2P messaging project. Rather than promoting the project, I want to clearly explain how it works, what's currently implemented, what's in progress, and what's planned for the future. Cybersecurity is a constantly evolving field and no system can be completely secure. I've created an exhaustive list of features and practices that help make the messaging app as secure as possible.

Demo

Current Status: Implemented Features

The following features are currently working and implemented:

  • P2P Architecture - The app is fully decentralized and does not rely on a central server for exchanging messages. WebRTC is used to establish direct peer-to-peer connections between browsers.

  • Peer Discovery - Peer IDs are cryptographically random, generated automatically client-side with good conflict resilience to prevent ID guessing attacks. The peerjs-server (open source, self-hostable) is used as a connection broker to establish WebRTC connections.

  • End-to-End Encryption - Messages are protected with an application-level cascading cipher layered on top of WebRTC's built-in encryption. The cascading cipher combines multiple protocols including Signal Protocol, MLS, and AES. While there has been some pushback on the cascading cipher approach, it functions at the application level and ensures that the strongest algorithm provides protection. Any failure results in a cascading failure, providing redundancy on top of WebRTC's mandated encryption. I plan to add more protocols to this cascade to investigate post-quantum solutions.

  • Perfect Forward Secrecy - If a key is compromised, past messages remain protected. WebRTC already provides reasonable forward secrecy support in Firefox, and the Signal and MLS protocols in the cascading cipher contribute additional resilience in this regard.

  • Key Management - Users manage their own keys without relying on a central authority. The focus is on local-only encryption keys. Key sets are generated for each new connection and reused in future sessions.

  • Secure Signaling - The initial connection between peers is established securely. While exchanging connection data offline is a good approach, I'm working on providing more options. It's possible to establish a WebRTC connection without a connection-broker as demonstrated here.

  • Minimal Infrastructure - Fewer points of failure and attack. With WebRTC, messages can be sent without a central server and will work in offline hotspot networks.

  • Multimedia Support - Users can share animations and videos. This is important for providing an appealing user experience. Progress has been made on the UI component library to provide various features and functionality users expect in a messaging app.

  • Minimal Metadata - The app minimizes metadata to prevent tracking of who's messaging whom or when. The metadata footprint is fairly minimal, though this is relative to how feature-rich the application becomes. Features like "user is typing" notifications can be disabled, though they're common in messaging apps. Similarly, read receipts can be useful but come with metadata overhead. I plan to discuss these features more in the future and provide the ability to disable them.

In Progress: Features Under Development

The following features are currently being worked on:

  • Open Source Strategy - Moving towards a hybrid approach where relevant repositories are open source.

  • Registration-Free Operation - Creating a messaging app that eliminates the need for users to register is a feature desired in the cybersecurity space. The webapp approach offers these capabilities and is working. However, as I explore monetization options, I'm unable to see how registration can be completely avoided.

  • Encrypted Storage - Browser-based cryptography is capable, and it's possible to have important data like encryption keys encrypted at rest. This works well when using passkeys to derive a password. This approach is still incomplete—improvements are needed to take advantage of the Filesystem API for better persistence. Passkeys won't address this easily because they get cleared when you clear site-data (and you lose the password for decrypting the data).

  • User Education - The app is fairly technical, and I need more time to provide better information to users. The current website has a lot of technical details, but I think it's a mess if you want to find specific information. This needs to be improved.

  • Offline Messaging - P2P messaging has limitations, but I have an idea for addressing this: a self-hosted version that remains online and proxies messages to users when they come online. This is still in the early stages of development and has yet to be demonstrated.

  • Self-Destructing Messages - This is a common feature in secure messaging apps. It should be relatively simple to implement and will be added as a feature "soon".

  • JavaScript Security Concerns - There's a lot of rhetoric against using JavaScript for a project like this because of concerns about code being served over the internet. This is understandable, but I think these concerns can be mitigated. I can provide a self-hostable static bundle to avoid fetching statics from the internet. There's additional investigation towards using service workers to cache necessary files for offline use. I would like to add an explicit button to "fetch latest statics". The functionality is working, but more needs to be done before rolling it out.

  • Decentralized Profile - Users will want to continue conversations across devices with multi-device sync. It's possible to implement a P2P solution for this. This is an ongoing investigation.

  • STUN/TURN Server Configuration - The app currently uses metered.ca TURN servers only for brokering P2P connections. You have the option to use your own API key to enable features like "relay-mode", which will proxy all messages. I'm open to making this as configurable as necessary if users want to add multiple of their own servers.

Future Work: Planned Features

The following features are planned for future development:

  • Regular Security Audits - This is important for identifying and fixing vulnerabilities promptly. Security audits are very expensive, and until there is funding, this won't be possible. An alternative is an in-house security audit. I have made attempts to create such audits for the Signal Protocol and MLS. I'm sure I can dive into more details, but ultimately an in-house audit is invalidated by any bias I might impart.

  • Anonymity Features - Enabling users to communicate without revealing their identity is a feature many privacy advocates want. P2P messaging has nuanced tradeoffs. I'd like to further investigate onion-style routing to hide origins, but I also notice that WebRTC is generally discouraged when using the TOR network. It could help if users use a VPN, but that strays further from what I can offer as part of my app. This is an ongoing investigation.

Demo

Frequently Asked Questions

Why are there closed source parts? - This project comes in two flavors: open-source and closed-source. To view the open source version, see here. I've tried several grant applications and places that provide funding for open source projects. I'm aware they exist—unfortunately, they rejected this project for funding. I'm sure many are inundated with project submissions that have more professional quality and are able to articulate details better than myself. Continuing with open source only seems to put me at a competitive disadvantage.

Monetization - I'm investigating introducing Clerk. I hope to use that to create a subscription model. I would like to charge $1 per month as per the minimum allowed by Clerk. I started off thinking I could avoid charging users entirely, given that it seems a norm for secure messaging apps to be free. But given the grant rejections and the lack of donations on GitHub Sponsors (completely understandable), it's clear that it won't be able to sustain the project. I tried Google AdSense on the website/blog, but it was making practically nothing; so I disabled it because it wasn't a good look when it goes against the whole "degoogling" angle. This project is currently not funded or monetized in any way. (It's not for lack of trying)

How does it compare against Signal, SimpleX, Element, etc? - The project is far from finished, and it wouldn't make sense to create something as clear as a comparison table. Especially because core features like group messaging aren't working. Some technical details can be seen here if you want to draw your own comparison:

JavaScript over the internet is not secure - I'm investigating using service workers to cache files. This is working to some degree, but needs improvement before I fully roll it out. I would like to aim for something like a button on the UI called "Update" that would invalidate the service-worker cache to trigger an update. I hope to have something more elegant than self-hosting on localhost or using a dedicated app. It's possible to provide a static bundle that can work from running index.html in a browser without the need to run a static server. The static bundle of the open source version can be seen and tested to work from this directory: https://github.com/positive-intentions/chat/tree/staging/Frontend. When I reach a reasonable level of stability on the app, I would like to investigate things like a dedicated app as is possible on the open source version: https://positive-intentions.com/blog/docker-ios-android-desktop

How is this different from any other messaging app? - The key distinction between this project and others like Signal and SimpleX is that it's presented as a PWA. A key cybersecurity feature of this form-factor is that it can avoid installation and registration. It's understandable that such a feature doesn't appeal to everyone, but along with the native build, it should cover all bases depending on your threat model.

What about Chat Control? - I see a lot of fear mongering in the cybersecurity community around chat control. I aim to create something that doesn't have the censorship pitfalls of a traditional architecture. A previous post on the matter: https://www.reddit.com/r/europrivacy/comments/1ndbkxn/help_me_understand_if_chatcontrol_could_affect_my

Is it AI-generated? - AI is being used appropriately to help me in various aspects. I hope it doesn't undermine the time and effort I put into the project.

The goal is to provide industry-grade security encapsulated into a standalone webapp. Feel free to reach out for clarity on any details or check out the following links:

IMPORTANT NOTE: It's worth repeating: this is still a work in progress and not ready to replace any existing solution. Many core features like group messaging are not working. Provided for testing, demo, and feedback purposes only.

Adapting the Signal Protocol for P2P Messaging

· 31 min read
xoron
positive-intentions

⚠️ WARNING: This document is not finished. The details in this document are subject to change.

Signal Protocol

The Signal Protocol has become the gold standard for end-to-end encrypted messaging, powering applications like WhatsApp, Signal, and Facebook Messenger. But what happens when you want to implement the same level of security in a truly peer-to-peer environment—one without centralized servers managing pre-keys and message routing?

In this article, we'll explore how to adapt the Signal Protocol's X3DH (Extended Triple Diffie-Hellman) key agreement and Double Ratchet algorithm for direct peer-to-peer communication over WebRTC. We'll discuss the challenges unique to P2P environments, propose practical solutions, and walk through a browser-based JavaScript implementation that maintains the security guarantees of the original protocol.

Introducing Quantum-Resistant Encryption in JavaScript

· 6 min read
xoron
positive-intentions

⚠️ NOTE: This is an experimental implementation for testing purposes. Post-quantum cryptography is an active area of research. While ML-KEM is a NIST standard, this JavaScript implementation has not undergone formal security audits. Use responsibly.

We're excited to announce that our P2P messaging application now supports quantum-resistant encryption using ML-KEM (CRYSTALS-Kyber), a NIST-standardized post-quantum key encapsulation mechanism. This addition brings quantum-resistant security to our cascading cipher system, providing protection against future quantum computing attacks.

What is Post-Quantum Cryptography?

Quantum computers pose a significant threat to current public-key cryptography. While quantum computers are still in early stages, they could eventually break widely used algorithms like RSA, ECC (Elliptic Curve Cryptography), and Diffie-Hellman using Shor's algorithm. Quantum-resistant cryptography uses mathematical problems that quantum computers struggle to solve.

Current Vulnerabilities

🔴 Vulnerable to Quantum Attacks:

  • RSA (Shor's algorithm)
  • Elliptic Curve Cryptography (Shor's algorithm)
  • Diffie-Hellman key exchange (Shor's algorithm)

🟢 Quantum-Resistant:

  • AES-256 (Grover's algorithm only reduces to AES-128 equivalent)
  • SHA-256/SHA-3 (quantum advantage limited)
  • ML-KEM (CRYSTALS-Kyber) - Lattice-based KEM
  • ML-DSA (CRYSTALS-Dilithium) - Lattice-based signatures

The ML-KEM (CRYSTALS-Kyber) Standard

ML-KEM (Modular Lattice-based Key Encapsulation Mechanism) is one of the first post-quantum algorithms standardized by NIST in 2024. It's designed as a key encapsulation mechanism (KEM) rather than direct encryption, making it ideal for establishing shared secrets between parties.

📚 Learn More:

Key Properties:

  • ⚡ Fast: ~30-50ms for key encapsulation/decapsulation
  • 📦 Compact: 1184-byte public keys, 1088-byte encapsulated keys
  • 🔒 Security: Based on Learning With Errors (LWE) problem
  • 🎯 Standard: NIST FIPS 203 compliant
  • 🌐 Pure JavaScript: No native dependencies

Integration with Cascading Cipher

The ML-KEM implementation is integrated as a cipher layer in our cascading cipher architecture:

import {
CascadingCipherManager,
MLSCipherLayer,
SignalCipherLayer,
AESCipherLayer,
MLKEMCipherLayer, // New post-quantum layer
} from 'cryptography/CascadingCipher';

How It Works

ML-KEM functions as a key exchange layer in the encryption cascade:

Encryption Flow:

  1. ML-KEM Layer: Receiver's public key → encapsulated shared key
  2. Signal Layer: Double Ratchet adds forward secrecy
  3. MLS Group Layer: Group messaging support
  4. AES Layer: Fast symmetric encryption

Why This Approach?

Defense in Depth:

  • ML-KEM protects against quantum attacks
  • Signal Protocol provides forward secrecy
  • MLS enables group messaging
  • AES adds fast symmetric encryption

Each layer provides independent protection. If ML-KEM is broken, other layers still protect data.

Implementation Details

ML-KEM Cipher Layer Architecture

The MLKEMCipherLayer class implements the standard CipherLayer interface:

class MLKEMCipherLayer implements CipherLayer {
readonly name = "ML-KEM-768";
readonly version = "1.0.0";

async encrypt(data: Uint8Array, keys: MLKEMKeys): Promise<EncryptedPayload>;
async decrypt(payload: EncryptedPayload, keys: MLKEMKeys): Promise<Uint8Array>;
}

Key Properties

  • Public Key Size: 1184 bytes (for KEM-768 parameter set)
  • Private Key Size: 64 bytes
  • Encapsulated Key: 1088 bytes
  • Shared Secret: 32-64 bytes (used as AES key material)

Security Features

✅ Zeroization:

  • All sensitive buffers are cleared after use
  • Shared secrets, private keys, IVs are zeroized

✅ Timing Attack Protection:

  • Constant-time key validation
  • No early returns in validation logic

✅ IV Reuse Protection:

  • Tracks used IVs per public key
  • Random IV generation with collision detection

✅ Input Validation:

  • Maximum plaintext size: 10MB (prevents DoS)
  • Strict parameter size checks

Example Usage

import { MlKem768 } from '@hpke/ml-kem';
import { MLKEMCipherLayer } from 'cryptography/CascadingCipher';

// Generate ML-KEM key pair
const kem = new MlKem768();
const keyPair = await kem.generateKeyPair();

// Create cipher layer
const layer = new MLKEMCipherLayer();

// Encrypt plaintext
const plaintext = new TextEncoder().encode('Quantum-secure message!');
const encrypted = await layer.encrypt(plaintext, {
publicKey: keyPair.publicKey
});

// Decrypt
const decrypted = await layer.decrypt(encrypted, {
privateKey: keyPair.privateKey
});

console.log(new TextDecoder().decode(decrypted)); // "Quantum-secure message!"

Module Federation Integration

The cryptography module is exposed via module federation for import in the P2P application:

// cryptography/webpack.config.js
module.exports = {
// Module federation config
name: 'cryptography',
exposes: {
'./CascadingCipher': './src/crypto/CascadingCipher/index.ts',
'./MLKEMUtils': './src/crypto/MLKEMUtils.ts',
// ... other exports
}
};

The P2P app imports via remote:

// p2p/webpack.config.js
module.exports = {
remotes: {
cryptography: 'cryptography@http://localhost:8083/remoteEntry.js'
}
};

Storybook Examples

Interactive Storybook stories demonstrate ML-KEM functionality:

  • MLKEMBeginnerTutorial.stories.js - Step-by-step ML-KEM basics
  • MLKEMDemo.stories.js - Real encryption/decryption examples
  • MLKEMTimingTests.stories.js - Performance benchmarks

Try the Live Demo:

Examples available at:

Performance Characteristics

Encryption Benchmarks:

  • Key Generation: ~10-15ms
  • Encapsulation: ~15-25ms
  • Decapsulation: ~15-25ms
  • Total Operation: ~30-50ms

Overhead Analysis:

  • Message Size: Adds ~1100 bytes (encapsulated key + IV + salt)
  • Processing Time: +30-50ms compared to classical-only encryption
  • Memory: Minimal (key material cached)

Comparison: Classical vs Post-Quantum

PropertyClassical (ECDH)Post-Quantum (ML-KEM)
Key Size32-65 bytes1184 bytes
Ciphertext32-65 bytes1088 bytes
Computation~5-10ms~30-50ms
Quantum Security❌ Vulnerable✅ Resistant
Standard StatusLegacyNIST-approved

Security Considerations

Current Status

✅ Implemented:

  • ML-KEM-768 key pair generation
  • Key encapsulation/decapsulation
  • Integration with cascading cipher
  • Security hardening (zeroization, timing protection)

⚠️ Limitations:

  • JavaScript implementation (not FIPS 140-2 validated)
  • No formal security audit
  • Experimental/educational use only
  • Performance overhead vs classical algorithms

Recommendations

✅ Suitable For:

  • Long-term confidential data storage
  • Future-proofing sensitive communications
  • Experimental/educational projects
  • Low-volume secure messaging

❌ Not Recommended For:

  • Production without formal security review
  • High-performance applications
  • Regulatory compliance without validation

Future Roadmap

Planned Enhancements:

  1. ML-DSA Integration - Post-quantum digital signatures
  2. Hybrid Key Exchange - Combine classical + post-quantum
  3. FIPS 140-2 Validation - Formal certification
  4. WASM Optimization - Improve performance
  5. Multiple Parameter Sets - Support KEM-512/1024 variants
  6. Self-Certified Public Keys - Simplified identity verification

Conclusion

The addition of ML-KEM post-quantum cryptography brings quantum-resistant security to our P2P messaging platform. By integrating it within the cascading cipher architecture, we provide defense-in-depth protection against both classical and quantum attacks.

While this implementation is still experimental, it demonstrates that building quantum-resistant applications in JavaScript is feasible. As quantum computing capabilities evolve, having quantum-resistant layers in place positions the project for long-term security.

Resources

Security, Privacy and Authentication

· 7 min read
xoron
positive-intentions

In digital communication, security and privacy are the major initials. Our latest project—a chat application built with JavaScript—aims to provide a robust communication platform fortified with industry-grade encryption. While we aspire to offer one of the most secure chat experiences available, declaring it "the most secure chat app in the world" might be premature. This article delves into the technology behind our app, current security measures, and the remaining challenges. We also welcome feedback to help refine our approach.