UEFI Secure Boot

Introduction

UEFI secure boot is a feature described by the latest UEFI specification (2.3.1c) which is available from the UEFI Forum Site. There have also been numerous blog posts about how UEFI secure boot works (e.g. here or here), so it will not be described here further. The purpose of this site is to keep relevant information for enabling people to play with secure booting systems. The goal is to have a working qemu system with the UEFI secure boot bios as well as various repositories for efi binary signing tools. This should enable people to produce their own boot media for secure boot systems

Intel and TianoCore

Intel has produced a project called TianoCore as an open firmware reference implementation of UEFI. One of the sub projects within TianoCore is OVMF which stands for Open Virtual Machine Firmware. It is OVMF that we are using to produce the virtual machine image for qemu that will run the UEFI secure boot environment. TianoCore secure boot is only really working as of version r13466 of the svn repository. This version has not yet been released as a downloadable zip file.

Since building TianoCore can be a bit of a challenge, a version supporting secure boot has been uploaded to the openSUSE build server here. The particular package you need for the virtual machine firmware is the OVMF rpm (download).

Booting the TianoCore Virtual Machine Image

After installing the rpm, execute

qemu-system-x86_64 -L /usr/share/qemu-ovmf/bios

And the secure boot bios will come up.  Unfortunately, there is currently no way to save the EFI nvram with qemu, so the platform will come up in pristine state with each new invocation of qemu.

If your platform is kvm capable, you may also use

qemu-kvm -L /usr/share/qemu-ovmf/bios

Booting Linux with TianoCore

By default, all the standard methods (elilo, grub or even native boot using the linux kernel efi stub) work.  However, TianoCore itself has a problem (as of release r13478) in that the ACPI CRS tables seem to be wrong, which causes the booting kernel to remap the EFI framebuffer and consequently be unable to attach (so you basically get no output on the screen once the kernel has booted).  The workarounds for this are to specify

pci=nocrs

on the kernel boot line.  This causes the kernel to ignore the ACPI CRS tables and thus attach normally to the EFI framebuffer.  Thanks to Peter Jones for this. Or to boot the kernel using the serial console

console=ttyS0

Playing with Secure Boot in Tianocore

By default, TianoCore boots up into Setup Mode, meaning the platform is not provisioned with any keys and the user can take control.  To take control, go to the EFI menu screens (type exit if you’re at the efi boot prompt) select the “Device Manager” entry, then “Secure Boot Configuration”.  Here you will see the status of the Secure Boot flag (“Attempt Secure Boot”) and the platform mode.  Setting the platform from “Standard Mode” to “Custom Mode” will allow you to edit the keys. Once the platform is in “Custom Mode”, a “Custom Secure Boot Options” menu will appear and you will be able to manipulate the four sets of key databases from here.  The format of all key files for openssl generated keys is DER format (by default openssl generates PEM format).  Note that the KEK, db and dbx options will ask you for a GUID as well as a key file.  The GUID is the platform’s way of identifying the key.  It serves no purpose other than for you to tell which key is which when you delete them (it’s not used at all in signature verification).  By default, since GUIDs aren’t really human readable, I just ignore this and the GUID is set to all zeros.

Because there’s no way to save the nvram area, these variables get reset on each invocation of qemu as well and entering three sets of keys with each invocation of qemu gets very old, very fast.  The repository

http://git.kernel.org/?p=linux/kernel/git/jejb/efitools.git;a=summary

contains a set of tools to help with this. Note that there’s a bug in gnu-efi earlier than 3.0q so you must have this installed if you want to build efi binaries that are capable of being signed (there’s a rpm for it in the openSUSE build service UEFI project)

If you type make, it will generate a set of keys and place them into a binary called LockDown.efi.  Execution of this efi binary will provision all the keys and place the platform into Secure Boot enabled User Mode (from this point on, it will only execute signed efi binaries)

UEFI Keys and openssl

The UEFI specification has several possible key types, but X509 is the most useful to us, since it’s the easiest to generate with openssl (The platform key is required to be X509 anyway).  Note that when the UEFI spec says “key”, it doesn’t mean the same as an openssl key.  In UEFI parlance, the “key” or “public key” means the public part (i.e. the X509 certificate); openssl uses the term key to mean the private key used to sign stuff with.

All keys (PK, and X509 keys in KEK and db) should be X509 CA keys (i.e. self signed).  The reason for this is that the platform will verify a key back to its root of trust.  It is possible to provision a signature chain going back to the root of trust, but it’s not easy, so starting with self signed CA certificates is easiest.  Note that from the CA, you can issue signing keys, but those keys cannot themselves then be used to create subordinate signing keys because the intermediate trust certificate will be missing from the signing chain.

