Skip to content

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.

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.

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:

Terminal window
sudo sbctl create-keys

Using “sudo sbctl create-keys” command user can create secure keys on the trusted system.

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).

After installation, verify the enrolled keys from the host:

Terminal window
efi-readvar -v PK
efi-readvar -v KEK
efi-readvar -v db

Jetson Orin targets produce two independent build artifacts in CI:

  1. The flash script (nix build .#nvidia-jetson-orin-agx-debug-from-x86_64-flash-script) which orchestrates NVIDIA’s flashing tools.
  2. 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:

Terminal window
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:

Terminal window
nix build .#nvidia-jetson-orin-agx-debug-from-x86_64-flash-script
sudo ./result/bin/flash-ghaf-host # clean unsigned flash, Secure Boot stays disabled
SIGNED_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 boot

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.

  1. 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.

  2. EFI signing: The flash script builds the ESP image at flash time. Before copying EFI binaries into the FAT partition, it signs each .efi file with sbsign using the db private key. This signs both systemd-boot (BOOTAA64.efi) and the UKI.

  3. Enforcement: Once in User Mode (SetupMode=0, SecureBoot=1), the UEFI firmware rejects any unsigned or incorrectly signed EFI binary with “Access denied”.

The repository contains two sets of keys:

  • modules/secureboot/keys/ — Production certificates generated from a NetHSM. Contains only .crt and .auth files (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.

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:

OptionDescription
ghaf.hardware.nvidia.orin.secureboot.enableEnable/disable UEFI Secure Boot key enrollment
ghaf.hardware.nvidia.orin.secureboot.keysSourceDirectory with PK.crt, KEK.crt, db.crt for enrollment
ghaf.hardware.nvidia.orin.secureboot.signingKeyDirDirectory with db.key and db.crt for flash-time signing

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:

Terminal window
# Build the flash script
nix 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-host

The flash script will:

  1. Sign BOOTAA64.efi and the UKI with the db key
  2. Build the ESP FAT image with the signed binaries
  3. Flash QSPI firmware (with key enrollment DTB overlay) and eMMC partitions

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.

For production builds, replace the key material:

  1. Generate production PK/KEK/db certificates (e.g., from a Hardware Security Module).
  2. Set keysSource to the directory containing the production .crt files.
  3. At flash time, set SECURE_BOOT_SIGNING_KEY_DIR to a directory containing the production db.key and db.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)
};

After flashing and booting, verify Secure Boot is active:

Terminal window
# Check UEFI variables
efi-readvar
# Check boot status
bootctl 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)

To regenerate the development keys:

Terminal window
cd modules/secureboot/dev-keys
for 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 3650
done

After regenerating, you must reflash the device for the new keys to take effect.