ESP32 Secure Boot v2 and Flash Encryption: Production Walkthrough
The two firmware steps you cannot take back
Most of an ESP32 hardening checklist is reversible. You can re-tune a watchdog, redesign a partition table, or rewrite an OTA self-test as many times as you like. Two steps are not like that. Enabling Secure Boot v2 and enabling flash encryption are enforced by burning one-time-programmable eFuses, and an eFuse bit only ever moves from 0 to 1. There is no undo, no recovery jumper, and no Espressif unlock tool. Get them wrong and the unit on your bench is electronic waste.
This is the hands-on companion to our firmware hardening checklist, zoomed all the way in on those two irreversible items. The rest of the checklist forgives mistakes; this part does not, which is exactly why it deserves its own walkthrough. Below is the real ESP-IDF flow — generating signing keys, enabling Secure Boot v2, enabling AES-XTS flash encryption in development versus release mode, the eFuses each step burns, and the order that keeps a mistake on the bench instead of in the field.
Why esp32 secure boot flash encryption is two controls, not one
People say "secure the firmware" as if it were a single switch. It is two independent controls that defend against two different attackers, and the most common production mistake is shipping one and assuming it covers the other.
- Secure Boot v2 verifies an RSA-3072 (or, on some chips, ECDSA) signature over the second-stage bootloader and every application image. The digest of your public key is burned into eFuse; the ROM and bootloader refuse to run anything not signed by the matching private key. It answers "is this firmware mine?"
- Flash encryption encrypts the contents of the external SPI flash with AES-XTS, using a key that lives in eFuse and is never exposed to software. A
read_flashdump yields ciphertext. It answers "can anyone read what is on the flash?"
Ship Secure Boot without encryption and your signed firmware still sits in plaintext on an external chip — keys, certificates, and provisioned credentials all readable with a clip and a USB adapter. Ship encryption without Secure Boot and an attacker can still get a modified image to boot, because nothing checks the signature. For an industrial product that touches a customer network, the baseline is both, together. The interesting engineering is not whether to enable them, it is the order.
The eFuse and step table
This is the core of the walkthrough. Each row is a discrete step in the flow, the eFuse (or class of eFuse) it burns, whether that burn can be undone, and the field consequence of getting it wrong. Read it top to bottom — the order of the rows is the recommended order of operations.
| Step | eFuse burned | Reversible? | Field consequence of getting it wrong |
|---|---|---|---|
| 1. Generate & back up signing key | None — happens on your host, no burn | Yes (regenerate before any burn) | Lose the private key after Secure Boot is enabled and you can never sign another image; the fleet is frozen at its last build and OTA is dead forever |
| 2. Enable Secure Boot v2 | Public-key digest block + Secure Boot enable bit | No (one-way) | Wrong/placeholder key burned → device only trusts a key you do not control; every future image is rejected and the unit will not boot |
| 3. Enable flash encryption — development mode | Encryption key block + flash-crypt config (download writes still allowed) | No (the burn) — but plaintext re-flash stays open, so the setup is still recoverable | Skipped entirely → you validate nothing before the irreversible release burn and discover bugs on production units |
| 4. Validate on sacrificial units | None — boot, OTA, and re-provision tested against encrypted flash | Yes (this step exists to be repeated) | Skipped → a broken signing or partition setup is only discovered after release mode has locked the door |
| 5. Enable flash encryption — release mode | Flash-crypt config advanced; key read-back and plaintext download disabled | No (one-way) | Burned before validation, or jumped to directly → no way to re-flash plaintext to fix a bad image; bricked |
| 6. Disable UART/JTAG download (lock down) | Download-mode disable / JTAG disable eFuses | No (one-way) | Disabled too early → you lock yourself out before the pipeline is proven; disabled too late → "secure" units ship with an open recovery door |
The single most expensive cell in that table is step 1's failure mode: losing the signing key after Secure Boot is live. It is not a brick you can see — the devices keep running their last image — but you can never ship them another line of firmware. Back the key up in your HSM or secrets manager before you burn anything, and treat it like the most valuable artifact in the project, because it is.
Step 1: generate and back up the signing key (no burn yet)
Nothing is irreversible yet, which is exactly why this step gets the most care. Secure Boot v2 uses an RSA-3072 key pair; you generate it once and protect the private half for the life of the product.
# Generate the Secure Boot v2 RSA-3072 signing key (host-side, no eFuse touched)
idf.py secure-boot-generate-signing-key secure_boot_signing_key.pem
# Derive the public-key digest that will (later) be burned into eFuse
espsecure.py digest_rsa_public_key \
--keyfile secure_boot_signing_key.pem \
--output secure_boot_digest.binThe .pem is your private key. If it leaks, anyone can sign firmware your fleet will trust; if you lose it, you can never sign again. It belongs in a hardware security module or a managed secrets vault with strict access control and offline backups — not in the repo, not on a laptop, not in CI plaintext. This is also where multi-key planning happens: Secure Boot v2 supports more than one key digest so you can rotate or revoke, and that decision is far cheaper to make now than after a single key is already burned across a fleet.
Step 2: enable Secure Boot v2 (first irreversible burn)
Secure Boot comes before flash encryption for one concrete debugging reason: once flash is encrypted, you can no longer read back plaintext to diagnose a broken signing setup. If your signing configuration is wrong, you want to find out while the flash is still readable. So you prove the signed-boot chain first, then layer encryption on top.
In menuconfig you enable Secure Boot v2 and point it at your signing key; the build then produces signed bootloader and application images. On the first boot of a signed image, the bootloader burns the public-key digest and the Secure Boot enable bit into eFuse, and from that instant the device will only run images signed by the matching private key.
The failure to avoid here is burning a placeholder or wrong key. We have seen teams flash a signed image built against a throwaway CI key "just to test," let it burn the digest, and turn the unit into a device that only trusts a key nobody kept. There is no recovery. The discipline is simple: the only key that ever reaches an eFuse-burning boot is the real, backed-up production key.
Step 3: enable flash encryption in development mode
Development mode is the safety net the whole flow is built around. With flash encryption enabled in development mode, the encryption key is burned, but the serial download interface stays usable: you can still flash a plaintext image and the bootloader will encrypt it in place on the next boot. That means a mistake — a bad partition table, a wrong key, a botched provisioning blob — is still fixable by re-flashing.
# Build with flash encryption (development mode) selected in menuconfig,
# then let the first encrypted boot burn the encryption key and config eFuses.
idf.py build
idf.py -p /dev/ttyUSB0 flash monitorDevelopment mode is not what ships. Its entire purpose is to let you exercise the encrypted-flash code path — signed boot, encrypted NVS reads, OTA into an encrypted partition — while you can still recover the unit if something is wrong. Treat it as a rehearsal with a real stage and real props, but the fire exits still open.
Step 4: validate the whole pipeline on sacrificial units
This is the step teams under schedule pressure want to skip, and it is the step that saves the production line. Before you advance to release mode you run the entire lifecycle against development-mode units you are willing to throw away:
- Sign and boot. Build a signed image, confirm it boots on a Secure-Boot-enabled, encryption-enabled unit, and confirm an unsigned image is rejected.
- Read-back check. Dump the flash and confirm it is ciphertext, not your firmware — proof encryption is actually active, not just configured.
- OTA through the encrypted path. Push a signed OTA update end to end and confirm the new image lands in the inactive slot, verifies, boots, and self-tests under encryption.
- Provisioning and NVS. Write per-device credentials into encrypted NVS and read them back across a reboot, so you know your factory provisioning flow survives encryption.
- Recovery rehearsal. Deliberately flash a broken image and recover it via the still-open development-mode download path, so the team has muscle memory for the one window where recovery is still possible.
Only when all five pass on multiple sacrificial units do you advance. If any of them fail after release mode, the unit is scrap — that is the whole reason this step exists before the irreversible burns, not after.
Step 5 and 6: release mode and locking the download door
Release mode and the download lockdown are the last two burns, done together, and only after step 4 is clean. Release mode advances the flash-encryption configuration so the encryption key can no longer be read back and the serial download path can no longer write a usable plaintext image. Disabling UART/JTAG download closes the remaining recovery and debug entry points so a unit in the field cannot be coerced into a re-flash or a memory read.
The two classic mistakes are mirror images of each other. Lock the download door too early — before validation — and you have sealed a unit you have not yet proven, with no way back in. Lock it too late, or forget to, and you ship "secure" devices whose download mode is wide open, which is functionally the same as having no encryption at all from an attacker's point of view. The correct moment is precisely once: after development mode has proven the entire pipeline, and as the final action in your factory flow.
Because these two burns are irreversible and order-sensitive, they belong in a controlled, automated manufacturing step with a checklist and a second operator — not a developer typing commands by hand against production hardware at 2 a.m. on a deadline.
The rule that prevents every brick: validate on units you can throw away
If you take one operating principle from this walkthrough, take this: no eFuse is ever burned for the first time on a unit you intend to ship. Every irreversible step — Secure Boot enable, encryption key, release mode, download lockdown — is exercised first on sacrificial pre-production units that exist to be destroyed if the flow is wrong. The cost of a dozen scrapped dev units is trivial; the cost of discovering a bad signing or encryption setup after it has been burned across a production batch is a field recall.
This is precisely the work GizanTech's firmware and IoT service builds into the manufacturing flow: signing-key custody, the Secure-Boot-then-encryption order, a development-mode validation cycle on sacrificial units, and the automated, checklisted release-and-lockdown burn that runs identically on every unit. If your team is approaching the eFuse step on an ESP32 product and wants the irreversible parts validated before they are committed, that is the right conversation to have before the first burn — not after.
FAQ
What is the correct order to enable Secure Boot v2 and flash encryption on the ESP32 without bricking units?
Generate and back up your signing key first, then enable Secure Boot v2, then enable flash encryption in development mode, validate the full sign-build-flash-boot-OTA pipeline on sacrificial units, and only then move to flash-encryption release mode and disable plaintext UART/JTAG download. Secure Boot before encryption matters because once flash is encrypted you can no longer read back plaintext to debug a broken signing setup. Doing the irreversible release-mode and download-disable burns last — after development mode has proven the pipeline end to end — is what keeps a mistake on the bench instead of in a bricked field unit.
What is the difference between flash encryption development mode and release mode, and which ships?
Development mode leaves the door open on purpose: you can still re-flash a plaintext image over the serial download interface and the bootloader will encrypt it in place, so you can iterate, fix a bad key setup, or re-provision. Release mode locks that door — the UART/JTAG download path can no longer write usable plaintext, and the encryption key is no longer readable or re-flashable. Release mode is the only one that actually protects firmware at rest, so release mode is what ships. Development mode exists solely to let you validate the pipeline on sacrificial units before you commit to the irreversible release burn.
Can Secure Boot or flash encryption be disabled or undone after the eFuses are burned?
No. Both are enforced by burning one-time-programmable eFuses, and eFuse bits can only go from 0 to 1, never back. Once the Secure Boot key digest is burned and the enable bit is set, the device will only ever run images signed by that key; once flash-encryption release mode is set and download is disabled, you cannot read or re-flash plaintext. There is no recovery jumper, no factory tool, and no Espressif unlock. This is why the entire flow is validated on units you are willing to throw away before it is ever run on production hardware.
Do I need both Secure Boot and flash encryption, or is one enough for production firmware?
For an industrial product you need both, because they defend against different attacks. Secure Boot stops an attacker from running modified or malicious firmware; flash encryption stops them from reading your firmware, keys, and provisioned credentials off the external SPI flash. Flash encryption alone still lets a tampered-but-unsigned image boot, and Secure Boot alone leaves all your secrets sitting in readable flash. They are complementary controls, and the standard production baseline enables both.
Frequently asked questions
What is the correct order to enable Secure Boot v2 and flash encryption on the ESP32 without bricking units?
Generate and back up your signing key first, then enable Secure Boot v2, then enable flash encryption in development mode, validate the full sign-build-flash-boot-OTA pipeline on sacrificial units, and only then move to flash-encryption release mode and disable plaintext UART/JTAG download. Secure Boot before encryption matters because once flash is encrypted you can no longer read back plaintext to debug a broken signing setup. Doing the irreversible release-mode and download-disable burns last — after development mode has proven the pipeline end to end — is what keeps a mistake on the bench instead of in a bricked field unit.
What is the difference between flash encryption development mode and release mode, and which ships?
Development mode leaves the door open on purpose: you can still re-flash a plaintext image over the serial download interface and the bootloader will encrypt it in place, so you can iterate, fix a bad key setup, or re-provision. Release mode locks that door — the UART/JTAG download path can no longer write usable plaintext, and the encryption key is no longer readable or re-flashable. Release mode is the only one that actually protects firmware at rest, so release mode is what ships. Development mode exists solely to let you validate the pipeline on sacrificial units before you commit to the irreversible release burn.
Can Secure Boot or flash encryption be disabled or undone after the eFuses are burned?
No. Both are enforced by burning one-time-programmable eFuses, and eFuse bits can only go from 0 to 1, never back. Once the Secure Boot key digest is burned and the enable bit is set, the device will only ever run images signed by that key; once flash-encryption release mode is set and download is disabled, you cannot read or re-flash plaintext. There is no recovery jumper, no factory tool, and no Espressif unlock. This is why the entire flow is validated on units you are willing to throw away before it is ever run on production hardware.
Do I need both Secure Boot and flash encryption, or is one enough for production firmware?
For an industrial product you need both, because they defend against different attacks. Secure Boot stops an attacker from running modified or malicious firmware; flash encryption stops them from reading your firmware, keys, and provisioned credentials off the external SPI flash. Flash encryption alone still lets a tampered-but-unsigned image boot, and Secure Boot alone leaves all your secrets sitting in readable flash. They are complementary controls, and the standard production baseline enables both.
Related solutions
See how we apply this in production, by industry: