ESP-IDF component that adapts the PQCA mldsa-native codebase for ML-DSA
signatures on ESP32-class targets.
The upstream project describes mldsa-native as a portable C90 implementation
of the FIPS 204 ML-DSA post-quantum signature standard, supported by the
Post-Quantum Cryptography Alliance. This component keeps upstream sources under
mldsa/ and isolates ESP32-specific integration in configuration, wrapper, and
CMake files.
- Target parameter set: ML-DSA-87.
- Primary tested target in this repository: ESP32-WROOM-32D class hardware.
- Build system: ESP-IDF component.
- Backend: portable C path only.
- Assembly and SIMD backends are not used for Xtensa LX6.
The component is suitable for lab integration and benchmarking in this project. Deployment use still requires system-level review of randomness, key storage, side-channel exposure, firmware update policy, and lifecycle handling.
firmware/components/ml_dsa/
├── include/ml_dsa.h
├── config/mldsa_config_esp32.h
├── mldsa/
├── examples/basic_sign_verify/
└── test_apps/ml_dsa_tests/
- ML-DSA-87 is selected for a high security margin in long-lived IoT identity experiments. Lower parameter sets would reduce memory and latency.
- Large temporary buffers are redirected from stack to heap because ESP32 FreeRTOS task stacks are small.
- Randomness is provided through ESP-IDF
esp_fill_random(). - The component keeps direct header configuration instead of a Kconfig menu.
- The reduced-RAM upstream mode is not enabled in this component because this project has prioritized the standard path and simpler review.
Key generation and signing require cryptographically strong randomness. On
classic ESP32, esp_fill_random() depends on the RF subsystem for hardware
randomness. Applications must ensure WiFi or Bluetooth is initialized before
generating keys or signatures.
Do not generate long-term keys while RF is disabled.
Public constants and functions are exposed through include/ml_dsa.h.
Typical flow:
#include "ml_dsa.h"
uint8_t pk[ML_DSA_PK_BYTES];
uint8_t sk[ML_DSA_SK_BYTES];
uint8_t sig[ML_DSA_SIG_BYTES];
size_t siglen = 0;
ml_dsa_keygen(pk, sk);
ml_dsa_sign(sig, &siglen, message, message_len, context, context_len, sk);
ml_dsa_verify(sig, siglen, message, message_len, context, context_len, pk);Callers are responsible for allocating buffers of the documented sizes and for protecting secret keys at rest.
Example project:
cd examples/basic_sign_verify
idf.py build
idf.py flash monitorComponent tests:
cd test_apps/ml_dsa_tests
idf.py build
idf.py flash monitor- Store ML-DSA secret keys encrypted at rest.
- Signing latency varies because ML-DSA uses rejection sampling.
- This component does not add countermeasures against power analysis, electromagnetic analysis, or fault injection.
- Upstream formal verification claims apply to upstream code and assumptions. The full consuming firmware still needs its own integration review.
Upstream project: https://github.com/pq-code-package/mldsa-native
This component keeps upstream licensing for vendored code and uses the license files included in this repository.