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
  1. The key is generated (ideally off-chip, in a secure environment).
  2. The key is burned into a designated eFuse key block using espefuse.py.
  3. The key’s purpose is set using espefuse.py (this also burns eFuses).
  4. The key block is write-protected by burning its WR_DIS bit.
  5. 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 to EFUSE_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 chosen BLOCK_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 the FLASH_CRYPT_CNT value). This means if you burn FLASH_CRYPT_CNT from 1 to 3, the firmware must be re-encrypted with the new effective key.
      • Once FLASH_CRYPT_CNT is set to 0b001 (or higher), it cannot be reset to 0b000. 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:
    1. Generate a flash encryption key.
    2. Burn the key into an eFuse key block and set its purpose.
    3. Protect the key block (write-protect, optionally read-protect).
    4. Enable flash encryption by burning FLASH_CRYPT_CNT (e.g., to 1) using espefuse.py. This is usually done during the first encrypted flash.
    5. 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 chosen BLOCK_KEY: Must be set to EFUSE_KEY_PURPOSE_SECURE_BOOT_V1 or EFUSE_KEY_PURPOSE_SECURE_BOOT_V2.
    • ABS_DONE_0 / ABS_DONE_1 (Secure Boot V1): Older eFuse bits related to secure boot enablement.
    • SOFT_DIS_JTAGHARD_DIS_JTAG (ESP32-S3 and later): eFuses to control JTAG accessibility, often configured alongside Secure Boot.
  • Process Overview (Simplified):
    1. Generate a signing key pair (private and public key).
    2. 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.
    3. 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.
    4. Enable Secure Boot by burning SECURE_BOOT_EN and other relevant eFuses using espefuse.py.
    5. 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.
  • espefuse.py burn_efuse <FIELD_NAME> <value>:
    • Used to burn general eFuse fields like FLASH_CRYPT_CNTSECURE_BOOT_ENDISABLE_DL_ENCRYPT, etc.
  • 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.

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.

  1. 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
  2. Observe:
    • Look for sections related to BLOCK_KEY0 through BLOCK_KEYN (N depends on the chip).
    • Note their current values (likely all zeros if never programmed).
    • Check their read_protected and write_protected status.
    • Examine the purpose field for each key block.
    • Find FLASH_CRYPT_CNTSECURE_BOOT_EN, and other security-related eFuses. Note their current values.
    This summary provides a baseline before any secure eFuse modifications.

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

Bash
openssl rand -out hmac_key.bin 32

espefuse.py commands (conceptual sequence):

Bash
# 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 consult espefuse.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:

  1. Generate Key: Create a 256-bit AES key (e.g., flash_encryption_key.bin).
  2. Burn Key: Use espefuse.py burn_key to burn flash_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.
  3. Initial Flash Encryption: The first time you flash an encrypted image, the idf.py flash process (or esptool.py write_flash) will typically prompt you to burn FLASH_CRYPT_CNT to 0b001 (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
  4. Further Hardening (Optional Release Mode):
    • To move to release mode, you might burn FLASH_CRYPT_CNT to 0b011 (value 3) or 0b111 (value 7).
    • You might also burn DISABLE_DL_ENCRYPTDISABLE_DL_DECRYPT, or DISABLE_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.

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 to BLOCK_KEY5).
    • Dedicated key purposes for DS and HMAC.
    • JTAG disabling eFuses (SOFT_DIS_JTAGHARD_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_CONFIGEFUSE_WR_DIS_KEY_PURPOSE, etc.) can differ. Always consult the target’s esp_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

  1. Research: eFuse Key Map for ESP32-C3:
    • Objective: Understand the specific eFuse key block layout and purposes for a modern ESP32 variant.
    • Task:
      1. Consult the ESP32-C3 Technical Reference Manual (TRM) and the components/efuse/esp32c3/esp_efuse_table.csv file from your ESP-IDF installation.
      2. Create a table documenting each eFuse key block available on the ESP32-C3.
      3. 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 its KEY_PURPOSE.
      4. List at least three different KEY_PURPOSE values relevant to the ESP32-C3 and briefly describe what they are used for.
  2. Thought Exercise: Secure Boot V2 Provisioning Outline:
    • Objective: Conceptualize the steps for provisioning an ESP32-S3 for Secure Boot V2.
    • Task:
      1. Assume you have generated an RSA-3072 key pair (private key private_secure_boot_key.pem and public key public_secure_boot_key.pem). The Digital Signature (DS) peripheral on ESP32-S3 will use this for signing.
      2. 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.
      3. For each conceptual espefuse.py command, briefly state its purpose and why it’s part of the sequence.
      4. What are the critical prerequisites before you would burn SECURE_BOOT_EN?
    • 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) and RD_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 to FLASH_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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top