Own and lock down your laptop: UEFI Secure Boot.

Posted on 23.01.2017

The Unified Extensible Firmware Interface Secure Boot functionality is much maligned by the internet community at large, who have in general not correctly understood its implications and possible uses. If you are looking for a more authoritative source than me, try Matthew Garrett's Some things you may have heard about Secure Boot which aren't entirely true. In this post I am going to walk through how UEFI Secure Boot can actually be to your benefit and how you can secure your own platform. I have followed this procedure on my own laptop, a Dell XPS 9343 from 2015 running Bios Revision A07 and I would encourage anyone with a UEFI platform, particularly a laptop, to consider going through this process for their own security. The guide I followed before writing this post was James Bottomley's Owning your Windows 8 UEFI Platform; there isn't that much new here, but I wanted to assume you were not current with UEFI.

Why might you want this? Well, a while back I was pointed to this article: A reasonably secure travel laptop. "Reasonably" is a bit modest here; the setup includes taking over your SPI flash, running firmware you can actually read the code of and signing just about every part of the boot chain. Unfortunately my laptop is not in the supported coreboot motherboards list and I doubt coreboot would work - I am also not comfortable opening up the chassis of my laptop to SPI flash my firmware in this case. So, I am "stuck" trusting Dell firmware.

Why would you want to do this? Well, obviously blocking unsigned code from booting on your device is desirable from the point of view of protecting against bootkits as well as raising the bar on evil maid attacks. For me, though, the real appeal is actually the less sexy motivation that, should my laptop ever be stolen it will essentially be a brick for the thief.

Some disclaimers and warnings are necessary before we begin though:

Got it? Good. Let's begin:

UEFI abbreviated history: what it is, isn't and how it works

EFI began life, according to wikipedia, as the Intel Boot Initiative as far back as 1998 to support booting newer Itanium processors without the restrictions imposed by BIOS firmware. It quickly became the Extensible Firmware Interface and a consortium, the UEFI consortium, was later set up and the standards handed over to them for continued development. This happened as far back as 1995.

When Apple switched from PowerPC to Intel, they also switched firmware, from OpenFirmware to EFI. However, they chose to implement EFI 1.x and are not UEFI compatible.

The really big push came in 2011 and 2012, when manufacturers began pushing UEFI for consumer motherboards and Microsoft mandated for Windows 8 Logo compatibility that firmware support UEFI. UEFI support, however, existed in Windows Vista SP1. What it isn't is a Microsoft-imposed standard.

Next up, we need to look into the UEFI boot process.

UEFI boot diagram from TianoCore

We shall quickly break down each stage after SEC:

Of course, we missed the SEC phase. The SEC phase is responsible for making sure everything that comes after, if Secure Boot is on, is appropriately signed.

UEFI firmware images are stored in UEFI Firmware Volume-format, for the most part, as described at the time of writing in the Platform Initialisation (UEFI PI 1.5) specifications, in Shared Architecture section 2.2.2. An in-depth discussion of this format is out of scope, but it is simple for the SEC, PEI and DXE phaes to read this filesystem.

UEFI drivers and applications themselves at a later stage are typically PE32+ files (64-bit "EXEs") and for users of the UEFI platform, it is this that matters, as shall be discussed below for Secure Boot.

UEFI Secure Boot: the theory

For this part, you will need the UEFI Spec, in particular sections 30.2, 30.3, 30.4 and 30.5 are relevant. We shall, however, begin at the beginning.

Keys

Firstly, we have the platform key. The specification describes this as establishing "a trust relationship between the platform owner and the firmware". More specifically, objects signed by the platform key will be accepted as allowed to update the firmware; those that are not will be refused. With the platform key enrolled, the system will refuse changes to important databases. For example, a new Key Exchange Key cannot be enrolled.

Secondly, we have the Key Exchange Keys. These are described as establishing trust between the Operating System and firmware, but perhaps more accurately is that they establish trust between the Operating System Vendor and the firmware. It's purpose is to allow an operating system vendor's signature lists to be loaded into the firmware and trusted.

There isn't a name for the last type of key officially, so I am going to call it an Image Key. The image key is what signs the binary that will be trusted - you are likely already familiar with this key as your key for code-signing a binary, for example.

Databases and Variables

