Skip to content

Latest commit

 

History

History
421 lines (316 loc) · 17.3 KB

File metadata and controls

421 lines (316 loc) · 17.3 KB

Key Lifecycle Management — eBootloader

Last updated: 2026-04-03

Applies to: Ed25519 signing keys used for firmware image verification in the eBootloader secure boot chain.


1. Overview

eBootloader uses Ed25519 (RFC 8032) digital signatures to verify firmware image authenticity and integrity. This document defines the full lifecycle of the signing keypair — from generation through rotation, revocation, and emergency response.

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Generate    │────>│   Deploy     │────>│   Verify     │
│  Keypair     │     │   Public Key │     │   at Boot    │
└──────────────┘     └──────────────┘     └──────────────┘
       │                    │                     │
       │              ┌─────┴─────┐               │
       │              │ Key Slot 0│               │
       │              │ Key Slot 1│               │
       │              └───────────┘               │
       │                                          │
       ▼                                          ▼
┌──────────────┐                          ┌──────────────┐
│   Rotate     │                          │   Revoke     │
│   (new key)  │                          │   (counter)  │
└──────────────┘                          └──────────────┘

2. Ed25519 Keypair Generation

2.1 Algorithm Specification

Parameter Value
Algorithm Ed25519 (EdDSA over Curve25519)
Private key size 32 bytes
Public key size 32 bytes
Signature size 64 bytes
Hash function SHA-512 (internal to Ed25519)
Reference RFC 8032, Section 5.1

2.2 Generation Procedure

Prerequisites:

  • Air-gapped workstation running a hardened OS (no network interfaces active)
  • Hardware random number generator (TRNG) or /dev/random with sufficient entropy
  • OpenSSL ≥ 1.1.1 or ed25519-keygen from the eBootloader tools

Steps:

# 1. Generate Ed25519 private key (PEM format)
openssl genpkey -algorithm Ed25519 -out eboot_signing_key.pem

# 2. Extract public key
openssl pkey -in eboot_signing_key.pem -pubout -out eboot_signing_pub.pem

# 3. Export raw 32-byte public key for embedding
openssl pkey -in eboot_signing_key.pem -pubout -outform DER | \
    tail -c 32 > eboot_signing_pub.raw

# 4. Generate SHA-256 hash of public key (for TLV embedding)
sha256sum eboot_signing_pub.raw > eboot_signing_pub.sha256

# 5. Verify the keypair
echo "test" | openssl pkeyutl -sign -inkey eboot_signing_key.pem | \
    openssl pkeyutl -verify -pubin -inkey eboot_signing_pub.pem

Alternative — using eBootloader tooling:

# Generate keypair and C header in one step
python3 tools/sign_image.py --genkey \
    --key-out keys/production_key.pem \
    --pub-header include/eos_signing_key.h

2.3 Key Storage After Generation

Storage Location Content Access Control
HSM or air-gapped vault Private key (eboot_signing_key.pem) Two-person access; encrypted at rest
CI/CD signing server Private key (encrypted) Build system service account only
Source repository Never — private keys must never be committed N/A

3. Public Key Embedding

3.1 Compiled-In Key (Default)

The public key is compiled directly into the stage-1 bootloader binary. This is the default for devices without OTP/eFuse capability.

Generated header (include/eos_signing_key.h):

// SPDX-License-Identifier: MIT
// Auto-generated by sign_image.py — do not edit manually

#ifndef EOS_SIGNING_KEY_H
#define EOS_SIGNING_KEY_H

#include <stdint.h>

/* Primary signing key (slot 0) */
static const uint8_t eos_signing_pubkey_0[32] = {
    0x3b, 0x6a, 0x27, 0xbc, /* ... 28 more bytes ... */
};

/* Backup signing key (slot 1) */
static const uint8_t eos_signing_pubkey_1[32] = {
    0x9d, 0x61, 0xb1, 0x9d, /* ... 28 more bytes ... */
};

#define EOS_SIGNING_KEY_COUNT 2

#endif /* EOS_SIGNING_KEY_H */

Verification logic:

int eos_image_verify_signature(const eos_image_header_t *hdr) {
    for (int i = 0; i < EOS_SIGNING_KEY_COUNT; i++) {
        const uint8_t *pubkey = (i == 0)
            ? eos_signing_pubkey_0
            : eos_signing_pubkey_1;
        int rc = eos_crypto_verify_signature(
            hdr->hash, EOS_SHA256_DIGEST_SIZE,
            hdr->signature, hdr->sig_len,
            pubkey, 32);
        if (rc == EOS_OK) return EOS_OK;
    }
    return EOS_ERR_SIGNATURE;
}

3.2 OTP/eFuse Key Storage

For devices with one-time-programmable memory:

