All posts

Post-Quantum Cryptography Reaches the Browser: ML-KEM and ML-DSA in Chrome 151's Web Crypto API

Chrome 151 opens an origin trial that adds NIST's post-quantum algorithms — ML-KEM, ML-DSA, the X-Wing hybrid, and ChaCha20-Poly1305 — directly to the Web Crypto API. Here's what changes for application-layer crypto, with working code.

Post-Quantum Cryptography Reaches the Browser: ML-KEM and ML-DSA in Chrome 151's Web Crypto API

Transport-layer post-quantum cryptography has been quietly protecting your traffic for a while now — Chrome switched its TLS key exchange to ML-KEM in 2024, and Cloudflare and others followed. But that protection stops at the edge. Anything your application encrypts or signs itself — end-to-end messages, signed tokens, sealed documents, client-side envelopes — has been stuck with RSA and elliptic-curve primitives that a sufficiently large quantum computer will eventually break.

Chrome 151, in beta as of July 3, 2026, starts closing that gap. It opens an origin trial that adds a batch of modern algorithms to the Web Cryptography API, including NIST's standardized post-quantum schemes. For the first time, crypto.subtle can do quantum-resistant key exchange and signatures without a WebAssembly library.

What Chrome 151 adds

The origin trial — labeled "WebCrypto algorithm updates" and specified in the WICG's Modern Algorithms in the Web Cryptography API draft — brings four things to crypto.subtle:

  • ML-KEM (FIPS 203) — a Key Encapsulation Mechanism for quantum-resistant key exchange, in the ML-KEM-512, ML-KEM-768, and ML-KEM-1024 parameter sets.
  • ML-DSA (FIPS 204) — a lattice-based digital signature scheme, in the ML-DSA-44, ML-DSA-65, and ML-DSA-87 sets.
  • X-Wing — a hybrid KEM that combines ML-KEM-768 with classical X25519, so a break in either component alone doesn't compromise the shared secret.
  • ChaCha20-Poly1305 (RFC 8439) — a widely deployed AEAD cipher that, until now, the Web Crypto API simply didn't expose.

The trial runs across Chrome 151 through 154 on desktop and Android, with a developer trial (behind a flag) available from Chrome 150. That means it is emphatically not Baseline yet — this is experimental surface you opt into, not something to ship to all users this quarter.

KEMs need a new mental model

If you've used the Web Crypto API before, encryption meant "encrypt these bytes to a public key." A Key Encapsulation Mechanism works differently, and the API reflects that. Instead of encrypting a message directly, the sender encapsulates: the algorithm uses the recipient's public key to generate a fresh shared secret and a ciphertext that carries it. The recipient runs decapsulate with their private key to recover the same shared secret. You then use that secret with a symmetric cipher such as AES-GCM to protect the actual payload.

To support this, the spec introduces four new methods on SubtleCryptoencapsulateKey, encapsulateBits, decapsulateKey, and decapsulateBits — plus matching key usages of the same names. encapsulateKey hands you the shared secret already imported as a ready-to-use CryptoKey; encapsulateBits gives you the raw bytes if you'd rather derive from them yourself.

ML-KEM key exchange, end to end

Here's the full round trip. The recipient generates a long-lived ML-KEM key pair and publishes the public key:

// Recipient: generate an ML-KEM-768 key pair
const { publicKey, privateKey } = await crypto.subtle.generateKey(
  { name: "ML-KEM-768" },
  true,                                  // extractable
  ["encapsulateKey", "decapsulateKey"],
);
// publicKey ends up usable for encapsulation,
// privateKey for decapsulation.

The sender takes that public key and encapsulates a shared AES-GCM key. encapsulateKey returns both the derived sharedKey (a CryptoKey) and the ciphertext you transmit alongside your message:

// Sender: derive a shared AES-GCM key + ciphertext to send
const { sharedKey, ciphertext } = await crypto.subtle.encapsulateKey(
  { name: "ML-KEM-768" },
  publicKey,
  { name: "AES-GCM", length: 256 },      // algorithm for the derived key
  false,                                 // derived key non-extractable
  ["encrypt", "decrypt"],
);

