1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
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).
|