1. Today's topic
A protected boot and OTA chain:
release build
→ firmware signing
→ provisioning
→ ROM bootloader
→ bootloader verification
→ app verification
→ OTA
→ self-test
→ confirm or rollback
→ optional anti-rollback floorMechanisms:
| Mechanism | Purpose |
|---|---|
| Secure Boot | Runs only authorised code |
| Flash Encryption | Conceals the contents of external Flash |
| OTA rollback | Returns to a working version after unsuccessful OTA |
| Anti-rollback | Rejects an old, signed but vulnerable version |
The main idea: these are different mechanisms, and they must be enabled in the correct order.
2. Why this matters
The device downloads OTA updates through EC25. TLS helps, but risks remain:
a compromised server;
a substituted file;
an old, signed but vulnerable version;
physical replacement of SPI Flash;
UART/JTAG access.Secure Boot verifies code, Flash Encryption conceals Flash, and anti-rollback revokes old security floors.
3. Theory
Secure Boot
The ESP32 chain:
ROM -> signed second-stage bootloader -> signed appThe private signing key is not stored on the device. The device stores verification material/a digest. On the classic ESP32, Secure Boot v2 is available only on revision 3.0+. Before provisioning:
esptool -p COM7 chip-id
espefuse -p COM7 summary --format json --file efuses-before.jsonFlash Encryption
This encrypts external Flash. Production mode must use a unique flash-encryption key for each device. Do not confuse:
Secure Boot -> authenticity
Flash Encryption -> confidentialityEnabling order
With external provisioning, the usual sequence is:
Flash Encryption first
Secure Boot secondRead protection for key blocks and the digest must be applied in the correct order. eFuses are irreversible.
OTA rollback
New firmware initially has PENDING_VERIFY status. After a local self-test:
esp_ota_mark_app_valid_cancel_rollback();On failure:
esp_ota_mark_app_invalid_rollback_and_reboot();Anti-rollback
security_version is a separate field; it is not the semantic version. The rule:
Product version changes frequently.
security_version is increased only to revoke vulnerable releases.Do not increase the security version in every nightly build.
Self-test
Confirm OTA only after a local self-test:
config valid;
critical tasks started;
input service alive;
watchdog supervisor alive;
power stable;
heap budget OK;
hardware revision compatible.Do not make the MQTT broker a mandatory condition for confirmation.
4. Common mistakes
- Treating Flash Encryption as a replacement for Secure Boot.
- Treating Secure Boot as a replacement for TLS.
- Enabling security on the only development board.
- Not checking the ESP32 revision.
- Storing the private signing key in Git/CI.
- Using one Flash Encryption key for the entire fleet.
- Enabling Secure Boot before writing read-protected keys.
- Increasing security_version in every release.
- Increasing the anti-rollback floor before the self-test.
- Making MQTT a mandatory OTA self-test.
- Leaving an old, forbidden image in the inactive OTA slot.
- Repeating eFuse commands without understanding that they are irreversible.
5. A practical task for 30–60 minutes
Without changing eFuses or enabling production security, create SECURITY_LIFECYCLE.md:
# Firmware security lifecycle
## Development
- Hardware Secure Boot disabled.
- Flash Encryption disabled or development-only on sacrificial boards.
- Development signing keys only.
- JTAG and UART available.
## Pilot
- Signed OTA images required.
- OTA rollback enabled.
- Hardware security tested only on dedicated boards.
- Production key is not present on developer workstations.
## Production
- Secure Boot enabled.
- Flash Encryption release mode.
- Unique flash-encryption key per device.
- UART/JTAG policy explicitly configured.
- Anti-rollback enabled only after staged fleet validation.
- Provisioning and eFuse reports archived.Create a test signing key:
espsecure generate-signing-key --version 2 --scheme rsa3072 keys/dev_secure_boot.pemEnable signed applications without hardware Secure Boot:
CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT=y
CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT=y
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK=nCheck:
idf.py secure-verify-signature --keyfile keys/dev_secure_boot.pem build/app.binChange one byte in the binary and confirm that verification fails.
6. What to read or try next
- ESP-IDF Secure Boot v2, Flash Encryption, OTA Rollback and Anti-rollback.
- espefuse summary and production provisioning.
- STM32Trust, X-CUBE-SBSFU and MCUboot/TrustZone for the specific STM32 series.
Criteria: Match the threat to the mechanism; do not change eFuses in this exercise.
Exercise
Compare OTA rollback and anti-rollback in a design review. Explain when a new application should be confirmed and why an external MQTT broker must not be the mandatory self-test condition.
Self-check criteria: Keep recovery and revocation separate, preserve the local self-test requirement and avoid irreversible provisioning actions.
Show the supplied answer
OTA rollback returns to a working image after a failed new application; anti-rollback disallows releases below a security floor. Confirm the new application after the local self-test. Broker availability is external to that local health check, so a missing broker should not be the required confirmation condition.