# 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 <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:
```cpp
#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:**

```bash
# 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
```

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 <ARDUINO_ROOT>/tools/bin_signing.py --bin <APPLICATION_BIN_FILE> --key private_key.pem --out firmware_signed.bin --hash <HASH_ALGORITHM>
```

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 <SHA2Builder.h>

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 `<ARDUINO_ROOT>/tools/bin_signing.py`.

**Requirements:**
```bash
pip install cryptography
```

**Usage:**
```bash
# 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`:
```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 <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
