ernesto cruz
Back to writing
June 8, 2026·6 min read

PicoBootGate: Pre-Boot Remote Authorization on UEFI

How to make a machine refuse to boot until a server says it can, while treating the network, the peripheral, and the operating system as hostile. A UEFI reference design with an academic paper behind it.

uefiedk2securityfirmwaresecure-boot

Say you want a machine to refuse to boot unless a server has authorized it. Think managed fleets, leased hardware, anti-theft, licensing that actually binds to a device. The check has to happen before the operating system loads, because once the OS is up, anything running there can lie. So it has to live in firmware. And firmware is the worst possible place to put a network client: it has no WiFi stack, and the cable, the radio, and the server on the other end are all things an attacker can get between.

PicoBootGate is my answer to that, built as a UEFI reference design with a paper behind it. The short version: push everything you cannot trust off the host, and let the host trust only a key and a counter it controls.

What it actually guarantees

One defensive goal, stated narrowly on purpose: the host refuses to load the OS unless a fresh, unaltered authorization ticket is present in NVRAM. "Fresh" means inside the validity window the server set. "Unaltered" means the ticket's authentication tag still verifies under a per-device key.

It is not boot-chain integrity. Secure Boot and TPM-based measured boot already do that, and a real deployment runs them alongside this, not instead of it. PicoBootGate answers a different question: not "is this firmware and OS untampered," but "is this specific machine allowed to boot right now."

The architecture

+-------------------+     USB CDC/ACM    +----------+   WiFi / TLS   +----------+
|  Host firmware    |<------------------>|  Pico W  |<-------------->|  Server  |
|  PicoWifiDxe (DXE)|   framed commands  |  network |                |  issues  |
|  BootGate   (BDS) |                    |  stack   |                |  tickets |
+-------------------+                    +----------+                +----------+

The host firmware never sees a WiFi frame, an IP packet, or a TLS handshake. The entire network stack lives on an attached Raspberry Pi Pico W, which the host talks to over a simple framed serial protocol (the same PicoWifiDxe bridge I wrote about separately). The server issues tickets; the Pico ferries them; the host verifies them.

The idea: shrink the trusted set

The whole design hinges on what is inside the trusted computing base and what is outside it.

Outside the TCB: the Pico W firmware, the WiFi radio, the TCP/IP stack, the TLS implementation, and the validation server's transport. All untrusted. An attacker can own every one of them.

Inside the TCB: the host's USB stack and the two firmware modules, an HMAC-SHA256 implementation (via EDK II's BaseCryptLib), and the NVRAM variable services.

The reason this works is that authorization does not depend on the channel being honest. It depends on a ticket the attacker cannot forge. A hostile Pico, or a man-in-the-middle on the USB cable, cannot manufacture a ticket the host will accept, because it does not hold the per-device key. The worst it can do is refuse to deliver tickets, which the design treats as denial of service, not as a security failure. That distinction (a blocked boot is annoying; a forged boot is fatal) is the whole game.

The ticket

The reference implementation uses a placeholder 60-byte format. Production deployments are expected to replace it, but it shows the shape:

offset  len  field
------  ---  -----
0       4    magic = "RTK1"
12      8    counter   (monotonic, little-endian)
20      8    expiry    (Unix epoch seconds)
28      32   HMAC-SHA256(device_key, bytes 0..27)

where device_key = HMAC-SHA256(master_secret, device_serial). Two properties fall out of this:

  • Unforgeable. Without the per-device key, you cannot produce a valid tag. The key never leaves the host's trust boundary, so nothing on the network or the peripheral can mint a ticket.
  • Replay-resistant. The counter is monotonic, and the host stores the highest value it has accepted. A captured old ticket has a counter that no longer climbs, so replaying it fails even though its HMAC is perfectly valid. The expiry adds a freshness bound on top.

NVRAM the OS cannot touch

The lock flag, the stored ticket, and the accepted-counter high-water mark live in three UEFI variables under a vendor GUID. They are marked NON_VOLATILE | BOOTSERVICE_ACCESS and deliberately not RUNTIME_ACCESS:

VariablePurpose
PicoLockFlag0 unlocked, 1 locked
PicoTicketthe stored ticket, opaque to the OS
PicoCounterhighest counter value accepted

Because they lack RUNTIME_ACCESS, an attacker with root after boot cannot read them through efivarfs or the Windows variable API, and cannot rewind the counter or clear the lock. The state that the security depends on is simply not addressable from the place the attacker eventually controls.

The boot decision

BootGate runs in the BDS phase, after the DXE core has dispatched the Pico driver. Its logic is a small state machine:

  1. Read PicoLockFlag. If clear, return success and let boot proceed.
  2. If set, verify the stored ticket offline (HMAC, counter, expiry).
  3. On success, advance the counter, and chain-load the next boot option.
  4. On failure, drop into a recovery UI: supply WiFi credentials and fetch a fresh ticket through the Pico, enter a manual unlock code, retry, or power off.

Notice that the common case (a valid stored ticket) needs no network at all. The Pico and the server only come into play when the host has to go get a new ticket, which keeps the trusted, security-critical path entirely offline.

Where it stops

Being honest about scope is part of the design. PicoBootGate does not defend against an attacker who detaches the SPI flash chip and rewrites NVRAM directly; that needs a hardware root of trust below this layer. It assumes provisioning is trusted, because whoever controls the firmware image at provisioning time controls the keys. And it cannot survive the validation server's signing key being stolen, which is an HSM-and-key-rotation problem on the server side.

None of those are cop-outs; they are the boundary of what a firmware-level authorization gate can reasonably own. Inside that boundary, the design holds a useful line: a machine that will not boot for a thief, a clone, or a replayed ticket, built on the assumption that the network and the peripheral doing the talking are already in enemy hands.

That, more than any single mechanism, is the lesson I took from building it. Decide what you are willing to trust, make that set as small as you can (here, a key, a counter, and some access-controlled NVRAM), and design so that everything outside it can fail or turn hostile without breaking the guarantee.