cd /news/ai-infrastructure/support-for-modern-cryptographic-alg… · home › topics › ai-infrastructure › article
[ARTICLE · art-143205] src=blog.cloudflare.com ↗ pub= topic=ai-infrastructure verified=true sentiment=↑ positive

Support for modern cryptographic algorithms in Workers

Cloudflare Workers added opt-in Web Crypto support for post-quantum algorithms, including ML-KEM-768 and ML-KEM-1024 for key encapsulation and ML-DSA-44, ML-DSA-65, and ML-DSA-87 for signatures, available behind the `webcrypto_modern_algorithms` compatibility flag. The APIs add `encapsulateBits()`, `decapsulateBits()`, `encapsulateKey()`, `decapsulateKey()`, `getPublicKey()`, `SubtleCrypto.supports()`, and JWK import/export, letting developers experiment with ML-KEM and ML-DSA without bundling a separate cryptographic implementation. Cloudflare said the primitives are building blocks for validating post-quantum integrations rather than a full migration path, while the underlying specification is still moving.

by read8 min views2 publishedOct 1, 2026
Support for modern cryptographic algorithms in Workers
Image: Cloudflare Blog

Today, Cloudflare Workers is adding support for post-quantum-resistant algorithms within Web Crypto. These are defined in Modern Algorithms in the Web Cryptography API draft community group report, and include:

  • ML-KEM-768 and ML-KEM-1024 for key encapsulation
  • ML-DSA-44, ML-DSA-65, and ML-DSA-87 for signatures
  • encapsulateBits() ,decapsulateBits() ,encapsulateKey() , anddecapsulateKey()
  • getPublicKey()
  • SubtleCrypto.supports()
  • JWK import and export for these algorithms

For developers preparing for the post-quantum transition, these opt-in Web Crypto APIs make it easier to experiment with ML-KEM and ML-DSA without bundling a separate cryptographic implementation. They do not provide a full migration path, but rather building blocks that can be used to validate your integration.

This support is available behind the webcrypto_modern_algorithms compatibility flag while the specification is still moving.

Background #

Web Crypto is one of those APIs you only notice when it lacks the primitive you need. If you want to experiment with newer post-quantum algorithms in a JavaScript environment, it’s hard. You either cannot build the protocol directly on top of Web Crypto, or you bring your own cryptography implementation in JavaScript or WebAssembly.

Neither option is ideal. They put the burden of selecting and maintaining cryptographic implementations on implementers, who see their applications get larger as they bundle cryptographic code. And this is work that needs to be reproduced for all downstream libraries. As the ecosystem needs to transition to post-quantum-resistant algorithms sooner than expected, we cannot wait for better post-quantum algorithms. Developers need access to these primitives now so they can test, evaluate, and improve post-quantum integrations.

In this post, we’ll explain how you can implement these primitives today, and start to prepare your applications for the post-quantum era.

The short version #

Here is what ML-KEM looks like in Workers. One side has a public key. The other side encapsulates a shared secret to that public key. The holder of the private key decapsulates it and gets the same secret.

const keys = await crypto.subtle.generateKey("ML-KEM-768", true, [
  "encapsulateBits",
  "decapsulateBits",
]);

const { sharedKey, ciphertext } = await crypto.subtle.encapsulateBits(
  "ML-KEM-768",
  keys.publicKey,
);

const sameSharedKey = await crypto.subtle.decapsulateBits(
  "ML-KEM-768",
  keys.privateKey,
  ciphertext,
);

There is no encryption in that snippet yet. ML-KEM gives both sides shared key material. Protocols such as Hybrid Public Key Encryption (HPKE) then feed that material into a key schedule and an AEAD such as the AES-GCM algorithm.

ML-DSA is closer to what most developers have already seen with Ed25519 or ECDSA: generate a key pair, sign bytes, verify bytes.

const data = new TextEncoder().encode("hello post-quantum");

