Original post

Secure boot tooling is terrible, can we do better?

Currently the most widely used tooling for secure boot is the Ubuntu sbsigntools and efitools. If you are currently using secure boot both of these packages are probably installed on your system. Both of them support the basics of generating signature lists and signing the EFI variables with certificates, but they still have differences which is a source of confusion.

efitools has 3 different ways of generating signature lists, cert-to-efi-hash-list, cert-to-sig-list and hash-to-efi-sig-list. “Luckily” there are man pages you can read which assumes you have some familiarity with UEFI itself.

sbsigntools has only sbsiglist which is mostly fine, but has non-obvious functionality. How do you sign the checksum of an EFI executable as an example? I figure that out after some searching on the github code search:

sha256sum file.efi | awk '{printf "0x"$1}' | xxd -r -g 1 -c 64  > sha256.bin
sbsiglist --type sha256 --output sha256.bin.siglist sha256.bin

I think it works, I haven’t tried yet. However it is not really a nice experience to use, and the documentation of the tool is severely lacking. The functionality should be obvious from the get . Why do you want this? A nice example is to keep a list of known bad checksums of things you don’t want booted on your system, without having to sign all of it.

Secure boot needs key enrollment if you want to use your own keys. sbsigntool got their undocumented sbkeysync utility which can live enroll secure boot keys it reads from a database in either /etc/secureboot/keys or /usr/share/secureboot/keys. Meanwhile efitools supports live enrollment through efi-updatevar, or if you can use the bundled KeyTool.efi which you can boot and load your keys from.

But which do you use for signing EFI executables? Only sbsigntool implements this! What you end up with is 3 different ways of enrolling keys, between 2-4 different ways of creating signature lists depending on your usage, and only one way to sign EFI executables.

This is probably fine and dandy for technical people that can be bothered to invest some time into their tools. But this isn’t a great user experience for most people, one which I’ll wager is a roadblock for people to adopt secure boot. It is a shame, people should be capable of rolling their own secure boot keys without having to understand UEFI in detail to do so.

There are multiple guides and instruction on the Gentoo wiki, Arch Linux Wiki, by Rod Smith and Greg Kroah-Hartman. These are great sources of information for how things work. But the inherent complexity of these articles underlines the problem confusing tooling.

We should do better.

sbctl

Around two years ago I tried to roll my own secure boot keys and found the entire experience maddening. The madness promptly made me write 400 lines of bash for efi-roller. The project enabled me to keep track of files I needed to sign between kernel upgrades, systemd upgrades and fwupdmgr upgrades. I didn’t need to conjure up some complicated hooks for my package manager to ensure I had all the files signed.

It has served me well quite well. However bash gets fairly limiting when you want some trivial option parsing in-between subcommand parsing, which I needed for my EFISTUB generation. In the end I decided it was better to start from scratch with Go to get some sanity back and wound up with sbctl.

sbctl aims to be a easy interface for managing your own secure boot keys and follows in the footsteps of efi-rooler.

$ sbctl create-keys
==> Creating secure boot keys...
  -> Using UUID d6e9af79-c6b5-4b43-b893-dbb7e6570142...
==> Signing [...]/keys/PK/PK.der.esl with [...]/keys/PK/PK.key...
==> Signing [...]/keys/KEK/KEK.der.esl with [...]/keys/PK/PK.key...
==> Signing [...]/keys/db/db.der.esl with [...]/keys/KEK/KEK.key...

$ sbctl enroll-keys
==> Syncing /usr/share/secureboot/keys to EFI variables...
==> Synced keys!

$ sbctl sign /boot/EFI/BOOT/BOOTX64.EFI
  -> Signing /boot/EFI/BOOT/BOOTX64.EFI...

No openssl commands. No cert-to-efi-sig-list or sbsiglist commands. And no need to figure out how to enroll keys. It just works. And this is how simple it ought to be.

sbctl can also track which files you would like to sign. This can help you make hooks towards your package manager which signs them between system upgrades.

λ ~ » sudo sbctl list-files 
==> File: /efi/EFI//linux-linux.efi
==> File: /usr/lib/fwupd/efi/fwupdx64.efi
  -> Output: /usr/lib/fwupd/efi/fwupdx64.efi.signed
==> File: /boot/EFI/BOOT/BOOTX64.EFI
==> File: /boot/EFI/arch/fwupdx64.efi
==> File: /boot/EFI/systemd/systemd-bootx64.efi
==> File: /boot/vmlinuz-linux

λ ~ » sudo sbctl sign-all
==> /boot/vmlinuz-linux has been signed...
==> /efi/EFI/Linux/linux-linux.efi has been signed...
  -> Signing /usr/lib/fwupd/efi/fwupdx64.efi...
==> /boot/EFI/BOOT/BOOTX64.EFI has been signed...
==> /boot/EFI/arch/fwupdx64.efi has been signed...
  -> Signing /boot/EFI/systemd/systemd-bootx64.efi...

The intention is to have a nice and practical utility which should be the center of the secure boot keys. It should enroll them, rotate the keys when you need to, and ensure they are secured well enough.

The code can be found on github: https://github.com/Foxboron/sbctl

I haven’t cut a first release yet as I would like some feedback on the general design of the tool. It would also be interesting to lean how well it fits into other peoples usecases for secure boot and what it covers and don’t cover. Another problem is that sbctl inherits the one of the problems discussed earlier.

goefi

sbctl still relies on sbsigntools for all of it’s functionality. Actively shelling out to tools is easy, but it’s not very satisfying when we are writing in a system language. This lead me to downloading the 2558 page long “Unified Extensible Firmware Interface Specification Version 2.8” which I started reading a month ago.

It’s frankly interesting when you realize how this work on Linux. Admittedly, I haven’t done a lot of low-level programming before. I thought you had to do some weird ioctl or syscalls to modify EFI variables, which during a weak moment made me consider properly learning C. In practise you are just reading and writing files under /sys/firmware/efi/efivars, which any language should capable of doing.

So far I have managed to read most of my Boot* entries, create EFI signature lists and have also reimplemented the UEFI variable signing, which you can now use replace the base functionality of sbsiglist and sbvarsig. Quite a bit of code left until I can use it for sbctl, but it’s starting to shape up nicely.

The library code is currently on github: https://github.com/Foxboron/goefi.

The end goal is to have a Go library capable of parsing and writing most of the structs from the UEFI specification in an API friendly way. This could hopefully enable software authors to write more user friendly secure boot tooling.

If reading asn1parse output and looking at hexdumps sounds like an evening well spent, or you enjoy figuring out nice top-level APIs, please do reach out and take a look at the code!

It would also very much like to hear about more use cases for sbctl and what people currently are doing with existing secure boot tooling.

Back to posts