The next question is, where are all these keys kept? Well, that's straightfoward. UEFI defines the concept of a signature database, a store for signatures and public keys and the type EFI_CERT_X509_GUID for signatures based on x509 certificates. It also supports certificates based on RSA-2048 public keys and "signatures" in the form of approved hashes (not to be confused with hash-based signatures). The EFI_CERT_X509_GUID type indicates that a signature will validate in an PKIX certificate chain up to the provided certificate as a root.

UEFI provides special-purpose global variables for the PK and KEK databases, named appropriately PK and KEK. The PK database contains a maximum of one entry, whereas the KEK database may contain multiple entries. Firmware can also provide PKDefault and KEKDefault variables as backups.

Other databases do exist, however. Let's go through them quickly:

For our purposes, we will likely only encounter db and dbx.

Secure boot modes

Secure boot can operate in a couple of modes, which are also worth discussing because they are important from a security perspective. They are:

Verifying a binary

As described in about UEFI, UEFI binaries are nothing more than PE binaries of Microsoft fame. UEFI also reuses Microsoft Authenticode for binary signing.

Whenever the firmware sees a call to LoadImage, either from the UEFI Shell, when it tries to load a bootloader or from third party code, i.e. anywhere from the Boot Services mode, it will take the signature and check it against the db and dbx databases. If it finds a match in the db database, the binary is loaded, otherwise, not.

Putting it all together

OK, so let's follow the chain up. We have a binary with a signature. Let's assume there's a certificate in the platform key database. Firstly, the signature on the binary is checked against db and dbx - let's assume there's no matching signature and we want to add our signature to db. To do this, the platform either needs to be in setup mode, or we need an update signed with a key in the KEK database. Assume we have such an update, but the KEK isn't available in the KEK database. Well, KEK entries are set either during setup mode, or by updates signed with the platform key. So, we need to be the platform owner.

In this way, the platform owner is the root of all trust on the system. They can delegate to third parties, but, changes to the setup once installed are their perogative alone. This setup is complex, but allows separation between platform owners and OS vendors quite nicely.

So, that's the terminology and workings of secure boot. Let's get practical!

The Dell XPS Firmware

The Dell XPS firmware can be accessed in two ways during bootup: you can press F2 to go directly to the setup program, or F12 from the boot menu, which takes you to the boot menu and offers the choice to enter setup.

The setup utility has two main options we are concerned with. Firstly, of course, you can turn UEFI secure boot on and off. The following screen explains it and there is very little complexity to the process. This is available in Secure Boot > Secure Boot Enable.

Photo of Secure boot enable/disable switch

The second dialog, available in Secure Boot > Expert Key Management, allows unsurprisingly for the management of keys. Here it is:

Photo of Secure boot key management

We could, but won't, do all our key management from here. However, I want to point out two very useful features here - we can back up the keys available on the platform and we can equally restore them from disk, or from the firmware itself, totally undoing anything we might later do. Simply turn off custom mode.

With these options in mind, I booted up the system in secure boot mode on my fedora system. You can, having done this, begin to explore your system. For example, you can examine the certificate database using the following command as root:

# efi-readvar
Variable PK, length 1395
PK: List 0, type X509
    Signature 0, size 1367, owner 70564dce-9afc-4ee3-85fc-949649d7e45c
        Subject:
            DC=com, DC=dell, OU=1, OU=Signing, CN=Dell Inc. UEFI Platform Key
        Issuer:
            DC=com, DC=dell, CN=Configuration, CN=Services, CN=Public Key Services, CN=AIA, CN=Dell Inc. Issuing CA 1
Variable KEK, length 1560
KEK: List 0, type X509
    Signature 0, size 1532, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation KEK CA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
Variable db, length 3143
db: List 0, type X509
    Signature 0, size 1515, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
db: List 1, type X509
    Signature 0, size 1572, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
Variable dbx, length 2343
dbx: List 0, type X509
    Signature 0, size 1663, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows PCA 2010
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
dbx: List 1, type SHA256
    Signature 0, size 48, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Hash:80b4d96931bf0d02fd91a61e19d14f1da452e66db2408ca8604d411f92659f0a
< snip >
Variable MokList has no entries

Hopefully my earlier talk of signature databases now makes more sense. You can also grep dmesg to see what the kernel believes has loaded:

# dmesg | grep "Loaded cert"
[    0.754704] EFI: Loaded cert 'Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53' linked to '.builtin_trusted_keys'
[    0.756192] EFI: Loaded cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4' linked to '.builtin_trusted_keys'
[    0.761671] EFI: Loaded cert 'Fedora Secure Boot CA: fde32599c2d61db1bf5807335d7b20e4cd963b42' linked to '.builtin_trusted_keys'
[    0.763101] EFI: Loaded cert 'Microsoft Windows PCA 2010: d14fa98a0708cef4241898e500fff3d6791d37bc' linked to '.system_blacklist_keyring'

