# SignedOTA - Secure OTA Updates with Signature Verification

This example demonstrates how to perform secure OTA updates with cryptographic signature verification using the ArduinoOTA library.

## Overview

**SignedOTA** adds an extra layer of security to Arduino OTA updates by requiring all firmware to be cryptographically signed with your private key. This protects against:

- ✅ Unauthorized firmware updates
- ✅ Man-in-the-middle attacks
- ✅ Compromised networks
- ✅ Firmware tampering
- ✅ Supply chain attacks

Even if an attacker gains access to your network, they **cannot** install unsigned firmware on your devices.

## Features

- **RSA & ECDSA Support**: RSA-2048/3072/4096 and ECDSA-P256/P384
- **Multiple Hash Algorithms**: SHA-256, SHA-384, SHA-512
- **Arduino IDE Compatible**: Works with standard Arduino OTA workflow
- **Optional Password Protection**: Add password authentication in addition to signature verification
- **Easy Integration**: Just a few lines of code

## Requirements

- **ESP32 Arduino Core 3.3.0+**
- **Python 3.6+** with `cryptography` library
<!-- vale Espressif-latest.Units = NO -->
- **OTA-capable partition scheme** (e.g., "Minimal SPIFFS (1.9MB APP with OTA)")
<!-- vale Espressif-latest.Units = YES -->

## Quick Start Guide

### 1. Generate Cryptographic Keys

```bash
# Navigate to Arduino ESP32 tools directory
cd <ARDUINO_ROOT>/tools

# Install Python dependencies
pip install cryptography

# Generate RSA-2048 key pair (recommended)
python bin_signing.py --generate-key rsa-2048 --out private_key.pem

# Extract public key
python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem
```

**⚠️ IMPORTANT: Keep `private_key.pem` secure! Anyone with this key can sign firmware for your devices.**

### 2. Setup the Example

1. Copy `public_key.h` (generated in step 1) to this sketch directory
2. Open `SignedOTA.ino` in Arduino IDE
<!-- vale Espressif-latest.TermsSingleCorrectSpelling = NO -->
3. Configure WiFi credentials:
   ```cpp
   const char *ssid = "YourWiFiSSID";
   const char *password = "YourWiFiPassword";
   ```
<!-- vale Espressif-latest.TermsSingleCorrectSpelling = YES -->
<!-- vale Espressif-latest.Units = NO -->
4. Select appropriate partition scheme:
   - **Tools → Partition Scheme → "Minimal SPIFFS (1.9MB APP with OTA)"**
<!-- vale Espressif-latest.Units = YES -->

### 3. Upload Initial Firmware

1. Connect your ESP32 via USB
2. Upload the sketch normally
3. Open Serial Monitor (115200 baud)
4. Note the device IP address

### 4. Build & Sign Firmware for OTA Update Example

**Option A: Using Arduino IDE**

```bash
# Export compiled binary
# In Arduino IDE: Sketch → Export Compiled Binary

# Sign the firmware
cd <ARDUINO_ROOT>/tools
python bin_signing.py \
  --bin /path/to/SignedOTA.ino.bin \
  --key private_key.pem \
  --out firmware_signed.bin
```

**Option B: Using arduino-cli**

```bash
# Compile and export
arduino-cli compile --fqbn esp32:esp32:esp32 --export-binaries SignedOTA

# Sign the firmware
cd <ARDUINO_ROOT>/tools
python bin_signing.py \
  --bin build/esp32.esp32.esp32/SignedOTA.ino.bin \
  --key private_key.pem \
  --out firmware_signed.bin
```

### 5. Upload Signed Firmware via OTA

Upload the signed firmware using `espota.py`:

```bash
python <ARDUINO_ROOT>/tools/espota.py -i <device-ip> -f firmware_signed.bin
```

The device will automatically:
1. Receive the signed firmware (firmware + signature)
2. Hash only the firmware portion
3. Verify the signature
4. Install if valid, reject if invalid

**Note**: You can also use the Update library's `Signed_OTA_Update` example for HTTP-based OTA updates.

## Configuration Options

### Hash Algorithms

Choose one in `SignedOTA.ino`:

```cpp
#define USE_SHA256  // Default, fastest
// #define USE_SHA384
// #define USE_SHA512
```

**Must match** the `--hash` parameter when signing:

```bash
python bin_signing.py --bin firmware.bin --key private.pem --out signed.bin --hash sha256
```

### Signature Algorithms

Choose one in `SignedOTA.ino`:

```cpp
#define USE_RSA  // For RSA keys
// #define USE_ECDSA  // For ECDSA keys
```

### Optional Password Protection

