So you want to dabble with custom Secure Boot keys?
Author: Rasmus Dahlberg Date: 2025-09-26
You came to the right place. This post gives a beginner’s guide to:
- The Secure Boot key hierarchy: PK, KEK, db, and dbx.
- What you should be aware of to grok
.esl
and.auth
files.
You should find this blog post useful if you’re thinking about managing your own custom Secure Boot keys, which is a do-it-yourself alternative to Microsoft’s Secure Boot key hierarchy and shim.
Prologue
Just to ensure we’re all on the same page before diving into the details.
Secure Boot is a verified boot technology provided by UEFI (the Unified Extensible Firmware Interface). This is pretty much what it sounds like: before UEFI is willing to boot an operating system image, the operating system image needs to be verified. Such verification requires a valid digital signature or a matching cryptographic hash in an allowlist; otherwise the operating system simply fails to boot.
This is all governed by a key hierarchy that decides which images to accept and reject, see Figure 1.
+----------------+
| PK |
+----------------+
|
v
+----------------+
| KEK |
+----------------+
/ \
v v
+---------------+ +----------------+
| db | | dbx |
+---------------+ +----------------+
Figure 1: The Secure Boot key hierarchy is composed of PK, KEK, db, and dbx.
PK is the root of the hierarchy, while db and dbx are the leaves and what's
consulted when deciding to accept or reject an operating system image.
Overview of db, dbx, KEK, and PK
Secure Boot has a location for storing trusted keys and allowlisted hashes. This location is called the authorized signature database, or db for short. There is also a forbidden signature database called dbx. The long names might sound a bit confusing, but what we’re really talking about here is a single 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.
For 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 accept image
foo
and reject image bar
. Another example: suppose unsigned image H(baz)
is in db and not in dbx. Secure Boot would accept image baz
.
If you want to update db or dbx, you can choose 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, you’re likely happy to just replace db and dbx with new values.
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 location called KEK. This is short for Key Exchange Key, which is yet another name that might be a bit confusing (so don’t get hung up on it). It’s possible to have multiple keys in KEK, but you’ll most likely be fine with just one key.
Similar to db and dbx, KEK can also be updated. It would be a problem if anyone could update KEK. (You can probably 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.
Note: PK and KEK are only useful for making updates to the key hierarchy, and it is the current contents of db and dbx that decide if an operating system image is accepted or rejected. While there is a logical hierarchy between PK, KEK, and db/dbx, there is no certificate chain or similar that’s explicit.
Secure Boot EFI variables
PK, KEK, db, and dbx are stored as EFI variables on the system’s 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 as authentication. The main exception to this is in Secure Boot’s so-called setup mode.
Try reading the EFI variable that shows if setup mode is on or off on a Debian system:
$ hexdump -C /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c
00000000 06 00 00 00 01 |.....|
00000005
The first four bytes are reserved for EFI metadata. 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. Warning: entering setup mode deletes the existing Secure Boot keys.
Also 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 byte after the EFI metadata shows Secure Boot is off (0).
Finally, try reading PK, KEK, db, and dbx:
$ hexdump -C /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
$ hexdump -C /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
$ hexdump -C /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
$ hexdump -C /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f
On a system with Secure Boot enabled you’d 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 the hex blurb variable-names 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 EFI 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? Each of these variables
store EFI signature lists. You will also find EFI signature lists in .esl
files, often before being written to their respective variables.
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’s break it down one part at a time.
SignatureType
: what type of list is this? Three useful list types to be aware
of include:
EFI_CERT_SHA256_GUID
: the GUID for a list of SHA256 hashes isc1c41626-504c-4092-aca9-41f936934328
. This is what you’d use to allowlist or disallow an operating system image.EFI_CERT_X509_GUID
: the GUID for a list of DER-encoded X.509 certificates isa5c059a1-94e4-4aa7-87b5-ab155c2bf072
. You should see this GUID matching the earlier hexdump of PK. In other words, look directly after the initial four bytes (the EFI metadata). You should seea1 59 c0 a5
, which is0xa5c059a1
in network byte order.EFI_CERT_X509_SHA256_GUID
: the GUID for a list of SHA256 certificate hashes is3bd2a492-96c0-4079-b420-fcf98ef103ed
. You’d use this for certificate revocation in dbx.
SignatureListSize
: byte size of the entire list (including the header).
SignatureHeaderSize
: size of each individual item in the list. If you think
this sounds impractical for X.509 certificates for which the size per
certificate may vary, then 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 show you previews using this.
Is it possible to have a list where the X.509 certificates have different sizes, or where the list contains both X.509 certificates and hashes? The shorter 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 some EFI signatures lists? Consider trying
cert-to-efi-sig-list
, cert-to-efi-hash-list
, and hash-to-efi-sig-list
. On
Debian, you’ll find these utilities in the efitools package.
Want a dense summary of what’s usually found as EFI signature lists in PK, KEK, db, and dbx? Here it is:
- 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 operating system images.
- dbx: SHA256 hashes of either X.509 certificates or operating system images to revoke.
Authentication descriptor
As mentioned earlier you’re not allowed to write any EFI signature list to PK, KEK, db, and dbx (unless setup mode is enabled). UEFI runtime services would basically say nope due to missing valid signatures.
To write any of these Secure Boot 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 formatted as an authentication_v2 descriptor as defined by the UEFI specification, which in turn references use of PKCS#7. If this is your jam, take a look at the specification and some software that implements it (e.g., the TianoCore EDK II reference implementation).
For the rest of us, 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.
So if you ever encounter a .auth
file, this is just an authentication
descriptor concatenated with an EFI signature list. Which, in other words, is a
signed and timestamped EFI signature list for a variable.
Want to create signed EFI signature lists? Consider trying sign-efi-sig-list
,
also from the efitools package on Debian. We unfortunately haven’t found a
good tool that verifies these signatures, but you can (at your own risk) play
with Secure Boot EFI variables on your own system by entering setup mode.
Epilogue
Secure Boot has four key variables that are accessible through the efivarfs filesystem: PK, KEK, db, and dbx. PK sits at the root of the hierarchy and authorizes updates to KEK. KEK is an intermediate part of the hierarchy and authorizes updates to db and dbx. Finally, db and dbx ultimately decide which operating system images to accept and reject when Secure Boot is enabled.
Each of the four variables stores EFI signature lists, which in practice usually contain X.509 certificates with RSA keys or SHA256 hashes. When updating a variable’s EFI signature list, it is signed using an authentication descriptor that’s tied to the data, variable name, and a timestamp (to prevent replays).
Hope this helped getting the lay of the land, and good luck managing your custom Secure Boot keys! If you’d like more examples, see the System Transparency HOW-TO documentation on Secure Boot.