Field OTP Address Size Description
pubkey_slot_0 OTP base + 0x00 32 bytes Primary public key
pubkey_slot_1 OTP base + 0x20 32 bytes Backup public key
key_revoke_mask OTP base + 0x40 4 bytes Bit mask — bit N=1 revokes slot N
security_version OTP base + 0x44 4 bytes Monotonic counter for anti-rollback

Advantages over compiled-in:

  • Public key cannot be modified by flash reprogramming
  • Key revocation is permanent (OTP bits are one-way)
  • Survives complete flash erase

3.3 Secure Element Key Storage

For high-security deployments using an external secure element (e.g., ATECC608B, OPTIGA Trust M):

Operation API
Store public key se_write_slot(slot_id, pubkey, 32)
Verify signature se_verify_ed25519(slot_id, hash, sig)
Read key hash se_read_slot_hash(slot_id, hash_out)

The secure element performs the signature verification internally — the public key is protected from modification by the SE's tamper-resistant hardware.


4. Dual-Key Slot Support

eBootloader supports two key slots (primary and backup) to enable seamless key rotation without bricking deployed devices.

4.1 Key Slot Architecture

┌─────────────────────────────────┐
│        Image Header             │
│   sig_type = ED25519            │
│   signature[64]                 │
│   (signed with either key)      │
└──────────────┬──────────────────┘
               │
        ┌──────┴──────┐
        ▼             ▼
  ┌──────────┐  ┌──────────┐
  │ Slot 0   │  │ Slot 1   │
  │ Primary  │  │ Backup   │
  │ pubkey   │  │ pubkey   │
  └──────────┘  └──────────┘

4.2 Verification Algorithm

The bootloader tries each non-revoked key slot in order:

  1. Check key_revoke_mask — skip any revoked slots
  2. Attempt signature verification with slot 0 public key
  3. If slot 0 fails, attempt with slot 1 public key
  4. If all slots fail → EOS_ERR_SIGNATURE

4.3 Slot Assignment Convention

Slot Purpose Typical Usage
Slot 0 Primary production key Signs all production firmware releases
Slot 1 Backup / rotation target Receives new key during rotation; becomes new primary

5. Key Rotation Procedure

Key rotation replaces the active signing key without disrupting deployed devices. The dual-slot architecture ensures that devices can verify firmware signed with either the old or new key during the transition window.

5.1 Rotation Timeline

Phase 1: Prepare          Phase 2: Transition       Phase 3: Revoke
(1 release cycle)         (2 release cycles)        (after full fleet update)

┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│ Generate new    │      │ Sign firmware   │      │ Revoke old key  │
│ keypair         │      │ with NEW key    │      │ (set revoke bit)│
│                 │      │                 │      │                 │
│ Install new     │      │ Old key still   │      │ Increment       │
│ pubkey in       │      │ accepted for    │      │ security version│
│ slot 1          │      │ verification    │      │                 │
│                 │      │                 │      │ Sign firmware   │
│ Continue signing│      │ Fleet updates   │      │ with new key    │
│ with OLD key    │      │ propagate       │      │ only            │
└─────────────────┘      └─────────────────┘      └─────────────────┘

5.2 Step-by-Step Procedure

Phase 1 — Prepare (release N):

  1. Generate new Ed25519 keypair on air-gapped workstation (see §2.2)
  2. Update eos_signing_pubkey_1 in include/eos_signing_key.h with new public key
  3. Build and sign stage-1 bootloader with existing (old) key
  4. Deploy bootloader update — devices now have both public keys
  5. Continue signing application firmware with old key (slot 0)

Phase 2 — Transition (releases N+1, N+2):

  1. Begin signing new application firmware with the new key (slot 1)
  2. Devices that received the bootloader update verify with slot 1
  3. Devices still on old bootloader verify with slot 0 (old firmware still deployed)
  4. Monitor fleet update telemetry — track percentage of devices on new bootloader

Phase 3 — Revoke (release N+3, after fleet convergence):

  1. Verify ≥99% of fleet has updated bootloader with both key slots
  2. Set key_revoke_mask bit 0 to revoke old key (slot 0)
  3. Move new key to slot 0; generate next rotation key in slot 1 (optional)
  4. Increment security_version monotonic counter
  5. Securely destroy old private key material

5.3 Rotation Checklist

  • New keypair generated on air-gapped workstation
  • New public key tested in CI with sign_image.py --verify
  • Bootloader binary with new public key deployed and confirmed on test fleet
  • Fleet telemetry confirms ≥99% adoption of new bootloader
  • Old key revocation applied (OTP bit or compiled-in flag)
  • Old private key material securely destroyed
  • Key inventory updated in HSM management system
  • Security counter incremented

6. Key Revocation via Monotonic Counter

6.1 Security Version Counter

The security version is a monotonic counter that can only increment. It is stored in:

