libraries/Update/examples/Signed_OTA_Update/README.md
This example demonstrates how to perform secure OTA (Over-The-Air) updates with signature verification on ESP32 devices using Arduino.
Code signing ensures that only firmware signed with your private key will be accepted by your devices. This protects against unauthorized firmware updates, even if an attacker gains access to your update server.
Python 3 with the cryptography package:
pip install cryptography
ESP32 Arduino Core with Update library
Generate an RSA-2048 key pair (recommended):
python <ARDUINO_ROOT>/tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem
python <ARDUINO_ROOT>/tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem
Or generate an ECDSA-P256 key pair (smaller, faster):
python <ARDUINO_ROOT>/tools/bin_signing.py --generate-key ecdsa-p256 --out private_key.pem
python <ARDUINO_ROOT>/tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem
Where <ARDUINO_ROOT> is your ESP32 Arduino installation path (e.g., ~/Arduino/hardware/espressif/esp32/).
IMPORTANT: Keep private_key.pem secure! Anyone with access to it can sign firmware for your devices.
public_key.h to the example directorySigned_OTA_Update.inoconst char *ssid = "YOUR_SSID";
const char *password = "YOUR_PASSWORD";
const char *firmwareUrl = "http://your-server.com/firmware_signed.bin";
Make changes to your sketch (e.g., add a version number)
Build the sketch and export the binary:
Sketch → Export Compiled Binary.bin file in the build folder of your sketch folder. For example build/espressif.esp32.esp32c6/Signed_OTA_Update.ino.bin.Sign the binary:
python <ARDUINO_ROOT>/tools/bin_signing.py --bin <APPLICATION_BIN_FILE> --key private_key.pem --out firmware_signed.bin
For other hash algorithms (for example SHA-384):
python <ARDUINO_ROOT>/tools/bin_signing.py --bin <APPLICATION_BIN_FILE> --key private_key.pem --out firmware_signed.bin --hash sha384
Upload firmware_signed.bin to your web server and make it accessible at the URL you configured.
Reset your ESP32. It will:
| Scheme | Key Size | Signature Size | Verification Speed | Security |
|---|---|---|---|---|
| RSA-2048 | 2048 bits | 256 bytes | Medium | High |
| RSA-3072 | 3072 bits | 384 bytes | Slower | Very High |
| RSA-4096 | 4096 bits | 512 bytes | Slowest | Maximum |
| ECDSA-P256 | 256 bits | 64 bytes | Fast | High |
| ECDSA-P384 | 384 bits | 96 bytes | Fast | Very High |
Recommendation: RSA-2048 or ECDSA-P256 provide good security with reasonable performance.
| Algorithm | Output Size | Speed | Security |
|---|---|---|---|
| SHA-256 | 32 bytes | Fast | High |
| SHA-384 | 48 bytes | Medium | Very High |
| SHA-512 | 64 bytes | Medium | Very High |
Recommendation: SHA-256 is sufficient for most applications.
installSignature() is called before Update.begin()You can verify a signed binary without flashing it:
python bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem
Match the hash algorithm between signing and verification:
Signing with SHA-384:
python bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384
Sketch configuration:
#define USE_SHA384
// Install signature verification (call before Update.begin())
bool Update.installSignature(UpdaterVerifyClass *sign);
UPDATE_ERROR_SIGN (14): Signature verification failedThis example is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0.
For issues and questions: