Hello,
I implemented and evaluated ML-KEM using an approach in which working memory is allocated during key creation and subsequently reused by decapsulation operations.
Main characteristics of the implementation architecture:
1. Measured stack usage remains slightly above 1 KiB and is largely independent of the selected ML-KEM parameter set.
2. Encapsulation and decapsulation perform no runtime memory allocations. The required memory is allocated during key creation and subsequently reused until the key is destroyed.
3. Decapsulation uses a pool of reusable workspace slots to preserve concurrency without per-operation allocation.
The number of slots is selected during key creation according to the maximum number of concurrent decapsulation operations expected in the target environment.
4. The common implementation core is written in portable C11 with minimal reliance on environment-specific facilities.
Allocation, entropy generation, synchronization, error handling, and other environment-dependent functionality are isolated in separate port layers.
Ports are currently provided for Linux userspace, the Linux kernel, and FreeRTOS.
The implementation supports ML-KEM-512, ML-KEM-768, and ML-KEM-1024. It has been validated using NIST KAT vectors and additional functional and stress tests.
Timing-leakage testing was performed using dudect. Performance was compared with PQClean, with generally comparable throughput across the three parameter sets.
The main trade-off is a larger one-time allocation during key creation, retention of the preallocated workspace memory for the lifetime of the key, and greater internal complexity.
Rather than obtaining working memory separately for each operation, the implementation reserves it in advance and reuses it across all subsequent operations.
For a given level of concurrency, this does not necessarily increase the working-memory requirement; it changes its lifetime and moves memory acquisition out of the operational path.
The size of the initial allocation increases with the configured number of workspace slots.
I would be interested in the community’s view on the following questions:
1. Is this memory-management trade-off useful for constrained or allocation-sensitive environments, or is caller-provided per-operation workspace generally a preferable model?
2. Are there cryptographic, portability, or concurrency concerns specific to ML-KEM that should be considered when working memory is retained and reused for the lifetime of a key?
3. More broadly, is this implementation model suitable for ML-KEM and potentially useful for other PQC algorithms? What environments or use cases, if any, would benefit from such an approach?
The implementation, documentation, test results, and benchmark methodology are available in the v1.4.0 release:
https://github.com/kstzv/ml-kem/releases/tag/v1.4.0Any technical feedback on the design and its trade-offs would be appreciated.
Best regards,
K. S. Zavertailo