Storage Mechanism Reversibility
OTP/eFuse One-time programmable bits Irreversible
Dedicated flash sector Counter with anti-tearing write Reversible (with flash access)
Secure element SE-managed counter Irreversible

6.2 Revocation Flow

Image header: security_counter = 5
Device OTP:   security_version = 5   → ACCEPT (5 ≥ 5)

Image header: security_counter = 4
Device OTP:   security_version = 5   → REJECT (4 < 5, rollback attempt)

Image header: security_counter = 6
Device OTP:   security_version = 5   → ACCEPT (6 ≥ 5)
                                        Update OTP to 6

6.3 Counter Management Rules

  1. Never decrement. The counter is monotonic — any attempt to write a lower value is silently ignored.
  2. Increment on key rotation. Every key rotation bumps the security version by 1.
  3. Increment on critical CVE. A security-critical fix may bump the counter to prevent rollback to vulnerable versions.
  4. Coordinate with fleet. Before incrementing, ensure the new firmware is available for all devices.

7. Production vs. Development Key Separation

7.1 Key Environments

Environment Key Purpose Storage Signing Authority
Development Local builds, unit tests, CI Plaintext PEM in developer workspace Any developer
Staging Pre-production validation Encrypted PEM on staging build server Release engineer
Production Release firmware HSM-protected; two-person authorization Release manager + security officer

7.2 Enforcement Mechanism

#if defined(EOS_BUILD_PRODUCTION)
    /* Production keys — compiled from HSM-exported header */
    #include "eos_signing_key_production.h"
#elif defined(EOS_BUILD_STAGING)
    #include "eos_signing_key_staging.h"
#else
    /* Development key — well-known test key */
    #include "eos_signing_key_dev.h"
#endif

7.3 Development Key Policy

Rule Rationale
Development private key is committed to the repository Enables any developer to build and test signed images locally
Development key is never used in production Production builds fail if development key header is detected
Production key never appears in source control Only the public key is embedded; private key stays in HSM
CI pipeline uses staging key for integration tests Tests signature verification without exposing production key

7.4 Well-Known Development Key

The development key is intentionally public and must never be used for production:

Development Private Key (Ed25519, PEM):
  MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikQ2Ig35R6DIxQ/BV1Cxz

Development Public Key SHA-256:
  9f836af87a484a954e6e74c5b4c0e842291d8883be51e832314bf055d42c73...

⚠️  THIS KEY IS PUBLIC. Images signed with this key are NOT authenticated.

8. Emergency Key Compromise Response

8.1 Compromise Classification

Severity Scenario Response Time
P0 — Confirmed compromise Private key material exposed publicly or to unauthorized party Immediate (< 4 hours)
P1 — Suspected compromise Unauthorized access to signing infrastructure detected < 24 hours
P2 — Precautionary Personnel change, infrastructure migration, policy violation < 7 days

8.2 P0 Response Procedure

┌─ HOUR 0 ──────────────────────────────────────────────┐
│ 1. Notify security team: security@embeddedos.org      │
│ 2. Revoke compromised key in CI/CD pipeline           │
│ 3. Halt all firmware signing with compromised key     │
└───────────────────────────────────────────────────────┘
         │
┌─ HOURS 1-4 ───────────────────────────────────────────┐
│ 4. Generate emergency replacement keypair (§2.2)      │
│ 5. Build emergency bootloader with new public key     │
│ 6. Sign emergency bootloader with backup key (slot 1) │
│ 7. Push emergency OTA to all connected devices        │
└───────────────────────────────────────────────────────┘
         │
┌─ HOURS 4-24 ──────────────────────────────────────────┐
│ 8. Increment security version counter                 │
│ 9. Revoke compromised key slot (OTP/eFuse)            │
│ 10. Monitor fleet for devices still on old key        │
│ 11. Publish security advisory                         │
└───────────────────────────────────────────────────────┘
         │
┌─ DAYS 1-7 ────────────────────────────────────────────┐
│ 12. Root cause analysis                               │
│ 13. Update key management procedures                  │
│ 14. Rotate all related credentials                    │
│ 15. Post-incident review                              │
└───────────────────────────────────────────────────────┘

8.3 Key Compromise Indicators

  • Firmware images signed with the production key that were not produced by the authorized build pipeline
  • Unauthorized access logs on the HSM or signing server
  • Public disclosure of key material (paste sites, repositories, social media)
  • Anomalous device behavior consistent with unauthorized firmware

8.4 Limitations

  • Compiled-in keys: Devices with compiled-in public keys require a bootloader update to rotate keys. If both key slots are compromised and the device cannot receive OTA updates, physical UART recovery is required.
  • OTP/eFuse keys: Once all OTP key slots are revoked, the device cannot verify any firmware. This is a permanent brick condition — plan key slots carefully.
  • Offline devices: Devices not connected to the network cannot receive emergency key rotation. They remain vulnerable until physically recovered.

References