11 KiB
ESP32 Arduino Update Library
The Update library provides functionality for Over-The-Air (OTA) firmware updates on ESP32 devices. It supports secure updates with signature verification, encrypted updates, and various update sources.
Features
- OTA Updates: Update firmware over Wi-Fi
- Signature Verification: RSA and ECDSA signature verification for secure updates (optional, must be enabled with
UPDATE_SIGN) - Image Encryption: Support for encrypted firmware updates (optional, can be disabled with
UPDATE_NOCRYPT) - Multiple Sources: HTTP, HTTPS, SD card, and custom sources
- Progress Callbacks: Monitor update progress
- MD5 Verification: Optional MD5 checksum verification
Quick Start
Basic OTA Update
#include <Update.h>
WiFiClient client;
size_t updateSize = client.available();
if (Update.begin(updateSize)) {
Update.writeStream(client);
if (Update.end()) {
Serial.println("Update successful!");
ESP.restart();
} else {
Serial.println("Update failed!");
}
}
Signed OTA Update (Recommended)
To enable signature verification, add -DUPDATE_SIGN to your build flags (e.g., in build_opt.h):
-DUPDATE_SIGN
Then in your sketch:
#include <Update.h>
// Include your public key (generated with bin_signing.py)
#include "public_key.h"
// Create verifier object (defaults to SHA-256)
UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN);
// Install signature verification BEFORE Update.begin()
Update.installSignature(&sign);
// Now perform the update as usual
if (Update.begin(updateSize)) {
Update.writeStream(client);
if (Update.end()) {
// Signature was verified successfully!
Serial.println("Signed update successful!");
ESP.restart();
} else {
if (Update.getError() == UPDATE_ERROR_SIGN) {
Serial.println("Signature verification failed!");
}
}
}
Signature Verification
Overview
Code signing ensures that only firmware signed with your private key will be accepted by your devices. This protects against:
- Unauthorized firmware updates
- Man-in-the-middle attacks
- Compromised update servers
- Supply chain attacks
Supported Algorithms
Signature Schemes:
- RSA-2048, RSA-3072, RSA-4096
- ECDSA-P256, ECDSA-P384
Hash Algorithms:
- SHA-256, SHA-384, SHA-512
Setup
- Generate Key Pair:
# RSA-2048 (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
# ECDSA-P256 (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
- Include Public Key in Sketch:
#include "public_key.h" // Generated by bin_signing.py
- Install Signature Verification:
Enable the feature by adding to build_opt.h:
-DUPDATE_SIGN
Then in your sketch:
// For RSA with SHA-256
UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, HASH_SHA256);
Update.installSignature(&sign);
// For ECDSA with SHA-384
UpdaterECDSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, HASH_SHA384);
Update.installSignature(&sign);
- Sign Your Firmware:
python <ARDUINO_ROOT>/tools/bin_signing.py --bin <APPLICATION_BIN_FILE> --key private_key.pem --out firmware_signed.bin --hash <HASH_ALGORITHM>
- Upload Signed Application Firmware:
The signed firmware includes the signature appended to the binary. Upload the newly created signed firmware instead of the original application binary.
Security Best Practices
-
Protect Your Private Key:
- Never commit it to version control
- Store it in secure, encrypted storage
- Limit access to authorized personnel only
- Consider using HSM for production
-
Use HTTPS:
- While signature verification protects integrity, HTTPS protects confidentiality
API Reference
UpdateClass Methods
begin()
bool begin(size_t size = UPDATE_SIZE_UNKNOWN,
int command = U_FLASH,
int ledPin = -1,
uint8_t ledOn = LOW,
const char *label = NULL)
Starts an update operation.
Parameters:
size: Size of the update in bytes (including signature if using signed updates)command: Update type (U_FLASH, U_SPIFFS, U_FATFS, U_LITTLEFS)ledPin: Optional LED pin to indicate progressledOn: LED on state (LOW or HIGH)label: Optional partition label
Returns: true on success, false on failure
installSignature()
bool installSignature(UpdaterVerifyClass *sign)
Installs signature verification. Must be called before begin().
Parameters:
sign: Signature verifier (UpdaterRSAVerifier or UpdaterECDSAVerifier)
Returns: true on success, false on failure
write()
size_t write(uint8_t *data, size_t len)
Writes data to the update.
Parameters:
data: Data bufferlen: Length of data
Returns: Number of bytes written
writeStream()
size_t writeStream(Stream &data)
Writes data from a stream.
Parameters:
data: Input stream
Returns: Number of bytes written
end()
bool end(bool evenIfRemaining = false)
Completes the update and verifies signature if enabled.
Parameters:
evenIfRemaining: Complete even if not all data was written
Returns: true if update succeeded and signature is valid, false otherwise
abort()
void abort()
Aborts the current update.
setMD5()
bool setMD5(const char *expected_md5)
Sets expected MD5 hash for verification.
Parameters:
expected_md5: MD5 hash as hex string (32 characters)
Returns: true on success, false on failure
getError()
uint8_t getError()
Returns the last error code.
Returns: Error code (see Error Codes below)
errorString()
const char *errorString()
Returns a human-readable error message.
Returns: Error message string
Hash Classes (from Hash Library)
The Update library uses the Hash library for hashing. Simply use the builders from that library:
#include <SHA2Builder.h>
SHA256Builder hash256; // SHA-256
SHA384Builder hash384; // SHA-384
SHA512Builder hash512; // SHA-512
See the Hash library documentation for more details.
Signature Verifier Classes
UpdaterRSAVerifier
RSA signature verifier.
UpdaterRSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256)
Parameters:
pubkey: Public key in PEM formatpubkeyLen: Length of public keyhashType: Hash algorithm (HASH_SHA256,HASH_SHA384, orHASH_SHA512). Defaults toHASH_SHA256.
UpdaterECDSAVerifier
ECDSA signature verifier.
UpdaterECDSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256)
Parameters:
pubkey: Public key in PEM formatpubkeyLen: Length of public keyhashType: Hash algorithm (HASH_SHA256,HASH_SHA384, orHASH_SHA512). Defaults toHASH_SHA256.
Error Codes
| Code | Name | Description |
|---|---|---|
| 0 | UPDATE_ERROR_OK | No error |
| 1 | UPDATE_ERROR_WRITE | Flash write failed |
| 2 | UPDATE_ERROR_ERASE | Flash erase failed |
| 3 | UPDATE_ERROR_READ | Flash read failed |
| 4 | UPDATE_ERROR_SPACE | Not enough space |
| 5 | UPDATE_ERROR_SIZE | Bad size given |
| 6 | UPDATE_ERROR_STREAM | Stream read timeout |
| 7 | UPDATE_ERROR_MD5 | MD5 check failed |
| 8 | UPDATE_ERROR_MAGIC_BYTE | Wrong magic byte |
| 9 | UPDATE_ERROR_ACTIVATE | Could not activate firmware |
| 10 | UPDATE_ERROR_NO_PARTITION | Partition not found |
| 11 | UPDATE_ERROR_BAD_ARGUMENT | Bad argument |
| 12 | UPDATE_ERROR_ABORT | Aborted |
| 13 | UPDATE_ERROR_DECRYPT | Decryption error |
| 14 | UPDATE_ERROR_SIGN | Signature verification failed |
Examples
- Signed_OTA_Update: Demonstrates signed OTA updates with RSA/ECDSA
- HTTPS_OTA_Update: HTTPS OTA update
- HTTP_Client_AES_OTA_Update: Encrypted OTA update
- SD_Update: Update from SD card
See the examples/ directory for complete examples.
Tools
bin_signing.py
Python script for key generation and firmware signing. Located in <ARDUINO_ROOT>/tools/bin_signing.py.
Requirements:
pip install cryptography
Usage:
# Generate keys
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
# Sign firmware (defaults to SHA-256)
python <ARDUINO_ROOT>/tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin
# Sign firmware with SHA-384
python <ARDUINO_ROOT>/tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384
# Verify signature
python <ARDUINO_ROOT>/tools/bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem
See <ARDUINO_ROOT>/tools/bin_signing.py --help for more options.
Troubleshooting
"Signature verification failed"
- Ensure firmware was signed with correct private key
- Verify public key in sketch matches private key
- Check signature scheme and hash algorithm match
- Verify signed binary wasn't corrupted
"Failed to install signature verification"
- Call
installSignature()beforeUpdate.begin() - Ensure hash and sign objects are properly initialized
"Update failed" with no specific error
- Check firmware size is correct (including signature)
- Ensure enough space in target partition
- Verify magic byte (0xE9) at start of firmware
Memory Issues
- Signature verification requires ~2KB of heap
- RSA-4096 uses more memory than ECDSA-P256
- Ensure sufficient free heap before starting update
Compile-Time Options
The Update library supports compile-time configuration to reduce code size if certain features are not needed:
UPDATE_SIGN
Enable signature verification support (disabled by default).
Add to your project's build_opt.h:
-DUPDATE_SIGN
Or add to your build flags in platformio.ini:
build_flags = -DUPDATE_SIGN
Effects:
- Enables signature verification classes and methods
- Adds RSA and ECDSA signature verification support
installSignature()method becomes available- Increases code size due to mbedtls cryptographic functions
UPDATE_NOCRYPT
Disable encryption/decryption support:
#define UPDATE_NOCRYPT
#include <Update.h>
Effects:
- Removes AES encryption support
- Reduces code size
setupCrypt()and related methods will not be available
Note: To enable signature verification while disabling encryption, add to build_opt.h:
-DUPDATE_SIGN
-DUPDATE_NOCRYPT
License
This library is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0.
Contributing
Contributions are welcome! Please submit issues and pull requests on GitHub: https://github.com/espressif/arduino-esp32
Support
- Documentation: https://docs.espressif.com/
- Forum: https://esp32.com/
- GitHub Issues: https://github.com/espressif/arduino-esp32/issues