These are what the kernel will later use to sign drivers, as the linux kernel on Fedora also enforces signatures.

Now we've explored the platform a little bit, let's get to the actual securing part.

Locking down your boot process

Install tools

Finally the fun begins. Firstly, I needed sbsign from Ubuntu. I could optionally have used pesign, but I prefer utilities that work directly with the files I want rather than the NSS key databases, which I find awkward to use. I also needed efitools for the efivars command added above. You can find both of these in Fedora's Copr:

Import these into your /etc/yum.repos.d/ then dnf install efitools sbsign.

Generate Keys

Secondly we need to generate some keys. The UEFI specification contains definitions for EFI_CERT_RSA2048_GUID and EFI_CERT_RSA2048_SHA256_GUID - although it does not specify what firmware might support for EFI PKIX chains, we can safely assume RSA-2048 keys are likely to be supported.

If you feel this key-size is insufficient, unfortunately unless your hardware supports something else you are likely to be stuck. If it helps, I would highlight the following quote from Thomas Pornin on key-size lengths (admittedly with regard to Elliptic Curves) (source).

Use P-256 to minimize trouble. If you feel that your manhood is threatened by using a 256-bit curve where a 384-bit curve is available, then use P-384

RSA-2048 should be sufficient for now. Also remember that any attack requires signature forgery for keys in your hardware and so physical access to your system - this is likely a safer scenario than RSA-2048 for https.

Anyway, enough, RSA-2048 is our only choice. So we are going to generate three key; a platform key, key-exchange-key and image signing key. Change the names as per your tastes.

cd ~/efi-secure-boot
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Platform Key 2017/" -keyout platform_key.pri -out platform_key.crt -days 9125 -nodes -sha256 
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=KEK 2017/" -keyout kek.pri -out kek.crt -days 9125 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Secure Boot Key 2017/" -keyout secureboot.pri -out secureboot.crt -days 9125 -nodes -sha256

We need the certificates in DER format in firmware (EFI signature lists), this being slightly more efficient than BER. So, we will convert:

openssl x509 -outform DER -in platform_key.crt -out platform_key.cer
openssl x509 -outform DER -in kek.crt -out kek.cer
openssl x509 -outform DER -in secureboot.crt -out secureboot.cer

At this stage there is an optional further step, which is to secure these keys. I recommend you take this step, but it isn't required. If you want to do this, jump to secure keys.

Generate and authenticate EFI signature lists

Next we need to generate EFI signature lists and create authenticated versions of these lists. Here are the steps you need to take for each set of keys.

First, we need a UUID to identify these objects. I used uuidgen like so:

uuidgen > uuid

to keep the uuid file. However, you could equally have set a session variable.

Then you can create a signature list:

cert-to-efi-sig-list -g `cat uuid` platformkey.crt platformkey.esl

and now sign it in three steps. I've separated this out into three steps so that users using hardware tokens can correctly use openssl smime -engine ... - for example, to use my smartcard setup detailed later I need to set

alias smime_token_pk="openssl smime -engine pkcs11 -keyform engine -inkey 'pkcs11:object=Antony%20Vennard%20Whiteprophet%20Platform%20Key%202016'"
alias smime_token_kek="openssl smime -engine pkcs11 -keyform engine -inkey 'pkcs11:object=Antony%20Vennard%20Whiteprophet%20KEK%202016'"
alias smime_token_sb="openssl smime -engine pkcs11 -keyform engine -inkey 'pkcs11:object=Antony%20Vennard%20Whiteprophet%20Secureboot%20Signing%20Key%202016'"

with the appropriate p11-kit setup described later on. We can now use our token to sign things (help for using rfc7512 appears later)!

sign-efi-sig-list -t ‘2017-01-01 00:00:00’ -o PK platformkey.esl platformkey.forsig
smime_token_pk -noattr -sign -binary -in platformkey.forsig -text -out platformkey.signed -signer platformkey.crt -outform DER -md sha256
sign-efi-sig-list -i platformkey.signed -t ‘2017-01-01 00:00:00’ PK platformkey.esl platformkey.auth

