cloudparts

mlkem-native

A portable C90 implementation of ML-KEM (FIPS 203) with formally verified C and assembly, meant to be copied into other cryptographic libraries; AWS-LC, liboqs and, through AWS-LC, rustls use it.

PQCA project
Type
Open source
Made by
PQ Code Package (Post-Quantum Cryptography Alliance)
Foundation
Post-Quantum Cryptography Alliance. Part of the PQ Code Package project, with mldsa-native and slhdsa-c. Part of the Linux Foundation
License
Apache 2.0, ISC or MIT, at your choice
Facts checked
28 October 2026

What it is

mlkem-native is the PQ Code Package’s implementation of ML-KEM, the NIST key-encapsulation standard behind hybrid TLS key exchange. It started as a fork of the ML-KEM reference code and is split into a fixed C frontend and swappable backends for the arithmetic and for SHA-3 (FIPS 202): portable C by default, plus native code for 64-bit Arm (Neon), x86_64 (AVX2), RISC-V (RVV) and POWER (VSX), and an experimental one for Armv8.1-M microcontrollers. It is not a library you link against so much as source you vendor: you copy the mlkem/ tree, or a single-file mlkem_native.c plus its assembly, into your own build, set the parameter set (512, 768 or 1024) and a symbol prefix in a config header, and supply your own random number generator and, if you like, your own SHA-3.

What sets it apart is proof. All C code for the core and the Arm and x86 backends is proved free of memory and integer overflow with the C Bounded Model Checker (CBMC), and all AArch64 and x86_64 assembly is proved functionally correct, memory-safe and constant-time at the object-code level in the HOL Light theorem prover, using AWS’s s2n-bignum models of those CPUs. It is tested against NIST’s ACVP and Google’s Wycheproof test vectors, and timing behaviour is also checked with valgrind across compilers and flags. That is why other libraries adopted it: it became the default ML-KEM in liboqs from version 0.13.0 in April 2025, and AWS imported it into AWS-LC in v1.50.0 the same month, which put it under rustls, whose default crypto provider is AWS-LC. Version 2.0.0, released on 7 August 2026, cleaned up the API; v1.3 is supported with bug fixes until February 2027.

Why teams pick it

  • Machine-checked proofs of memory safety, functional correctness and constant-time assembly, with a published analysis of what the proofs do and don’t cover
  • Fast on the platforms that matter, with hand-written and SLOTHY-superoptimised Arm assembly and AVX2, RVV and POWER backends
  • Built to be embedded: C90, no build system to adopt, configurable symbol prefix, bring-your-own RNG and SHA-3, and a permissive choice of three licences
  • Already in production paths through AWS-LC and rustls, and the default ML-KEM in liboqs

Watch out for

  • Only ML-KEM: signatures live in the sibling projects mldsa-native and slhdsa-c, and hybrid key exchange, TLS and key storage are the host library’s job
  • Protects against timing side channels only; power, electromagnetic, speculative-execution and fault-injection attacks are out of scope
  • The C code assumes constant-time 32-bit multiplication, which some small cores without a hardware multiplier don’t provide
  • Not a FIPS 140 module by itself: validation comes with the library that embeds it, and v2.0 changed APIs that integrators on v1.x must migrate from before February 2027

Alternatives

What all post-quantum cryptography have in common →Compare side by side →

How it fits with other technologies

Generate, encapsulate, decapsulate (MLK_CONFIG_NAMESPACE_PREFIX set to mlkem) · c

#include "mlkem_native/mlkem_native.h"   /* ML-KEM-768 by default */

uint8_t pk[MLKEM768_PUBLICKEYBYTES], sk[MLKEM768_SECRETKEYBYTES];
uint8_t ct[MLKEM768_CIPHERTEXTBYTES];
uint8_t ss_alice[MLKEM_BYTES], ss_bob[MLKEM_BYTES];

if (mlkem_keypair(pk, sk) != 0) abort();       /* uses your randombytes() */
if (mlkem_enc(ct, ss_bob, pk) != 0) abort();   /* rejects a malformed pk */
if (mlkem_dec(ss_alice, ct, sk) != 0) abort(); /* ss_alice == ss_bob */

See it in a real stack

No architecture uses mlkem-native yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2024–2026

When the Linux Foundation launched the Post-Quantum Cryptography Alliance on 6 February 2024, one of its two founding projects was the PQ Code Package, set up to build high-assurance, production-ready implementations of the coming standards, starting with ML-KEM. The mlkem-native repository was created in March 2024, and earlier PQ Code Package efforts with similar aims, mlkem-c-generic and mlkem-c-embedded, have since been archived.

The team, led by maintainers Hanno Becker and Matthias Kannwischer, combined a reference-code base with verified assembly. A first alpha came out in December 2024 and v1.0.0 on 4 June 2025, by which time liboqs and AWS-LC had already adopted it. Releases in 2026 added RISC-V and POWER backends, and v2.0.0 followed in August 2026, while the same approach was applied to signatures in mldsa-native, which became liboqs’s default ML-DSA in July 2026.

Why it’s built this way

Prove the code instead of only testing it
Bugs in cryptographic code hide in rare inputs and in timing. CBMC proves the C free of overflows for every input, and HOL Light proves the assembly computes the right result in constant time.
Fork the reference implementation
Starting from the ML-KEM reference code kept the frontend close to the code NIST and reviewers already know, so the optimised backends are the only part that differs much.
Source to embed, not a library to link
The users it targets, cryptographic libraries such as AWS-LC and liboqs, have their own build systems, RNGs and SHA-3, so mlkem-native lets them plug those in and namespace its symbols.

Timeline

  1. 2024

    PQ Code Package founded with the PQCA (February 6); mlkem-native repository created (March); first alpha (December)

  2. 2025

    Default ML-KEM in liboqs 0.13.0 and imported into AWS-LC v1.50.0 (April); v1.0.0 released (June 4)

  3. 2026

    v2.0.0 released (August 7); v1.3 supported until February 2027

Sources7 · facts checked 28 October 2026

Improve this page on GitHub