|
DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
|
This guide explains how to protect a device running DeterministicESPAsyncWebServer against a physically present attacker - one who can read the SPI flash, attach a debugger, or re-flash the chip - using the two ESP32 hardware features that close those gaps: Secure Boot v2 (only firmware you signed will run) and Flash Encryption (the flash is unreadable without the per-device key fused into the chip). The library's own crypto (TLS, SSH, SNMPv3, JWT/OIDC, the hash-chained audit log) defends the network; Secure Boot and Flash Encryption defend the silicon.
⚠️ Everything here burns eFuses, which is irreversible. A wrong key, a lost key, or the wrong mode permanently bricks the chip or locks you out. Practice on a sacrificial board, read the official docs end to end, and keep signing keys backed up offline. This is an ESP-IDF /
espefuse.py/espsecure.pyworkflow; it is configured at the bootloader level, not from an Arduino sketch.
The library keeps these secrets/assets in flash; without Flash Encryption they can be dumped with esptool.py read_flash, and without Secure Boot the firmware can be replaced with a malicious build:
| Asset | Where it lives | Protected by |
|---|---|---|
| Firmware image (your logic + keys) | app partition | Secure Boot + Flash Encryption |
| SSH RSA host private key | NVS (ssh_host_key / priv_der) | Flash Encryption (+NVS enc) |
| TLS server key & certificate | flash / NVS (your storage) | Flash Encryption |
| SNMPv3 USM auth/priv keys, JWT secret | flash / NVS / build constants | Flash Encryption |
| WiFi credentials, config blobs | NVS (config_store) | Flash Encryption (+NVS enc) |
Audit-log chain (services/audit_log) | RAM ring (+ your sink) | see "Audit log" below |
The SSH private key is already loaded to the stack and wiped after use (ssh_rsa.h), so it never rests in static RAM - Flash Encryption protects the at-rest NVS copy, completing the chain.
Secure Boot v2 makes the chip's ROM verify a digital signature on the bootloader, and the bootloader verify the signature on the app, on every boot. The public key's SHA-256 digest is fused into the chip; the private signing key stays on your build machine.
After Secure Boot is active the chip refuses any image not signed by your key - including a re-flashed development build - so you must sign every future update with the same key.
Flash Encryption transparently encrypts the bootloader, app, and partitions marked encrypted with a key generated on the device and stored in a read/write protected eFuse block (it never leaves the chip). A flash dump is then ciphertext.
Flash Encryption does not automatically encrypt the NVS partition. To protect the SSH host key and config_store blobs, enable NVS encryption: add an nvs_keys partition (itself protected by Flash Encryption) and initialize NVS with nvs_flash_secure_init(). See the ESP-IDF "NVS Encryption" guide.
espefuse.py burn_efuse DIS_PAD_JTAG / soft-disable), and UART download mode or Secure Download Mode (ENABLE_SECURITY_DOWNLOAD) so the bootloader cannot be used to read memory.DETWS_ENABLE_DIAG off** (it exposes the compile-time config), prefer HTTPS/WSS (DETWS_ENABLE_TLS) and mTLS (DETWS_ENABLE_MTLS) for control planes, and gate state-changing routes (auth + DETWS_ENABLE_CSRF).The hash chain in services/audit_log makes on-device tampering detectable, but an attacker who holds the Flash Encryption key could in principle recompute the chain. For true off-device tamper-evidence, install a sink (detws_audit_set_sink) that forwards every record - with its chain hash - to an append-only remote store (syslog / an HTTP log service / a write-once SD file) the device cannot later rewrite. Secure Boot + Flash Encryption raise the bar to recover that key; the remote sink is what survives if it is ever breached.
The Arduino-ESP32 core is built on ESP-IDF, but Secure Boot and Flash Encryption are bootloader/eFuse settings, not sketch options. To use them you either build via ESP-IDF (with Arduino as a component) or supply a custom sdkconfig / partition + bootloader to your PlatformIO build, then burn eFuses with espefuse.py. The application code in this library is unchanged either way - these features protect the image and the flash beneath it.
espefuse.py