Chapter 174: Secure eFuse Usage with ESP-IDF
Chapter Objectives
By the end of this chapter, you will be able to:
- Understand how eFuses are utilized for storing cryptographic keys securely.
- Identify dedicated eFuse key blocks and their intended purposes (e.g., Flash Encryption, Secure Boot, HMAC, Digital Signature).
- Explain the significance and operation of read and write protection mechanisms for eFuse-stored keys.
- Describe the process of programming (burning) cryptographic keys into eFuses using the
espefuse.py
tool. - Understand the fundamental principles and eFuse configurations for enabling critical security features like Flash Encryption and Secure Boot.
- Appreciate the role of eFuses in conjunction with hardware cryptographic accelerators.
- Recognize variant-specific eFuse security capabilities and key management differences across the ESP32 family.
- Identify common pitfalls and best practices when dealing with secure eFuses.
Introduction
In Chapter 173, we explored the fundamentals of eFuses as one-time programmable (OTP) memory. Now, we delve into one of their most critical applications: establishing a hardware root of trust and enabling robust security features on ESP32 devices. By securely storing cryptographic keys and critical security configuration bits within eFuses, the ESP32 can protect sensitive code and data, ensure software integrity, and authenticate itself.
Secure eFuse usage is paramount for building products that require protection against unauthorized access, firmware tampering, and intellectual property theft. Features like Flash Encryption, Secure Boot, and hardware-accelerated cryptography rely heavily on the immutable and tamper-resistant nature of eFuses.
This chapter will focus on the eFuse blocks dedicated to key storage, the mechanisms to protect these keys, and how these eFuses underpin core ESP32 security functionalities. We will primarily discuss management through the espefuse.py
command-line tool, as direct in-application programming of security-critical eFuses is typically reserved for highly controlled manufacturing processes.
A Word of Extreme Caution: The operations discussed in this chapter involve permanent modifications to the device. Incorrectly programming security-related eFuses can render a device permanently inoperable, lock out debugging capabilities, or irretrievably compromise its security. Proceed with the utmost care and only on development hardware you can afford to lose.
eFuse Field | Purpose | Consequence of Burning (Irreversible) |
---|---|---|
FLASH_CRYPT_CNT |
Enables and controls the state of Flash Encryption. | Enables encryption permanently. Disabling it will render flash unreadable. Each bit burned changes the key. |
SECURE_BOOT_EN |
Enables Secure Boot, ensuring only signed firmware will run. | Device will refuse to boot any unsigned firmware. A mistake here can brick the device. |
RD_DIS.* |
Read-disables a key block, making it inaccessible to the CPU. | The key can no longer be read by software, only used by hardware. Essential for security, but prevents software verification. |
WR_DIS.* |
Write-disables a key block or field, making it read-only. | The key or field can never be changed or updated again. |
JTAG_DISABLE |
Permanently disables the JTAG debugging interface. | JTAG debugging is lost forever, significantly hindering failure analysis. |
Theory
eFuse Key Blocks and Key Purposes
ESP32 variants dedicate specific eFuse blocks for storing cryptographic keys. These are not general-purpose user data blocks but are often tied to specific hardware crypto accelerators or security features. The number of key blocks and their sizes (typically 256 bits per key slot) vary across chip variants.
Each key stored in these blocks can be assigned a Key Purpose. This purpose dictates how the key can be used by the hardware. For example, a key designated for Flash Encryption cannot be used for Secure Boot, and vice-versa. This hardware-enforced separation of key usage is a crucial security principle.
%%{init: {'theme':'base', 'themeVariables': {'primaryColor': '#DBEAFE', 'primaryTextColor': '#1E40AF', 'primaryBorderColor': '#2563EB'}}}%% graph LR subgraph "eFuse Key Block (e.g., BLOCK_KEY1)" direction TB K[<b>256-bit Key Slot</b><br>Stores the cryptographic key] subgraph "Protection & Configuration" direction LR P(<b>Key Purpose</b><br>e.g., Secure Boot) W(<b>WR_DIS</b><br>Write Disable Bit) R(<b>RD_DIS</b><br>Read Disable Bit) end K --> P K --> W K --> R end subgraph "Hardware Peripherals" direction TB SB[Secure Boot Engine] FE["Flash Encryption (AES)"] HM[HMAC Accelerator] DS[Digital Signature] end P -- "Dictates Usage" --> SB P -- "Dictates Usage" --> FE P -- "Dictates Usage" --> HM P -- "Dictates Usage" --> DS %% Styling classDef keyBlock fill:#DBEAFE,stroke:#2563EB,stroke-width:2px,color:#1E40AF; classDef configNode fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef hardwareNode fill:#D1FAE5,stroke:#059669,stroke-width:1px,color:#065F46; class K keyBlock; class P,W,R configNode; class SB,FE,HM,DS hardwareNode;
Common key purposes, managed via espefuse.py
and internal eFuse fields, include:
EFUSE_KEY_PURPOSE_SECURE_BOOT_V1
/EFUSE_KEY_PURPOSE_SECURE_BOOT_V2
: The key (or a digest of the key) used by the Secure Boot mechanism to verify the digital signature of the bootloader and application firmware.EFUSE_KEY_PURPOSE_XTS_AES_128_KEY
/EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_1
/EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_2
: Keys used by the AES hardware accelerator for Flash Encryption/Decryption.EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL
/EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG
/EFUSE_KEY_PURPOSE_HMAC_DOWN_DIGITAL_SIGNATURE
/EFUSE_KEY_PURPOSE_HMAC_UP
: Keys used by the HMAC (Hash-based Message Authentication Code) peripheral for generating MACs, often used in challenge-response authentication or protecting specific operations.EFUSE_KEY_PURPOSE_DIGITAL_SIGNATURE_KEY
: A private key used by the Digital Signature (DS) peripheral for signing data. The corresponding public key is used for verification.EFUSE_KEY_PURPOSE_USER
: A key that can be used by the application for general cryptographic purposes via the hardware crypto accelerators, but its usage might still be restricted by other eFuse settings.EFUSE_KEY_PURPOSE_MAX
: Represents the total number of available key purposes.
%%{init: {'theme':'base', 'themeVariables': {'primaryColor': '#DBEAFE', 'primaryTextColor': '#1E40AF', 'primaryBorderColor': '#2563EB'}}}%% graph LR subgraph "eFuse Key Block (e.g., BLOCK_KEY1)" direction TB K[<b>256-bit Key Slot</b><br>Stores the cryptographic key] subgraph "Protection & Configuration" direction LR P(<b>Key Purpose</b><br>e.g., Secure Boot) W(<b>WR_DIS</b><br>Write Disable Bit) R(<b>RD_DIS</b><br>Read Disable Bit) end K --> P K --> W K --> R end subgraph "Hardware Peripherals" direction TB SB[Secure Boot Engine] FE["Flash Encryption (AES)"] HM[HMAC Accelerator] DS[Digital Signature] end P -- "Dictates Usage" --> SB P -- "Dictates Usage" --> FE P -- "Dictates Usage" --> HM P -- "Dictates Usage" --> DS %% Styling classDef keyBlock fill:#DBEAFE,stroke:#2563EB,stroke-width:2px,color:#1E40AF; classDef configNode fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef hardwareNode fill:#D1FAE5,stroke:#059669,stroke-width:1px,color:#065F46; class K keyBlock; class P,W,R configNode; class SB,FE,HM,DS hardwareNode;
Read and Write Protection for eFuse Keys
Once a cryptographic key is burned into an eFuse key block, it’s vital to protect it from unauthorized modification and, in many cases, from being read out by software.
- Write Protection (
WR_DIS
): Each key block (or individual key slot within it) has an associated write-disable eFuse bit. Once this bit is burned, the corresponding key block/slot becomes permanently read-only. This prevents attackers from overwriting or erasing the provisioned key. - Read Protection (
RD_DIS
): Many key blocks also have an associated read-disable eFuse bit. Burning this bit prevents the key from being read out by software (CPU). However, the key remains usable by the hardware cryptographic peripherals (like AES, SHA, HMAC, DS). This is a powerful security measure: the key can be used for encryption/decryption or signing/verification by the hardware, but it cannot be exfiltrated by potentially malicious software running on the device.
Lifecycle of a Protected Key:
%%{init: {'theme':'base', 'themeVariables': {'primaryColor': '#DBEAFE', 'primaryTextColor': '#1E40AF', 'primaryBorderColor': '#2563EB'}}}%% sequenceDiagram participant SEC as Secure Environment<br>(Host PC) participant TOOL as espefuse.py participant EFUSE as ESP32 eFuse SEC->>SEC: 1. Generate Key<br>(e.g., hmac_key.bin) SEC->>TOOL: 2. Initiate Burn Command TOOL->>EFUSE: 3. Burn Key Bits activate EFUSE EFUSE-->>TOOL: OK deactivate EFUSE TOOL->>EFUSE: 4. Burn Key Purpose activate EFUSE EFUSE-->>TOOL: OK deactivate EFUSE TOOL->>EFUSE: 5. Burn Write-Protect Bit (WR_DIS) activate EFUSE Note over TOOL,EFUSE: Key is now immutable EFUSE-->>TOOL: OK deactivate EFUSE TOOL->>EFUSE: 6. Burn Read-Protect Bit (RD_DIS) activate EFUSE Note over TOOL,EFUSE: Key is now unusable by CPU,<br>only by hardware accelerators EFUSE-->>TOOL: OK deactivate EFUSE TOOL-->>SEC: 7. Success
- The key is generated (ideally off-chip, in a secure environment).
- The key is burned into a designated eFuse key block using
espefuse.py
. - The key’s purpose is set using
espefuse.py
(this also burns eFuses). - The key block is write-protected by burning its
WR_DIS
bit. - Optionally, if the key should only be used by hardware, the key block is read-protected by burning its
RD_DIS
bit.
Flash Encryption
Flash Encryption protects the confidentiality of the firmware stored in the external SPI flash memory. If an attacker physically desolders the flash chip, they cannot read the firmware without the correct decryption key.
- eFuse Role:
BLOCK_KEY
(e.g.,EFUSE_BLK_KEY0
toEFUSE_BLK_KEY5
): One or more of these blocks store the actual Flash Encryption key(s) (e.g., 256-bit AES key).KEY_PURPOSE
for the chosenBLOCK_KEY
: Must be set to a flash encryption purpose (e.g.,EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_1
).FLASH_CRYPT_CNT
(eFuse field): This 3-bit eFuse field is crucial.000
(0): Flash encryption is disabled.001
(1): Flash encryption enabled in “Development” mode. The key is derived from an eFuse key block. Firmware is encrypted. UART bootloader can re-flash encrypted binaries.011
(3): Flash encryption enabled in “Release” mode. Similar to state 1, but UART bootloader might have restrictions.111
(7): Flash encryption permanently enabled in “Release” mode. UART bootloader cannot re-flash encrypted binaries if download encryption is also disabled.- Important: Each time a bit in
FLASH_CRYPT_CNT
is burned (transitioning from 0 to 1), the effective flash encryption key changes (often by XORing the eFuse key with theFLASH_CRYPT_CNT
value). This means if you burnFLASH_CRYPT_CNT
from 1 to 3, the firmware must be re-encrypted with the new effective key. - Once
FLASH_CRYPT_CNT
is set to0b001
(or higher), it cannot be reset to0b000
. Disabling flash encryption is generally not possible after enabling it without rendering the flash content unreadable.
DISABLE_DL_ENCRYPT
/DISABLE_DL_DECRYPT
(eFuse fields): Control whether the ROM bootloader allows encrypted firmware download or requires plain text firmware during download mode.
- Process Overview:
- Generate a flash encryption key.
- Burn the key into an eFuse key block and set its purpose.
- Protect the key block (write-protect, optionally read-protect).
- Enable flash encryption by burning
FLASH_CRYPT_CNT
(e.g., to 1) usingespefuse.py
. This is usually done during the first encrypted flash. - The ESP-IDF build system and
idf.py flash
command handle the encryption of firmware binaries when flash encryption is enabled.
Secure Boot
Secure Boot ensures that the device only executes authentic firmware that has been digitally signed by a trusted entity. It prevents attackers from loading malicious or unauthorized code.
- eFuse Role:
SECURE_BOOT_EN
(eFuse field): A single bit that, when burned, enables Secure Boot. This is irreversible.BLOCK_KEY
(e.g.,EFUSE_BLK_KEY1
): Stores the public key digest (for Secure Boot V1 on ESP32) or the actual private key for the Digital Signature peripheral (for Secure Boot V2 on newer chips like S3, C3).KEY_PURPOSE
for the chosenBLOCK_KEY
: Must be set toEFUSE_KEY_PURPOSE_SECURE_BOOT_V1
orEFUSE_KEY_PURPOSE_SECURE_BOOT_V2
.ABS_DONE_0
/ABS_DONE_1
(Secure Boot V1): Older eFuse bits related to secure boot enablement.SOFT_DIS_JTAG
,HARD_DIS_JTAG
(ESP32-S3 and later): eFuses to control JTAG accessibility, often configured alongside Secure Boot.
- Process Overview (Simplified):
- Generate a signing key pair (private and public key).
- Secure Boot V1 (ESP32):
- Burn a digest of the public key into an eFuse key block.
- The bootloader and application are signed with the private key.
- The ROM bootloader verifies the signature on the software bootloader using the public key digest from eFuses.
- Secure Boot V2 (ESP32-S3, C3, etc.):
- The private signing key can be stored in an eFuse key block and made usable only by the Digital Signature (DS) peripheral (by read-protecting the key).
- The bootloader and application are signed.
- The ROM bootloader uses the DS peripheral (with the eFuse-protected key) to verify signatures.
- Enable Secure Boot by burning
SECURE_BOOT_EN
and other relevant eFuses usingespefuse.py
. - The ESP-IDF build system handles the signing of binaries.
%%{init: {'theme':'base', 'themeVariables': {'primaryColor': '#DBEAFE', 'primaryTextColor': '#1E40AF', 'primaryBorderColor': '#2563EB'}}}%% graph TD A(Power On) --> B[ROM Bootloader]; B --> C{Verify SW Bootloader<br>Signature}; C -- "Uses Key Digest or DS Key<br>from eFuse" --> D[SW Bootloader]; D --> E{Verify Application<br>Signature}; E -- "Uses Key Digest or DS Key<br>from eFuse" --> F[Application Code]; F --> G(Execution); C -- Invalid Signature --> H(<b>HALT</b>); E -- Invalid Signature --> H; %% Styling classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef startNode fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef endNode fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46; classDef haltNode fill:#FEE2E2,stroke:#DC2626,stroke-width:2px,color:#991B1B; class A startNode; class B,D,F process; class C,E decision; class G endNode; class H haltNode;
HMAC and Digital Signature (DS) Peripherals
Newer ESP32 variants (like S3, C3, C6, H2) include hardware HMAC and DS peripherals.
- HMAC: Used for generating message authentication codes. A key stored in an eFuse block (with purpose
EFUSE_KEY_PURPOSE_HMAC_...
) can be used by the HMAC peripheral. This is useful for challenge-response authentication schemes where the key itself is never exposed to software. - DS: Used for generating and verifying RSA or ECC digital signatures. A private key stored in an eFuse block (with purpose
EFUSE_KEY_PURPOSE_DIGITAL_SIGNATURE_KEY
and read-protected) can be used by the DS peripheral to sign data without software access to the key. This is crucial for Secure Boot V2 and other attestation features.
espefuse.py
for Secure eFuse Management
The espefuse.py
tool is the primary mechanism for provisioning secure eFuses.
espefuse.py burn_key <BLOCK_NUM_OR_NAME> <KEYFILE.bin> <KEY_PURPOSE> [--key-read-protected] [--key-write-protected]
:- Burns a key from
KEYFILE.bin
(raw binary, typically 32 bytes for a 256-bit key) into the specified key block. - Sets the
KEY_PURPOSE
for that key. - Optionally burns the read-protection and/or write-protection bits for the key block.
- Burns a key from
espefuse.py burn_efuse <FIELD_NAME> <value>
:- Used to burn general eFuse fields like
FLASH_CRYPT_CNT
,SECURE_BOOT_EN
,DISABLE_DL_ENCRYPT
, etc.
- Used to burn general eFuse fields like
espefuse.py read_protect_efuse <BLOCK_NUM_OR_NAME_OR_FIELD>
/espefuse.py write_protect_efuse <BLOCK_NUM_OR_NAME_OR_FIELD>
:- More granular control over burning protection bits if not done during
burn_key
.
- More granular control over burning protection bits if not done during
Order of Operations is Critical:
Typically, you burn the key, then its purpose, then its write-protection, and finally its read-protection (if needed). For security features like Flash Encryption or Secure Boot, specific sequences of espefuse.py commands are documented by Espressif.
Practical Examples
The following examples focus on using espefuse.py
. Direct in-application programming of these critical security eFuses is highly discouraged and generally not done outside specialized manufacturing environments.
EXTREME WARNING: These commands permanently alter eFuses. DO NOT RUN THEM ON PRODUCTION DEVICES OR ANY DEVICE YOU ARE NOT WILLING TO POTENTIALLY BRICK. Always use
espefuse.py summary
first. Test on dedicated development hardware.
Example 1: Inspecting Key Blocks and Security eFuses
This is a safe, read-only operation.
- Command (run from your host terminal with ESP-IDF environment sourced):Replace /dev/ttyUSB0 with your device’s serial port.
espefuse.py --port /dev/ttyUSB0 summary
- Observe:
- Look for sections related to
BLOCK_KEY0
throughBLOCK_KEYN
(N depends on the chip). - Note their current values (likely all zeros if never programmed).
- Check their
read_protected
andwrite_protected
status. - Examine the
purpose
field for each key block. - Find
FLASH_CRYPT_CNT
,SECURE_BOOT_EN
, and other security-related eFuses. Note their current values.
- Look for sections related to
Example 2: Conceptual – Burning and Protecting a User Key for HMAC
This example outlines the conceptual steps to burn a key intended for use by the HMAC peripheral. This is for illustration; adapt field names and block numbers to your specific chip after careful review of its TRM and espefuse.py summary
output.
Assume:
- You have an ESP32-S3.
- You want to burn a 256-bit (32-byte) key into
BLOCK_KEY1
. - The key will be used by the HMAC peripheral (e.g., for
HMAC_DOWN_ALL
). - The key should be write-protected and read-disabled from software.
- You have a key file named
hmac_key.bin
(32 bytes of random data).
Generate a key file (example using OpenSSL on host):
openssl rand -out hmac_key.bin 32
espefuse.py
commands (conceptual sequence):
# Step 1: Burn the key to BLOCK_KEY1, set its purpose to HMAC_DOWN_ALL,
# and simultaneously write-protect and read-protect it.
# The exact KEY_PURPOSE name might vary, check espefuse.py summary or docs.
# For ESP32-S3, the purpose is set in a separate efuse field for the block,
# so burn_key might not set it directly, or it might have specific options.
# More robustly:
# Command to burn key (check exact syntax for your ESP-IDF version and chip)
# This command assumes BLOCK_KEY1 is the name/alias for the target key block.
espefuse.py --port /dev/ttyUSB0 burn_key BLOCK_KEY1 hmac_key.bin EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL --key-read-protected --key-write-protected
# If key purpose needs to be set separately for your chip/IDF version:
# espefuse.py --port /dev/ttyUSB0 burn_key BLOCK_KEY1 hmac_key.bin
# espefuse.py --port /dev/ttyUSB0 burn_efuse KEY_PURPOSE_1 EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL # Assuming KEY_PURPOSE_1 corresponds to BLOCK_KEY1
# espefuse.py --port /dev/ttyUSB0 write_protect_efuse BLOCK_KEY1 # Or the specific WR_DIS field for BLOCK_KEY1
# espefuse.py --port /dev/ttyUSB0 read_protect_efuse BLOCK_KEY1 # Or the specific RD_DIS field for BLOCK_KEY1
Confirmation: espefuse.py
will ask for confirmation. Type “BURN” to proceed if you are absolutely sure.
Verification: After burning, run espefuse.py --port /dev/ttyUSB0 summary
again.
Verify BLOCK_KEY1
is no longer all zeros (though it will be unreadable if read-protected).
Verify its read_protected
and write_protected
status are True (y)
.
Verify its purpose
is set correctly.
Tip: The exact
espefuse.py burn_key
syntax and how key purposes are associated can vary slightly between ESP-IDF versions and chip generations. Always consultespefuse.py --help
and the latest official Espressif documentation for your specific setup.
Example 3: Conceptual – Enabling Flash Encryption
Enabling flash encryption is a multi-step process that irreversibly alters the device. Refer to the official ESP-IDF Flash Encryption documentation for the precise, up-to-date procedure. Below is a highly simplified conceptual outline:
- Generate Key: Create a 256-bit AES key (e.g.,
flash_encryption_key.bin
). - Burn Key: Use
espefuse.py burn_key
to burnflash_encryption_key.bin
into a designated eFuse key block (e.g.,BLOCK_KEY0
) with the appropriate flash encryption purpose (e.g.,EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_1
). Also, write-protect and (usually) read-protect this key block. - Initial Flash Encryption: The first time you flash an encrypted image, the
idf.py flash
process (oresptool.py write_flash
) will typically prompt you to burnFLASH_CRYPT_CNT
to0b001
(value 1). This enables flash encryption in development mode.# This step is often handled by idf.py flash when it detects
# that flash encryption is being enabled for the first time.
# Manually, it would be something like:
# espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CNT 1
- Further Hardening (Optional Release Mode):
- To move to release mode, you might burn
FLASH_CRYPT_CNT
to0b011
(value 3) or0b111
(value 7). - You might also burn
DISABLE_DL_ENCRYPT
,DISABLE_DL_DECRYPT
, orDISABLE_DL_CACHE
to restrict ROM bootloader capabilities. - Each change to
FLASH_CRYPT_CNT
requires re-encrypting and re-flashing the application with the new effective key.
- To move to release mode, you might burn
Warning: Enabling Flash Encryption is a one-way street. Once
FLASH_CRYPT_CNT
is non-zero, you cannot easily revert to unencrypted flash without losing the data, as the hardware will always attempt decryption.
Variant Notes
Security eFuse capabilities are a key differentiator between ESP32 variants:
Secure Boot Variants:
Feature | Secure Boot V1 | Secure Boot V2 |
---|---|---|
Example Chip | ESP32 | ESP32-S3, ESP32-C3, ESP32-C6, etc. |
eFuse Storage | Stores a SHA-256 digest of the public signing key. | Can store the private signing key, protected by RD_DIS. |
Verification | Bootloader re-calculates public key from signature and compares its digest to the one in eFuse. | Uses the hardware Digital Signature (DS) peripheral with the eFuse-stored private key to directly verify signatures. |
Security | Secure. Verifies image integrity. | More secure. The private key never leaves the chip hardware, preventing its leakage. |
Flexibility | Simpler key management. | More robust, supports features like key revocation checks. |
- ESP32 (Original):
- Supports Flash Encryption and Secure Boot v1.
- Key blocks (BLK1, BLK2, BLK3) can be used for keys. BLK0 is system.
FLASH_CRYPT_CNT
(3 bits),SECURE_BOOT_EN
(1 bit).- AES, SHA hardware accelerators.
- ESP32-S2:
- Supports Flash Encryption. Secure Boot is typically handled by software (though some eFuse support for it might exist).
- Fewer eFuse key blocks compared to ESP32.
- AES, SHA hardware accelerators. Digital Signature (DS) peripheral might be limited or absent.
- ESP32-S3:
- Enhanced security features: Flash Encryption, Secure Boot v2, Digital Signature (DS) peripheral, HMAC peripheral.
- More eFuse key blocks (e.g., up to 6,
BLOCK_KEY0
toBLOCK_KEY5
). - Dedicated key purposes for DS and HMAC.
- JTAG disabling eFuses (
SOFT_DIS_JTAG
,HARD_DIS_JTAG
). - World Controller for peripheral access control.
- ESP32-C3 (RISC-V):
- Similar security features to ESP32-S3: Flash Encryption, Secure Boot v2, DS, HMAC.
- Fewer eFuse key blocks than S3, but dedicated for these purposes.
- ESP32-C6 (RISC-V):
- Builds upon C3 features, with Wi-Fi 6 and 802.15.4.
- Robust security eFuses for Flash Encryption, Secure Boot v2, DS, HMAC.
- Specific eFuses related to its radio capabilities might also exist.
- ESP32-H2 (RISC-V):
- Focus on 802.15.4 (Thread/Zigbee).
- Security features comparable to C-series chips (Flash Encryption, Secure Boot v2, DS, HMAC) tailored for IoT endpoint security.
Key Differences Summary:
- Number of Key Blocks: Newer chips (S3, C6) tend to have more.
- Secure Boot Version: ESP32 uses V1 (public key digest). S3, C3, C6, H2 use V2 (often leveraging DS peripheral with eFuse-stored private key).
- Hardware Crypto: Availability and capabilities of DS and HMAC peripherals vary.
- eFuse Field Names: Specific names for control bits (
FLASH_CRYPT_CONFIG
,EFUSE_WR_DIS_KEY_PURPOSE
, etc.) can differ. Always consult the target’sesp_efuse_table.csv
and TRM.
Common Mistakes & Troubleshooting Tips
Mistake / Issue | Symptom(s) | Troubleshooting / Solution |
---|---|---|
Bricked by Secure Boot | Device fails to boot after burning SECURE_BOOT_EN . |
Solution: Unrecoverable. The bootloader signature was invalid or the key was wrong. Test the complete signing process on a dev board *before* burning the eFuse. |
“flash read err” after enabling encryption | Device boot fails with a flash read error. | Solution: You enabled/changed FLASH_CRYPT_CNT but did not re-flash the application with the correctly re-encrypted binary. Re-encrypt and re-flash. |
Lost key file | You burned a key, protected it, and then lost the original key file on your PC. | Solution: If the key is read-protected, it is unrecoverable. For other keys, there is no recovery. Implement a secure key backup policy *before* burning eFuses. |
Incorrect burn order | A key is read-disabled before you could verify it was written correctly. | Solution: Follow the correct order: 1. Burn Key/Purpose, 2. Write-Protect, 3. Read-Protect. This allows verification before making the key unreadable. |
Using –do-not-confirm carelessly | You accidentally burn the wrong eFuse because the confirmation prompt was skipped. | Solution: Only use this flag in fully-tested, automated production scripts. Never for manual or experimental commands. |
Exercises
- Research: eFuse Key Map for ESP32-C3:
- Objective: Understand the specific eFuse key block layout and purposes for a modern ESP32 variant.
- Task:
- Consult the ESP32-C3 Technical Reference Manual (TRM) and the
components/efuse/esp32c3/esp_efuse_table.csv
file from your ESP-IDF installation. - Create a table documenting each eFuse key block available on the ESP32-C3.
- For each key block, list its size (in bits), the eFuse field name for its write-disable bit (
WR_DIS
), its read-disable bit (RD_DIS
), and the eFuse field name that defines itsKEY_PURPOSE
. - List at least three different
KEY_PURPOSE
values relevant to the ESP32-C3 and briefly describe what they are used for.
- Consult the ESP32-C3 Technical Reference Manual (TRM) and the
- Thought Exercise: Secure Boot V2 Provisioning Outline:
- Objective: Conceptualize the steps for provisioning an ESP32-S3 for Secure Boot V2.
- Task:
- Assume you have generated an RSA-3072 key pair (private key
private_secure_boot_key.pem
and public keypublic_secure_boot_key.pem
). The Digital Signature (DS) peripheral on ESP32-S3 will use this for signing. - Outline the sequence of
espefuse.py
commands you would theoretically use to:- Burn the private key into a designated eFuse key block (e.g.,
BLOCK_KEY0
). - Set the purpose of this key block to be for the Digital Signature peripheral.
- Ensure this private key in eFuse is write-protected and read-disabled (so only the DS hardware can use it).
- Enable Secure Boot V2 (by burning
SECURE_BOOT_EN
). - Optionally, disable JTAG.
- Burn the private key into a designated eFuse key block (e.g.,
- For each conceptual
espefuse.py
command, briefly state its purpose and why it’s part of the sequence. - What are the critical prerequisites before you would burn
SECURE_BOOT_EN
?
- Assume you have generated an RSA-3072 key pair (private key
- Note: This is a thought exercise. Do not attempt to run these commands without deep understanding and a specific, tested procedure from Espressif documentation.
Summary
- Secure eFuses are dedicated for storing cryptographic keys and critical security configuration bits, forming a hardware root of trust.
- Key blocks in eFuses can store keys (e.g., 256-bit AES keys) which are assigned specific
KEY_PURPOSE
values by hardware. WR_DIS
(write-disable) andRD_DIS
(read-disable) eFuses protect keys from modification and software exfiltration, respectively.- Flash Encryption relies on eFuse-stored keys and the
FLASH_CRYPT_CNT
eFuse to protect firmware confidentiality. Changes toFLASH_CRYPT_CNT
are irreversible and alter the effective encryption key. - Secure Boot uses eFuse-stored key digests (V1) or relies on eFuse-protected private keys for hardware-assisted signature verification (V2 via DS peripheral) using
SECURE_BOOT_EN
. espefuse.py
is the primary tool for managing secure eFuses, including burning keys (burn_key
), setting purposes, and configuring protection bits.- ESP32 variants differ significantly in their secure eFuse capabilities, number of key blocks, and support for features like Secure Boot V2, HMAC, and DS.
- Extreme caution is required when programming secure eFuses, as errors can be irreversible and may brick the device or compromise security.
Further Reading
- ESP-IDF Programming Guide – Security Features:
- https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32/security.html (Select your target chip for specific details).
- ESP-IDF Flash Encryption Documentation:
- ESP-IDF Secure Boot V2 Documentation (Example for ESP32-S3):
espefuse.py
tool documentation:- Technical Reference Manual (TRM) for your specific ESP32 variant:
- The “eFuse Controller,” “AES,” “SHA,” “HMAC,” “Digital Signature,” and “Secure Boot” chapters are essential.