The Embedded New Testament

The "Holy Bible" for embedded engineers


Project maintained by theEmbeddedGeorge Hosted on GitHub Pages — Theme by mattgraham

Secure Boot and Chain of Trust

Building Unbreakable Trust from Power-On to Runtime
Understanding how secure boot creates a foundation of trust that protects your entire system


📋 Table of Contents


🏗️ Trust Foundation

Why Trust Matters from the First Instruction

Imagine you’re building a house. You wouldn’t start with the walls or roof—you’d start with a solid foundation. Secure boot is that foundation for your embedded system’s security.

The Trust Problem

When your device powers on, it’s in a completely untrusted state. Anyone could have:

The Solution: Secure Boot

Secure boot ensures that only trusted, verified code can run on your system. It’s like having a security guard that checks everyone’s ID before they enter a building.


🔗 Chain of Trust

Building Trust Step by Step

A chain of trust works like a relay race where each runner must verify the next runner before passing the baton. If any link in the chain is broken, trust is lost.

The Trust Chain Visualization

Power-On → ROM → Bootloader → Kernel → Applications
   ↓         ↓        ↓         ↓         ↓
Untrusted → Trusted → Trusted → Trusted → Trusted
   State     Code     Code      Code      Code

How It Works:

  1. ROM (Read-Only Memory) - Contains the initial trusted code
  2. Bootloader - Verified by ROM before execution
  3. Kernel - Verified by bootloader before execution
  4. Applications - Verified by kernel before execution

Trust Verification Process

┌─────────────────────────────────────┐
│           ROM (Trusted)             │
│  ┌─────────────────────────────┐    │
│  │   Public Key Hash           │    │
│  │   (Hardcoded in ROM)       │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘
              ↓
        Verify Bootloader
              ↓
┌─────────────────────────────────────┐
│        Bootloader (Verified)        │
│  ┌─────────────────────────────┐    │
│  │   Digital Signature         │    │
│  │   (Signed by trusted key)   │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘
              ↓
        Verify Kernel
              ↓
┌─────────────────────────────────────┐
│          Kernel (Verified)          │
│  ┌─────────────────────────────┐    │
│  │   Digital Signature         │    │
│  │   (Signed by trusted key)   │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

🚀 Secure Boot Process

Step-by-Step Boot Verification

Phase 1: Hardware Initialization

When power is applied, the system starts in a completely untrusted state:

// Hardware reset sequence
void hardware_reset() {
    // Clear all registers and memory
    clear_all_registers();
    clear_all_memory();
    
    // Disable all peripherals except essential ones
    disable_non_essential_peripherals();
    
    // Set security mode to highest level
    set_security_mode(SECURITY_MODE_HIGHEST);
    
    // Jump to ROM code
    jump_to_rom();
}

Phase 2: ROM Code Execution

The ROM contains the first trusted code that can’t be modified:

// ROM code (immutable, trusted)
void rom_entry_point() {
    // Initialize minimal hardware
    init_minimal_hardware();
    
    // Load bootloader from storage
    uint8_t* bootloader = load_bootloader_from_storage();
    
    // Verify bootloader signature
    if (!verify_bootloader_signature(bootloader)) {
        // Bootloader verification failed
        enter_secure_failure_mode();
        return;
    }
    
    // Calculate bootloader hash
    uint8_t calculated_hash[32];
    calculate_hash(bootloader, BOOTLOADER_SIZE, calculated_hash);
    
    // Compare with trusted hash
    if (!compare_hashes(calculated_hash, TRUSTED_BOOTLOADER_HASH)) {
        // Hash mismatch - bootloader corrupted
        enter_secure_failure_mode();
        return;
    }
    
    // Bootloader is trusted - execute it
    execute_bootloader(bootloader);
}

Phase 3: Bootloader Verification

The bootloader must verify itself and the next component:

// Bootloader verification
typedef struct {
    uint8_t signature[64];      // Digital signature
    uint8_t hash[32];          // Code hash
    uint32_t version;          // Version number
    uint32_t flags;            // Security flags
    uint8_t reserved[16];      // Reserved for future use
} BootloaderHeader;