This creates an auth file with our platform key that can be loaded into firmware. The smime separation steps came from someone I only known as Trevor commenting on James Bottomley's blog.

It is also additionally possible to simply skip the timestamping phase and sign directly with smime. Those wanting to sign in one line without hardware should run:

sign-efi-sig-list -g `cat uuid` -a -t ‘2017-01-01 00:00:00’ -c platformkey.crt -k platformkey.pri PK pk.esl platformkey.auth

Next we create an empty signature list signed by our platform key. This allows us to remove the platform key. On my laptop this is an optional nicety, since the firmware can do it. However, if your platform cannot do this then you need this option:

> empty_pk.esl
sign-efi-sig-list -t ‘2017-01-01 00:00:00’ -k platformkey.pri -c platformkey.crt PK empty_pk.esl empty_pk.auth

Ironically, if you actually need this option your hardware isn't Windows logo compatible!

Next step, we create an ESL and AUTH pair for our KEK and image key:

cert-to-efi-sig-list -g `cat uuid` kek.crt kek.esl
cert-to-efi-sig-list -g `cat uuid` secureboot.crt secureboot.esl

and now you sign the KEK list with your platform key as follows:

sign-efi-sig-list -t ‘2017-01-01 00:00:00’ -o KEK kek.esl kek.forsig
smime_token_pk -noattr -sign -binary -in kek.forsig -text -out kek.signed -signer platformkey.crt -outform DER -md sha256
sign-efi-sig-list -i kek.signed -t ‘2017-01-01 00:00:00’ KEK kek.esl kek.auth

It is vitally important you sign this with your platform key - you should see my earlier explanation in action right here!

Finally we do the same thing, but with the image key. Here goes:

sign-efi-sig-list -t ‘2017-01-01 00:00:00’ -o DB secureboot.esl secureboot.forsig
smime_token_kek -noattr -sign -binary -in secureboot.forsig -text -out secureboot.signed -signer kek.crt -outform DER -md sha256
sign-efi-sig-list -i secureboot.signed -t ‘2017-01-01 00:00:00’ DB secureboot.esl secureboot.auth

Again, this is signed with your KEK, which is allowed to update the DB and DBX signature lists / databases.

Sign bootloader, keytool

How (and whether this is even necessary) you do this depends on the outcome you want to achieve. If you intend to remove the Microsoft UEFI CAs from your KEK and DB, then you will need to replace the signatures on your bootloader as only things signed with your image key will be allowed to boot.

I have placed KeyTool.efi in my working directory ~/efi-secure-boot. For the Fedora boot process, the shim requires signing. This in turn holds its own key database, which signs the kernel and modules built as part of Fedora. I haven't, but if you intend to replace these you will need to compile the kernel yourself for each update.

The easy way, using files on disk, is to sign the relevant files like so:

sudo cp /boot/efi/EFI/BOOT/BOOTX64.EFI ./BOOTX64.EFI
sudo cp /boot/efi/EFI/fedora/shim.efi ./shim.efi
sbsign --key /home/antony/efi-secure-boot/secureboot.pri --cert /home/antony/efi-secure-boot/secureboot.crt --output SHELL-signed.efi SHELL.EFI
sbsign --key /home/antony/efi-secure-boot/secureboot.pri --cert /home/antony/efi-secure-boot/secureboot.crt --output shim-signed.efi shim.efi
sbsign --key /home/antony/efi-secure-boot/secureboot.pri --cert /home/antony/efi-secure-boot/secureboot.crt --output BOOTX64-signed.efi BOOTX64.EFI
sbsign --key /home/antony/efi-secure-boot/secureboot.pri --cert /home/antony/efi-secure-boot/secureboot.crt --output KeyTool-signed.efi KeyTool.efi

The install steps for these are part of "copy to efi sp".

Sbsign has no options for the use of hardware tokens, so we need to fall back to pesign if you wish to install to a token. Refer to Protecting your Keys for NSS-specific stuff here.

Assuming our keys are stored in a module named "CardOS" on a token named "UEFI" and our certificate has the common name "Secureboot Signing Key 2017" then the following command should be run to sign a binary:

pesign --certdir ~/efi-secure-boot/nss -t UEFI -i SHELL.EFI -s -c "Secureboot Signing Key 2017" -o SHELL-signed.EFI

Repeat this for all binaries that need signing.

Sign modules

This step is optional. Any custom modules you have will need signing each time the kernel is updated. I wrote the following script to do this:

#!/bin/sh