For more details on all the keys and how they work, see this post.

Creating UEFI CA keys

The easiest way is to use the x509 CA creation command

openssl req -new -x509 -newkey rsa:2048 -keyout PK.key -out PK.crt -days <length
> -subj “/CN=<my common name>/”

Will create two files, PK.crt (which is the public certificate we’ll use as the UEFI key) and PK.key which is the private signing key (note: the UEFI spec mandates that all X509 keys be 2048 bit rsa keys).  Fill in <days> with how long you want the certificate to be valid for and <my common name> with whatever information you want the common name to be (UEFI doesn’t use this, but it’s customary for every X509 certificate to have at least a common name).  We’re still not quite done, because the keys must be in DER form.  Conversion is done like this

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

Note that the .cer extension is what tells UEFI that the file contains an x509 key, so you must use it.

 Signing UEFI binaries

There are two sets of tools available in Linux for this:

  1. Jeremy Kerr’s sbtools available from git://kernel.ubuntu.com/jk/sbsigntool
  2. Peter Jones’ pesign available from git://github.com/vathpela/pesign.git

The opensuse build service UEFI project contains both, but I’ve mostly played with sbsigntools which is what’s described below.

Once you have your .crt and .key files, you can sign efi binaries like this

sbsign –key KEK.key –cert KEK.crt –output HelloWorld-signed.efi HelloWorld.efi

If you’re building this yourself, you must have version 0.3 or later of the sbsigntools because earlier versions wouldn’t correctly sign efi binaries generated by gnu-efi.

