Skip to content

Add stage0: measured kernel-less UEFI network bootloader#7

Merged
HarryR merged 1 commit into
mainfrom
stage0-uefi-netboot
Jun 12, 2026
Merged

Add stage0: measured kernel-less UEFI network bootloader#7
HarryR merged 1 commit into
mainfrom
stage0-uefi-netboot

Conversation

@HarryR

@HarryR HarryR commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

What

stage0 is the kernel-less sibling of stage1: a pure-UEFI application the firmware boots directly, which fetches a _stage0 user-data document from the cloud metadata service, downloads a UEFI payload over the network, measures it into the TPM (PCR 14), and chain-loads it — no Linux kernel in the chain.

Design

  • TPM reuse. Reuses vaportpm-attest unchanged via its TpmTransport seam (git dep, pinned to main @ 15770b1); a Tcg2Transport ships raw TPM commands over EFI_TCG2_PROTOCOL.SubmitCommand (src/tcg2.rs), so the same pcr_extend used on Linux runs here.
  • Networking. Payload download uses raw EFI_TCP4 (src/tcp4.rs) because OVMF/EDK2 HttpDxe won't drain a multi-segment response body; hostname URLs resolve over EFI_DNS4 (src/dns4.rs). Metadata still uses EFI_HTTP at fixed link-local IPs.
  • Admission policies (src/config.rs, src/sig.rs): pinned sha256, or an ed25519 detached signature (<url>.sig) verified against a release pubkey pinned in metadata — letting payloads roll forward without editing VM metadata. The release private key stays offline and never touches a deployed machine.
  • No db-signing of payloads. stage0 loads them through a temporary EFI_SECURITY2_ARCH_PROTOCOL.FileAuthentication override (src/secauth.rs, shim's security_policy_install trick), so the deployment keeps its ephemeral-key / locked-varstore lockdown (the db key signs only stage0, then is destroyed) and can still chain-load late-bound payloads — otherwise mutually exclusive.
  • Minimal attestation surface. Only PCR 14 is measured: "stage0 ran and loaded this hash." Config/key/signature are not measured (PCR 15 deliberately dropped vs. stage1).

Tested

Under QEMU (x86_64 + aarch64), end to end: DNS4 → TCP4 download → ed25519 verify → PCR 14 extend → security-arch-override LoadImage → payload runs and reads back PCR 14. Builds clean for both *-unknown-uefi targets; clippy + rustfmt pass. All builds run in the build container, never on the host.

Follow-ups (not blocking)

  • Confirm EFI_TCP4 / EFI_DNS4 (and EFI_HTTP) drivers are present on real AWS Nitro firmware — same unknown noted in the README's risk section.
  • EC2 / GCP validation pending.

🤖 Generated with Claude Code

stage0 is the kernel-less sibling of stage1: a pure-UEFI application the
firmware boots directly, which fetches a `_stage0` user-data document from the
cloud metadata service, downloads a UEFI payload over the network, measures it
into the TPM (PCR 14), and chain-loads it — no Linux kernel in the chain.

Key design points:
- Reuses vaportpm-attest unchanged via its TpmTransport seam (git dep); a
  Tcg2Transport ships raw TPM commands over EFI_TCG2_PROTOCOL.SubmitCommand
  (src/tcg2.rs), so the same pcr_extend used on Linux runs here.
- Payload download uses raw EFI_TCP4 (src/tcp4.rs), because OVMF/EDK2 HttpDxe
  won't drain a multi-segment response body; hostname URLs resolve over
  EFI_DNS4 (src/dns4.rs). Metadata still uses EFI_HTTP at fixed link-local IPs.
- Two admission policies (src/config.rs, src/sig.rs): pinned sha256, or an
  ed25519 detached signature (<url>.sig) verified against a release pubkey
  pinned in metadata — letting payloads roll forward without editing metadata.
- Payloads are not db-signed. stage0 loads them through a temporary
  EFI_SECURITY2_ARCH_PROTOCOL.FileAuthentication override (src/secauth.rs,
  shim's security_policy_install trick), so the deployment keeps its
  ephemeral-key/locked-varstore lockdown while still chain-loading late-bound
  payloads. Only PCR 14 is measured — the attestation surface is just
  "stage0 ran and loaded this hash".

Build/test (all in the build container; never on the host):
  make tools/build-stage0/<arch>/stage0.efi
  make test-stage0-<arch>   # builds+signs a test payload, serves it over a
                            # DNS name, boots stage0 end to end under QEMU

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@HarryR HarryR force-pushed the stage0-uefi-netboot branch from acae776 to d501f22 Compare June 12, 2026 15:12
@HarryR HarryR merged commit 39fd5e6 into main Jun 12, 2026
4 checks passed
@HarryR HarryR deleted the stage0-uefi-netboot branch June 12, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant