aboutsummaryrefslogtreecommitdiff
path: root/content/post/so-you-want-to-dabble-with-secure-boot-keys.md
diff options
context:
space:
mode:
Diffstat (limited to 'content/post/so-you-want-to-dabble-with-secure-boot-keys.md')
-rw-r--r--content/post/so-you-want-to-dabble-with-secure-boot-keys.md315
1 files changed, 315 insertions, 0 deletions
diff --git a/content/post/so-you-want-to-dabble-with-secure-boot-keys.md b/content/post/so-you-want-to-dabble-with-secure-boot-keys.md
new file mode 100644
index 0000000..9bc6750
--- /dev/null
+++ b/content/post/so-you-want-to-dabble-with-secure-boot-keys.md
@@ -0,0 +1,315 @@
+---
+title: "So you want to dabble with Secure Boot keys?"
+date: 2025-06-21
+---
+# So you want to dabble with Secure Boot keys?
+
+You came to the right place. This post gives a beginners guide to:
+
+* The Secure Boot key hierarchy: PK, KEK, db, and dbx.
+* What you should be aware of to grok `.esl` and `.auth` files.
+
+This is useful to understand if you're looking to take control of the Secure
+Boot key hierarchy on your own systems--a deployment option called *custom keys*.
+
+But just to ensure we are all on the same page before diving into some details.
+Secure Boot is a *verified boot* technology provided by UEFI. This is pretty
+much what it sounds like: before the firmware is willing to boot an operating
+system image, it needs to be verified. Such verification requires the operating
+system image to be signed (digital signature) or allowlisted by (cryptographic)
+hash.
+
+Secure Boot is also somewhat related to a Linux kernel feature called
+*lockdown*. Depending on exactly which Linux kernel is used, lockdown may be
+enabled *automatically* as a result of using Secure Boot. In lockdown,
+signature verification on the kernel and kernel modules are enforced. Secure
+Boot keys may be consulted also for this verification, but the devil is in the
+[details](https://git.glasklar.is/system-transparency/project/documentation/-/blob/main/archive/2025-04-04-sb-notes.md?ref_type=heads#what-we-need-to-know-about-the-kernel)
+here.
+
+Instead of making a mega post that never gets published, I'll try to revisit
+what you need to know about signing images and the kernel in a follow-up post.
+
+## Key hierarchy
+
+Secure Boot has a variable containing trusted keys and allowlisted hashes. This
+variable is called *the authorized signature database*, or *db* for short.
+There is also a *forbidden signature database* called *dbx*. I find the long
+names a bit confusing, but what we're really talking about here is a place to
+define what Secure Boot should base its trust on (db) and another place to
+define what should no longer be trusted (dbx) *despite* what's in db. You're
+right to think of dbx as revocation.
+
+As an example, suppose there are two operating system images `foo` and `bar`
+signed by a key in db and `H(bar)` is in dbx. Secure Boot would consider image
+`foo` valid whereas bar would be invalid. Suppose unsigned image `H(baz)` is in
+db and not in dbx. Secure boot would consider image `baz` valid.
+
+If you want to update db or dbx, you can chose to *replace* or *append*. If you
+replace, the old values are completely replaced with the new values. If you
+append, the new values are appended to the existing values. In a custom key
+setup I don't see that much reason to bother with appending, but it's possible.
+
+It would be a problem if anyone could update db or dbx. Therefore, UEFI only
+permits such updates if they are signed by a trusted key. This trusted key
+is in a Secure Boot variable called KEK. This is short for *Key Exchange Key*,
+which is yet another name I find a bit confusing. It is possible to have
+multiple keys in KEK.
+
+Similar to db and dbx, KEK can also be updated. It would be a problem if anyone
+could update KEK. (You see where this is going now.) Therefore, UEFI will only
+permit such updates if they are signed by a trusted key. This trusted key is
+called PK, short for *Platform Key*. This is the root of the Secure Boot key
+hierarchy. To replace PK, UEFI requires the new key to be signed by the current
+key.
+
+Below is a figure summarizing the Secure Boot key hierarchy.
+
+ +----------------+
+ | PK |
+ +----------------+
+ |
+ v
+ +----------------+
+ | KEK |
+ +----------------+
+ / \
+ v v
+ +---------------+ +----------------+
+ | db | | dbx |
+ +---------------+ +----------------+
+
+To be a bit more detailed about what each part of the hierarchy typically
+stores:
+
+ - PK: a single X.509 certificates with an RSA key.
+ - KEK: X.509 certificates with RSA keys.
+ - db: X.509 certificates with RSA keys *or* SHA256 hashes of images.
+ - dbx: SHA256 hashes of either X.509 certificates or images to revoke.
+
+The above is not exhaustive, but I'm sorry to inform you're stuck with RSA keys.
+
+## EFI variables
+
+PK, KEK, db, and dbx are stored as EFI variables on the main SPI flash. If this
+sounds like mumbo jumbo: in the same place that your UEFI firmware is stored,
+there is a read+write region where variables can be persisted across boots.
+UEFI runtime services make some of these variables accessible read-only to the
+operating system. Other variables may also be available for writing, either
+with or without additional authentication. PK, KEK, db, and dbx updates usually
+require valid signatures (the only exception to this is in so-called *setup
+mode*).
+
+Try reading the EFI variable that shows if Secure Boot is on or off:
+
+ $ hexdump -C /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
+ 00000000 06 00 00 00 00 |.....|
+ 00000005
+
+The first four bytes are EFI metadata. The final byte shows Secure Boot is off
+(0).
+
+Try reading the EFI variable that shows if Secure Boot setup mode is on or off:
+
+ $ hexdump -C /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c
+ 00000000 06 00 00 00 01 |.....|
+ 00000005
+
+The final byte says setup mode is on (1). This means it is currently possible
+to provision a new PK without having a valid signature. Setup mode can be
+enabled via the UEFI menu. This resets the current Secure Boot key hierarchy (!).
+
+Try reading PK, KEK, db, and dbx:
+
+* /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
+* /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
+* /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
+* /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f
+
+On a system with Secure Boot enabled you might see something like this:
+
+ $ hexdump -C /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
+ 00000000 27 00 00 00 a1 59 c0 a5 e4 94 a7 4a 87 b5 ab 15 |'....Y.....J....|
+ 00000010 5c 2b f0 72 12 03 00 00 00 00 00 00 f6 02 00 00 |\+.r............|
+ 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 00000030 30 82 02 e2 30 82 01 ca a0 03 02 01 02 02 14 2e |0...0...........|
+ 00000040 9c f5 6c d3 e2 aa a3 04 2b 37 c6 88 75 e1 6d 96 |..l.....+7..u.m.|
+ 00000050 19 f9 be 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b |...0...*.H......|
+ 00000060 05 00 30 2b 31 0b 30 09 06 03 55 04 03 0c 02 50 |..0+1.0...U....P|
+ 00000070 4b 31 1c 30 1a 06 03 55 04 0a 0c 13 53 79 73 74 |K1.0...U....Syst|
+ 00000080 65 6d 20 54 72 61 6e 73 70 61 72 65 6e 63 79 30 |em Transparency0|
+ 00000090 1e 17 0d 32 35 30 36 31 35 31 35 33 37 33 39 5a |...250615153739Z|
+ 000000a0 17 0d 33 35 30 36 31 33 31 35 33 37 33 39 5a 30 |..350613153739Z0|
+ 000000b0 2b 31 0b 30 09 06 03 55 04 03 0c 02 50 4b 31 1c |+1.0...U....PK1.|
+ 000000c0 30 1a 06 03 55 04 0a 0c 13 53 79 73 74 65 6d 20 |0...U....System |
+ 000000d0 54 72 61 6e 73 70 61 72 65 6e 63 79 30 82 01 22 |Transparency0.."|
+ 000000e0 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 |0...*.H.........|
+ 000000f0 82 01 0f 00 30 82 01 0a 02 82 01 01 00 a9 db 0b |....0...........|
+ 00000100 52 89 cc 20 58 e5 71 da 24 9e 18 20 c6 33 8c 78 |R.. X.q.$.. .3.x|
+ 00000110 f0 32 85 d5 c6 57 5d dc 34 cf 2f 79 e7 5f ce 46 |.2...W].4./y._.F|
+ 00000120 ed 47 b4 bb 26 f6 8a 17 84 ee ec a2 f7 92 34 92 |.G..&.........4.|
+ 00000130 de b1 26 07 d6 a7 2b d7 aa 6c 87 b0 5b 58 da bb |..&...+..l..[X..|
+ 00000140 4d 30 b1 8b b6 44 96 50 96 b3 6c e7 3b ca eb 79 |M0...D.P..l.;..y|
+ 00000150 53 08 a6 bb 36 07 b1 8c 9f 0a 78 d7 6b 69 48 e7 |S...6.....x.kiH.|
+ 00000160 87 2f fd 9b bc 8d f5 fc cc 8b fe b2 b7 fd 62 c7 |./............b.|
+ 00000170 99 0e f6 52 29 55 99 83 25 72 c8 5c 19 6b 17 12 |...R)U..%r.\.k..|
+ 00000180 e8 97 14 1f 8d 20 81 3f 3d 63 52 1a 2b bc 58 34 |..... .?=cR.+.X4|
+ 00000190 dd fa 6c 67 43 87 f3 2c ac 87 b5 fd b6 b0 22 22 |..lgC..,......""|
+ 000001a0 6a a9 a8 81 64 1c 52 dc 82 4d c7 2e 98 af b0 fe |j...d.R..M......|
+ 000001b0 4f ae 67 65 21 83 57 cb 7b 89 03 57 31 a1 16 ef |O.ge!.W.{..W1...|
+ 000001c0 39 1e 52 80 3d 96 64 03 bc 7a f5 a7 4d 63 a9 ef |9.R.=.d..z..Mc..|
+ 000001d0 b8 61 bd d3 80 0f 44 ed 42 64 3e e8 25 c5 64 24 |.a....D.Bd>.%.d$|
+ 000001e0 64 13 34 43 85 ac 00 a9 26 17 7a 57 c9 48 fb f9 |d.4C....&.zW.H..|
+ 000001f0 04 89 36 dc c7 d0 b1 38 cf 49 b2 d5 81 02 03 01 |..6....8.I......|
+ 00000200 00 01 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 |..0...*.H.......|
+ 00000210 00 03 82 01 01 00 00 52 52 45 73 f7 ba 80 c5 6e |.......RREs....n|
+ 00000220 13 b5 45 0a 96 57 36 fd ea 47 8f 1a a8 b1 59 bf |..E..W6..G....Y.|
+ 00000230 6a ef 4d 21 92 ea b8 f3 7e 20 7b b6 30 58 18 e3 |j.M!....~ {.0X..|
+ 00000240 9e 18 2c 85 18 ca 1c 6f fc 18 80 3b d0 e3 13 f2 |..,....o...;....|
+ 00000250 aa 8d 87 76 cb 4d 50 01 3d 54 71 2c f4 25 1a 05 |...v.MP.=Tq,.%..|
+ 00000260 cc 9e 7d dd 0e 61 ce 14 ce 6d 78 9d 75 12 75 4e |..}..a...mx.u.uN|
+ 00000270 b4 87 fa 19 3b 5d 73 c7 ab 54 f1 ae 65 c1 7e 9e |....;]s..T..e.~.|
+ 00000280 31 5b 16 48 2e 88 19 30 91 5a 2c 8c 33 1c eb 17 |1[.H...0.Z,.3...|
+ 00000290 9e 4b 44 10 70 61 72 88 b6 cb 28 d0 c4 bc 96 43 |.KD.par...(....C|
+ 000002a0 36 88 f6 67 0d 93 3a 0c 11 4b f6 73 e9 0f a0 b5 |6..g..:..K.s....|
+ 000002b0 58 4c da d4 4f 02 4e 19 dd df 4e 44 f0 6f 3b 90 |XL..O.N...ND.o;.|
+ 000002c0 c9 ff d7 64 cc 4a 9e c5 93 86 29 41 17 21 37 db |...d.J....)A.!7.|
+ 000002d0 80 1f be 24 a3 a3 62 07 b5 e2 d0 d8 21 fd 9c 09 |...$..b.....!...|
+ 000002e0 95 2f 26 5f c3 60 45 86 e0 36 61 2a e7 8c 1b 49 |./&_.`E..6a*...I|
+ 000002f0 33 eb 10 49 bb 3c 80 f5 76 8a 1c 4c 3f 9e a2 d3 |3..I.<..v..L?...|
+ 00000300 c1 dd d6 3a 5b 63 b7 fb 72 ac 93 4e f1 f7 e2 e8 |...:[c..r..N....|
+ 00000310 3a 1b f9 19 6a 4b |:...jK|
+ 00000316
+
+You may have noticed that the variable names' hex blurbs were different for db
+and dbx. Without going into detail, the UEFI specification defines exactly
+which 16-byte Globally Unique IDentifier (GUID) to use for which variable.
+Think of GUIDs as a way to ensure there won't be any accidental naming
+collisions.
+
+## EFI signature list
+
+So what exactly is stored in PK, KEK, db, and dbx? I'm glad you asked.
+
+Each of these variables store *EFI signature lists* (ESL). If you ever
+encountered a `.esl` file, this is exactly the kind of bytes that are stored in
+PK, KEK, db, and dbx.
+
+The UEFI specification defines [EFI signature lists][] as follows:
+
+ #pragma pack(1)
+ typedef struct _EFI_SIGNATURE_DATA {
+ EFI_GUID SignatureOwner;
+ UINT8 SignatureData [_];
+ } EFI_SIGNATURE_DATA;
+
+ typedef struct _EFI_SIGNATURE_LIST {
+ EFI_GUID SignatureType;
+ UINT32 SignatureListSize;
+ UINT32 SignatureHeaderSize;
+ UINT32 SignatureSize;
+ // UINT8 SignatureHeader [SignatureHeaderSize];
+ // EFI_SIGNATURE_DATA Signatures [__][SignatureSize];
+ } EFI_SIGNATURE_LIST;
+ #pragma pack()
+
+Maybe a bit clunky to read so let me break it down for you.
+
+`SignatureType`: what type of list is this? The GUID for a list of SHA256
+hashes is `c1c41626-504c-4092-aca9-41f936934328` (`EFI_CERT_SHA256_GUID`). This
+is what you'd use to allowlist or revoke a particular EFI application by hash.
+The GUID for a list of DER-encoded X.509 certificates is
+`a5c059a1-94e4-4aa7-87b5-ab155c2bf072` (`EFI_CERT_X509_GUID`). You should see
+this GUID matches the earlier hexdump of PK modulo little endian / network byte
+order. For example, `a5c059a1` is in little endian. Reverse it to get `a1 59
+c0 a5`. To revoke a particular certificate by hash in dbx, you'd use a
+different type with GUID `3bd2a492-96c0-4079-b420-fcf98ef103ed`
+(`EFI_CERT_X509_SHA256_GUID`).
+
+There are many more types defined but the above should keep you covered.
+
+`SignatureListSize`: byte size of the entire list (including the header).
+
+`SignatureHeaderSize`: size of each individual item in the list. If you're think
+that this sounds impractical for X.509 certificates you're definitely on to
+something.
+
+`SignatureHeader`: defined for each `SignatureType`, empty for the above types.
+
+`Signatures`: the per-type specific data which starts with an *owners GUID*
+followed by the actual data (such as a DER encoded certificate or a SHA256
+hash). The owner's GUID can be anything and is mainly helpful to label entries.
+For example, some tools and UEFI menus might show you previews using this.
+
+If you paid attention you might wonder how to have a list where the X.509
+certificates have different sizes, or how to have both certificates and hashes
+in a single list. The answer is: if you have two valid EFI signature lists,
+then you can always concatenate them to form a single valid EFI signature list.
+
+ $ cat demo-cert.esl > list.esl
+ $ cat demo-hash.esl >> list.esl
+
+Want to format EFI signatures lists? Checkout `cert-to-efi-sig-list`,
+`cert-to-efi-hash-list`, and `hash-to-efi-sig-list` in the [efitools][] package
+on Debian.
+
+[EFI signature lists]: https://uefi.org/specs/UEFI/2.11/32_Secure_Boot_and_Driver_Signing.html#efi-signature-data
+[efitools]: https://packages.debian.org/search?keywords=efitools
+
+## Authentication descriptor
+
+As mentioned earlier you can't just write any EFI signature list to PK, KEK, db,
+and dbx. UEFI runtime services would say nope due to missing valid signatures.
+
+To write any of these variables, the data being written is expected to start
+with an authentication descriptor. This is just a fancy way of saying *prepend
+a signature to the EFI signature list*. This signature happens to be an
+[authentication_v2 descriptor][] as defined by the UEFI specification, which in
+turn references use of PKCS#7. If this is your jam, knock your self out by
+reading the specification and possibly looking at some tools implementing it.
+
+For the rest of you, what's important to know is that this signature spans both
+the input EFI signature list, the EFI variable name and GUID, as well as a
+timestamp. UEFI runtime services will not apply a signed update to the wrong
+variable, and also not apply an update that's older than the previous update.
+
+If you ever encountered a `.auth` file, this is just an authentication
+descriptor concatenated with the signed EFI signature list. If anyone knows
+if the newer authentication_v3 descriptor has seen wide deployment I'd be very
+interested to hear from you. Until then I will stick to the authentication_v2
+descriptor.
+
+Want to create signed EFI signature lists? Checkout `sign-efi-sig-list` from
+the [efitools][] package on Debian. I unfortunately haven't found a good tool
+that verifies these signatures, but you can always (at your own risk) play with
+the Secure Boot EFI variables on your own system by entering setup mode. If you
+don't know the possible implications of this you probably shouldn't do it.
+
+[authentication_v2 descriptor]: https://uefi.org/specs/UEFI/2.11/08_Services_Runtime_Services.html#using-the-efi-variable-authentication-2-descriptor
+
+## Final words
+
+You learned that the Secure Boot key hierarchy consists of four variables: PK,
+KEK, db, and dbx. PK sits at the top of the hierarchy and stores a single X.509
+certificate. KEK is an intermediate part of the hierarchy and stores one or
+more X.509 certificates used to sign-off db and dbx updates. What Secure Boot
+consults when when validating an image is db (trusted X.509 certificates /
+SHA256 hashes). In addition to getting a thumbs up via db, neither the image
+nor the X.509 certificate must be revoked by hash in dbx. Only RSA keys are
+supported.
+
+You also got an intuition for the exact format that PK, KEK, db, and dbx use:
+EFI signature lists. You can concatenate multiple EFI signature lists into a
+single larger EFI signature list, e.g., using `cat` for two `.esl` files. For
+UEFI runtime services to update PK, KEK, db, or dbx, the new EFI signature list
+needs to be signed. This is done using an authentication descriptor that binds
+the signature to the respective variables, data, and timestamps (to avoid
+replays). This authentication descriptor is concatenated with its data to form
+a `.auth` file. The bytes stored in this file can be shipped off to UEFI
+runtime services to request variable updates. Only the data is written on
+success. In other words, you will find EFI signature lists but not
+authentication headers in PK, KEK, db, and dbx.
+
+Want to try the mentioned [efitools][] with a bit more hand holding? I recently
+documented a few HOW-TO guides for the System Transparency project
+[here](https://git.glasklar.is/system-transparency/project/docs/-/tree/main/content/docs/how-to/secure-boot).