22 thoughts on “UEFI Secure Boot

  1. Finnbarr P. Murphy

    A clarification. You state that the PK (The platform key) is “required to be X509″. This is not a UEFI Specification requirement but rather a Microsoft requirement. In fact the UEFI Specification recommends RSA 2048 (see 2.3.1, Errata C, Section 27.5, top of page 1451.)

    Reply
    1. jejb Post author

      I’m just going by the code in Tianocore as I look at it. The way it’s coded (doubtless according to the MS specs) is that the PK can *only* be X509. The KEK can contain both X509 and RSA2048

      Reply
  2. Seth

    “CRS” is not an ACPI table type — it’s a METHOD for particular devices in the ACPI DSDT/SSDT tables. Please correct your post.

    Reply
  3. xsoliman3

    Presumably the ‘OVMF bios’ binary would run on the Windows version of Qemu, if I could extract it from the .rpm

    I already have qemu running the older (duet) efi-bios

    “You must use the CVS version of QEMU to use the BIOS (the version
    0.9.0 won’t work).

    Replace the default QEMU BIOS (usually in /usr/local/share/qemu) by
    the file ‘bios.bin’. “

    Reply
  4. Pingback: No Boot on UEFI box (Samsung 350V)

  5. Tony

    I have a problem: I’ve tried to boot windows 8 32 bit with tianocore but it gives me the problem related to ACPI and CRS tables, so it results that the (virtual) Machine is not ACPI-capable. Any suggestions to solve this problem?

    Reply
  6. Darek

    I would like to see such solutions in the future:
    * You have UEFI + Secure Boot turned on – Linux auto repair system like Windows 8 does.
    * You have UEFI + Secure Boot turned off – Linux behave normally, do nothing.
    …instead of just passing by something (UEFI + Secure boot) you can just turn off in BIOS ;)

    The solution should be automatic and simple to work for all “UEFI/Secure Boot” users, even those non-technical.

    Reply
  7. giggler

    You covered creation of PK with openssl but what about the KEK that signed by PK? sounds like a dumb question I know but I want to make sure I have the process clear.

    Reply
    1. jejb Post author

      It’s the same process: all keys in the system may be self signed (they don’t have to be, but self signed certificates are just the easiest ones to describe how to create), so you use the PK process but substitute PK< ->KEK

      openssl req -new -x509 -newkey rsa:2048 -keyout KEK.key -out KEK.crt -days < length> -subj “/CN=< my common name>/”

      Reply
    1. jejb Post author

      I looked into this. The first thing I had to do was unbreak all the builds which had collapsed due to various bitrot effects. All the necessary UEFI pieces should now be building … I’m still testing them, however.

      By “latest” I assume you mean UDK 2014 which is r15322? Once I complete testing of the current openSUSE 13.1 OVMF, I’ll see if I can do that, but it may take some time given the eccentric and finicky way edk2 builds.

      Reply
      1. Maarten

        Hi James, your efforts are very much appreciated. I’m quite keen on attempting legacy-free device passthrough as per here. The pre-built binaries linked to on that page for Fedora are what I’d be interested in for openSUSE. It would be good to have the following commit in there: link as I’m using the Q35 chipset model in most VMs.

        Reply
  8. 0ffer

    Your HashTool.efi can browse EFI media in Text USer Interface.
    It seems that many users would be useful Run.efi for browse EFI media and launch EFI applications.

    Reply
  9. Dave

    Unable to affect keys in user mode

    I downloaded and built the latest version (v1.4.2+2014-05-27); deleted all keys from UEFI; ran LockDown.efi from EFI Shell and reset system.
    Booted off EFI USB stick with signed shell (efi\boot\bootx64.efi) successfully. Secure boot is now enabled and in user mode – only signed executables are allow to run.

    I am unable to append a new key to the db now I am in user mode; I get a Security Violation error (26).

    i.e.
    UpdateVars-signed.efi -a db DB2.auth

    The DB2 key and authentication file was generated as follows:

    openssl req -new -x509 -newkey rsa:2048 -subj “/CN=DB2/” -keyout DB2.key -out DB2.crt -days 3650 -nodes -sha256
    cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc DB2.crt DB2.esl
    sign-efi-sig-list -a -c KEK.crt -k KEK.key db DB2.esl DB2.auth

    I also tried using the KeyTool-signed.efi with same problem.

    I can however append the DB2 key when in setup mode and update the variables manually:

    UpdateVars.efi db DB.auth
    UpdateVars.efi -a db DB2.auth
    UpdateVars.efi KEK KEK.auth
    UpdateVars.efi PK PK.auth

    I have also tried this under Ubuntu 14.04 (signed grubx64.efi) using the following:

    sudo efi-updatevar -a -c DB2.crt -k KEK.key db

    This time I get a Cannot write to db, wrong filesystem permissions

    Also I am unable to switch from user to setup mode using the noPK.auth file either using UpdateVar or KeyTool

    e.g.
    UpdateVars-signed.efi PK noPK.auth

    Do you have any ideas as to why I am having no success?

    Reply
    1. Dave

      I have had a response from AMI regarding this issue, does this help …

      It looks like the Linux utility is not quite making the signature data per spec. This is listed in section 7.2.1 of the UEFI spec. The following two bolded lines from this section are not compatible:
      4. Construct a DER-encoded PKCS
      Construct a DER-encoded PKCS #7 version 1.5 SignedData (see [RFC2315]) with the signed content as follows:
      a SignedData.version shall be set to 1
      b SignedData.digestAlgorithms shall contain the digest algorithm used when preparing the signature. Only a digest algorithm of SHA-256 is accepted.
      c SignedData.contentInfo.contentType shall be set to id-data
      d SignedData.contentInfo.content shall be absent (the content is provided in the Data parameter to the SetVariable() call)
      e SignedData.certificates shall contain, at a minimum, the signer’s DER-encoded X.509 certificate
      f SignedData.crls is optional.
      g SignedData.signerInfos shall be constructed as:
      — SignerInfo.version shall be set to 1
      — SignerInfo.issuerAndSerial shall be present and as in the signer’s certificate
      — SignerInfo.authenticatedAttributes shall not be present.

      a.: The SignedData sequence from the utility begins with “SignedData” OID 1 2 840 113549 1 7 2. The spec says the first thing must be SignedData.version.
      g.: The utility is including the SignerInfo.authenticatedAttributes field. According to the spec, this should not be present.

      Reply
      1. jejb Post author

        This might be due either to a difference in openssl versions or the presence of additional terms in the signing certificate. When I look at what I get for authenticated variable updates, I do see version 1, however, I do see some attributes like s/mime types and signature date.

        You can check the signature yourself using asn1parse from openssl (it’s at offset 40 in the bundle)

        openssl asn1parse -inform DER -offset 40 -i -in file

        The starting data should look like:

        0:d=0 hl=4 l=1138 cons: SEQUENCE
        4:d=1 hl=2 l= 9 prim: OBJECT :pkcs7-signedData
        15:d=1 hl=4 l=1123 cons: cont [ 0 ]
        19:d=2 hl=4 l=1119 cons: SEQUENCE
        23:d=3 hl=2 l= 1 prim: INTEGER :01
        […]

        That integer with value :01 is version 1

        It’s possible there’s an extension in your signing certificate that forces the version up (RFC5652 extended RFC2315 and defines versions up to 5 depending on particular signing and signed data characteristics).

        The attributes problem should be fixed by version 1.4.3

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>