KEYDIR=/home/antony/efisb-keys
PRIVKEY="pkcs11:object=Antony%20Vennard%20Whiteprophet%20Secureboot%20Signing%20Key%202016"
PUBKEY=$KEYDIR/whiteprophet_db.cer
# SIGN_CMD=/usr/src/kernels/$(uname -r)/scripts/sign-file 
SIGN_CMD=/root/sign-file

rm -rf /root/signmodules
mkdir /root/signmodules
pushd /root/signmodules

cp /lib/modules/$(uname -r)/extra/wl/wl.ko .
cp /lib/modules/$(uname -r)/misc/vmmon.ko .
cp /lib/modules/$(uname -r)/misc/vmnet.ko .

$SIGN_CMD sha256 $PRIVKEY $PUBKEY wl.ko
$SIGN_CMD sha256 $PRIVKEY $PUBKEY vmmon.ko
$SIGN_CMD sha256 $PRIVKEY $PUBKEY vmnet.ko

cp -f wl.ko /lib/modules/$(uname -r)/extra/wl/wl.ko
cp -f vmmon.ko /lib/modules/$(uname -r)/misc/vmmon.ko
cp -f vmnet.ko /lib/modules/$(uname -r)/misc/vmnet.ko

popd

This should be run as the root user and is very straightforward - it copies the unsigned modules from their place in /lib, signs them and reinserts them into /lib.

Update March 2017: If you follow the updated instructions on preparing your smart card, you can use p11-kit instead. OpenSC's engine-pkcs11 will load modules directly from p11-kit, meaning you do not need to modify sign-file and it will work straight out of the box.

Deprecated instructions: Unfortunately, the linux kernel sign-file has a bug where it does not correctly use SSL engines. I have patched sign-file in this repository on github.

Copy to EFI SP

We are now ready to begin modifying our EFI System Partition, by installing our signed bootloaders and loading our keys. To do this:

sudo mkdir /boot/efi/secureboot
sudo cp ./KeyTool-signed.efi /boot/efi/secureboot/KeyTool.efi
sudo cp ./shim-signed.efi /boot/efi/EFI/fedora/shim.efi
sudo cp ./BOOTX64-signed.efi /boot/efi/EFI/BOOT/BOOTX64.EFI
sudo cp ./SHELL-signed.efi /boot/efi/EFI/BOOT/SHELL.EFI

This copies the files to the relevant places on the UEFI system partition. Note that if the shim is updated, you will need to repeat this process. If you want to prevent that (not recommended), do this:

sudo dnf lock shim

This will force DNF to prevent modifications to the shim package when you run dnf upgrade.

We are going to use the UEFI shell to launch KeyTool.efi. Not specifically necessary, but I wanted the shell around for other testing. So, first up reate the shell entry:

sudo efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/BOOT/SHELL.EFI --label "UEFI SHELL"

Now if you run the tool with no arguments as root you should have something like this:

$ sudo efibootmgr
BootCurrent: 0001
Timeout: 0 seconds
BootOrder: 0001,0000,0002
Boot0000  Windows Boot Manager
Boot0001* Fedora
Boot0002* UEFI SHELL

Perfect. Now, for using the shell on my system I needed to make the console font actually usable, due to the HIDPI display. To do that, edit /boot/efi/startup.nsh as root to contain:

mode 100 31

Note this needs to be in a specific text format. This should read:

$ file startup.nsh
startup.nsh: Little-endian UTF-16 Unicode text, with no line terminators

Working out how to set this up is an exercise left to the reader.

Finally, let us copy all our esl and auth files to the SP as well. We technically do not need the ESL files, but you will see later depending on the order you choose, they can be used in place of auth files. So, here we go:

sudo cp *.esl /boot/efi/secureboot
sudo cp *.auth /boot/efi/secureboot

Key modifications on firmware

We are now ready to actually reboot! Let's do that, and enter the firmware utility. We are going to enter the the Secure Boot section and then use the Expert Key Usage to clear the platform key. You additionally have the choice here to clear the KEK and DB keys too. I personally did. The implications are this:

Remove at least the platform key and your choice regarding the KEK and DB keys, and leave secure boot off for now.

While we are here - it is vitally important that you set a firmware password. Not doing so allows anyone with physical access to trivially reset your platform key or just disable secure boot. You want to make them at least get out an SPI flash tool!

Next - reboot into the boot menu. You should now have something like this:

Screenshot showing UEFI boot menu with secure boot off

