Flash Encryption
Flash contents are stored encrypted under a key held on-die, so a raw chip dump yields only ciphertext. It closes the offline read that Secure Boot does not.
Flash Encryption stores the contents of external flash in encrypted form, with the key held in on-die eFuses, so a physical readout of the chip yields only ciphertext. It closes the offline read path that Secure Boot alone leaves open.
What it is
On an ESP32 the firmware lives in a flash chip that sits outside the SoC, inside the module can. Anyone with a clip and a programmer can read that chip. Flash Encryption makes the stored bytes ciphertext: the plaintext only ever exists inside the SoC, reconstructed on the fly. The encryption key is generated on-device and burned into an eFuse block whose read-back is then disabled, so the key cannot be extracted even by the running firmware.
Why it matters
Reading SPI flash with a soic8-clip and a ch341a, or after desoldering, is trivial and non-invasive. Flash Encryption defeats that offline path: even with a perfect full dump, the attacker holds no plaintext firmware to grep for credentials or load into a disassembler. Paired with Secure Boot it is complete in both directions: encryption stops the offline read, Secure Boot stops the offline write.
How it works
A key is generated and burned into eFuse. The flash controller decrypts data transparently as the CPU fetches it (AES-XTS on the S2/S3/C-series, a tweaked AES-256 scheme on the original ESP32). The key never leaves the die, and read-back of the key eFuse is disabled. From software and from the CPU's point of view flash simply reads as plaintext; from a wire tap or an external reader it reads as ciphertext.
How to inspect or attack it
Check whether it is actually burned before assuming a dump is useless.
espefuse.py -p /dev/ttyUSB0 summary # look for FLASH_CRYPT_CNT / SPI_BOOT_CRYPT_CNT
flashrom -p ch341a_spi -r dump.bin # off-chip read: ciphertext if enabled
binwalk -E dump.bin # entropy: flat high across the image = encrypted
If the fuse is not burned, the dump is plaintext and you are done. If it is burned, a raw read is ciphertext and you must pivot: read from inside a running chip (a debug interface, an app-level leak, a memory disclosure bug), or attack the key directly via side-channel or fault injection. In practice the usual way in is misconfiguration: development mode left enabled (still re-flashable), only the app encrypted while the bootloader or a data partition is not, or a key accidentally left in a readable region.
Pitfalls
- Development mode is not release mode. A dev-mode device can often still be re-flashed, which undermines the whole guarantee.
- Partial encryption is common: confirm that every sensitive partition, not just the app, is covered.
- Encryption without Secure Boot stops reading but not writing, and vice versa. Assess both fuses; one without the other is a half measure.
- High entropy alone does not prove encryption, since compressed data also looks random. Correlate with the eFuse state.