ESPM Security¶
ESPM's module security rests on four pillars: integrity verification before a module runs, isolation while it runs, deterministic erasure when it stops, and constrained capabilities throughout. Together they ensure unsigned code never executes and no module trace survives a power cycle.
Signature verification¶
Every module blob is [32-byte HMAC-SHA256][ELF]. Before relocation the firmware verifies the tag with a key derived from the device's master_key:
Because the signing key is the device key, a blob is bound to one device. Verification is controlled by CONFIG_ESPM_VERIFY_ENABLE (on by default) and must stay enabled in production. A signature mismatch returns ESPM_ERR_SIGNATURE and the module is never loaded.
ELF validation¶
After the signature passes, espm_validate parses the object and rejects anything malformed: bad ELF magic or class, wrong machine type (must be EM_XTENSA or EM_RISCV), wrong file type (must be ET_REL), too many sections, or code/data over the limits (64 KB .text, 32 KB .data+.bss). Unknown relocation types and unresolved symbols also fail the load.
Execution isolation¶
flowchart TB
M["Module task"] --> WD["Per-module watchdog<br/>30 s, polled 500 ms"]
M --> PANIC{"Fault?"}
PANIC -->|"Xtensa"| KILL["Kill module only<br/>device keeps running"]
PANIC -->|"RISC-V"| REBOOT["Device reboots"]
- Per-module watchdog. Each module runs in its own FreeRTOS task with an independent watchdog (default 30 s, polled every 500 ms). A module that stops feeding it is killed.
- Panic isolation (Xtensa). A module-aware exception handler maps a faulting program counter to the owning module and purges only that module. Firmware faults still restart the device. On RISC-V there is no exception interception, so a module fault reboots the device.
Capability constraints¶
- Modules cannot allocate executable memory (
MALLOC_CAP_EXECis denied), so code can only enter IRAM through the verified ELF loader. - GPIO 6-11 are blocked on the ESP32 classic because they drive the internal SPI flash bus.
- Modules reach firmware services only through the syscall table; they do not link against arbitrary ESP-IDF symbols.
Memory erasure¶
On unload (clean stop, watchdog timeout, or panic purge) the module's IRAM and PSRAM are overwritten with zeros before being freed. No payload bytes remain in RAM, and because nothing is written to flash, a power cycle returns the device to a clean hollow shell.
Persistence trade-off¶
If built with CONFIG_ESPM_PERSIST_ENABLE, a module can be saved to encrypted NVS and auto-loaded at boot. This is the one path by which a module survives a power cycle; leave it disabled unless persistence is a requirement, and remember that persisted modules are stored under the encrypted NVS config.