# 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 ```cpp #include 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: ```cpp #include // 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:** ```bash # RSA-2048 (recommended) python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem # ECDSA-P256 (smaller, faster) python /tools/bin_signing.py --generate-key ecdsa-p256 --out private_key.pem python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem ``` 2. **Include Public Key in Sketch:** ```cpp #include "public_key.h" // Generated by bin_signing.py ``` 3. **Install Signature Verification:** Enable the feature by adding to `build_opt.h`: ``` -DUPDATE_SIGN ``` Then in your sketch: ```cpp // 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); ``` 4. **Sign Your Firmware:** ```bash python /tools/bin_signing.py --bin --key private_key.pem --out firmware_signed.bin --hash ``` 5. **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() ```cpp 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() ```cpp 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() ```cpp 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() ```cpp size_t writeStream(Stream &data) ``` Writes data from a stream. **Parameters:** - `data`: Input stream **Returns:** Number of bytes written #### end() ```cpp 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() ```cpp void abort() ``` Aborts the current update. #### setMD5() ```cpp 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() ```cpp uint8_t getError() ``` Returns the last error code. **Returns:** Error code (see Error Codes below) #### errorString() ```cpp 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: ```cpp #include SHA256Builder hash256; // SHA-256 SHA384Builder hash384; // SHA-384 SHA512Builder hash512; // SHA-512 ``` See the [Hash library documentation](../Hash/README.md) for more details. ### Signature Verifier Classes #### UpdaterRSAVerifier RSA signature verifier. ```cpp 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. ```cpp 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 `/tools/bin_signing.py`. **Requirements:** ```bash pip install cryptography ``` **Usage:** ```bash # Generate keys python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem # Sign firmware (defaults to SHA-256) python /tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin # Sign firmware with SHA-384 python /tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384 # Verify signature python /tools/bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem ``` See `/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`: ```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: ```cpp #define UPDATE_NOCRYPT #include ``` **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