Files

324 lines
9.0 KiB
Markdown
Raw Permalink Normal View History

2026-05-22 21:52:50 +03:00
# 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.