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: ....
Running ./result/bin/flash-ghaf-host without -s clears signed-image overrides and flashes the standard unsigned image bundled with the build output.
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 the -s runtime flag automatically pull in QSPI-side enrollment 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 (used when invoked without -s) and one with the UefiDefaultSecurityKeys DTBO plus PK/KEK/db ESLs baked in (used whenever -s/--signed-sd-image is on the command line). The wrapper dispatches on the flag at flash time, so:
nix build .#nvidia-jetson-orin-agx-debug-from-x86_64-flash-scriptsudo ./result/bin/flash-ghaf-host # clean unsigned flash, Secure Boot stays disabledSIGNED_SD_IMAGE=$(nix path-info .#nvidia-jetson-orin-agx-debug-from-x86_64)sudo ./result/bin/flash-ghaf-host -s "$SIGNED_SD_IMAGE" # signed BOOTAA64.EFI swap + PK/KEK/db enrolled on first bootAfter 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.