const iv = crypto.getRandomValues(new Uint8Array(12));
const sealed = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  sharedKey,
  new TextEncoder().encode("transfer authorized"),
);
// Send { ciphertext, iv, sealed } to the recipient.

The recipient reverses it — decapsulateKey recovers the identical AES-GCM key from the KEM ciphertext, and normal AES-GCM decryption follows:

// Recipient: recover the same shared key, then decrypt
const sharedKey = await crypto.subtle.decapsulateKey(
  { name: "ML-KEM-768" },
  privateKey,
  ciphertext,
  { name: "AES-GCM", length: 256 },
  false,
  ["encrypt", "decrypt"],
);

const plaintext = await crypto.subtle.decrypt(
  { name: "AES-GCM", iv },
  sharedKey,
  sealed,
);
console.log(new TextDecoder().decode(plaintext)); // "transfer authorized"

Adopting the X-Wing hybrid is a one-line change: swap "ML-KEM-768" for "X-Wing" in all three calls. You get post-quantum security from the ML-KEM half and a classical safety net from X25519, which is the conservative default most teams should reach for while these schemes are still young.

ML-DSA signatures

Signatures use the familiar sign and verify methods — only the algorithm name changes. This is the piece that matters for signed software artifacts, license tokens, or any integrity check you want to outlive the arrival of practical quantum computers:

const { publicKey, privateKey } = await crypto.subtle.generateKey(
  { name: "ML-DSA-65" },
  true,
  ["sign", "verify"],
);

const message = new TextEncoder().encode("release-2.4.0");

const signature = await crypto.subtle.sign(
  { name: "ML-DSA-65" },
  privateKey,
  message,
);

const valid = await crypto.subtle.verify(
  { name: "ML-DSA-65" },
  publicKey,
  signature,
  message,
);
console.log(valid); // true

ML-DSA-65 is the middle security level and a sensible starting point; ML-DSA-44 is lighter, ML-DSA-87 is the most conservative.

The costs you should plan for

Post-quantum primitives are not a free drop-in, and it's worth being honest about the trade-offs before you architect around them.

Sizes are much larger. ML-KEM and ML-DSA keys, ciphertexts, and signatures are considerably bigger than their RSA or elliptic-curve equivalents — think kilobytes where you're used to a few hundred bytes. An ML-DSA signature dwarfs an Ed25519 one. If you embed signatures in tokens, headers, or QR codes, measure the impact before committing.

It's an origin trial, not Baseline. Feature-detect and fall back. Because these algorithms are gated behind the trial and only in Chromium for now, treat availability as the exception:

async function hasMlKem() {
  try {
    await crypto.subtle.generateKey(
      { name: "ML-KEM-768" },
      false,
      ["encapsulateKey", "decapsulateKey"],
    );
    return true;
  } catch {
    return false;
  }
}

Hybrids are the safe bet. The cryptographic community's consensus during this transition is to combine post-quantum and classical schemes rather than bet everything on the newer math. X-Wing exists precisely for that reason — prefer it over bare ML-KEM unless you have a specific reason not to.

Practical takeaways

The threat model here is "harvest now, decrypt later": an adversary recording encrypted traffic today to break once quantum hardware matures. That makes long-lived confidential data — not ephemeral session traffic — the real priority for post-quantum protection.

Start by inventorying where your application does its own cryptography rather than relying on TLS: end-to-end encrypted content, client-side sealed storage, signed tokens, and update-verification flows. Those are the surfaces this API targets. Prototype behind the origin trial now with X-Wing for key exchange and ML-DSA-65 for signatures, keep your existing classical path as the fallback, and budget for the larger payload sizes in your protocol design. Don't rip out RSA or ECDSA yet — this is experimental, single-engine surface — but the migration path from lab to production just got dramatically shorter, and the teams that map their crypto surface today will move fastest when these algorithms reach Baseline.

Sources: Chrome 151 beta — Chrome for Developers · Modern Algorithms in the Web Cryptography API (WICG draft) · Google Chrome Switches to ML-KEM for Post-Quantum Cryptography

Back to all posts
Next step

Need help shipping software?

Tell us what you're trying to build. A discovery call, a one-page summary within 48 hours, a proposal within a week.

Response · 48h·NDA on request·US contracts only