Select the UEFI Shell entry and allow startup.nsh to run - pressing any key escept esc runs it immediately. Then run the following commands

fs0:
cd secureboot
KeyTool.efi

As shown below (with intermediate ls):

Screenshot showing UEFI shell in fs0:\secureboot

This runs KeyTool, which looks like this:

Screenshot of keytool, with edit-keys selected

Note the words "platform is in setup mode" - since we have deleted the platform key this is required as per the UEFI spec as discussed above.

The next step is to perform the enrollment, which is fairly uneventful and mechanical. In the edit keys menu you should be able to go to each of the key categories in turn. To do this, proceed as follows:

  1. Go to the Platform Key option and select "replace platform key". Navigate to fs:0\secureboot and select your platformkey.auth. As soon as this is done the platform will switch to user mode.
  2. Go to the Key Exchange Key option and select "add key". Navigate to fs0:\secureboot and select kek.auth.
  3. Go to the DB signature list option and select "add key". Navigate to fs0:\secureboot and select secureboot.auth.

You can actually proceed in a different order. Until the platform key has been loaded, you can load any of the other keys in any order directly from their esl file, as the signature checks will not be enforced. Once the platform key is loaded, you must proceed in this order.

Below is an example screenshot with the Microsoft keys left in for the DB entry:

Screenshot of the Allowed Signatures database being edited in keytool

and a picture of the platform key replacement dialog:

Screenshot of KeyTool's Platform Key replacement dialog with warning about user mode

We are now ready to reboot again. Let's do that. Reboot into firmware, enter your firmware password, set uefi boot to enabled and then reboot again.

Now, your boot screen should look something like this:

Screenshot of the boot menu with secure boot on

Select Fedora. If you did everything correctly, the system will boot. Congratulations, secure boot is enabled. To check this, do the following:

$ efi-readvar
Variable PK, length 883
PK: List 0, type X509
    Signature 0, size 855, owner 8a4d80e9-1fff-490a-9537-7725ea401f77
        Subject:
            CN=Antony Vennard Whiteprophet Platform Key 2016
        Issuer:
            CN=Antony Vennard Whiteprophet Platform Key 2016
Variable KEK, length 865
KEK: List 0, type X509
    Signature 0, size 837, owner 8a4d80e9-1fff-490a-9537-7725ea401f77
        Subject:
            CN=Antony Vennard Whiteprophet KEK 2016
        Issuer:
            CN=Antony Vennard Whiteprophet KEK 2016
Variable db, length 903
db: List 0, type X509
    Signature 0, size 875, owner 8a4d80e9-1fff-490a-9537-7725ea401f77
        Subject:
            CN=Antony Vennard Whiteprophet Secureboot Signing Key 2016
        Issuer:
            CN=Antony Vennard Whiteprophet Secureboot Signing Key 2016
Variable dbx, length 2343
dbx: List 0, type X509
    Signature 0, size 1663, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows PCA 2010
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
dbx: List 1, type SHA256
< snip >

you can also see what certificates were detected on boot:

$ dmesg | grep Loaded cert
[    0.761784] EFI: Loaded cert 'Antony Vennard Whiteprophet Secureboot Signing Key 2016: 2d7ed9070075c9c23ef13f176701e68d84d708e7' linked to '.builtin_trusted_keys'
[    0.763389] EFI: Loaded cert 'Fedora Secure Boot CA: fde32599c2d61db1bf5807335d7b20e4cd963b42' linked to '.builtin_trusted_keys'
[    0.764699] EFI: Loaded cert 'Microsoft Windows PCA 2010: d14fa98a0708cef4241898e500fff3d6791d37bc' linked to '.system_blacklist_keyring'

What if I left the Microsoft Keys in place?

I did not (the above are outputs from my system) but assuming you did, you would see:

$ efi-readvar
Variable PK, length 883
PK: List 0, type X509
    Signature 0, size 855, owner 8a4d80e9-1fff-490a-9537-7725ea401f77
        Subject:
            CN=Antony Vennard Whiteprophet Platform Key 2016
        Issuer:
            CN=Antony Vennard Whiteprophet Platform Key 2016
Variable KEK, length 2425
KEK: List 0, type X509
    Signature 0, size 1532, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation KEK CA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
KEK: List 1, type X509
    Signature 0, size 837, owner 8a4d80e9-1fff-490a-9537-7725ea401f77
        Subject:
            CN=Antony Vennard Whiteprophet KEK 2016
        Issuer:
            CN=Antony Vennard Whiteprophet KEK 2016