const { publicKey, privateKey } = await crypto.subtle.generateKey(
  "ML-DSA-44",
  false,
  ["sign", "verify"],
);

const signature = await crypto.subtle.sign("ML-DSA-44", privateKey, data);
const valid = await crypto.subtle.verify(
  "ML-DSA-44",
  publicKey,
  signature,
  data,
);

These examples are deliberately small. They are not protocols. They are the JavaScript hooks for cryptographic primitives that protocols need.

Why this matters #

Post-quantum migration is not one switch. It is a lot of protocols, libraries, services, and deployment environments learning how to use different primitives.

Some of that work is already visible in TLS and SSH. OpenSSH added support for mlkem768x25519 in 2024. HPKE has a draft for post-quantum and hybrid KEMs ongoing at the IETF. The IETF published RFC 9964 for ML-DSA in JOSE, as well as an adopted draft for JWE using PQ & PQ/T HPKE. HTTP Message Signatures can use different signature algorithms, as long as the signer and verifier agree on how to produce and verify the signature.

To support all these on Cloudflare Workers, developers needed support for the underlying cryptographic primitives within Web Crypto.

Without it, a Workers developer could still experiment with post-quantum code, but they had to bundle a separate implementation. That is useful for portability and for early experiments, but it is not where we want every production application to end up.

Signing JWTs with ML-DSA #

Signed JSON Web Tokens (JWTs) are a familiar example that protect using JSON Web Signatures (JWS). With a panva/jose library that maps ML-DSA-* algorithms to Web Crypto, the application code is as follows:

import * as jose from "jose";

const alg = "ML-DSA-44";
const { publicKey, privateKey } = await jose.generateKeyPair(alg);

const jwt = await new jose.SignJWT({ sub: "alice" })
  .setProtectedHeader({ alg })
  .setIssuedAt()
  .setExpirationTime("5m")
  .sign(privateKey);

await jose.jwtVerify(jwt, publicKey);

JWTs are only one example. The larger point is that libraries can delegate ML-DSA operations to the runtime instead of carrying their own implementation for every environment. With Workers supporting ML-DSA natively, libraries can delegate signing to the runtime rather than shipping their own implementation.

HPKE and OHTTP #

ML-KEM is a key encapsulation mechanism. On its own, it gives two parties shared key material. HPKE turns that into a complete encryption construction by adding a key schedule and an AEAD.

Libraries such as panva/hpke are already structured around Web Crypto and runtime support. With the Workers runtime exposing ML-KEM, HPKE implementations can use the native primitive where available.

import * as HPKE from "hpke";

const plaintext = new TextEncoder().encode("Hello World!");

const suite = new HPKE.CipherSuite(
  HPKE.KEM_ML_KEM_768,
  HPKE.KDF_HKDF_SHA256,
  HPKE.AEAD_AES_128_GCM,
);

const recipient = await suite.GenerateKeyPair();
const sealed = await suite.Seal(recipient.publicKey, plaintext);

const opened = await suite.Open(
  recipient.privateKey,
  sealed.encapsulatedSecret,
  sealed.ciphertext,
);

This is the shape we want for protocols such as OHTTP as well (which we’ve discussed before). OHTTP uses HPKE. If HPKE can use a post-quantum KEM through Web Crypto, then that peer can start discussing migrating to a ciphersuite that supports these primitives.

Libraries may require runtime-specific integration changes. Here, HPKE.CipherSuite selects implementations according to the algorithms available in the runtime.

Getting a public key from a private key #

Several protocols need to publish or derive a public key after a private key. Previously, this often meant keeping both around or doing format-specific work.

The new getPublicKey() helper does the direct thing:

const publicKey = await crypto.subtle.getPublicKey(privateKey, ["verify"]);

For ML-KEM, the usage is different because public keys encapsulate and private keys decapsulate:

const publicKey = await crypto.subtle.getPublicKey(privateKey, [
  "encapsulateBits",
]);

