Signed OTA Update Pipeline (ESP32 + STM32)
⚠️ Reference implementation for product teams and training. Adaptations to your hardware/toolchain are planned.
Summary / Value Proposition
- Security by design: Ed25519 signatures + SHA-256 integrity + anti-rollback.
- Robustness: A/B slots, watchdog-friendly OTA writes, rollback on errors.
- Transparency: Canonical manifest (verifiable, diffable); reproducible signing process.
- DevEx: Python signer, verifier, CI workflow (GitHub Actions) – ready to use immediately.
- Cross-target: ESP32 (ESP-IDF v5.x) & STM32 (bootloader skeleton) from a single toolkit.
Highlights
- Canonical Manifest (JSON): device, version, slot, timestamp, image.sha256/size, meta.*
- Signatures: Ed25519 over the canonicalized manifest body (stable sorting/separators)
- Anti-rollback: Monotonic version check (NVS/Flash) + optional HW counter
- A/B schema: Safe switching, self-test, commit only when healthy
- CI-capable: Build → Sign → Artifact, key injection via secrets, release promotion
- Extensible: SBOM (CycloneDX), release signatures (cosign), staged rollouts
Deliverables
- Tooling ("signer/"): Keygen, signer, package builder (.ota ZIP with manifest.json + firmware.bin), verifier
- ESP32 App: OTA flow (download/stream), versioning, NVS hook, rollback support via IDF bootloader
- STM32 Bootloader: Slot selection, manifest/hash validation (Ed25519 verify as port/library), jump logic, rollback marker
- CI Workflow: GitHub Actions template (build, sign, artifact upload)
- Documentation: Threat model, test plan, integration guide
Architecture
[CI Build] ──(Artifacts)──> [Signer] ──( .ota Package )──> [OTA Server/CDN]
│
(HTTPS, Auth)
│
[Device Fleet]
ESP32: Downloader → Verify(Signature+Hash) → Stream to OTA Partition → Reboot → Self-Test → Commit
STM32: Bootloader selects slot → Verify(Signature+Hash) → Jump → on errors Rollback/Alternate Slot
Security Principles
- Authenticity & Integrity: Ed25519 over manifest; SHA-256 over image
- "Verify-before-commit": No activation without fully verified data
- Rollback resilience: A/B schema + monotonic version
- Key hygiene: Offline root, short-lived CI keys, rotation & pinning of public keys
- Supply chain: Optional SBOM + release signatures + reproducible builds
Technical Details
Manifest (Excerpt)
Json
{
"body": {
"device": "esp32|stm32",
"version": "1.2.3",
"slot": "ota_0|ota_1|app_a|app_b",
"timestamp": "2025-09-03T12:00:00Z",
"image": { "sha256": "…", "size": 524288 },
"meta": { "min_bootloader": "1.0.0", "rollback_protected": true }
},
"signature": { "algo": "ed25519", "value": "BASE64_SIG" }
}
ESP32 (ESP-IDF v5.x)
- Partitions: factory, ota_0, ota_1
- OTA stream via esp_https_ota
- Pre-commit checks: manifest signature, image hash, version
- Self-test after reboot; esp_ota_mark_app_valid_cancel_rollback()
STM32 (Bootloader)
- Slots A/B + manifest header in reserved area
- Verify (signature/hash) before jump
- Rollback marker in backup SRAM/option bytes
- Power-fail-safe switching
Example Flow (CI → Device)
- Build (ESP32/STM32) → binary artifact
- Sign: signer.py generates .ota with manifest + signature
- Publish: Upload to OTA server/CDN, release via environment gate
- Deploy: Device downloads package (HTTPS), verifies manifest/hash, streams to target slot
- Commit: After self-tests → commit, otherwise rollback
Quickstart / Commands
Bash
# 1) Generate keys (offline)
cd signer && python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python generate_keys.py # keys/ed25519_priv.pem + ed25519_pub.pem
# 2) Build ESP32
idf.py -C esp32 set-target esp32 && idf.py -C esp32 build
# 3) Sign package
python signer.py \
--pubkey keys/ed25519_pub.pem \
--privkey keys/ed25519_priv.pem \
--device esp32 --version 1.0.0 --slot ota_0 \
--image ../builds/esp32-app.bin \
--out ../dist/esp32-app-1.0.0.ota
# 4) Verify package
python verify_package.py --pubkey keys/ed25519_pub.pem --package ../dist/esp32-app-1.0.0.ota
- Device-side Ed25519 verification integrated/ported as a lightweight library (e.g., TweetNaCl port).
- Public key stored on the device (rotation via update possible).
- Optional: NVS encryption (ESP32) & Secure Boot/TrustZone/TPM integration.
Test Strategy
- Unit: Signer/verifier (schema, canonicalization, round-trip), version logic
- Integration (ESP32): Happy path, signature error, hash mismatch, rollback path
- Integration (STM32): Slot matrix (A/B valid/invalid), brownout during update
- HIL: Network interruptions, watchdog triggers, recovery scenarios
Variants & Services
FAQ
Contact & Demo
SEO / Metadata (optional)
Html
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Signed OTA Update Pipeline (ESP32 + STM32)",
"provider": {
"@type": "Organization",
"name": "Büngener Software",
"url": "https://buengener-software.de"
},
"areaServed": "EU",
"serviceType": "Embedded OTA Security",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock"
}
}
</script>
Versioning
- v1.0 – Reference pipeline (tooling, ESP32/STM32, CI, docs)