ESP32-H2
But the shell script and commands can be adopted for any ESP32 chipset that supports secure boot
Secure boot ensures that our ESP32-H2 only runs firmware that we have authorized.
- The RSA-PSS's public key is stored on the device. The corresponding RSA-PSS private key is kept at a secret place and is never accessed by the device.
- Only one public key can be generated and stored in the chip during manufacturing.
- The same image format and signature verification method is applied for applications and the second stage bootloader.
- No secrets are stored on the device. Therefore, it is immune to passive side-channel attacks, e.g., timing or power analysis.
When enabled:
- Only firmware signed with your private key will be accepted
- Prevents unauthorized or malicious code from running
- Protects against firmware tampering and unauthorized updates
- Once enabled, cannot be disabled (permanent security feature)
- Requires initial setup with a signing key
- All firmware updates must be signed
- Provides hardware-level security
- Essential for IoT devices requiring secure deployments
💡 Need local system installation.
- Generate & Process signing key:
# In your project directory
# 1. Generate signing key, if it is the first time and we do not have the secure_boot_signing_key.pem
espsecure.py generate_signing_key secure_boot_signing_key.pem
# 2. Process the public key for efuse:
espsecure.py digest_sbv2_public_key --keyfile secure_boot_signing_key.pem --output secure_boot_digest.bin
- Burn the key and enable secure boot:
💡 Can’t be un-done !!
# Set key purpose
espefuse.py --port <PORT> burn_efuse KEY_PURPOSE_0 SECURE_BOOT_DIGEST0
# Burn the key
espefuse.py --port <PORT> burn_key BLOCK_KEY0 secure_boot_digest.bin SECURE_BOOT_DIGEST0
# Enable secure boot
espefuse.py --port <PORT> burn_efuse SECURE_BOOT_EN 1
# Clean binary directory
rm -rf binary
mkdir binary
# Compile
arduino-cli compile -v --fqbn esp32:esp32:esp32h2:UploadSpeed=921600,CDCOnBoot=default,FlashFreq=64,FlashMode=qio,FlashSize=4M,PartitionScheme=min_spiffs,DebugLevel=none,EraseFlash=all,JTAGAdapter=default,ZigbeeMode=default --output-dir binary .
# Sign bootloader
espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output binary/<SKETCH_NAME>.ino.signed.bootloader.bin binary/<SKETCH_NAME>.ino.bootloader.bin
# Sign partition table
espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output binary/<SKETCH_NAME>.ino.signed.partitions.bin binary/<SKETCH_NAME>.ino.partitions.bin
# Sign application
espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output binary/<SKETCH_NAME>.ino.signed.bin binary/<SKETCH_NAME>.ino.bin
4.1. From Terminal
Can't use
arduino-cli
# For esp32-h2, as an example
esptool.py --chip esp32h2 --port <PORT> --baud 921600 \
--before default_reset --after hard_reset write_flash -e -z --flash_mode keep \
--flash_freq keep --flash_size 4MB --force \
0x0 "binary/<SKETCH_NAME>.ino.signed.bootloader.bin" \
0x8000 "binary/<SKETCH_NAME>.ino.signed.partitions.bin" \
0xe000 "/Users/saurabhdatta/Library/Arduino15/packages/esp32/hardware/esp32/3.0.7/tools/partitions/boot_app0.bin" \
0x10000 "binary/<SKETCH_NAME>.ino.signed.bin"
💡 Note! if
—-force
is not used, it will show an error:A fatal error occurred: Secure Boot detected, writing to flash regions < 0x8000 is disabled to protect the bootloader.
This is expected and good - it shows the secure boot protection is working!
Using
--force
is okay in this case because we're uploading signed binaries.
4.2. From web tool: dattasaurabh82.github.io/webespflasher/
Yes and so, the manual steps 2 and 4 can be made faster with a helper script called secure_boot_process.sh
Just do:
# For esp32-h2, as an example
# Adopt the script and the cmd settings for your chip set
./secure_boot_process.sh --port <PORT>
# compiles, signs and uploads signed binaries