1. Today's topic

A protected boot and OTA chain:

text
release build
→ firmware signing
→ provisioning
→ ROM bootloader
→ bootloader verification
→ app verification
→ OTA
→ self-test
→ confirm or rollback
→ optional anti-rollback floor

Mechanisms:

MechanismPurpose
Secure BootRuns only authorised code
Flash EncryptionConceals the contents of external Flash
OTA rollbackReturns to a working version after unsuccessful OTA
Anti-rollbackRejects 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:

text
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:

text
ROM -> signed second-stage bootloader -> signed app

The 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:

text
esptool -p COM7 chip-id
espefuse -p COM7 summary --format json --file efuses-before.json

Flash Encryption

This encrypts external Flash. Production mode must use a unique flash-encryption key for each device. Do not confuse:

text
Secure Boot -> authenticity
Flash Encryption -> confidentiality

Enabling order

With external provisioning, the usual sequence is:

text
Flash Encryption first
Secure Boot second

Read 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:

c
esp_ota_mark_app_valid_cancel_rollback();

On failure:

c
esp_ota_mark_app_invalid_rollback_and_reboot();

Anti-rollback

security_version is a separate field; it is not the semantic version. The rule:

text
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:

c
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:

markdown
# 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:

text
espsecure generate-signing-key --version 2 --scheme rsa3072 keys/dev_secure_boot.pem

Enable signed applications without hardware Secure Boot:

text
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=n

Check:

text
idf.py secure-verify-signature --keyfile keys/dev_secure_boot.pem build/app.bin

Change 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.
Which mechanism rejects an old but correctly signed vulnerable firmware version?

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.