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. Support for modern cryptographic algorithms in Workers 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 https://wicg.github.io/webcrypto-modern-algos/ draft community group report https://www.w3.org/standards/types/ reports , 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 , and decapsulateKey - 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 https://developer.mozilla.org/en-US/docs/Web/API/Web Crypto API 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 https://blog.cloudflare.com/post-quantum-roadmap/ , we cannot wait https://blog.cloudflare.com/ml-dsa-will-have-to-do/ 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. js 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 https://www.rfc-editor.org/rfc/rfc9180.html 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. js 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 https://www.openssh.org/pq.html in 2024. HPKE has a draft for post-quantum and hybrid KEMs https://datatracker.ietf.org/doc/html/draft-ietf-hpke-pq-05 ongoing at the IETF. The IETF published RFC 9964 https://www.rfc-editor.org/rfc/rfc9964.html for ML-DSA in JOSE, as well as an adopted draft https://datatracker.ietf.org/doc/draft-ietf-jose-hpke-pq-pqt/ for JWE using PQ & PQ/T HPKE. HTTP Message Signatures can use different signature algorithms https://www.iana.org/assignments/http-message-signature/http-message-signature.xhtml , 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 https://github.com/panva/jose library that maps ML-DSA- algorithms to Web Crypto, the application code is as follows: js 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 https://github.com/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. js 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 https://datatracker.ietf.org/doc/html/rfc9458 as well which we’ve discussed before https://blog.cloudflare.com/stronger-than-a-promise-proving-oblivious-http-privacy-properties/ . 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 loading a private key. Previously, this often meant keeping both around or doing format-specific work. The new getPublicKey helper does the direct thing: js const publicKey = await crypto.subtle.getPublicKey privateKey, "verify" ; For ML-KEM, the usage is different because public keys encapsulate and private keys decapsulate: js 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. js 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 https://github.com/nodejs/node , Deno https://github.com/denoland/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 https://developers.cloudflare.com/workers/configuration/compatibility-flags/ . // 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 https://developers.cloudflare.com/workers/runtime-apis/web-crypto/ . How it’s been implemented Workers run on workerd https://github.com/cloudflare/workerd . It’s an open-source runtime https://blog.cloudflare.com/workerd-open-source-workers-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 https://web-platform-tests.org/ 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 https://github.com/cloudflare/workerd/pull/6403 from panva https://github.com/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 https://blog.cloudflare.com/ml-dsa-will-have-to-do/ the-signature-algorithms 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 https://wicg.github.io/webcrypto-modern-algos/ 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 https://github.com/cloudflare/workerd/pull/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 https://github.com/WICG/webcrypto-modern-algos/issues/1 , cSHAKE, TurboSHAKE, and HPKE discussed in wicg/webcrypto-modern-algos 2 https://github.com/WICG/webcrypto-modern-algos/issues/2 . An implementation has already been tested against panva/hpke https://github.com/panva/hpke and panva/jose https://github.com/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 https://developers.cloudflare.com/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.