Firmware Signing
Zelta uses ECDSA P-256 digital signatures to ensure firmware authenticity and integrity.
Why Sign Firmware?
Without signatures, attackers could:
- Man-in-the-middle attacks: Intercept downloads and inject malicious firmware
- Server compromise: Upload unauthorized firmware if server is breached
- Replay attacks: Deliver old, vulnerable firmware versions
Signatures ensure that:
- Firmware was created by an authorized party (authenticity)
- Firmware hasn't been modified (integrity)
- Only your private key can sign valid updates
How It Works
Signing Flow (Dashboard)
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Firmware │────▶│ SHA-256 │────▶│ ECDSA │
│ Binary │ │ Hash │ │ Sign │
└─────────────┘ └──────────────┘ └─────────────┘
│
Private Key ────────┘
│
▼
┌─────────────┐
│ Signature │
│ (Base64) │
└─────────────┘
Verification Flow (Device)
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Downloaded │────▶│ SHA-256 │────▶│ ECDSA │
│ Firmware │ │ Hash │ │ Verify │
└─────────────┘ └──────────────┘ └─────────────┘
│
Public Key ─────────┘
Signature ──────────┘
│
▼
┌─────────────┐
│ Valid / │
│ Invalid │
└─────────────┘
Algorithm Details
| Property | Value | |----------|-------| | Algorithm | ECDSA | | Curve | P-256 (secp256r1) | | Hash | SHA-256 | | Signature Format | DER, Base64-encoded | | Key Format | SPKI (public), PKCS#8 (private) |
Generating Keys
In Dashboard (Recommended)
- Go to Firmware page
- Click Generate Keypair in the Signing Key section
- A new ECDSA P-256 keypair is generated in your browser
- Download the private key immediately and store securely
- Click Save Public Key to Org
Using OpenSSL
# Generate private key
openssl ecparam -genkey -name prime256v1 -noout -out private_key.pem
# Extract public key
openssl ec -in private_key.pem -pubout -out public_key.pem
# Convert to DER for embedding
openssl ec -pubin -in public_key.pem -outform DER -out public_key.der
Embedding Public Key
Export the public key for your firmware:
- In Key Manager, select format (C, C++, or Rust)
- Click Copy Code
- Paste into your firmware source
C Format
static const uint8_t public_key_der[91] = {
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
// ... rest of key
};
C++ Format
constexpr std::array<uint8_t, 91> public_key_der = {{
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
// ... rest of key
}};
Rust Format
const PUBLIC_KEY_DER: [u8; 91] = [
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
// ... rest of key
];
Verification on Device
The SDK handles verification automatically:
// In zelta_download_and_apply():
// 1. Download firmware and compute hash
// 2. Verify hash matches server-provided hash
// 3. Verify signature using embedded public key
// 4. Only then apply the update
For manual verification:
#include <zelta/verify.h>
// After downloading firmware
uint8_t hash[32];
zelta_compute_hash(firmware_data, firmware_size, hash);
// Verify signature
int ret = zelta_verify_signature(
hash, sizeof(hash),
update_info.signature,
public_key_der, sizeof(public_key_der)
);
if (ret != ZELTA_OK) {
LOG_ERR("Signature verification failed!");
return ret;
}
Security Considerations
Private Key Protection
- Never upload private key to server
- Store in secure location (hardware security module, encrypted storage)
- Use separate keys for development and production
- Rotate keys periodically
Key Rotation
When rotating keys:
- Generate new keypair
- Update embedded public key in firmware
- Deploy firmware update with OLD key (devices still have old public key)
- Once all devices updated, switch to new key for signing
- Key fingerprint in dashboard shows which key signed each firmware
Compromise Response
If private key is compromised:
- Generate new keypair immediately
- Push emergency update signed with old key containing new public key
- Mark old key as revoked
- Sign all future updates with new key
Troubleshooting
"Signature verification failed"
- Ensure public key in firmware matches key used for signing
- Check if firmware was modified during download (hash mismatch)
- Verify signature format is Base64-encoded DER
"Invalid public key"
- Key must be in SPKI DER format
- Check key length (should be 91 bytes for P-256)
- Ensure key wasn't truncated