| 
 | 1 | +# SignedOTA - Secure OTA Updates with Signature Verification  | 
 | 2 | + | 
 | 3 | +This example demonstrates how to perform secure OTA updates with cryptographic signature verification using the ArduinoOTA library.  | 
 | 4 | + | 
 | 5 | +## Overview  | 
 | 6 | + | 
 | 7 | +**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:  | 
 | 8 | + | 
 | 9 | +- ✅ Unauthorized firmware updates  | 
 | 10 | +- ✅ Man-in-the-middle attacks  | 
 | 11 | +- ✅ Compromised networks  | 
 | 12 | +- ✅ Firmware tampering  | 
 | 13 | +- ✅ Supply chain attacks  | 
 | 14 | + | 
 | 15 | +Even if an attacker gains access to your network, they **cannot** install unsigned firmware on your devices.  | 
 | 16 | + | 
 | 17 | +## Features  | 
 | 18 | + | 
 | 19 | +- **RSA & ECDSA Support**: RSA-2048/3072/4096 and ECDSA-P256/P384  | 
 | 20 | +- **Multiple Hash Algorithms**: SHA-256, SHA-384, SHA-512  | 
 | 21 | +- **Arduino IDE Compatible**: Works with standard Arduino OTA workflow  | 
 | 22 | +- **Optional Password Protection**: Add password authentication in addition to signature verification  | 
 | 23 | +- **Easy Integration**: Just a few lines of code  | 
 | 24 | + | 
 | 25 | +## Requirements  | 
 | 26 | + | 
 | 27 | +- **ESP32 Arduino Core 3.3.0+**  | 
 | 28 | +- **Python 3.6+** with `cryptography` library  | 
 | 29 | +- **OTA-capable partition scheme** (e.g., "Minimal SPIFFS (1.9MB APP with OTA)")  | 
 | 30 | + | 
 | 31 | +## Quick Start Guide  | 
 | 32 | + | 
 | 33 | +### 1. Generate Cryptographic Keys  | 
 | 34 | + | 
 | 35 | +```bash  | 
 | 36 | +# Navigate to Arduino ESP32 tools directory  | 
 | 37 | +cd <ARDUINO_ROOT>/tools  | 
 | 38 | + | 
 | 39 | +# Install Python dependencies  | 
 | 40 | +pip install cryptography  | 
 | 41 | + | 
 | 42 | +# Generate RSA-2048 key pair (recommended)  | 
 | 43 | +python bin_signing.py --generate-key rsa-2048 --out private_key.pem  | 
 | 44 | + | 
 | 45 | +# Extract public key  | 
 | 46 | +python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem  | 
 | 47 | +```  | 
 | 48 | + | 
 | 49 | +**⚠️ IMPORTANT: Keep `private_key.pem` secure! Anyone with this key can sign firmware for your devices.**  | 
 | 50 | + | 
 | 51 | +### 2. Setup the Example  | 
 | 52 | + | 
 | 53 | +1. Copy `public_key.h` (generated in step 1) to this sketch directory  | 
 | 54 | +2. Open `SignedOTA.ino` in Arduino IDE  | 
 | 55 | +3. Configure WiFi credentials:  | 
 | 56 | +   ```cpp  | 
 | 57 | +   const char *ssid = "YourWiFiSSID";  | 
 | 58 | +   const char *password = "YourWiFiPassword";  | 
 | 59 | +   ```  | 
 | 60 | +4. Select appropriate partition scheme:  | 
 | 61 | +   - **Tools → Partition Scheme → "Minimal SPIFFS (1.9MB APP with OTA)"**  | 
 | 62 | + | 
 | 63 | +### 3. Upload Initial Firmware  | 
 | 64 | + | 
 | 65 | +1. Connect your ESP32 via USB  | 
 | 66 | +2. Upload the sketch normally  | 
 | 67 | +3. Open Serial Monitor (115200 baud)  | 
 | 68 | +4. Note the device IP address  | 
 | 69 | + | 
 | 70 | +### 4. Build & Sign Firmware for OTA Update Example  | 
 | 71 | + | 
 | 72 | +**Option A: Using Arduino IDE**  | 
 | 73 | + | 
 | 74 | +```bash  | 
 | 75 | +# Export compiled binary  | 
 | 76 | +# In Arduino IDE: Sketch → Export Compiled Binary  | 
 | 77 | + | 
 | 78 | +# Sign the firmware  | 
 | 79 | +cd <ARDUINO_ROOT>/tools  | 
 | 80 | +python bin_signing.py \  | 
 | 81 | +  --bin /path/to/SignedOTA.ino.bin \  | 
 | 82 | +  --key private_key.pem \  | 
 | 83 | +  --out firmware_signed.bin  | 
 | 84 | +```  | 
 | 85 | + | 
 | 86 | +**Option B: Using arduino-cli**  | 
 | 87 | + | 
 | 88 | +```bash  | 
 | 89 | +# Compile and export  | 
 | 90 | +arduino-cli compile --fqbn esp32:esp32:esp32 --export-binaries SignedOTA  | 
 | 91 | + | 
 | 92 | +# Sign the firmware  | 
 | 93 | +cd <ARDUINO_ROOT>/tools  | 
 | 94 | +python bin_signing.py \  | 
 | 95 | +  --bin build/esp32.esp32.esp32/SignedOTA.ino.bin \  | 
 | 96 | +  --key private_key.pem \  | 
 | 97 | +  --out firmware_signed.bin  | 
 | 98 | +```  | 
 | 99 | + | 
 | 100 | +### 5. Upload Signed Firmware via OTA  | 
 | 101 | + | 
 | 102 | +Upload the signed firmware using `espota.py`:  | 
 | 103 | + | 
 | 104 | +```bash  | 
 | 105 | +python <ARDUINO_ROOT>/tools/espota.py -i <device-ip> -f firmware_signed.bin  | 
 | 106 | +```  | 
 | 107 | + | 
 | 108 | +The device will automatically:  | 
 | 109 | +1. Receive the signed firmware (firmware + signature)  | 
 | 110 | +2. Hash only the firmware portion  | 
 | 111 | +3. Verify the signature  | 
 | 112 | +4. Install if valid, reject if invalid  | 
 | 113 | + | 
 | 114 | +**Note**: You can also use the Update library's `Signed_OTA_Update` example for HTTP-based OTA updates.  | 
 | 115 | + | 
 | 116 | +## Configuration Options  | 
 | 117 | + | 
 | 118 | +### Hash Algorithms  | 
 | 119 | + | 
 | 120 | +Choose one in `SignedOTA.ino`:  | 
 | 121 | + | 
 | 122 | +```cpp  | 
 | 123 | +#define USE_SHA256  // Default, fastest  | 
 | 124 | +// #define USE_SHA384  | 
 | 125 | +// #define USE_SHA512  | 
 | 126 | +```  | 
 | 127 | + | 
 | 128 | +**Must match** the `--hash` parameter when signing:  | 
 | 129 | + | 
 | 130 | +```bash  | 
 | 131 | +python bin_signing.py --bin firmware.bin --key private.pem --out signed.bin --hash sha256  | 
 | 132 | +```  | 
 | 133 | + | 
 | 134 | +### Signature Algorithms  | 
 | 135 | + | 
 | 136 | +Choose one in `SignedOTA.ino`:  | 
 | 137 | + | 
 | 138 | +```cpp  | 
 | 139 | +#define USE_RSA  // For RSA keys  | 
 | 140 | +// #define USE_ECDSA  // For ECDSA keys  | 
 | 141 | +```  | 
 | 142 | + | 
 | 143 | +### Optional Password Protection  | 
 | 144 | + | 
 | 145 | +Add password authentication **in addition to** signature verification:  | 
 | 146 | + | 
 | 147 | +```cpp  | 
 | 148 | +const char *ota_password = "yourpassword";  // Set password  | 
 | 149 | +// const char *ota_password = nullptr;  // Disable password  | 
 | 150 | +```  | 
 | 151 | + | 
 | 152 | +## How It Works  | 
 | 153 | + | 
 | 154 | +```  | 
 | 155 | +┌─────────────────┐  | 
 | 156 | +│  Build Firmware │  | 
 | 157 | +└────────┬────────┘  | 
 | 158 | +         │  | 
 | 159 | +         ▼  | 
 | 160 | +┌─────────────────┐  | 
 | 161 | +│  Sign Firmware  │ ← Uses your private key  | 
 | 162 | +│  (bin_signing)  │  | 
 | 163 | +└────────┬────────┘  | 
 | 164 | +         │  | 
 | 165 | +         ▼  | 
 | 166 | +┌─────────────────────────┐  | 
 | 167 | +│ firmware_signed.bin     │  | 
 | 168 | +│ [firmware][signature]   │  | 
 | 169 | +└────────┬────────────────┘  | 
 | 170 | +         │  | 
 | 171 | +         ▼ OTA Upload  | 
 | 172 | +┌─────────────────────────┐  | 
 | 173 | +│      ESP32 Device       │  | 
 | 174 | +│  ┌──────────────────┐   │  | 
 | 175 | +│  │ Verify Signature │   │ ← Uses your public key  | 
 | 176 | +│  │  ✓ or ✗          │   │  | 
 | 177 | +│  └──────────────────┘   │  | 
 | 178 | +│         │               │  | 
 | 179 | +│    ✓ Valid?             │  | 
 | 180 | +│    ├─ Yes: Install      │  | 
 | 181 | +│    └─ No:  Reject       │  | 
 | 182 | +└─────────────────────────┘  | 
 | 183 | +```  | 
 | 184 | + | 
 | 185 | +## Troubleshooting  | 
 | 186 | + | 
 | 187 | +### "Begin Failed" Error  | 
 | 188 | + | 
 | 189 | +**Cause**: Signature verification setup failed, or partition scheme issue  | 
 | 190 | + | 
 | 191 | +**Solutions**:  | 
 | 192 | +1. Check partition scheme (use "Minimal SPIFFS (1.9MB APP with OTA)")  | 
 | 193 | +2. Verify `public_key.h` is in the sketch directory  | 
 | 194 | +3. Check hash and signature algorithm match your key type  | 
 | 195 | + | 
 | 196 | +### "End Failed" Error  | 
 | 197 | + | 
 | 198 | +**Cause**: Signature verification failed  | 
 | 199 | + | 
 | 200 | +**Solutions**:  | 
 | 201 | +1. Ensure firmware was signed with the **correct private key**  | 
 | 202 | +2. Verify hash algorithm matches (SHA-256, SHA-384, SHA-512)  | 
 | 203 | +3. Check firmware wasn't corrupted during signing/transfer  | 
 | 204 | +4. Confirm you signed the **correct** `.bin` file  | 
 | 205 | + | 
 | 206 | +### "Receive Failed" Error  | 
 | 207 | + | 
 | 208 | +**Cause**: Network timeout or connection issue  | 
 | 209 | + | 
 | 210 | +**Solutions**:  | 
 | 211 | +1. Check WiFi signal strength  | 
 | 212 | +2. Ensure device is reachable on the network  | 
 | 213 | +3. Try increasing timeout: `ArduinoOTA.setTimeout(5000)`  | 
 | 214 | + | 
 | 215 | +### Upload Fails  | 
 | 216 | + | 
 | 217 | +**Issue**: OTA upload fails or times out  | 
 | 218 | + | 
 | 219 | +**Solutions**:  | 
 | 220 | +1. Verify device is on the same network  | 
 | 221 | +2. Check firewall settings aren't blocking port 3232  | 
 | 222 | +3. Ensure WiFi signal strength is adequate  | 
 | 223 | +4. If using password protection, ensure the password is correct  | 
 | 224 | +5. Try: `python <ARDUINO_ROOT>/tools/espota.py -i <device-ip> -f firmware_signed.bin -d`  | 
 | 225 | + | 
 | 226 | +## Security Considerations  | 
 | 227 | + | 
 | 228 | +### Best Practices  | 
 | 229 | + | 
 | 230 | +✅ **Keep private key secure**: Never commit to git, store encrypted  | 
 | 231 | +✅ **Use strong keys**: RSA-2048+ or ECDSA-P256+  | 
 | 232 | +✅ **Use HTTPS when possible**: For additional transport security  | 
 | 233 | +✅ **Add password authentication**: Extra layer of protection  | 
 | 234 | +✅ **Rotate keys periodically**: Generate new keys every 1-2 years  | 
 | 235 | + | 
 | 236 | +### What This Protects Against  | 
 | 237 | + | 
 | 238 | +- ✅ Unsigned firmware installation  | 
 | 239 | +- ✅ Firmware signed with wrong key  | 
 | 240 | +- ✅ Tampered/corrupted firmware  | 
 | 241 | +- ✅ Network-based attacks (when combined with password)  | 
 | 242 | + | 
 | 243 | +### What This Does NOT Protect Against  | 
 | 244 | + | 
 | 245 | +- ❌ Physical access (USB flashing still works)  | 
 | 246 | +- ❌ Downgrade attacks (no version checking by default)  | 
 | 247 | +- ❌ Replay attacks (no timestamp/nonce by default)  | 
 | 248 | +- ❌ Key compromise (if private key is stolen)  | 
 | 249 | + | 
 | 250 | +### Additional Security  | 
 | 251 | + | 
 | 252 | +For production deployments, consider:  | 
 | 253 | + | 
 | 254 | +1. **Add version checking** to prevent downgrades  | 
 | 255 | +2. **Add timestamp validation** to prevent replay attacks  | 
 | 256 | +3. **Use secure boot** for additional protection  | 
 | 257 | +4. **Store keys in HSM** or secure key management system  | 
 | 258 | +5. **Implement key rotation** mechanism  | 
 | 259 | + | 
 | 260 | +## Advanced Usage  | 
 | 261 | + | 
 | 262 | +### Using ECDSA Instead of RSA  | 
 | 263 | + | 
 | 264 | +ECDSA keys are smaller and faster:  | 
 | 265 | + | 
 | 266 | +```bash  | 
 | 267 | +# Generate ECDSA-P256 key  | 
 | 268 | +python bin_signing.py --generate-key ecdsa-p256 --out private_key.pem  | 
 | 269 | +python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem  | 
 | 270 | +```  | 
 | 271 | + | 
 | 272 | +In `SignedOTA.ino`:  | 
 | 273 | + | 
 | 274 | +```cpp  | 
 | 275 | +#define USE_SHA256  | 
 | 276 | +#define USE_ECDSA  // Instead of USE_RSA  | 
 | 277 | +```  | 
 | 278 | +
  | 
 | 279 | +### Using SHA-384 or SHA-512  | 
 | 280 | +
  | 
 | 281 | +For higher security:  | 
 | 282 | +
  | 
 | 283 | +```bash  | 
 | 284 | +# Sign with SHA-384  | 
 | 285 | +python bin_signing.py --bin firmware.bin --key private.pem --out signed.bin --hash sha384  | 
 | 286 | +```  | 
 | 287 | + | 
 | 288 | +In `SignedOTA.ino`:  | 
 | 289 | + | 
 | 290 | +```cpp  | 
 | 291 | +#define USE_SHA384  // Instead of USE_SHA256  | 
 | 292 | +#define USE_RSA  | 
 | 293 | +```  | 
 | 294 | +
  | 
 | 295 | +### Custom Partition Label  | 
 | 296 | +
  | 
 | 297 | +To update a specific partition:  | 
 | 298 | +
  | 
 | 299 | +```cpp  | 
 | 300 | +ArduinoOTA.setPartitionLabel("my_partition");  | 
 | 301 | +```  | 
 | 302 | + | 
 | 303 | +## Support  | 
 | 304 | + | 
 | 305 | +For issues and questions:  | 
 | 306 | + | 
 | 307 | +- Update Library README: `libraries/Update/README.md`  | 
 | 308 | +- ESP32 Arduino Core: https://github.com/espressif/arduino-esp32  | 
 | 309 | +- Forum: https://github.com/espressif/arduino-esp32/discussions  | 
 | 310 | + | 
 | 311 | +## License  | 
 | 312 | + | 
 | 313 | +This library is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0.  | 
 | 314 | + | 
0 commit comments