Variable db, length 4046
db: List 0, type X509
    Signature 0, size 1515, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
db: List 1, type X509
    Signature 0, size 1572, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
db: List 2, type X509
    Signature 0, size 875, owner 8a4d80e9-1fff-490a-9537-7725ea401f77
        Subject:
            CN=Antony Vennard Whiteprophet Secureboot Signing Key 2016
        Issuer:
            CN=Antony Vennard Whiteprophet Secureboot Signing Key 2016
Variable dbx, length 2343
dbx: List 0, type X509
    Signature 0, size 1663, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows PCA 2010
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
dbx: List 1, type SHA256
< snip >

and

$ dmesg | grep Loaded cert
[    0.754704] EFI: Loaded cert 'Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53' linked to '.builtin_trusted_keys'
[    0.756192] EFI: Loaded cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4' linked to '.builtin_trusted_keys'
[    0.760148] EFI: Loaded cert 'Antony Vennard Whiteprophet Secureboot Signing Key 2016: 2d7ed9070075c9c23ef13f176701e68d84d708e7' linked to '.builtin_trusted_keys'
[    0.761671] EFI: Loaded cert 'Fedora Secure Boot CA: fde32599c2d61db1bf5807335d7b20e4cd963b42' linked to '.builtin_trusted_keys'
[    0.763101] EFI: Loaded cert 'Microsoft Windows PCA 2010: d14fa98a0708cef4241898e500fff3d6791d37bc' linked to '.system_blacklist_keyring'

Do I have to do any maintenace?

As mentioned in the above, I would make specific note of two things:

  1. If the shim is ever updated you will need to resign it.
  2. You need to sign any custom modules every time you upgrade your kernel (i.e. kernel modules built by dkms or via third party products, such as VMware Workstation). This is not ideal and there is no real (secure) way to automate it, sadly.

I am currently undertaking a lot of smartcard research; while not directly relevant to UEFI secure boot, it would be a shame not to further protect the keys being used.

The specifics of your smartcard are certainly out of scope. I am going to assume you have PKCS#11-supporting libraries for your smartcard or device and that you have aliased

alias p11=pkcs11-tool -M /path/to/your/pkcs11.so

