Secure Boot
This section describes Secure Boot and how to create secure keys.
The reader is expected to know the fundamentals of UEFI and have a basic understanding of Secure Boot UEFI specification.
Enabling Secure Boot
Section titled “Enabling Secure Boot”Secure Boot enrollment is performed during installation when the installer is
run with the -s flag. The installer expects the system firmware to be in
Setup Mode (PK cleared). If PK is present, enrollment will be skipped and you
must clear PK in firmware settings before retrying.
Creating Secure Boot Keys
Section titled “Creating Secure Boot Keys”Secure Boot keys can be created with sbctl, a Secure Boot Manager. sbctl is available in Nixpkgs as pkgs.sbctl.
After you installed sbctl or entered a Nix shell, use the following command to create your Secure Boot keys:
sudo sbctl create-keysUsing “sudo sbctl create-keys” command user can create secure keys on the trusted system.
Current Implementation
Section titled “Current Implementation”By default, Ghaf ships pre-generated public enrollment artifacts staged from
/etc/ghaf/secureboot/keys. Secure Boot enrollment is performed by the installer
when invoked with -s, and it enrolls PK/KEK/db using EFI authentication files
(.auth).
The current key set is sourced from tiiuae/ghaf-infra-pki (yubi-uefi).
Secure Boot Verification
Section titled “Secure Boot Verification”After installation, verify the enrolled keys from the host:
efi-readvar -v PKefi-readvar -v KEKefi-readvar -v dbJetson Orin signed flashing workflow
Section titled “Jetson Orin signed flashing workflow”Jetson Orin targets produce two independent build artifacts in CI:
- The flash script (
nix build .#nvidia-jetson-orin-agx-debug-from-x86_64-flash-script) which orchestrates NVIDIA’s flashing tools. - The filesystem image (
nix build .#nvidia-jetson-orin-agx-debug-from-x86_64) that contains the ESP and root partitions.
After the filesystem image is signed, pass its Nix store path directly to the flash helper:
SIGNED_SD_IMAGE=$(nix path-info .#nvidia-jetson-orin-agx-debug-from-x86_64)./result/bin/flash-ghaf-host -s "$SIGNED_SD_IMAGE"The -s/--signed-sd-image flag extracts BOOTAA64.EFI and the kernel from the signed image, wires them into the flashing workdir, and launches NVIDIA’s flashing script without requiring any additional staging directories or host key material.
The argument to -s/--signed-sd-image must be the signed image output directory (for example, the value returned by nix path-info), not the *.img.zst file itself. The directory is expected to contain esp.offset, esp.size, root.offset, and root.size, plus either sd-image/*.img.zst or *.img.zst at the top level. Passing the image file directly will fail with Signed image directory not found: ....
Whether -s is optional depends on how the board sizes its partitions. Every non-verity Orin board pins
ghaf.hardware.nvidia.orin.flashScriptOverrides.appPartitionSizeBytes, so flash.xml carries static ESP/APP sizes and the
flash script embeds no image at all. For those targets -s is required, and running without it exits with
ERROR: this flash script was built without an embedded sdImage. This is deliberate: deriving the sizes from the built
image instead puts system.build.sdImage – and through it every microvm – into the flash script’s evaluation closure,
which costs roughly 10 GiB of RAM to evaluate and exceeds the CI eval worker’s memory limit.
The verity targets use a different partition template that always embeds the image, so for those -s stays optional and
omitting it flashes the bundled image.
ghaf.hardware.nvidia.orin.secureboot.enable defaults to true only when ghaf.hardware.nvidia.orin.flashScriptOverrides.signedArtifactsPath is set at Nix evaluation time; otherwise it defaults to false. Key enrollment, ESL generation, and the UefiDefaultSecurityKeys overlay are therefore skipped for unsigned builds, so a default flash-ghaf-host (no -s) boots the device with Secure Boot disabled instead of bricking it to the UEFI shell.
To make QSPI-side enrollment selectable at flash time without requiring an evaluation-time signedArtifactsPath, the *-flash-script output ships two pre-built QSPI firmware variants behind a single flash-ghaf-host entrypoint: one without enrollment material (the default) and one with the UefiDefaultSecurityKeys DTBO plus PK/KEK/db ESLs baked in (selected by --secure-boot). -s/--signed-sd-image is deliberately not the selector: with static APP sizing every flash supplies an image with -s, signed or not, so keying Secure Boot off it would brick every unsigned flash. The wrapper dispatches on its own --secure-boot flag:
nix build .#nvidia-jetson-orin-agx-debug-from-x86_64-flash-scriptSIGNED_SD_IMAGE=$(nix path-info .#nvidia-jetson-orin-agx-debug-from-x86_64)
# Unsigned QSPI, Secure Boot stays disabled. -s is still required: the script embeds no image.sudo ./result/bin/flash-ghaf-host -s "$SIGNED_SD_IMAGE"
# SB-enabled QSPI: signed BOOTAA64.EFI swap + PK/KEK/db enrolled on first boot.# Pair --secure-boot with a *signed* image, or the enrolled firmware refuses the unsigned BOOTAA64.EFI.sudo ./result/bin/flash-ghaf-host --secure-boot -s "$SIGNED_SD_IMAGE"After a successful -s flash the device boots into Ghaf with Secure Boot enabled and efi-readvar reports the Ghaf UEFI PK → KEK → db certificate chain.
For ad-hoc debugging you can also point -s to a copy of the signed build outside the Nix store (for example, cp -a $(nix path-info …) /tmp/orin-signed and then pass /tmp/orin-signed), which is useful when the original store path is unavailable on the flashing host.
For CI jobs that still need a deterministic artifact directory (for example when reusing the same signed files across multiple runs), use the extractor helper provided by jetpack-nixos and point ghaf.hardware.nvidia.orin.flashScriptOverrides.signedArtifactsPath at the staged directory.
Jetson Orin Secure Boot (A/B verity targets)
Section titled “Jetson Orin Secure Boot (A/B verity targets)”On Jetson Orin targets with verity boot, Secure Boot is handled differently from the x86 laptop flow and from the sd-image flow described above. The NVIDIA UEFI firmware supports standard UEFI Secure Boot key enrollment via a device tree overlay, and EFI binaries (systemd-boot and UKIs) are signed at flash time rather than build time.
This keeps private signing keys out of the Nix store while still producing a fully signed boot chain.
How it works
Section titled “How it works”-
Key enrollment: During flash, a DTB overlay (
UefiDefaultSecurityKeys.dtbo) embeds PK, KEK, and db certificates into the firmware. On first boot, the UEFI enrolls these keys and transitions from Setup Mode to User Mode. -
EFI signing: The flash script builds the ESP image at flash time. Before copying EFI binaries into the FAT partition, it signs each
.efifile withsbsignusing the db private key. This signs both systemd-boot (BOOTAA64.efi) and the UKI. -
Enforcement: Once in User Mode (SetupMode=0, SecureBoot=1), the UEFI firmware rejects any unsigned or incorrectly signed EFI binary with “Access denied”.
Key material
Section titled “Key material”The repository contains two sets of keys:
-
modules/secureboot/keys/— Production certificates generated from a NetHSM. Contains only.crtand.authfiles (public material). -
modules/secureboot/dev-keys/— Self-signed development keys for testing. Contains both.crt(public) and.key(private) files. These keys are checked into the repository for convenience — they are explicitly not secret and must not be used in production.
Configuration
Section titled “Configuration”Secure Boot is enabled by default for Orin targets via jetson-orin.nix:
ghaf.hardware.nvidia.orin.secureboot.enable = lib.mkDefault true;The Orin profile overrides the enrollment certificates to use dev keys:
ghaf.hardware.nvidia.orin.secureboot.keysSource = lib.mkDefault ../secureboot/dev-keys;Available options:
| Option | Description |
|---|---|
ghaf.hardware.nvidia.orin.secureboot.enable |
Enable/disable UEFI Secure Boot key enrollment |
ghaf.hardware.nvidia.orin.secureboot.keysSource |
Directory with PK.crt, KEK.crt, db.crt for enrollment |
ghaf.hardware.nvidia.orin.secureboot.signingKeyDir |
Directory with db.key and db.crt for flash-time signing |
Flashing with Secure Boot
Section titled “Flashing with Secure Boot”The flash script reads the signing key from SECURE_BOOT_SIGNING_KEY_DIR (or
falls back to the signingKeyDir option). Since private keys are not copied into
the Nix store, you must point the environment variable to the working tree:
# Build the flash scriptnix build .#nvidia-jetson-orin-agx-verity-debug-from-x86_64-flash-script
# Flash with dev keys (device must be in recovery mode)sudo SECURE_BOOT_SIGNING_KEY_DIR=$PWD/modules/secureboot/dev-keys \ ./result/bin/flash-ghaf-hostThe flash script will:
- Sign
BOOTAA64.efiand the UKI with the db key - Build the ESP FAT image with the signed binaries
- Flash QSPI firmware (with key enrollment DTB overlay) and eMMC partitions
OTA update image signing
Section titled “OTA update image signing”UKIs in OTA update images must also be signed for Secure Boot to accept them.
In debug builds, the uki-signing-key-dir option signs UKIs at Nix build
time using the dev keys. This is gated by an assertion — it cannot be used in
release builds because it places private keys in the Nix store:
ghaf.partitioning.verity.uki-signing-key-dir = ./path/to/dev-keys;In production, the OTA update server is responsible for signing UKIs before distributing them to devices. The device never holds private signing keys. The exact signing integration depends on the update server implementation and is not yet implemented.
Production key workflow
Section titled “Production key workflow”For production builds, replace the key material:
- Generate production PK/KEK/db certificates (e.g., from a Hardware Security Module).
- Set
keysSourceto the directory containing the production.crtfiles. - At flash time, set
SECURE_BOOT_SIGNING_KEY_DIRto a directory containing the productiondb.keyanddb.crt(e.g., from an HSM-backed PKCS#11 token or a secure build machine).
ghaf.hardware.nvidia.orin.secureboot = { keysSource = /path/to/production/certs; # PK.crt, KEK.crt, db.crt signingKeyDir = "/secure/signing/keys"; # db.key, db.crt (runtime path)};Verification
Section titled “Verification”After flashing and booting, verify Secure Boot is active:
# Check UEFI variablesefi-readvar
# Check boot statusbootctl status | head -10# Should show: Secure Boot: enabled (user)
# Check SetupMode is off (enforcing)hexdump -C /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c# Last byte should be 00 (not in setup mode)Generating new dev keys
Section titled “Generating new dev keys”To regenerate the development keys:
cd modules/secureboot/dev-keysfor name in PK KEK db; do openssl req -new -x509 -newkey rsa:2048 -nodes \ -keyout "$name.key" -out "$name.crt" \ -subj "/CN=Ghaf Dev Secure Boot $name/" -days 3650doneAfter regenerating, you must reflash the device for the new keys to take effect.