ML-KEM implementation with bounded memory use and a FreeRTOS port

Hello,

I have developed an implementation of ML-KEM, the standardized post-quantum key-encapsulation mechanism defined in FIPS 203. It uses an architecture intended to make the algorithm practical in constrained systems and RTOS environments.

The implementation is written in portable C11. Its core is independent of a particular operating system and relies only on the C standard library or equivalent facilities supplied by an environment-specific port. Thin ports are currently provided for Linux userspace, the Linux kernel, and FreeRTOS.

The main architectural properties are:

  • Small and predictable stack usage: slightly above 1 KiB in the measured configurations, although the exact value depends on the compiler, target architecture, and build configuration.

  • No dynamic memory allocation on the encapsulation or decapsulation hot paths.

  • Explicit memory ownership and lifecycle management. Working memory is provisioned when the relevant key context is created and is retained, reused, wiped, and released together with that context.

  • A reusable pool of decapsulation contexts. The caller selects the maximum number of simultaneous decapsulation operations when creating the secret-key context.

  • Controllable memory/throughput trade-off. Increasing the number of pool slots permits more concurrent decapsulations, while reducing it lowers the persistent memory requirement.

  • Concurrency without increasing per-operation stack usage or requesting additional memory from the environment during decapsulation.

The purpose of the pool is to combine low stack usage with controlled parallelism. Instead of allocating a large temporary workspace for every operation, each decapsulation temporarily acquires a preallocated slot. Therefore, the application can choose its concurrency limit according to the available memory and expected workload while keeping memory consumption bounded throughout the lifetime of the key.

I have also created and tested a concrete FreeRTOS port using the CORTEX_MPS2_QEMU_IAR_GCC demonstration project. The test configuration emulated an Arm MPS2 AN385 platform with a Cortex-M3 processor using QEMU; testing was not performed on physical hardware.

Repository:

Current release (v1.4.0):

I would be interested in feedback from FreeRTOS and embedded developers on the following questions:

  • Is this memory model useful for real RTOS or constrained-device workloads?

  • Is selecting the decapsulation concurrency limit at key creation a reasonable interface for such environments?

  • Are there other FreeRTOS-specific integration concerns or target configurations that would be valuable to test?

I have not gone through your code but in general, your choices look reasonable for a resource constrained environment.

Would you please elaborate what are you looking for?

Thank you. I am mainly looking for FreeRTOS-specific constraints or configurations that could expose portability issues not visible in my current QEMU setup — for example, small heaps/task stacks, different heap implementations, or concurrent decapsulation

The implementation uses larger one-time allocations and then reuses that memory to keep stack usage small and avoid allocations during operations. I would appreciate suggestions for other configurations or real embedded targets worth testing

One additional point I would be interested in is atomic operations. The implementation currently relies on atomic state for managing reusable decapsulation slots. For FreeRTOS targets, would you generally recommend relying on standard C11 atomics where the toolchain supports them, using FreeRTOS-provided atomic facilities, or taking another approach for better portability across targets?

You should be able to use any device of your choice to test it.

FreeRTOS-provided atomics would be most portable. Not all the platforms have C11 support - so by using C11 specific features, you’d be excluding those.

I actually handled this during the FreeRTOS port by introducing selectable atomic backends through #elif and typedef. At build time, either ATOMIC_STDATOMIC or ATOMIC_FREERTOS can be selected, explicitly choosing between C11 atomics and the FreeRTOS implementation. Thanks for the answers!