for the following steps:

  1. Create an NSS database (you can also use these steps instead of sbsign if you prefer pesign, without needing hardware. Also creating a database isn't strictly necessary):

    mkdir ~/efi-secure-boot/nss
    certutil -d ~/efi-secure-boot/nss -N
    

    Note: we are creating a separate database to avoid affecting any configuration you already have in your browser / email client.

  2. Prepare and load your token. My token actually only loads successfully from Windows due to a buggy PKCS#11 library. However if you did want to load from Linux because you are using a token that works, then follow the instructions in this section. If you use an alternative method to load your token you can try part 4 to check it has been successful.

    Initialize your token (options my vary):

    p11 --init-token --slot 1 -l --login-type so --init-pin
    

    Convert your keys to DER format too:

    openssl rsa -in platformkey.pri -out form DER -out platformkey.key
    openssl rsa -in kek.pri -out form DER -out kek.key
    openssl rsa -in secureboot.pri -out form DER -out secureboot.key
    

    Then load the lot to your token:

    p11 -l -p <pin> -w key -a <label> --usage-sign --usage-decrypt --private -i platformkey.key  
    p11 -l -p <pin> -w key -a <label> --usage-sign --usage-decrypt --private -i kek.key  
    p11 -l -p <pin> -w key -a <label> --usage-sign --usage-decrypt --private -i secureboot.key  
    p11 -l -p <pin> -w cert -a <label> -i platformkey.cer
    p11 -l -p <pin> -w cert -a <label> -i kek.cer
    p11 -l -p <pin> -w cert -a <label> -i secureboot.cer
    
  3. Add your PKCS#11 module to your database:

    modutil -dbdir ~/efi-secure-boot/nss -add CardOS -libfile /opt/cardos/lib/libcardos11.so
    

    Now when you run

    $ modutil -dbdir ~/efi-secure-boot/nss -list
    
    Listing of PKCS #11 Modules
    -----------------------------------------------------------
      1. NSS Internal PKCS #11 Module
         slots: 2 slots attached
        status: loaded
    
         slot: NSS Internal Cryptographic Services
        token: NSS Generic Crypto Services
    
         slot: NSS User Private Key and Certificate Services
        token: NSS Certificate DB
    
      2. CardOS
        library name: /opt/cardos/lib/libcardos11.so
         slots: 1 slot attached
        status: loaded
    
         slot: Gemalto PC Twin Reader (97AD6082) 00 00
        token: UEFI
    -----------------------------------------------------------
    
  4. You are now ready to use pesign. You can check your certificates are detected with:

    $ certutil -d ~/efi-secure-boot/nss -h UEFI -L
    
    Certificate Nickname                                         Trust Attributes
                                                                 SSL,S/MIME,JAR/XPI
    
    Enter Password or Pin for "UEFI":
    UEFI:Antony Vennard Whiteprophet Platform Key 2016           u,u,u
    UEFI:Antony Vennard Whiteprophet KEK 2016                    u,u,u
    UEFI:Antony Vennard Whiteprophet Secureboot Signing Key 2016 u,u,u
    

    which you could also check with:

    $ p11 --login -p <pin> -O
    Using slot 0 with a present token (0x1)
    Private Key Object; RSA 
      label:      Antony Vennard Whiteprophet Platform Key 2016
      ID:         1368a2b2abeab7bb1e0f3054059d6aad
      Usage:      decrypt, sign, unwrap, derive
    
    Private Key Object; RSA 
      label:      Antony Vennard Whiteprophet KEK 2016
      ID:         bbd6608d6fb11e29109637028e29d610
      Usage:      decrypt, sign, unwrap, derive
    
    Private Key Object; RSA 
      label:      Antony Vennard Whiteprophet Secureboot Signing Key 2016
      ID:         1e8b010bd1a7ec5bdf8b47bdbe9c6ada
      Usage:      decrypt, sign, unwrap, derive
    
    Certificate Object, type = X.509 cert
      label:      Antony Vennard Whiteprophet Platform Key 2016
      ID:         1368a2b2abeab7bb1e0f3054059d6aad
    Certificate Object, type = X.509 cert
      label:      Antony Vennard Whiteprophet KEK 2016
      ID:         bbd6608d6fb11e29109637028e29d610
    Certificate Object, type = X.509 cert
      label:      Antony Vennard Whiteprophet Secureboot Signing Key 2016
      ID:         1e8b010bd1a7ec5bdf8b47bdbe9c6ada
    
  5. Set up OpenSSL to use your token's engine, both for openssl smime commands and for sign-file.

    Updated March 2017: the best way to achieve this is to plug your PKCS#11 module into p11-kit. To do this, you need the configuration instructions, which boils down to creating either /etc/pkcs11/modules/yourlib.module or ~/.pkcs11/modules/yourlib.module. For my use case, I created /etc/pkcs11/modules/cardos.module with the contents:

    module: /opt/cardos/lib/libcardos11.so
    managed: yes
    

    This will then load the PKCS#11 library into all applications that use p11-kit.

    Many thanks to David Woodhouse for his advice and feedback regarding using p11-kit.

    Deprecated instructions: I created /root/uefisb/openssl.cnf for sign-file to use and ~/efi-secure-boot/openssl.cnf for openssl smime. These both contain:

    openssl_conf = openssl_init
    engines = engine_section
    
    [openssl_init]
    engines = engine_section
    
    [engine_section]
    pkcs11 = pkcs11_cardos
    
    [pkcs11_cardos]
    engine_id = pkcs11
    dynamic_path = /usr/lib64/openssl/engines/libpkcs11.so
    MODULE_PATH = /opt/cardos/lib/libcardos11.so
    init = 0
    

    The remaining instructions are still valid:

    You can then use this for signatures as detailed above using for example:

    alias smime_token="openssl smime -engine pkcs11 -keyform engine -inkey 'pkcs11:object=Antony%20Vennard%20Whiteprophet%20Secureboot%20Signing%20Key%202016'"
    

    The -inkey option is defined in rfc7512. If you want to find yours without too much thought, you can also try:

    p11tool --provider=... --list-all --login
    

Since I am using Atos Cards for this particular use, I checked with the Windows viewer just to make sure everything was there.

Screenshot of CardOS API Viewer showing three keys on the card

End

That is it. You should now have a secure boot system with optionally protected keys that only you can use.

Revisions

  1. 2017-03-09 - updated to incorporate the advice of David Woodhouse on using p11-kit instead of the horribleness of OpenSSL module configuration.
  2. 2017-01-23 - initial post.