bool verify_bootloader_integrity(uint8_t* bootloader) {
    BootloaderHeader* header = (BootloaderHeader*)bootloader;
    
    // Step 1: Verify signature using ROM public key
    uint8_t* code_start = bootloader + sizeof(BootloaderHeader);
    uint32_t code_size = BOOTLOADER_SIZE - sizeof(BootloaderHeader);
    
    if (!verify_signature(header->signature, 
                         code_start, 
                         code_size, 
                         ROM_PUBLIC_KEY)) {
        return false; // Signature verification failed
    }
    
    // Step 2: Verify code hash
    uint8_t calculated_hash[32];
    calculate_hash(code_start, code_size, calculated_hash);
    
    if (memcmp(calculated_hash, header->hash, 32) != 0) {
        return false; // Hash mismatch
    }
    
    // Step 3: Check version compatibility
    if (header->version < MIN_SUPPORTED_VERSION) {
        return false; // Version too old
    }
    
    // Step 4: Check security flags
    if (header->flags & FLAG_DEBUG_ENABLED) {
        return false; // Debug mode not allowed
    }
    
    return true; // All checks passed
}

Phase 4: Kernel Verification

The bootloader verifies the kernel before loading it:

// Kernel verification process
bool verify_kernel(uint8_t* kernel_image) {
    KernelHeader* header = (KernelHeader*)kernel_image;
    
    // Verify kernel signature
    if (!verify_signature(header->signature, 
                         kernel_image + sizeof(KernelHeader),
                         header->code_size,
                         BOOTLOADER_PUBLIC_KEY)) {
        return false;
    }
    
    // Verify kernel hash
    uint8_t calculated_hash[32];
    calculate_hash(kernel_image + sizeof(KernelHeader),
                  header->code_size,
                  calculated_hash);
    
    if (memcmp(calculated_hash, header->hash, 32) != 0) {
        return false;
    }
    
    // Check kernel security attributes
    if (!verify_kernel_security_attributes(header)) {
        return false;
    }
    
    return true;
}

🛠️ Implementation Details

Building Your Secure Boot System

1. Cryptographic Primitives

You’ll need several cryptographic functions:

// Cryptographic function prototypes
typedef struct {
    uint8_t modulus[256];      // RSA modulus
    uint8_t exponent[4];       // RSA exponent
} RSAPublicKey;

// Hash calculation (SHA-256)
void calculate_sha256(const uint8_t* data, 
                     uint32_t length, 
                     uint8_t* hash);

// RSA signature verification
bool verify_rsa_signature(const uint8_t* signature,
                         const uint8_t* data,
                         uint32_t data_length,
                         const RSAPublicKey* public_key);

// HMAC calculation for integrity
void calculate_hmac(const uint8_t* key,
                   uint32_t key_length,
                   const uint8_t* data,
                   uint32_t data_length,
                   uint8_t* hmac);

2. Key Management

Managing cryptographic keys is critical:

// Key storage structure
typedef struct {
    uint8_t key_id[16];        // Unique key identifier
    uint8_t public_key[256];   // Public key data
    uint32_t key_type;         // Key type (RSA, ECC, etc.)
    uint32_t key_size;         // Key size in bits
    uint32_t permissions;      // What this key can verify
    uint8_t reserved[32];      // Reserved for future use
} TrustedKey;

// Key database
typedef struct {
    uint32_t num_keys;         // Number of trusted keys
    TrustedKey keys[MAX_KEYS]; // Array of trusted keys
    uint8_t reserved[64];      // Reserved for future use
} KeyDatabase;

// Find key by ID
TrustedKey* find_trusted_key(const uint8_t* key_id) {
    for (int i = 0; i < key_database.num_keys; i++) {
        if (memcmp(key_database.keys[i].key_id, key_id, 16) == 0) {
            return &key_database.keys[i];
        }
    }
    return NULL; // Key not found
}

3. Secure Storage

Protect keys and sensitive data:

// Secure storage implementation
typedef struct {
    uint8_t data[256];         // Encrypted data
    uint8_t iv[16];            // Initialization vector
    uint8_t tag[16];           // Authentication tag
} EncryptedData;

// Encrypt sensitive data
bool encrypt_secure_data(const uint8_t* plaintext,
                        uint32_t length,
                        const uint8_t* key,
                        EncryptedData* encrypted) {
    
    // Generate random IV
    if (!generate_random_bytes(encrypted->iv, 16)) {
        return false;
    }
    
    // Encrypt data using AES-GCM
    if (!aes_gcm_encrypt(plaintext, length,
                         key, encrypted->iv,
                         encrypted->data, encrypted->tag)) {
        return false;
    }
    
    return true;
}

