3.3.7
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user