This site is not affiliated with or endorsed by Cloudflare, Inc. It showcases deployable experiments across most Cloudflare products.
Edge Platform

ML-KEM (Post-Quantum)

Post-quantum ML-KEM-768 (Kyber) key encapsulation with Web Crypto at the edge - PQC demo for quantum-resistant key exchange

ML-KEM (formerly Kyber) is NIST's post-quantum key encapsulation standard. This Worker demonstrates ML-KEM-768 with the Web Crypto API on Cloudflare Workers: generate a key pair, encapsulate a shared secret, and decapsulate to verify - a pasteable PQC reference for quantum-resistant key exchange at the edge.

API Reference

GET /keygen

Generate an ML-KEM-768 key pair, encapsulate to a ciphertext, and decapsulate to verify that the shared secrets match.

Parameters: None

Example request

curl https://ml-kem.example.workers.dev/keygen

Example response

{
  "algorithm": "ML-KEM-768",
  "publicKey": "a1b2c3d4...",
  "ciphertext": "e5f6g7h8...",
  "encapsulatedSecret": "i9j0k1l2...",
  "decapsulatedSecret": "i9j0k1l2...",
  "secretsMatch": true,
  "publicKeyLength": 1184,
  "ciphertextLength": 1088,
  "sharedSecretLength": 32
}

Fields

  • algorithm string - The ML-KEM variant used (ML-KEM-768)
  • publicKey string - Hex-encoded public key
  • ciphertext string - Hex-encoded ciphertext from encapsulation
  • encapsulatedSecret string - Truncated hex-encoded shared secret (first 32 chars + "...")
  • decapsulatedSecret string - Truncated hex-encoded shared secret from decapsulation
  • secretsMatch boolean - true when encapsulated and decapsulated secrets match
  • publicKeyLength number - Public key size in bytes (1184 for ML-KEM-768)
  • ciphertextLength number - Ciphertext size in bytes (1088 for ML-KEM-768)
  • sharedSecretLength number - Shared secret size in bytes (32)

Use Cases

  • Demonstrating post-quantum cryptography at the edge
  • Testing ML-KEM-768 implementation for migration planning
  • Building quantum-resistant key exchange demos
  • Educational exploration of NIST post-quantum standards

Limitations

  • Uses the webcrypto_modern_algorithms compatibility flag (may not be available in all environments)
  • Generates a fresh key pair on every request (stateless demo)
  • Shared secrets are truncated in response for readability
  • No persistent storage or key management

Use in your project

Copy these files into an existing Worker. Prefer Deployment to try the full experiment first. Source: apps/experiments/ml-kem.

DependenciesNoneBindingsNonePlatformWeb Crypto (`crypto.subtle`), `webcrypto_modern_algorithms` flag
export async function generateKeyPairAndTest() {  const keyPair = (await crypto.subtle.generateKey(    {      name: "ML-KEM",      namedCurve: "ML-KEM-768",    },    true,    ["deriveBits"]  )) as CryptoKeyPair;  const publicKeyExported = await crypto.subtle.exportKey("raw", keyPair.publicKey);  const publicKeyHex = arrayBufferToHex(publicKeyExported);  const { ciphertext, sharedSecret: encapsulatedSecret } = await crypto.subtle.encapsulate(    keyPair.publicKey  );  const ciphertextHex = arrayBufferToHex(ciphertext);  const encapsulatedSecretHex = arrayBufferToHex(encapsulatedSecret);  const decapsulatedSecret = await crypto.subtle.decapsulate(keyPair.privateKey, ciphertext);  const decapsulatedSecretHex = arrayBufferToHex(decapsulatedSecret);  const secretsMatch = encapsulatedSecretHex === decapsulatedSecretHex;  return {    algorithm: "ML-KEM-768",    publicKey: publicKeyHex,    ciphertext: ciphertextHex,    encapsulatedSecret: encapsulatedSecretHex.substring(0, 32) + "...",    decapsulatedSecret: decapsulatedSecretHex.substring(0, 32) + "...",    secretsMatch,    publicKeyLength: publicKeyExported.byteLength,    ciphertextLength: ciphertext.byteLength,    sharedSecretLength: decapsulatedSecret.byteLength,  };}function arrayBufferToHex(buffer: ArrayBuffer): string {  return Array.from(new Uint8Array(buffer))    .map((b) => b.toString(16).padStart(2, "0"))    .join("");}

Deployment

Deploy to Cloudflare Workers

Deploy to Cloudflare Workers

The Worker is live once deployment succeeds.

Test the endpoint

curl https://ml-kem.your-subdomain.workers.dev/keygen

You should see a JSON response with secretsMatch: true.

Local Development

cd apps/experiments/ml-kem
npm install
npm run dev

The Worker runs at http://localhost:8787.

Test locally

curl http://localhost:8787/keygen

Configuration

Compatibility flags

This experiment requires the webcrypto_modern_algorithms flag in wrangler.json:

{
  "compatibility_flags": ["webcrypto_modern_algorithms"]
}

No bindings or secrets required.

Cloudflare Features Used

  • Workers - Edge compute platform
  • Web Crypto API - Cryptographic operations at the edge
  • ML-KEM-768 post-quantum key encapsulation (via webcrypto_modern_algorithms flag)

Next Steps

On this page