// Decrypt sensitive data
bool decrypt_secure_data(const EncryptedData* encrypted,
                        const uint8_t* key,
                        uint8_t* plaintext,
                        uint32_t* length) {
    
    // Decrypt using AES-GCM
    if (!aes_gcm_decrypt(encrypted->data, encrypted->tag,
                         key, encrypted->iv,
                         plaintext, length)) {
        return false;
    }
    
    return true;
}

🚀 Advanced Features

Beyond Basic Secure Boot

1. Rollback Protection

Prevent attackers from downgrading to vulnerable versions:

// Version management
typedef struct {
    uint32_t major_version;    // Major version number
    uint32_t minor_version;    // Minor version number
    uint32_t patch_version;    // Patch version number
    uint32_t build_number;     // Build number
} VersionInfo;

// Check for rollback attempts
bool check_version_rollback(const VersionInfo* new_version) {
    VersionInfo current_version = get_current_version();
    
    // Compare version numbers
    if (new_version->major_version < current_version.major_version) {
        return false; // Major version rollback
    }
    
    if (new_version->major_version == current_version.major_version) {
        if (new_version->minor_version < current_version.minor_version) {
            return false; // Minor version rollback
        }
        
        if (new_version->minor_version == current_version.minor_version) {
            if (new_version->patch_version < current_version.patch_version) {
                return false; // Patch version rollback
            }
        }
    }
    
    return true; // No rollback detected
}

2. Secure Update Process

Ensure updates maintain security:

// Update verification
typedef struct {
    uint8_t signature[64];     // Update signature
    uint8_t hash[32];          // Update hash
    VersionInfo version;       // Update version
    uint32_t update_size;      // Size of update
    uint8_t update_type;       // Type of update
    uint8_t reserved[15];      // Reserved
} UpdateHeader;

bool verify_update_package(const uint8_t* update_data) {
    UpdateHeader* header = (UpdateHeader*)update_data;
    
    // Verify update signature
    if (!verify_signature(header->signature,
                         update_data + sizeof(UpdateHeader),
                         header->update_size,
                         UPDATE_SIGNING_KEY)) {
        return false;
    }
    
    // Check for rollback
    if (!check_version_rollback(&header->version)) {
        return false;
    }
    
    // Verify update hash
    uint8_t calculated_hash[32];
    calculate_hash(update_data + sizeof(UpdateHeader),
                  header->update_size,
                  calculated_hash);
    
    if (memcmp(calculated_hash, header->hash, 32) != 0) {
        return false;
    }
    
    return true;
}

3. Secure Recovery Mode

Provide recovery options while maintaining security:

// Recovery mode handling
typedef enum {
    RECOVERY_MODE_NONE,
    RECOVERY_MODE_UPDATE,
    RECOVERY_MODE_FACTORY_RESET,
    RECOVERY_MODE_DEBUG
} RecoveryMode;

bool enter_recovery_mode(RecoveryMode mode) {
    // Check if recovery mode is allowed
    if (!is_recovery_mode_allowed(mode)) {
        return false;
    }
    
    // Verify recovery mode request
    if (!verify_recovery_request(mode)) {
        return false;
    }
    
    // Set recovery mode
    set_recovery_mode(mode);
    
    // Reboot into recovery mode
    system_reboot();
    
    return true;
}

🎯 Key Takeaways

Fundamental Principles

  1. Trust starts at power-on - Every component must be verified
  2. Chain of trust is unbreakable - One broken link breaks everything
  3. Cryptographic verification - Use strong crypto for all verifications
  4. Secure key management - Protect your keys as much as your code
  5. Rollback protection - Prevent downgrade attacks

Implementation Checklist

Common Pitfalls

  1. Weak cryptography - Use industry-standard algorithms
  2. Key exposure - Never expose private keys
  3. No rollback protection - Attackers can downgrade to vulnerable versions
  4. Insufficient verification - Verify everything, trust nothing
  5. No recovery plan - Plan for secure recovery scenarios

📚 Additional Resources


Next Topic: TPM 2.0 BasicsCryptographic Foundations