Files
esp32-core/libraries/Update/README.md
T
2026-05-22 21:52:50 +03:00

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!");
  }
}

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

  1. 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
  1. Include Public Key in Sketch:
#include "public_key.h"  // Generated by bin_signing.py
  1. 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);
  1. 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>
  1. 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

  1. 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
  2. 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 progress
  • ledOn: 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 buffer
  • len: 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 format
  • pubkeyLen: Length of public key
  • hashType: Hash algorithm (HASH_SHA256, HASH_SHA384, or HASH_SHA512). Defaults to HASH_SHA256.

UpdaterECDSAVerifier

ECDSA signature verifier.

UpdaterECDSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256)

Parameters:

  • pubkey: Public key in PEM format
  • pubkeyLen: Length of public key
  • hashType: Hash algorithm (HASH_SHA256, HASH_SHA384, or HASH_SHA512). Defaults to HASH_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() before Update.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