UEFI and Secure Boot

The Unified Extensible Firmware Interface (UEFI) is a specification that defines a software interface between an operating system and platform firmware. In this post, we will discuss a very useful feature of UEFI, which is the support for Secure Boot.

What is UEFI Secure Boot?

UEFI Secure Boot ensures every code launched by firmware is trusted. To be sure the code is trusted, it must be signed by a secure boot key. UEFI contains the public keys to verify launched code. The code itself is signed by using a private key. All unsigned code will be rejected by firmware. So, if you keep the private key in a secure environment, and set the strong administrative password for UEFI, only a software image you approved by signing it can be executed by the firmware.

Secure Boot Keys

Following are the keys that are used by Secure Boot:

  • Database Key (DB) – This key type is used to sign or verify the binaries (boot loaders, boot managers, shells, drivers, etc.) that UEFI runs.
  • Forbidden Signature Key (DBX)—The DBX is a sort of anti-trusted keys DB; it contains keys and hashes that correspond to known malware or otherwise undesirable software.
  • Key Exchange Key (KEK) – The KEK is used to sign keys so that the firmware accepts them as valid when entering them into the database (either the DB or the DBX). Without the KEK, the firmware would have no way of knowing whether a new key was valid or was being fed by malware.
  • Platform Key (PK) – The PK is the top-level key in Secure Boot, and it serves a function relative to the KEK similar to that of the KEK to the DB and DBX. UEFI Secure Boot supports a single PK.

All of these key types are similar and use Public Key Infrastructure (PKI). Every key type has a private key to sign files and a public key to verify the signature. Obviously, the public key is not sensitive; it needn’t (and in fact, it mustn’t) be hidden away or kept secret. UEFI stores public keys in NVRAM. The private key, however, is sensitive because it can be used to sign malicious code. So, it is important to store private keys in a secure environment.

Generate Secure Boot keys

A sample script for generating Secure Boot Keys:

#!/bin/bash

#Common Name to embed in the keys

NAME=”replace with your name”

openssl req -new -x509 -newkey rsa:2048 -subj “/CN=$NAME PK/” -keyout PK.key

        -out PK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj “/CN=$NAME KEK/” -keyout KEK.key

        -out KEK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj “/CN=$NAME DB/” -keyout DB.key

        -out DB.crt -days 3650 -nodes -sha256

openssl x509 -in PK.crt -out PK.cer -outform DER

openssl x509 -in KEK.crt -out KEK.cer -outform DER

openssl x509 -in DB.crt -out DB.cer -outform DER

GUID=$(python3 -c ‘import uuid; print(str(uuid.uuid1()))’)

echo “$GUID” > myGUID.txt

cert-to-efi-sig-list -g “$GUID” PK.crt PK.esl

cert-to-efi-sig-list -g “$GUID” KEK.crt KEK.esl

cert-to-efi-sig-list -g “$GUID” DB.crt DB.esl

rm -f noPK.esl

touch noPK.esl

sign-efi-sig-list -t “$(date –date=’1 second’ +’%Y-%m-%d %H:%M:%S’)”

                  -k PK.key -c PK.crt PK PK.esl PK.auth

sign-efi-sig-list -t “$(date –date=’1 second’ +’%Y-%m-%d %H:%M:%S’)”

                  -k PK.key -c PK.crt PK noPK.esl noPK.auth

sign-efi-sig-list -t “$(date –date=’1 second’ +’%Y-%m-%d %H:%M:%S’)”

                  -k PK.key -c PK.crt KEK KEK.esl KEK.auth

sign-efi-sig-list -t “$(date –date=’1 second’ +’%Y-%m-%d %H:%M:%S’)”

                  -k KEK.key -c KEK.crt db DB.esl DB.auth

opy DB.cer, KEK.cer, and PK.cer keys to a FAT drive to import keys into UEFI.

The DB.key and DB.crt are used to sign a unified kernel image.

Example:

sbsign --key DB.key --cert DB.crt --output mykernel.efi mykernel.efi

Grubless environment

Most x84 Linux distributes use GRUB as a boot loader. Usually, it is the most simple and compatible way. But note, most x86 based hardware comes from the factory pre-loaded with Microsoft keys, while GRUB is signed with one of these keys. Also, GRUB itself can contain 0-day vulnerabilities. All these create additional potential security threats.

UEFI is designed to be a universal bootloader. So an additional bootloader is useless in a UEFI environment. UEFI firmware can load Linux kernel directly. Also, most UEFI firmwares provide a boot menu and you can select what OS or what Linux kernel to boot. All these UEFI features make GRUB redundant.

Boot Linux kernel directly from UEFI

First, you need to create a Unified Kernel Image. The unified kernel image contains kernel, initramfs, and kernel cmdline. It is a UEFI application and UEFI can run this application directly.

Unified kernel image must be signed with the DB private key. UEFI doesn’t start unsigned applications or applications signed by unknown keys.

We use two unified kernel images (UKI), one per bank. UKIs are stored in the HDD boot partition. Boot partition is a FAT formatted file system.

Creating Unified Kernel Image

To create Unified Kernel Image run the command:

objcopy --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 --add-section .cmdline=/proc/cmdline --change-section-vma .cmdline=0x30000 --add-section .linux="/boot/vmlinux" --change-section-vma .linux=0x40000 --add-section .initrd="/boot/initramfs.img" --change-section-vma .initrd=0x3000000 /usr/lib/systemd/boot/efi/linuxx64.efi.stub mykernel.efi

Sign kernel image and copy mykernel.efi into EFI partition (usually mounted as /boot/efi).

Use efibootmgr utility to create boot menu item:

efibootmgr -c -d /dev/sda1 -L "My Kernel" -l 'mykernel.efi'

Now you can reboot, enter your UEFI, import Secure Boot keys and boot your kernel directly using the boot menu.

Finally, your server can run only the kernel you signed by a private key available only to you.

Need help with setting security boot on x86 platform?
Share this article:

Get proposal

We will be happy to answer any question.
Please fill out the following fields: