Cloudflare Workers is introducing native support for post-quantum-resistant cryptographic algorithms within Web Crypto, as specified in the Modern Algorithms in the Web Cryptography API draft community group report. The implementation includes key encapsulation mechanisms, signature algorithms, and supporting helper functions designed to ease the transition to quantum-resistant cryptography.
The newly available primitives comprise:
- 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(), and decapsulateKey() functions
- getPublicKey() helper
- SubtleCrypto.supports() for runtime capability detection
- JWK import and export support for these algorithms
For developers preparing for post-quantum migration, these opt-in Web Crypto APIs simplify experimentation with ML-KEM and ML-DSA by eliminating the need to include separate cryptographic implementations. While they do not constitute a complete migration strategy, they provide foundational building blocks for validating post-quantum integrations. The feature is currently available behind the webcrypto_modern_algorithms compatibility flag as the specification continues to evolve.
Why Web Crypto support matters
Web Crypto typically goes unnoticed until it lacks a required cryptographic primitive. Testing newer post-quantum algorithms in JavaScript environments has been difficult: developers either cannot implement protocols directly using Web Crypto or must bundle their own cryptographic implementation in JavaScript or WebAssembly. Both approaches create problems. They shift the responsibility for selecting and maintaining cryptographic code to application developers, who see their applications grow as they include cryptographic libraries. This work must then be repeated across all downstream libraries. As the ecosystem faces pressure to transition to post-quantum-resistant algorithms sooner than anticipated, waiting for improved post-quantum algorithms is not viable. Developers require access to these primitives immediately to test, evaluate, and refine post-quantum integrations.
Implementation examples
ML-KEM key encapsulation
ML-KEM operates by having one party hold a public key while another party encapsulates a shared secret to that public key. The holder of the private key then decapsulates it to obtain the same secret. The following demonstrates the basic pattern in Workers:
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,
);
The code snippet above does not include encryption itself. ML-KEM provides both parties with shared key material. Protocols such as Hybrid Public Key Encryption (HPKE) then use that material in a key schedule and an AEAD cipher like AES-GCM.
ML-DSA signatures
ML-DSA follows the familiar pattern of Ed25519 or ECDSA: generate a key pair, sign data, and verify the signature.
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,
);
Post-quantum migration across protocols
Post-quantum migration is not a single transition but rather a coordinated effort across numerous protocols, libraries, services, and deployment environments to adopt different cryptographic primitives. This work is already underway in several areas. OpenSSH added support for mlkem768x25519 in 2024. The IETF has a draft for post-quantum and hybrid key encapsulation mechanisms within HPKE. RFC 9964 was published for ML-DSA in JOSE, alongside an adopted draft for JWE using post-quantum and post-quantum/traditional hybrid HPKE. HTTP Message Signatures can employ different signature algorithms provided the signer and verifier agree on production and verification methods. Supporting all these protocols on Cloudflare Workers required exposing the underlying cryptographic primitives through Web Crypto.
Real-world use cases
JWT signing with ML-DSA
Signed JSON Web Tokens (JWTs) represent a familiar use case protected through JSON Web Signatures (JWS). Using a library such as panva/jose that maps ML-DSA algorithms to Web Crypto, application code becomes straightforward:
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 serve as just one example. The broader significance is that libraries can delegate ML-DSA operations to the runtime instead of maintaining their own implementation for each environment. With Workers supporting ML-DSA natively, libraries can offload signing to the runtime rather than shipping their own code.
HPKE and OHTTP
ML-KEM functions as a key encapsulation mechanism that on its own provides two parties with shared key material. HPKE completes the 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 leverage the native primitive when 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 pattern is the desired shape for protocols such as OHTTP as well. OHTTP relies on HPKE. If HPKE can use a post-quantum KEM through Web Crypto, then participants can begin exploring migration to ciphersuites supporting these primitives. Libraries may require runtime-specific integration adjustments, where HPKE.CipherSuite selects implementations based on the algorithms available in the runtime.
Deriving public keys from private keys
Several protocols require publishing or deriving a public key after loading a private key. Previously, this often required keeping both keys in memory or performing format-specific operations. The new getPublicKey() helper simplifies this:
const publicKey = await crypto.subtle.getPublicKey(privateKey, ["verify"]);
For ML-KEM, usage differs because public keys encapsulate while private keys decapsulate:
const publicKey = await crypto.subtle.getPublicKey(privateKey, [ "encapsulateBits", ]);
This minimal API aims to eliminate boilerplate code used extensively across libraries dealing with public key cryptography.
Runtime capability detection
Because the API is not yet universally supported across runtimes, libraries should verify its availability rather than assuming it exists everywhere.
if (SubtleCrypto.supports("sign", "ML-DSA-44")) {
const keys = await crypto.subtle.generateKey("ML-DSA-44", false, [
"sign",
"verify",
]);
}
Libraries operating across Workers, Node.js, Deno, browsers, and other Web-compatible runtimes require this type of check. It also accommodates scenarios where only portions of the modern algorithms proposal are implemented.
Current support and configuration
The initial Workers implementation supports ML-KEM-768 as a key encapsulation mechanism and ML-DSA-44 as a signature algorithm. All require the webcrypto_modern_algorithms flag to be enabled. For completeness, ML-KEM-1024, ML-DSA-65, and ML-DSA-87 are also supported. ML-KEM-512 is not supported because the BoringSSL version used by Workers does not expose it. Rather than introduce a separate implementation solely for that variant, the initial rollout focuses on algorithms available through the native crypto library.
To enable these algorithms in a Workers project, include the following in your wrangler.jsonc:
{
// Opt into modern crypto algorithms
"compatibility_flags": [
"webcrypto_modern_algorithms"
]
}
Implementation details
Workers operates on workerd, 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. The change also introduces Web Platform Tests for the modern algorithms API surface, Workers-specific tests for compatibility flag behavior, and TypeScript definitions under the new Workers types.
This work was separated from a larger proposal by panva. The implementation discussed here represents the first phase of the modern Web Crypto algorithm specification, focusing 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 included in this initial release. This narrower scope facilitates review and provides library authors with concrete functionality to test before the complete modern algorithms proposal is finalized.
ML-DSA public keys and signatures are substantially larger than RSA or Ed25519 equivalents. While integrating these algorithms in the runtime improves performance and reduces bundling requirements, it does not alter the fact that key sizes, signature sizes, and ciphertext sizes are increasing, whether transmitted over the wire or stored.
Future additions
The WICG proposal encompasses more than ML-KEM and ML-DSA. Several algorithms from the original contribution by Filip Skokan in cloudflare/workerd#6403 have not yet been implemented and require further review. These include the SHA-3 hash function, ChaCha20-Poly1305 AEAD (with ongoing 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 correctness.
A practical question also exists regarding when these algorithms should transition from opt-in to default behavior. Currently, all algorithms are gated behind a compatibility flag. The API is based on a draft specification, and feedback from library authors is desired before treating it as stable.
Getting started
This change does not automatically make every protocol post-quantum-safe. Rather, it provides Workers developers and library authors with the cryptographic primitives they previously lacked: ML-KEM for key encapsulation, ML-DSA for signatures, and helper APIs that make these primitives accessible through Web Crypto. If you maintain a library that currently bundles its own post-quantum implementation, this is an opportune moment to test the native API and share feedback. The most effective way to identify limitations is to deploy real protocol code on top of it.
Cloudflare acknowledges Filip Skokan for the original contribution and iterations, Felix Hanau, James Snell, Bas Westerbaan, and Peter Wu for code review, and Daniel Huigens for co-authoring the specification work underlying this implementation.