Project 15: Secure Boot Exploration
Set up a complete UEFI Secure Boot environment from scratch—generate your own PKI hierarchy, sign custom EFI binaries, enroll keys in firmware, and verify that the chain of trust correctly accepts signed code while rejecting unsigned or maliciously-signed binaries.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | ★★★★☆ Expert |
| Time Estimate | 2-3 weeks |
| Language | C (bootloader), Python/Shell (signing tools) |
| Prerequisites | Projects 7-8 completed, basic cryptography concepts, x509 certificate understanding |
| Key Topics | Public Key Infrastructure (PKI), x509 certificates, Authenticode signatures, UEFI Secure Boot key hierarchy, trust chains, PE/COFF signing |
1. Learning Objectives
After completing this project, you will be able to:
- Understand the UEFI Secure Boot architecture - Know the complete trust chain from Platform Key (PK) to signed bootloaders
- Generate a complete PKI hierarchy - Create Platform Keys, Key Exchange Keys, and signature database certificates using OpenSSL
- Sign EFI binaries with Authenticode - Use sbsign and related tools to create valid signatures on PE32+ executables
- Enroll custom keys in UEFI firmware - Configure OVMF and real hardware to trust your certificates
- Debug signature verification failures - Diagnose and fix common Secure Boot enrollment and signing issues
- Understand the security implications - Know how Secure Boot prevents bootkits and rootkits, and its limitations
2. Theoretical Foundation
2.1 What is Secure Boot?
Secure Boot is a UEFI security feature that ensures only cryptographically signed and verified code can execute during the boot process. It establishes a chain of trust from firmware to bootloader to operating system kernel.
┌─────────────────────────────────────────────────────────────────────────────┐
│ SECURE BOOT OVERVIEW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ WITHOUT SECURE BOOT: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Firmware ──→ ANY bootloader ──→ ANY kernel ──→ OS loads │ │
│ │ │ │ │ │ │
│ │ │ (Could be │ │ │
│ │ │ malware!) (Rootkit?) │ │
│ │ │ │ │
│ │ PROBLEM: No verification at boot level │ │
│ │ Bootkits can hide from ALL OS security │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ WITH SECURE BOOT: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Firmware ──┬──→ Signed bootloader ──┬──→ Signed kernel ──→ OS │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ Verify signature │ Verify signature │ │
│ │ Trusts │ against db │ (bootloader does this) │ │
│ │ built-in │ │ │ │ │
│ │ keys │ ┌────▼────┐ │ │ │
│ │ │ │ PASS? │──NO──→ REJECT (boot halted) │ │
│ │ │ └────┬────┘ │ │
│ │ │ │YES │ │
│ │ │ ▼ │ │
│ │ │ Execute bootloader │ │
│ │ │ │
│ │ RESULT: Only trusted code can run before OS loads │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.2 The Secure Boot Key Hierarchy
UEFI Secure Boot uses a hierarchical key structure. Understanding this hierarchy is essential:
┌─────────────────────────────────────────────────────────────────────────────┐
│ SECURE BOOT KEY HIERARCHY │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ PLATFORM KEY │ │
│ │ (PK) │ │
│ │ │ │
│ │ The "root" key │ │
│ │ Owner: Hardware │ │
│ │ manufacturer or │ │
│ │ system owner │ │
│ └────────┬────────┘ │
│ │ │
│ │ Signs/authorizes │
│ ▼ │
│ ┌─────────────────┐ │
│ │ KEY EXCHANGE KEY│ │
│ │ (KEK) │ │
│ │ │ │
│ │ Can update db │ │
│ │ and dbx │ │
│ │ │ │
│ │ Owner: OS vendor│ │
│ │ (Microsoft, │ │
│ │ Canonical, etc.)│ │
│ └────────┬────────┘ │
│ │ │
│ │ Signs/authorizes │
│ ┌────────────────┴────────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ SIGNATURE DB │ │ FORBIDDEN │ │
│ │ (db) │ │ SIGNATURES (dbx)│ │
│ │ │ │ │ │
│ │ Trusted signing │ │ Revoked keys │ │
│ │ certificates │ │ and hashes │ │
│ │ │ │ │ │
│ │ Bootloaders are │ │ Known-bad │ │
│ │ verified against│ │ bootloaders │ │
│ │ these certs │ │ blocked here │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ │ Verifies │
│ ▼ │
│ ┌─────────────────┐ │
│ │ SIGNED EFI │ │
│ │ BINARIES │ │
│ │ │ │
│ │ • Bootloaders │ │
│ │ • OS Loaders │ │
│ │ • UEFI Drivers │ │
│ │ • Option ROMs │ │
│ └─────────────────┘ │
│ │
│ TRUST FLOW: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ PK (root of trust) │ │
│ │ │ │ │
│ │ └──→ Authorizes who can modify KEK │ │
│ │ │ │
│ │ KEK (key exchange) │ │
│ │ │ │ │
│ │ └──→ Authorizes who can modify db/dbx │ │
│ │ │ │
│ │ db (allowed signatures) │ │
│ │ │ │ │
│ │ └──→ Contains certificates that can sign bootloaders │ │
│ │ │ │
│ │ dbx (forbidden signatures) │ │
│ │ │ │ │
│ │ └──→ Contains revoked certificates and blocked hashes │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.3 Certificate Chain Verification Flow
When UEFI firmware loads an EFI binary with Secure Boot enabled, it performs this verification:
┌─────────────────────────────────────────────────────────────────────────────┐
│ SIGNATURE VERIFICATION FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ EFI Binary Loaded │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Step 1: Extract Authenticode signature from PE header │ │
│ │ (Located in Security Directory of PE Optional Header) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Step 2: Check if binary hash is in dbx (forbidden) │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Hash in dbx? │ │ │
│ │ │ │ │ │
│ │ │ YES ─────────────────────────────────→ REJECT │ │
│ │ │ │ │ │ │
│ │ │ NO │ │ │
│ │ └────┬─────────────────┘ │ │
│ └──────────────┼──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Step 3: Check if signing certificate is in dbx │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Cert in dbx? │ │ │
│ │ │ │ │ │
│ │ │ YES ─────────────────────────────────→ REJECT │ │
│ │ │ │ │ │ │
│ │ │ NO │ │ │
│ │ └────┬─────────────────┘ │ │
│ └──────────────┼──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Step 4: Verify signature against db certificates │ │
│ │ │ │
│ │ For each certificate in db: │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ a) Extract public key from db certificate │ │ │
│ │ │ b) Decrypt signature using public key → claimed hash │ │ │
│ │ │ c) Calculate actual hash of PE binary (Authenticode hash) │ │ │
│ │ │ d) Compare claimed hash with actual hash │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────────┐ │ │ │
│ │ │ │ Hashes match? │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ YES ──────────────────────────────→ ACCEPT │ │ │
│ │ │ │ │ │ (Boot continues) │ │ │
│ │ │ │ NO │ │ │ │
│ │ │ └────┬─────────────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ Try next certificate │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ No certificate verified the signature? │ │
│ │ │ │ │
│ │ └─────────────────────────────────────────→ REJECT │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ REJECTION BEHAVIOR: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Secure Boot Violation │ │
│ │ ───────────────────── │ │
│ │ │ │
│ │ Invalid signature detected in BOOTX64.EFI │ │
│ │ │ │
│ │ The system cannot verify the signature. │ │
│ │ This file will not be loaded. │ │
│ │ │ │
│ │ Press any key to continue... │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.4 The Authenticode Signature Format
UEFI uses Microsoft’s Authenticode format for signing PE32+ executables. Understanding this structure helps with debugging:
┌─────────────────────────────────────────────────────────────────────────────┐
│ PE/COFF AUTHENTICODE STRUCTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ PE32+ EXECUTABLE STRUCTURE: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ DOS Header (64 bytes) │ │ │
│ │ │ e_magic: "MZ" (0x5A4D) │ │ │
│ │ │ e_lfanew: Offset to PE header │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ PE Header │ │ │
│ │ │ Signature: "PE\0\0" (0x00004550) │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ File Header (20 bytes) │ │ │ │
│ │ │ │ Machine: 0x8664 (x86-64) │ │ │ │
│ │ │ │ NumberOfSections │ │ │ │
│ │ │ │ SizeOfOptionalHeader │ │ │ │
│ │ │ └────────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Optional Header (PE32+: 240 bytes) │ │ │ │
│ │ │ │ ... │ │ │ │
│ │ │ │ CheckSum (offset 0x58) ← SET TO ZERO FOR HASHING │ │ │ │
│ │ │ │ ... │ │ │ │
│ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ Data Directories (16 entries, 8 bytes each) │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ Entry 4: Security Directory ← SIGNATURE HERE │ │ │ │ │
│ │ │ │ │ VirtualAddress: Offset to signature │ │ │ │ │
│ │ │ │ │ Size: Size of signature data │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ NOTE: This entry is EXCLUDED from hash │ │ │ │ │
│ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │
│ │ │ └────────────────────────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ Section Headers │ │ │
│ │ │ .text, .data, .rdata, etc. │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ Section Data │ │ │
│ │ │ Code, initialized data, etc. │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ AUTHENTICODE SIGNATURE │ │ │
│ │ │ (Pointed to by Security Directory) │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ WIN_CERTIFICATE structure: │ │ │ │
│ │ │ │ dwLength: Total size │ │ │ │
│ │ │ │ wRevision: 0x0200 │ │ │ │
│ │ │ │ wCertificateType: 0x0002 (PKCS#7) │ │ │ │
│ │ │ │ bCertificate[]: PKCS#7 SignedData blob │ │ │ │
│ │ │ └────────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ PKCS#7 SignedData contains: │ │ │
│ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ • DigestAlgorithm (SHA-256 for modern) │ │ │ │
│ │ │ │ • ContentInfo (indirect data - hash of file) │ │ │ │
│ │ │ │ • Certificates (x509 signing certificate chain) │ │ │ │
│ │ │ │ • SignerInfo (actual signature data) │ │ │ │
│ │ │ └────────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ AUTHENTICODE HASH CALCULATION: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ The hash covers the entire PE file EXCEPT: │ │
│ │ 1. The CheckSum field in Optional Header (set to 0) │ │
│ │ 2. The Security Directory entry (VirtualAddress + Size) │ │
│ │ 3. The actual signature data at the end │ │
│ │ │ │
│ │ This allows the signature to be appended without changing │ │
│ │ the hash of the code and data sections. │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.5 UEFI Signature Database Format
The db, dbx, KEK, and PK variables use a specific format defined by UEFI:
┌─────────────────────────────────────────────────────────────────────────────┐
│ EFI SIGNATURE DATABASE FORMAT │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ EFI_SIGNATURE_LIST structure: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ SignatureType: GUID identifying the signature type │ │ │
│ │ │ │ │ │
│ │ │ Common types: │ │ │
│ │ │ • EFI_CERT_SHA256_GUID - SHA256 hash of binary │ │ │
│ │ │ • EFI_CERT_RSA2048_GUID - Raw RSA-2048 public key │ │ │
│ │ │ • EFI_CERT_X509_GUID - x509 certificate (most common) │ │ │
│ │ │ • EFI_CERT_X509_SHA256_GUID - SHA256 hash of x509 cert │ │ │
│ │ │ • EFI_CERT_X509_SHA384_GUID - SHA384 hash of x509 cert │ │ │
│ │ │ • EFI_CERT_X509_SHA512_GUID - SHA512 hash of x509 cert │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ SignatureListSize: Total size of this list including header │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ SignatureHeaderSize: Size of optional header (usually 0) │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ SignatureSize: Size of each signature entry │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ Signatures[]: Array of EFI_SIGNATURE_DATA structures │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ EFI_SIGNATURE_DATA: │ │ │ │
│ │ │ │ SignatureOwner: GUID identifying who added this │ │ │ │
│ │ │ │ SignatureData[]: The actual signature/cert data │ │ │ │
│ │ │ └──────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ AUTHENTICATED VARIABLE STRUCTURE (for PK, KEK, db, dbx): │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ These variables have authentication attributes set and must │ │
│ │ be updated using a signed payload: │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ EFI_VARIABLE_AUTHENTICATION_2: │ │ │
│ │ │ TimeStamp: Monotonic timestamp to prevent rollback │ │ │
│ │ │ AuthInfo: WIN_CERTIFICATE containing PKCS#7 signature │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ To update db: │ │
│ │ 1. Create new EFI_SIGNATURE_LIST with your certificate │ │
│ │ 2. Sign the update with a key in KEK │ │
│ │ 3. Submit via SetVariable() with authentication header │ │
│ │ │ │
│ │ To update KEK: │ │
│ │ 1. Create new EFI_SIGNATURE_LIST with KEK certificate │ │
│ │ 2. Sign the update with the Platform Key (PK) │ │
│ │ 3. Submit via SetVariable() with authentication header │ │
│ │ │ │
│ │ To update PK (take ownership): │ │
│ │ 1. If no PK set: Can enroll directly (Setup Mode) │ │
│ │ 2. If PK exists: Must sign with existing PK │ │
│ │ 3. Clearing PK returns system to Setup Mode │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.6 Trust Chain from Firmware to OS Kernel
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPLETE TRUST CHAIN │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ HARDWARE ROOT OF TRUST │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Firmware stored in SPI flash (read-only portions) │ │ │
│ │ │ Platform Key embedded or enrolled at factory │ │ │
│ │ │ Secure Boot policy enforced by firmware │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ Trusts PK │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ PLATFORM KEY (PK) │ │ │
│ │ │ │ │ │
│ │ │ Owner: Hardware manufacturer or system administrator │ │ │
│ │ │ Purpose: Ultimate authority over Secure Boot policy │ │ │
│ │ │ Can: Update/replace KEK, clear all keys, disable SB │ │ │
│ │ │ │ │ │
│ │ │ Microsoft PK is pre-enrolled on most consumer devices │ │ │
│ │ │ You can enroll your own PK (taking ownership) │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ Authorizes KEK │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ KEY EXCHANGE KEY (KEK) │ │ │
│ │ │ │ │ │
│ │ │ Owner: OS vendors (Microsoft, Canonical, Red Hat, etc.) │ │ │
│ │ │ Purpose: Manage which bootloaders are trusted │ │ │
│ │ │ Can: Add/remove certificates in db, add hashes to dbx │ │ │
│ │ │ Cannot: Modify PK, bypass Secure Boot │ │ │
│ │ │ │ │ │
│ │ │ Typically includes: │ │ │
│ │ │ • Microsoft KEK (for Windows updates to db) │ │ │
│ │ │ • OEM KEK (for firmware updates) │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ Authorizes db/dbx │ │
│ │ │ │ │
│ │ ┌─────────┴─────────┐ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │
│ │ │ SIGNATURE DB (db) │ │ FORBIDDEN DB (dbx) │ │ │
│ │ │ │ │ │ │ │
│ │ │ Trusted certificates:│ │ Revoked/blocked: │ │ │
│ │ │ • Microsoft UEFI CA │ │ • Revoked certs │ │ │
│ │ │ • Microsoft Windows │ │ • Known-bad hashes │ │ │
│ │ │ Production PCA │ │ • Vulnerable shim │ │ │
│ │ │ • Your custom cert │ │ versions │ │ │
│ │ │ (after enrollment) │ │ │ │ │
│ │ │ │ │ Checked FIRST - │ │ │
│ │ │ │ │ if hash/cert is in │ │ │
│ │ │ │ │ dbx, REJECT even if │ │ │
│ │ │ │ │ also in db │ │ │
│ │ └──────────┬───────────┘ └──────────────────────┘ │ │
│ │ │ │ │
│ │ Verifies │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ UEFI BOOTLOADER │ │ │
│ │ │ │ │ │
│ │ │ Signed by a certificate in db │ │ │
│ │ │ │ │ │
│ │ │ Examples: │ │ │
│ │ │ • shim (signed by Microsoft, chains to distro cert) │ │ │
│ │ │ • grubx64.efi (signed by distro) │ │ │
│ │ │ • bootmgfw.efi (Windows, signed by Microsoft) │ │ │
│ │ │ • Your BOOTX64.EFI (signed by your db cert) │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ Bootloader verifies (for shim/GRUB) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ OS KERNEL │ │ │
│ │ │ │ │ │
│ │ │ Linux: vmlinuz (signed, verified by shim/GRUB) │ │ │
│ │ │ Windows: winload.efi (signed, verified by bootmgfw) │ │ │
│ │ │ │ │ │
│ │ │ Kernel then verifies modules (kernel lockdown mode) │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.7 Why Secure Boot Matters
Understanding the security implications:
┌─────────────────────────────────────────────────────────────────────────────┐
│ THREATS MITIGATED BY SECURE BOOT │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ BOOTKIT ATTACK (Without Secure Boot): │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Attacker gains temporary admin access │ │
│ │ 2. Replaces bootloader with malicious version │ │
│ │ 3. System reboots... │ │
│ │ 4. Malicious bootloader runs FIRST │ │
│ │ 5. Installs rootkit in kernel before OS loads │ │
│ │ 6. Hides from all OS-level security tools │ │
│ │ │ │
│ │ Result: Undetectable persistent compromise │ │
│ │ │ │
│ │ Real examples: │ │
│ │ • TDL4/TDSS (2008) - Infected millions of PCs │ │
│ │ • Rovnix (2011) - Bootkited banking trojan │ │
│ │ • FinSpy (2011+) - Commercial spyware with bootkit │ │
│ │ • MosaicRegressor (2020) - First in-the-wild UEFI bootkit │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ WITH SECURE BOOT ENABLED: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Attacker gains temporary admin access │ │
│ │ 2. Attempts to replace bootloader... │ │
│ │ 3. System reboots... │ │
│ │ 4. Firmware checks bootloader signature │ │
│ │ 5. Signature INVALID - malicious code not signed by trusted key │ │
│ │ 6. Boot HALTED - attack prevented! │ │
│ │ │ │
│ │ Alternative attack paths blocked: │ │
│ │ • Modified kernel: Also must be signed │ │
│ │ • Evil maid attack: Physical access doesn't help without key │ │
│ │ • Supply chain attack: Must compromise signing keys │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ LIMITATIONS OF SECURE BOOT: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Does NOT protect against: │ │
│ │ • Firmware vulnerabilities (SMM attacks, SPI flash writes) │ │
│ │ • Signed but vulnerable bootloaders (need dbx updates) │ │
│ │ • Physical attacks that bypass firmware entirely │ │
│ │ • Attacks after OS loads (kernel exploits, userspace malware) │ │
│ │ • Side-channel attacks on TPM │ │
│ │ │ │
│ │ Real bypass examples: │ │
│ │ • BlackLotus (2023) - Exploits CVE-2022-21894 in Windows BCD │ │
│ │ • Various shim vulnerabilities requiring dbx updates │ │
│ │ • GRUB vulnerabilities (BootHole, 2020) │ │
│ │ │ │
│ │ Secure Boot is ONE layer in defense-in-depth │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.8 Historical Context
- 2011: UEFI Secure Boot introduced in UEFI 2.3.1
- 2012: Windows 8 ships with Secure Boot requirement for logo certification
- 2012: Linux community concern about Microsoft control; shim solution developed
- 2013: Ubuntu, Fedora, SUSE ship with Microsoft-signed shim
- 2020: BootHole vulnerability affects GRUB2, requires mass dbx updates
- 2021: Microsoft begins requiring Secure Boot for Windows 11
- 2023: BlackLotus becomes first in-the-wild bootkit to bypass Secure Boot
3. Project Specification
3.1 What You Will Build
A complete Secure Boot environment demonstrating the full key hierarchy:
- Generate your own PKI - Platform Key (PK), Key Exchange Key (KEK), and signature database (db) certificates
- Sign custom EFI binaries - Create Authenticode signatures on your bootloader from Project 7 or 8
- Enroll keys in OVMF - Configure the UEFI firmware to trust your certificates
- Verify the trust chain - Demonstrate that signed binaries boot and unsigned binaries are rejected
3.2 Functional Requirements
| ID | Requirement | Description |
|---|---|---|
| FR-1 | Generate PK certificate | Create self-signed x509 certificate for Platform Key |
| FR-2 | Generate KEK certificate | Create x509 certificate signed by or co-equal to PK |
| FR-3 | Generate db certificate | Create x509 certificate for signing bootloaders |
| FR-4 | Sign EFI binary | Use sbsign to add Authenticode signature to BOOTX64.EFI |
| FR-5 | Enroll keys in OVMF | Configure Secure Boot with your custom keys |
| FR-6 | Boot signed binary | Verify signed bootloader executes successfully |
| FR-7 | Reject unsigned binary | Verify unsigned bootloader is blocked |
| FR-8 | Reject wrongly-signed binary | Verify binary signed with unknown key is blocked |
3.3 Non-Functional Requirements
| ID | Requirement | Description |
|---|---|---|
| NFR-1 | Reproducible | Scripted key generation and enrollment process |
| NFR-2 | Documented | Clear explanation of each cryptographic step |
| NFR-3 | OVMF compatible | Works with standard OVMF Secure Boot builds |
| NFR-4 | Debuggable | Can inspect signature and enrollment state |
3.4 Example Usage / Expected Output
# Step 1: Generate your own PKI hierarchy
$ ./generate-keys.sh
Generating Platform Key (PK)...
Creating private key: PK.key (RSA 2048)
Creating certificate: PK.crt (self-signed, valid 10 years)
Converting to DER: PK.cer
Creating EFI signature list: PK.esl
Creating signed auth file: PK.auth
Generating Key Exchange Key (KEK)...
Creating private key: KEK.key (RSA 2048)
Creating certificate: KEK.crt (self-signed, valid 10 years)
Converting to DER: KEK.cer
Creating EFI signature list: KEK.esl
Creating signed auth file: KEK.auth (signed with PK)
Generating Signature Database certificate (db)...
Creating private key: db.key (RSA 2048)
Creating certificate: db.crt (self-signed, valid 10 years)
Converting to DER: db.cer
Creating EFI signature list: db.esl
Creating signed auth file: db.auth (signed with KEK)
Keys generated successfully!
# Step 2: Sign your bootloader
$ sbsign --key db.key --cert db.crt --output BOOTX64.EFI.signed BOOTX64.EFI
Signing BOOTX64.EFI...
Signature successful!
# Verify the signature
$ sbverify --cert db.crt BOOTX64.EFI.signed
Signature verification OK
# Step 3: Create OVMF VARS file with your keys enrolled
$ ./enroll-keys.sh
Creating OVMF_VARS.fd from template...
Enrolling PK...
Enrolling KEK...
Enrolling db...
Setting Secure Boot mode to User Mode...
OVMF_VARS.fd created with your keys!
# Step 4: Test with signed binary (SUCCESS)
$ qemu-system-x86_64 \
-machine q35 \
-drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.secboot.fd \
-drive if=pflash,format=raw,file=OVMF_VARS.fd \
-drive file=disk-signed.img,format=raw \
-net none
# Output in QEMU:
Hello from Secure Boot!
========================
Secure Boot Status: ENABLED
This binary is properly signed and trusted.
Bootloader executing successfully...
# Step 5: Test with unsigned binary (REJECTED)
$ qemu-system-x86_64 \
-machine q35 \
-drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.secboot.fd \
-drive if=pflash,format=raw,file=OVMF_VARS.fd \
-drive file=disk-unsigned.img,format=raw \
-net none
# Output in QEMU:
┌───────────────────────────────────────────────────────┐
│ │
│ Secure Boot Violation │
│ │
│ Invalid signature detected in: │
│ \EFI\BOOT\BOOTX64.EFI │
│ │
│ The file is not signed, or the signature │
│ cannot be verified against the signature │
│ database (db). │
│ │
│ Press any key to continue... │
│ │
└───────────────────────────────────────────────────────┘
# Step 6: Test with wrongly-signed binary (REJECTED)
$ sbsign --key rogue.key --cert rogue.crt --output BOOTX64.EFI.rogue BOOTX64.EFI
$ qemu-system-x86_64 \
-machine q35 \
-drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.secboot.fd \
-drive if=pflash,format=raw,file=OVMF_VARS.fd \
-drive file=disk-rogue.img,format=raw \
-net none
# Output: Same Secure Boot Violation (signature exists but cert not in db)
3.5 Real World Outcome
Upon completion, you will have:
- Understanding of PKI - Practical experience with x509 certificates and key hierarchies
- Secure Boot expertise - Can configure Secure Boot for enterprise or embedded deployments
- Security debugging skills - Can diagnose signature verification failures
- Portfolio demonstration - Shows security and firmware expertise
4. Solution Architecture
4.1 High-Level Design
┌─────────────────────────────────────────────────────────────────────────────┐
│ SECURE BOOT PROJECT ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ KEY GENERATION WORKFLOW: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ openssl req/x509 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ PK.key │ │ KEK.key │ │ db.key │ (Private keys) │ │
│ │ │ PK.crt │ │ KEK.crt │ │ db.crt │ (Certificates) │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ cert-to-efi-sig-list │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ PK.esl │ │ KEK.esl │ │ db.esl │ (Signature lists) │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ sign-efi-sig-list │ │
│ │ │ │ │ │ │
│ │ │ (self-sign) │ (sign w/PK) │ (sign w/KEK) │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ PK.auth │ │ KEK.auth │ │ db.auth │ (Auth update files)│ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ SIGNING WORKFLOW: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────┐ │ │
│ │ │ BOOTX64.EFI │ (Unsigned bootloader from Project 7/8) │ │
│ │ │ (unsigned) │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ sbsign │ │ │
│ │ │ │ │ │
│ │ │ sbsign --key db.key --cert db.crt \ │ │ │
│ │ │ --output BOOTX64.EFI.signed BOOTX64.EFI │ │ │
│ │ │ │ │ │
│ │ │ Steps performed by sbsign: │ │ │
│ │ │ 1. Calculate Authenticode hash of PE file │ │ │
│ │ │ 2. Create PKCS#7 SignedData structure │ │ │
│ │ │ 3. Sign hash with db.key private key │ │ │
│ │ │ 4. Embed db.crt certificate in signature │ │ │
│ │ │ 5. Append signature to PE file │ │ │
│ │ │ 6. Update PE Security Directory pointer │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ BOOTX64.EFI │ │ │
│ │ │ (signed) │ Ready for Secure Boot! │ │
│ │ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ENROLLMENT WORKFLOW: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Option A: OVMF Setup Menu (Manual) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ 1. Boot OVMF with empty VARS │ │ │
│ │ │ 2. Press ESC to enter Setup │ │ │
│ │ │ 3. Device Manager → Secure Boot Configuration │ │ │
│ │ │ 4. Enroll PK from file (PK.cer or PK.auth) │ │ │
│ │ │ 5. Enroll KEK from file │ │ │
│ │ │ 6. Enroll db from file │ │ │
│ │ │ 7. Enable Secure Boot │ │ │
│ │ │ 8. Save and Reset │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Option B: efi-updatevar (Scripted, from Linux) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ # Boot into OVMF with Secure Boot in Setup Mode │ │ │
│ │ │ efi-updatevar -e -f db.esl db │ │ │
│ │ │ efi-updatevar -e -f KEK.esl KEK │ │ │
│ │ │ efi-updatevar -f PK.auth PK # This enables User Mode │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Option C: Pre-built VARS file │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Use virt-fw-vars or similar tool to create OVMF_VARS.fd │ │ │
│ │ │ with keys pre-enrolled (best for automation) │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ VERIFICATION WORKFLOW: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ QEMU with Secure Boot OVMF │ │
│ │ │ │ │
│ │ │ Loads BOOTX64.EFI │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ UEFI Secure Boot Verification │ │ │
│ │ │ │ │ │
│ │ │ 1. Check SecureBoot variable = 0x01 (enabled) │ │ │
│ │ │ 2. Extract signature from BOOTX64.EFI │ │ │
│ │ │ 3. Verify signature against db certificates │ │ │
│ │ │ │ │ │
│ │ │ SIGNED WITH db.key: │ │ │
│ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Certificate in signature matches db.crt in db │ │ │ │
│ │ │ │ → Signature verification PASSES │ │ │ │
│ │ │ │ → Binary executes! │ │ │ │
│ │ │ └──────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ UNSIGNED OR WRONG KEY: │ │ │
│ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ No signature, or certificate not in db │ │ │ │
│ │ │ │ → Signature verification FAILS │ │ │ │
│ │ │ │ → "Secure Boot Violation" error displayed │ │ │ │
│ │ │ │ → Binary does NOT execute │ │ │ │
│ │ │ └──────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.2 Project Structure
secure-boot-lab/
├── keys/ # Generated keys and certificates
│ ├── PK.key # Platform Key private key
│ ├── PK.crt # Platform Key certificate (PEM)
│ ├── PK.cer # Platform Key certificate (DER)
│ ├── PK.esl # Platform Key EFI signature list
│ ├── PK.auth # Platform Key authenticated variable
│ ├── KEK.key # Key Exchange Key private key
│ ├── KEK.crt # KEK certificate (PEM)
│ ├── KEK.cer # KEK certificate (DER)
│ ├── KEK.esl # KEK EFI signature list
│ ├── KEK.auth # KEK authenticated variable
│ ├── db.key # Signature database private key
│ ├── db.crt # db certificate (PEM)
│ ├── db.cer # db certificate (DER)
│ ├── db.esl # db EFI signature list
│ ├── db.auth # db authenticated variable
│ └── noPK.auth # Empty PK for resetting to Setup Mode
├── bootloader/ # Your EFI binaries
│ ├── BOOTX64.EFI # Unsigned bootloader
│ └── BOOTX64.EFI.signed # Signed bootloader
├── scripts/
│ ├── generate-keys.sh # Key generation script
│ ├── sign-binary.sh # Signing script
│ ├── enroll-keys-ovmf.sh # OVMF enrollment script
│ └── verify-signature.sh # Verification script
├── ovmf/
│ ├── OVMF_CODE.secboot.fd # OVMF firmware with Secure Boot
│ └── OVMF_VARS.fd # OVMF variables with your keys
├── images/
│ ├── disk-signed.img # Disk with signed bootloader
│ ├── disk-unsigned.img # Disk with unsigned bootloader
│ └── disk-rogue.img # Disk with wrongly-signed bootloader
├── Makefile # Build automation
└── README.md # Documentation
5. Implementation Guide
5.1 Development Environment Setup
Required packages (Ubuntu/Debian):
sudo apt update
sudo apt install \
openssl \
efitools \
sbsigntool \
qemu-system-x86 \
ovmf \
uuid-runtime \
python3 \
mtools \
dosfstools
Required packages (Fedora):
sudo dnf install \
openssl \
efitools \
sbsigntools \
qemu-system-x86 \
edk2-ovmf \
uuid \
python3 \
mtools \
dosfstools
Locate OVMF files:
# Ubuntu/Debian
OVMF_CODE=/usr/share/OVMF/OVMF_CODE.secboot.fd
OVMF_VARS=/usr/share/OVMF/OVMF_VARS.fd
# Fedora
OVMF_CODE=/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd
OVMF_VARS=/usr/share/edk2/ovmf/OVMF_VARS.fd
# Verify Secure Boot capable OVMF exists
ls -la $OVMF_CODE
5.2 The Core Question You’re Answering
How does cryptographic verification establish trust in the boot process, and how can you configure and manage this trust hierarchy?
This project forces you to understand:
- How public key cryptography enables signature verification
- Why hierarchical trust (PK → KEK → db) provides flexibility
- How UEFI stores and manages cryptographic material
- What happens when signature verification fails
5.3 Concepts You Must Understand First
Before implementing, verify you understand:
- Public key cryptography basics
- Self-assessment: What’s the relationship between a private key and public key? Why can you verify a signature with only the public key?
- Reference: “Serious Cryptography” Chapter 11
- x509 certificates
- Self-assessment: What information does a certificate contain? What makes it “self-signed”?
- Reference: RFC 5280
- Hash functions
- Self-assessment: Why is SHA-256 used in Authenticode? What would happen if two files had the same hash?
- Reference: “Serious Cryptography” Chapter 6
- PE executable format
- Self-assessment: Where is the Authenticode signature stored in a PE file? What parts of the file are hashed?
- Reference: “Practical Binary Analysis” Chapter 3
- UEFI variables
- Self-assessment: What’s the difference between runtime and boot-time-only variables? Why are Secure Boot variables authenticated?
- Reference: UEFI Specification Chapter 8
5.4 Questions to Guide Your Design
Key Generation:
- Why RSA-2048 for Secure Boot keys? Would RSA-4096 be better? What about ECC?
- What subject name should your certificates have? Does it matter?
- How long should your certificates be valid?
Signing:
- What hash algorithm does sbsign use by default? Is SHA-1 still acceptable?
- Can you sign a binary multiple times (dual-sign)? Why would you?
- How do you verify a signature without Secure Boot (offline)?
Enrollment:
- What happens if you enroll KEK before PK?
- Why does enrolling PK transition from Setup Mode to User Mode?
- How do you return to Setup Mode if you lose your PK private key?
Security:
- What if an attacker gets your db.key? What’s the recovery process?
- How do you revoke a compromised bootloader without revoking the signing key?
- Why is dbx checked before db?
5.5 Thinking Exercise
Before generating keys, work through this scenario:
You’re setting up Secure Boot for an embedded device. The device manufacturer wants to:
- Maintain ultimate control (they own the PK)
- Allow OS vendor updates (OS vendor has KEK access)
- Support both Windows and Linux bootloaders (both certs in db)
- Be able to revoke specific bootloader versions (use dbx)
Questions:
- How many keys/certificates do you need? (Answer: 4 - PK, KEK, db-windows, db-linux)
- Who signs updates to db? (Answer: KEK holder - could be manufacturer or OS vendor)
- How do you add a hash to dbx? (Answer: KEK holder signs the dbx update)
- What if the OS vendor’s signing key is compromised? (Answer: Add their cert to dbx using KEK)
5.6 Hints in Layers
Hint 1: Getting Started (Conceptual Direction)
Start with the simplest working case: generate all three keys (PK, KEK, db) yourself, with the same owner. This is a “self-signed” hierarchy for testing. Later you can explore more complex scenarios.
The key tools are:
openssl- Generate keys and certificatescert-to-efi-sig-list- Convert certificates to UEFI formatsign-efi-sig-list- Create authenticated variable updatessbsign- Sign EFI binaries- OVMF Setup menu - Enroll keys interactively
Hint 2: Key Generation Commands (More Specific)
# Create directory for keys
mkdir -p keys
cd keys
# Define a GUID for your keys (use same GUID for all)
MY_GUID=$(uuidgen)
echo "Using GUID: $MY_GUID"
# Generate Platform Key (PK)
openssl req -newkey rsa:2048 -nodes -keyout PK.key \
-new -x509 -sha256 -days 3650 \
-subj "/CN=My Platform Key/" -out PK.crt
# Generate Key Exchange Key (KEK)
openssl req -newkey rsa:2048 -nodes -keyout KEK.key \
-new -x509 -sha256 -days 3650 \
-subj "/CN=My Key Exchange Key/" -out KEK.crt
# Generate Signature Database key (db)
openssl req -newkey rsa:2048 -nodes -keyout db.key \
-new -x509 -sha256 -days 3650 \
-subj "/CN=My Signature Database Key/" -out db.crt
Hint 3: Converting to UEFI Format (Technical Details)
# Convert certificates to DER format
openssl x509 -outform DER -in PK.crt -out PK.cer
openssl x509 -outform DER -in KEK.crt -out KEK.cer
openssl x509 -outform DER -in db.crt -out db.cer
# Create EFI Signature Lists
cert-to-efi-sig-list -g "$MY_GUID" PK.crt PK.esl
cert-to-efi-sig-list -g "$MY_GUID" KEK.crt KEK.esl
cert-to-efi-sig-list -g "$MY_GUID" db.crt db.esl
# Create authenticated variable updates
# Note: Order matters! PK is self-signed, KEK is signed by PK, db is signed by KEK
# PK signs itself (bootstrap)
sign-efi-sig-list -g "$MY_GUID" -k PK.key -c PK.crt PK PK.esl PK.auth
# PK signs KEK update
sign-efi-sig-list -g "$MY_GUID" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
# KEK signs db update
sign-efi-sig-list -g "$MY_GUID" -k KEK.key -c KEK.crt db db.esl db.auth
# Create empty PK for clearing (returns to Setup Mode)
sign-efi-sig-list -g "$MY_GUID" -k PK.key -c PK.crt PK /dev/null noPK.auth
Hint 4: Signing and Verification (Advanced Details)
# Sign your bootloader
sbsign --key db.key --cert db.crt \
--output BOOTX64.EFI.signed \
BOOTX64.EFI
# Verify signature (without Secure Boot)
sbverify --cert db.crt BOOTX64.EFI.signed
# Should output: "Signature verification OK"
# Inspect signature details
sbverify --list BOOTX64.EFI.signed
# Shows: signature type, signer, digest algorithm
# Verify against wrong certificate (should fail)
sbverify --cert KEK.crt BOOTX64.EFI.signed
# Should output: "Signature verification failed"
# Extract the embedded certificate from signed binary
sbverify --cert /dev/null BOOTX64.EFI.signed 2>&1 | grep -A20 "Certificate"
QEMU Command for Testing:
# Copy OVMF_VARS to get a writable copy
cp /usr/share/OVMF/OVMF_VARS.fd ./OVMF_VARS.fd
# Boot with Secure Boot capable OVMF
qemu-system-x86_64 \
-machine q35 \
-m 256M \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.secboot.fd \
-drive if=pflash,format=raw,file=./OVMF_VARS.fd \
-drive file=disk.img,format=raw \
-net none \
-serial stdio
# In OVMF:
# 1. Press ESC to enter Setup
# 2. Device Manager → Secure Boot Configuration
# 3. Current Secure Boot State: should show "Setup Mode"
# 4. Secure Boot Mode → Custom Mode
# 5. Custom Secure Boot Options → PK Options → Enroll PK → Enroll PK Using File
# 6. Select your PK.cer file from the ESP
# 7. Repeat for KEK and db
# 8. Attempt to Boot → should succeed with signed, fail with unsigned
5.7 The Interview Questions They’ll Ask
If you mention Secure Boot on your resume, expect:
- “Explain the UEFI Secure Boot key hierarchy”
- Strong answer: “Secure Boot uses a three-tier hierarchy. The Platform Key (PK) is the root of trust, controlled by the hardware owner. PK authorizes Key Exchange Keys (KEK), typically held by OS vendors. KEK holders can update the signature database (db) and forbidden signature database (dbx). Binaries are signed with certificates in db. When loading an EFI binary, firmware first checks dbx for revocation, then verifies the signature against db certificates.”
- “How would you set up Secure Boot for a custom embedded device?”
- Strong answer: “First, generate a complete key hierarchy with OpenSSL - PK for device owner, KEK for authorized update providers, db certificates for bootloader signing. Sign the bootloader with sbsign. Enroll keys either in firmware Setup Mode or by pre-provisioning the NVRAM. Test by verifying signed binaries boot and unsigned binaries are rejected. Document the key management process and secure storage requirements for private keys.”
- “What happens if a signing key is compromised?”
- Strong answer: “Depends on which key. If db key is compromised, add the compromised certificate to dbx using a KEK-signed update. All binaries signed by that key become invalid. If KEK is compromised, the PK holder must revoke it and issue a new KEK. If PK is compromised, the device is fully compromised - recovery requires physical intervention like reflashing firmware. This is why PK private keys must be extremely well protected, often in HSMs.”
- “How does shim work with Secure Boot?”
- Strong answer: “Shim is a first-stage bootloader signed by Microsoft’s UEFI CA, which is pre-enrolled in db on most devices. Shim contains an embedded ‘Machine Owner Key’ (MOK) database. When shim loads, it verifies the next-stage bootloader (like GRUB) against either the UEFI db or its MOK list. This allows Linux distributions to use their own signing keys without requiring Microsoft to sign every bootloader update. Users can also enroll their own keys in MOK for custom kernels.”
- “What vulnerabilities have affected Secure Boot?”
- Strong answer: “Several classes of vulnerabilities exist. BootHole (2020) was a buffer overflow in GRUB2 that allowed arbitrary code execution before signature verification. BlackLotus (2023) exploits CVE-2022-21894 in Windows Boot Manager to bypass Secure Boot entirely. There have also been firmware vulnerabilities allowing direct NVRAM manipulation, and issues where dbx updates weren’t deployed widely enough to revoke known-bad bootloaders. The key lesson is that Secure Boot is one layer in defense-in-depth, not a complete solution.”
5.8 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Public Key Cryptography | “Serious Cryptography” by Jean-Philippe Aumasson | Ch. 10-11 |
| x509 Certificates | “Bulletproof SSL and TLS” by Ivan Ristic | Ch. 3-4 |
| Authenticode Format | “Practical Binary Analysis” by Dennis Andriesse | Ch. 3 |
| UEFI Security | “Beyond BIOS” by Vincent Zimmer | Ch. 21 |
| Boot Security | “Rootkits and Bootkits” by Alex Matrosov | Ch. 8-9 |
5.9 Implementation Phases
Phase 1: Environment Setup (Day 1)
- Install all required tools
- Locate OVMF Secure Boot files
- Verify QEMU can boot OVMF
- Access OVMF Setup menu
Milestone: OVMF boots, shows “Setup Mode” in Secure Boot Configuration
Phase 2: Key Generation (Days 2-3)
- Generate PK key pair and certificate
- Generate KEK key pair and certificate
- Generate db key pair and certificate
- Convert to DER format
- Create EFI signature lists
- Create authenticated variable files
Milestone: All .key, .crt, .cer, .esl, .auth files generated
Phase 3: Binary Signing (Days 4-5)
- Build or obtain unsigned BOOTX64.EFI (from Project 7)
- Sign with sbsign using db key
- Verify signature with sbverify
- Create test binaries: signed, unsigned, wrongly-signed
Milestone: Three test binaries ready (signed, unsigned, rogue)
Phase 4: Key Enrollment (Days 6-8)
- Create disk images with key files and bootloaders
- Boot OVMF in Setup Mode
- Enroll db certificate
- Enroll KEK certificate
- Enroll PK (transitions to User Mode)
- Enable Secure Boot
Milestone: OVMF shows “User Mode” and “Secure Boot Enabled”
Phase 5: Verification Testing (Days 9-11)
- Boot with signed binary - should succeed
- Boot with unsigned binary - should fail
- Boot with wrongly-signed binary - should fail
- Document error messages and behavior
Milestone: All three test cases produce expected results
Phase 6: Automation and Polish (Days 12-14)
- Create shell scripts for key generation
- Create scripts for signing
- Create pre-enrolled OVMF_VARS.fd
- Document the entire process
- Create Makefile for automation
Milestone: Single make test runs all scenarios
6. Testing Strategy
6.1 Test Matrix
| Test Case | Binary Type | Expected Result | Verification |
|---|---|---|---|
| TC-1 | Signed with db key | Boot succeeds | Bootloader message appears |
| TC-2 | Unsigned | Boot fails | “Secure Boot Violation” shown |
| TC-3 | Signed with unknown key | Boot fails | “Secure Boot Violation” shown |
| TC-4 | Signed, cert in dbx | Boot fails | “Secure Boot Violation” shown |
| TC-5 | Hash in dbx | Boot fails | “Secure Boot Violation” shown |
| TC-6 | Secure Boot disabled | All boot | Any binary runs |
6.2 Verification Commands
# Check Secure Boot state from Linux (if testing on real HW)
mokutil --sb-state
# Expected: "SecureBoot enabled" or "SecureBoot disabled"
# List enrolled keys
efi-readvar -v PK
efi-readvar -v KEK
efi-readvar -v db
efi-readvar -v dbx
# Verify signature on binary
sbverify --list BOOTX64.EFI.signed
# Check PE headers
objdump -x BOOTX64.EFI.signed | grep -A5 "Security Directory"
6.3 QEMU Test Script
#!/bin/bash
# test-secure-boot.sh
OVMF_CODE="/usr/share/OVMF/OVMF_CODE.secboot.fd"
OVMF_VARS="./OVMF_VARS.fd"
test_boot() {
local disk="$1"
local expected="$2"
echo "Testing: $disk (expecting: $expected)"
timeout 30 qemu-system-x86_64 \
-machine q35 \
-m 256M \
-drive if=pflash,format=raw,readonly=on,file="$OVMF_CODE" \
-drive if=pflash,format=raw,file="$OVMF_VARS" \
-drive file="$disk",format=raw \
-net none \
-nographic \
2>&1 | tee /tmp/boot-output.txt
if grep -q "$expected" /tmp/boot-output.txt; then
echo "PASS: $disk"
else
echo "FAIL: $disk - expected '$expected'"
fi
}
# Reset OVMF_VARS to enrolled state
cp OVMF_VARS.enrolled.fd "$OVMF_VARS"
# Test signed (should boot)
test_boot "disk-signed.img" "Hello from Secure Boot"
# Reset OVMF_VARS
cp OVMF_VARS.enrolled.fd "$OVMF_VARS"
# Test unsigned (should fail)
test_boot "disk-unsigned.img" "Secure Boot Violation"
7. Common Pitfalls and Debugging
7.1 Key Generation Issues
| Problem | Symptom | Solution |
|---|---|---|
| Wrong certificate format | “Invalid certificate” during enrollment | Use DER format (.cer) not PEM (.crt) for OVMF enrollment |
| Missing GUID | efi-sig-list tools fail | Generate GUID with uuidgen, use consistently |
| RSA key too short | Signature rejected | Use RSA-2048 minimum (RSA-4096 works too) |
| Self-signed CA issues | Certificate chain validation fails | For testing, self-signed is fine; in production, use proper CA |
7.2 Signing Issues
| Problem | Symptom | Solution |
|---|---|---|
| sbsign not found | Command not found | Install sbsigntool package |
| Wrong key/cert pair | Signature created but invalid | Ensure .key and .crt are from same keypair |
| SHA-1 hash | May be rejected by strict firmware | Use --hash sha256 with sbsign |
| Binary already signed | Can’t re-sign | Use sbattach --remove first or sign fresh binary |
7.3 Enrollment Issues
| Problem | Symptom | Solution |
|---|---|---|
| Can’t find files in OVMF | Files not visible | Ensure ESP has correct structure (/EFI/BOOT/) |
| “Not in Setup Mode” | Can’t enroll keys | Clear PK first or use signed auth files |
| KEK/db enrollment fails | Access denied | Ensure PK enrolled first, use correct auth files |
| PK enrollment fails | Invalid signature | For first PK, system must be in Setup Mode |
7.4 Boot Verification Issues
| Problem | Symptom | Solution |
|---|---|---|
| Signed binary rejected | “Secure Boot Violation” | Verify db certificate matches signing cert |
| All binaries boot | No Secure Boot enforcement | Check Secure Boot actually enabled in OVMF |
| QEMU doesn’t show SB error | Black screen | Add -serial stdio to see console output |
| Wrong OVMF_VARS used | Keys not enrolled | Verify correct VARS file path |
7.5 Debugging Commands
# Check certificate fingerprint
openssl x509 -in db.crt -noout -fingerprint -sha256
# Compare enrolled vs generated certificate
efi-readvar -v db -o enrolled-db.esl
# Then compare with your db.esl
# Examine signature in PE file
osslsigncode verify -in BOOTX64.EFI.signed
# Debug OVMF with serial output
qemu-system-x86_64 ... -serial stdio -D qemu-debug.log -d guest_errors
# Check PE security directory
objdump -x BOOTX64.EFI.signed | grep -A10 "Security"
8. Extensions and Challenges
Extension 1: Add Certificate to dbx (Revocation)
Revoke your db certificate by adding it to dbx, demonstrating how compromised keys are handled.
# Create dbx entry for your db certificate
cert-to-efi-sig-list -g "$MY_GUID" db.crt dbx-revoke.esl
sign-efi-sig-list -a -g "$MY_GUID" -k KEK.key -c KEK.crt dbx dbx-revoke.esl dbx-revoke.auth
# After enrolling, previously-signed binaries should be rejected
Extension 2: Add Hash to dbx
Revoke a specific binary by its hash (without revoking the signing key).
# Calculate Authenticode hash
pesign --hash --in BOOTX64.EFI.signed
# Create dbx entry for this specific hash
# (Requires creating EFI_SIGNATURE_LIST with SHA256 type)
Extension 3: Shim Bootloader Integration
Set up a Linux-style boot chain with shim → GRUB → kernel.
- Download or build shim (signed by Microsoft CA)
- Enroll Microsoft UEFI CA in db
- Enroll your MOK (Machine Owner Key)
- Sign GRUB with your MOK
- Test the full chain
Extension 4: Custom CA Chain
Instead of self-signed certificates, create a proper CA hierarchy:
# Create CA
openssl req -x509 -newkey rsa:4096 -keyout CA.key -out CA.crt ...
# Create db certificate signed by CA
openssl req -newkey rsa:2048 -keyout db.key -out db.csr ...
openssl x509 -req -in db.csr -CA CA.crt -CAkey CA.key -out db.crt ...
Extension 5: Hardware Security Module (HSM) Signing
For production, use an HSM for key storage:
# Sign using PKCS#11 token (example with SoftHSM)
sbsign --engine pkcs11 \
--key "pkcs11:object=db-key" \
--cert db.crt \
BOOTX64.EFI
Extension 6: Test on Real Hardware
Deploy your Secure Boot configuration to actual hardware:
- Create bootable USB with keys and signed bootloader
- Enter UEFI Setup, clear existing keys
- Enroll your keys
- Test signed vs unsigned boot
- Document any hardware-specific issues
9. Real-World Connections
Enterprise Secure Boot Deployment
| Use Case | How Secure Boot is Used |
|---|---|
| Corporate Laptops | IT department enrolls corporate KEK, can push bootloader updates |
| Servers/Data Centers | Custom PK per organization, strict control over boot chain |
| Embedded Devices | Manufacturer PK, prevents end-user from running unauthorized firmware |
| Cloud VMs | Confidential computing VMs use Secure Boot with vTPM |
Linux Distribution Implementation
| Distribution | Approach |
|---|---|
| Ubuntu | Uses Microsoft-signed shim, Canonical MOK for kernel signing |
| Fedora | Uses Microsoft-signed shim, Red Hat MOK |
| Arch Linux | Does not ship with Secure Boot by default; users self-enroll |
| openSUSE | Uses Microsoft-signed shim, SUSE MOK |
Career Applications
- Firmware Security Engineer - Design Secure Boot configurations for products
- Security Researcher - Find and responsibly disclose boot-level vulnerabilities
- Enterprise IT - Deploy and manage Secure Boot across organization
- Embedded Systems - Implement Secure Boot for IoT and industrial devices
- Cloud Security - Configure Secure Boot for VM infrastructure
10. Resources
Official Documentation
- UEFI Specification - Chapter 32: Secure Boot
- Microsoft Authenticode - Signature format
- OVMF Secure Boot Wiki - TianoCore documentation
Tools Documentation
- sbsigntools - EFI binary signing
- efitools - Key management utilities
- mokutil - Machine Owner Key management
Tutorials and Guides
- ArchWiki - Secure Boot - Comprehensive Linux guide
- Rodsbooks - Secure Boot - Rod Smith’s excellent guide
- Gentoo Wiki - Secure Boot - Detailed walkthrough
Security Research
- BootHole Vulnerability - GRUB2 vulnerability analysis
- BlackLotus Analysis - ESET research
- UEFI Security Best Practices - UEFI Forum whitepaper
11. Self-Assessment Checklist
Understanding
- I can explain the PK → KEK → db hierarchy and why each level exists
- I understand the difference between db and dbx
- I know what Authenticode is and how PE signing works
- I can explain why Secure Boot prevents bootkits
- I understand Setup Mode vs User Mode
- I know how shim enables Linux distribution signing
Implementation
- I can generate a complete key hierarchy with OpenSSL
- I can convert certificates to UEFI signature list format
- I can sign EFI binaries with sbsign
- I can verify signatures with sbverify
- I can enroll keys in OVMF (manually or scripted)
- My signed binary boots successfully
- Unsigned/wrongly-signed binaries are rejected
Debugging
- I can inspect PE signature details
- I can read enrolled keys with efi-readvar
- I can diagnose “Secure Boot Violation” errors
- I know how to reset to Setup Mode
Portfolio
- Scripts are clean and documented
- README explains the full process
- I can demonstrate and explain the project in an interview
12. Submission / Completion Criteria
Your project is complete when:
-
Key generation works: All scripts produce valid PK, KEK, and db certificates
- Signing works:
sbsignsuccessfully signs your bootloadersbverifyconfirms the signature is valid
- Enrollment works:
- Keys are enrolled in OVMF (Setup Mode → User Mode)
- Secure Boot shows “Enabled”
- Verification works:
- Signed binary boots successfully
- Unsigned binary is rejected with “Secure Boot Violation”
- Binary signed with unknown key is rejected
- Documentation:
- Scripts are commented
- README explains the process
- You can explain the key hierarchy to someone else
- Understanding demonstrated:
- You can answer the interview questions
- You can explain why each step is necessary
Congratulations! Upon completing this project, you understand one of the most important security mechanisms in modern computing. Secure Boot protects billions of devices from boot-level malware. This knowledge is directly applicable to firmware security, enterprise IT, and embedded systems development.
Previous: P14 - Graphics Mode Bootloader Next: P16 - Bootloader with Interactive Shell