Add password authentication **in addition to** signature verification:

```cpp
const char *ota_password = "yourpassword";  // Set password
// const char *ota_password = nullptr;  // Disable password
```

## How It Works

```
┌─────────────────┐
│  Build Firmware │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Sign Firmware  │ ← Uses your private key
│  (bin_signing)  │
└────────┬────────┘
         │
         ▼
┌─────────────────────────┐
│ firmware_signed.bin     │
│ [firmware][signature]   │
└────────┬────────────────┘
         │
         ▼ OTA Upload
┌─────────────────────────┐
│      ESP32 Device       │
│  ┌──────────────────┐   │
│  │ Verify Signature │   │ ← Uses your public key
│  │  ✓ or ✗          │   │
│  └──────────────────┘   │
│         │               │
│    ✓ Valid?             │
│    ├─ Yes: Install      │
│    └─ No:  Reject       │
└─────────────────────────┘
```

## Troubleshooting

### "Begin Failed" Error

**Cause**: Signature verification setup failed, or partition scheme issue

**Solutions**:
<!-- vale Espressif-latest.Units = NO -->
1. Check partition scheme (use "Minimal SPIFFS (1.9MB APP with OTA)")
2. Verify `public_key.h` is in the sketch directory
3. Check hash and signature algorithm match your key type
<!-- vale Espressif-latest.Units = YES -->

### "End Failed" Error

**Cause**: Signature verification failed

**Solutions**:
1. Ensure firmware was signed with the **correct private key**
2. Verify hash algorithm matches (SHA-256, SHA-384, SHA-512)
3. Check firmware wasn't corrupted during signing/transfer
4. Confirm you signed the **correct** `.bin` file

### "Receive Failed" Error

**Cause**: Network timeout or connection issue

**Solutions**:
1. Check Wi-Fi signal strength
2. Ensure device is reachable on the network
3. Try increasing timeout: `ArduinoOTA.setTimeout(5000)`

### Upload Fails

**Issue**: OTA upload fails or times out

**Solutions**:
1. Verify device is on the same network
2. Check firewall settings aren't blocking port 3232
3. Ensure Wi-Fi signal strength is adequate
4. If using password protection, ensure the password is correct
5. Try: `python <ARDUINO_ROOT>/tools/espota.py -i <device-ip> -f firmware_signed.bin -d`

## Security Considerations

### Best Practices

✅ **Keep private key secure**: Never commit to git, store encrypted
✅ **Use strong keys**: RSA-2048+ or ECDSA-P256+
✅ **Use HTTPS when possible**: For additional transport security
✅ **Add password authentication**: Extra layer of protection
✅ **Rotate keys periodically**: Generate new keys every 1-2 years

### What This Protects Against

- ✅ Unsigned firmware installation
- ✅ Firmware signed with wrong key
- ✅ Tampered/corrupted firmware
- ✅ Network-based attacks (when combined with password)

### What This Does NOT Protect Against

<!-- vale Espressif-latest.Cursing = NO -->
- ❌ Physical access (USB flashing still works)
- ❌ Downgrade attacks (no version checking by default)
- ❌ Replay attacks (no timestamp/nonce by default)
- ❌ Key compromise (if private key is stolen)
<!-- vale Espressif-latest.Cursing = YES -->

### Additional Security

For production deployments, consider:

1. **Add version checking** to prevent downgrades
2. **Add timestamp validation** to prevent replay attacks
3. **Use secure boot** for additional protection
4. **Store keys in HSM** or secure key management system
5. **Implement key rotation** mechanism

## Advanced Usage

### Using ECDSA Instead of RSA

ECDSA keys are smaller and faster:

```bash
# Generate ECDSA-P256 key
python bin_signing.py --generate-key ecdsa-p256 --out private_key.pem
python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem
```

In `SignedOTA.ino`:

```cpp
#define USE_SHA256
#define USE_ECDSA  // Instead of USE_RSA
```

### Using SHA-384 or SHA-512

For higher security:

```bash
# Sign with SHA-384
python bin_signing.py --bin firmware.bin --key private.pem --out signed.bin --hash sha384
```

In `SignedOTA.ino`:

```cpp
#define USE_SHA384  // Instead of USE_SHA256
#define USE_RSA
```

### Custom Partition Label

To update a specific partition:

```cpp
ArduinoOTA.setPartitionLabel("my_partition");
```

## Support

For issues and questions:

- Update Library README: `libraries/Update/README.md`
- ESP32 Arduino Core: https://github.com/espressif/arduino-esp32
- Forum: https://github.com/espressif/arduino-esp32/discussions

## License

This library is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0.