This is a small API that aims to remove code used a lot across libraries that deal with public key cryptography.

Checking support #

Because the API is not yet supported across runtimes, libraries should check for it instead of assuming it exists everywhere.

if (SubtleCrypto.supports("sign", "ML-DSA-44")) {
  const keys = await crypto.subtle.generateKey("ML-DSA-44", false, [
    "sign",
    "verify",
  ]);
}

Libraries that run across Workers, Node.js, Deno, browsers, and other Web-interoperable runtimes need this kind of check. It also helps when only part of the modern algorithms proposal is implemented.

What is supported today #

The initial Workers implementation supports ML-KEM-768 as a KEM and ML-DSA-44 as a signature algorithm. All require the webcrypto_modern_algorithms flag to be set.

// Include the following in your `wrangler.jsonc`
{
	// Opt into modern crypto algorithms
	"compatibility_flags": [
		"webcrypto_modern_algorithms"
	]
}

For completeness, we also support ML-KEM-1024, ML-DSA-65, and ML-DSA-87. ML-KEM-512 is not supported because the BoringSSL version used by Workers does not expose it. Rather than add a separate implementation just for that variant, we are starting with the algorithms available through the native crypto library.

The most recent list of supported algorithms can always be found on our developer documentation.

How it’s been implemented #

Workers run on workerd. It’s an open-source runtime built on V8. The implementation adds ML-KEM and ML-DSA support to workerd's Web Crypto layer, backed by BoringSSL primitives.

This change also adds Web Platform Tests for the modern algorithms API surface, Workers-specific tests for compatibility flag behavior, and TypeScript definitions under the new Workers types.

We split this out from a larger proposal from panva. The change discussed in this blog, which is the first part of the modern Web Crypto algorithm specification, focuses on ML-KEM, ML-DSA, helper APIs, and JWK support. Other algorithms from the W3C Web Incubator Community Group (WICG) proposal, such as SHA-3, cSHAKE, TurboSHAKE, and ChaCha20-Poly1305, are not part of this initial change.

That smaller scope makes review easier. It also gives library authors something concrete to test before the whole modern algorithms proposal is implemented.

Note that ML-DSA public keys and signatures are substantially larger than RSA or Ed25519. The integration of these algorithms in the runtime improves performance and reduces the need for bundling. However, it does not change the reality that the size of keys, signatures, or ciphertext is increasing, on the wire or when stored.

What may come next #

The WICG proposal covers more than ML-KEM and ML-DSA. We have not implemented the following from the original contribution by Filip Skokan in cloudflare/workerd#6403, which will need further review. This includes the SHA-3 hash function, ChaCha20-Poly1305 AEAD (discussion about XChaCha20-Poly1305 in wicg/webcrypto-modern-algos#1), cSHAKE, TurboSHAKE, and HPKE (discussed in wicg/webcrypto-modern-algos#2). An implementation has already been tested against panva/hpke and panva/jose test suites to verify the implementation.

There is also a practical question about when this should become default, rather than opt-in. For now, all these algorithms are gated behind a compatibility flag. The API is based on a draft, and we want feedback from library authors before treating it as stable.

Start experimenting today #

This change does not make every protocol post-quantum by itself. It gives Workers developers and library authors the primitives they were missing: ML-KEM for key encapsulation, ML-DSA for signatures, and helper APIs that make those primitives usable through Web Crypto.

If you maintain a library that currently bundles its own post-quantum implementation, this is a good time to try the native API and tell us what does not fit. The fastest way to find the rough edges is to put real protocol code on top of it. All the details are in our changelog.

We would like to thank Filip Skokan for the original contribution and iterations, Felix Hanau, James Snell, Bas Westerbaan, and Peter Wu for reviewing the code, and Daniel Huigens for co-authoring the specification work this implementation follows.

── more in #ai-infrastructure 4 stories · sorted by recency
── more on @cloudflare 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/support-for-modern-c…] indexed:0 read:8min 2026-10-01 · —