Quick Start Guide to Enable Secure Boot for Jetson Thor#
This guide describes how to enable Secure Boot on a Jetson Thor device, covering environment setup, key generation, fuse burning, EKB image preparation, UEFI key enrollment, QSPI signing and flashing, and OS installation.
Environment Setup#
To follow this guide, we recommend opening two terminal windows:
Use one terminal window on your host machine to run commands.
Use another terminal window to monitor the Jetson Thor UART output:
sudo minicom -D /dev/ttyACM0 -w -c on
Requirements#
The following steps must be performed on a Linux host machine (Ubuntu 22.04 or later).
The following software packages are required:
libftdi-devfor USB debug port support.openssh-serverpackage for OpenSSL.efitoolspackage for thecert-to-efi-sig-listutility.Full installation of the latest Jetson Linux release on the host.
A USB cable connecting the USB-C port of the Jetson Thor device to the host.
If necessary, a USB cable that connects the Debug USB-C port (behind the lid cover) to the host.
You also need a full installation of the latest Jetson Linux release:
Jetson_Linux_<VERSION>_aarch64.tbz2Tegra_Linux_Sample-Root-Filesystem_<VERSION>_aarch64.tbz2host_overlay_fskp_tools_<VERSION>_aarch64.tbz2public_sources.tbz2
Note
The current released version (<VERSION>) is R39.2.0.
Create a working directory on your host machine:
mkdir ~/work_dir
cd ~/work_dir
Download the BSP packages into ~/work_dir, and then unzip and install them as follows:
# Execute the following commands in ~/work_dir
tar -xpvf ./Jetson_Linux_<VERSION>_aarch64.tbz2
sudo tar -xpf ./Tegra_Linux_Sample-Root-Filesystem_<VERSION>_aarch64.tbz2 -C \
Linux_for_Tegra/rootfs/
tar -xvjf ./host_overlay_fskp_tools_<VERSION>_aarch64.tbz2
tar -xvf ./public_sources.tbz2
tar -xvf ./Linux_for_Tegra/source/nvidia-jetson-optee-source.tbz2 -C \
Linux_for_Tegra/source/
cd Linux_for_Tegra/
sudo ./apply_binaries.sh
sudo ./tools/l4t_flash_prerequisites.sh
Throughout the rest of this guide, we refer to the Linux_for_Tegra directory as <top>.
Some utilities must be run from this parent directory and are denoted with <top> in the
command path.
Pre-UEFI#
Pre-UEFI includes low-level firmware boot components like MB1, MB2, and CPU bootloader UEFI. Secure Boot at this level is ensured through fuses, so you need to prepare a fuse configuration file that contains keys and other settings to enable Secure Boot, convert the file into a fuseblob, and then burn the fuseblob to the device.
Prepare the PKC Keys#
Create a directory for your PKC keys:
cd ~/work_dir mkdir pkc_keys cd pkc_keys touch pkc_keylist.xml
Generate PKC keys.
Tip
We recommend that you generate all 16 keys because you can burn keys only once.
Add all keys to
pkc_keylist.xml. Ensure that all keys are listed inpkc_keylist.xmlas shown in the following example:<entry_list> <bct active_index="0" chip_id="0x260" pcp_file="nv_combo.pcp" pcps_file="nv_combo.pcps" pcps_hash_file="nv_combo.pcps.hash" /> <entry hash_file="nv_rsa3k_v3_0.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_rsa3k_v3_0.pem" key_id="0" mode="pkc" pub_file="nv_rsa3k_v3_0.pub" /> <entry hash_file="nv_rsa3k_v3_1.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_rsa3k_v3_1.pem" key_id="1" mode="pkc" pub_file="nv_rsa3k_v3_1.pub" /> <entry hash_file="nv_rsa3k_v3_2.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_rsa3k_v3_2.pem" key_id="2" mode="pkc" pub_file="nv_rsa3k_v3_2.pub" /> <entry hash_file="nv_rsa3k_v3_3.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_rsa3k_v3_3.pem" key_id="3" mode="pkc" pub_file="nv_rsa3k_v3_3.pub" /> <entry hash_file="nv_ecp256_v3_0.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp256_v3_0.pem" key_id="4" mode="ec" pub_file="nv_ecp256_v3_0.pub" /> <entry hash_file="nv_ecp256_v3_1.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp256_v3_1.pem" key_id="5" mode="ec" pub_file="nv_ecp256_v3_1.pub" /> <entry hash_file="nv_ecp256_v3_2.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp256_v3_2.pem" key_id="6" mode="ec" pub_file="nv_ecp256_v3_2.pub" /> <entry hash_file="nv_ecp256_v3_3.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp256_v3_3.pem" key_id="7" mode="ec" pub_file="nv_ecp256_v3_3.pub" /> <entry hash_file="nv_ecp521_v3_0.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp521_v3_0.pem" key_id="8" mode="ec521" pub_file="nv_ecp521_v3_0.pub" /> <entry hash_file="nv_ecp521_v3_1.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp521_v3_1.pem" key_id="9" mode="ec521" pub_file="nv_ecp521_v3_1.pub" /> <entry hash_file="nv_ecp521_v3_2.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp521_v3_2.pem" key_id="10" mode="ec521" pub_file="nv_ecp521_v3_2.pub" /> <entry hash_file="nv_ecp521_v3_3.pub.hash" key="/home/nvidia/work_dir/pkc_key/nv_ecp521_v3_3.pem" key_id="11" mode="ec521" pub_file="nv_ecp521_v3_3.pub" /> <entry hash_file="xmss_v3_0.pub.hash" key="/home/nvidia/work_dir/pkc_key/xmss_v3_0.key" key_id="12" mode="xmss" pub_file="xmss_v3_0.pub" /> <entry hash_file="xmss_v3_1.pub.hash" key="/home/nvidia/work_dir/pkc_key/xmss_v3_1.key" key_id="13" mode="xmss" pub_file="xmss_v3_1.pub" /> <entry hash_file="xmss_v3_2.pub.hash" key="/home/nvidia/work_dir/pkc_key/xmss_v3_2.key" key_id="14" mode="xmss" pub_file="xmss_v3_2.pub" /> <entry hash_file="xmss_v3_3.pub.hash" key="/home/nvidia/work_dir/pkc_key/xmss_v3_3.key" key_id="15" mode="xmss" pub_file="xmss_v3_3.pub" /> </entry_list>
Note
As of the current release, the value of
keyin each entry must be the absolute path to the key.Generate the PKC hash to use in the fuse blob using
tegrasign_v3.py. After all keys have been entered into the PKC list, run the following command:sudo <top>/bootloader/tegrasign_v3.py --key <pkc_keylist> --pubkeyhash <active_pkc.pcp> \ <pkc_keylist.hash>
<pkc_keylist>is the name of the PKC key list XML file generated in the previous step.<active_pkc.pcp>is the name of the output public cryptography parameter file of the active key.<pkc_keylist.hash>is the name of the output public key digest of all the public keys in the PKC key list.
The resulting hash is printed to the terminal. Save this value; you will need to paste it into
fuse.xmlas described in the next section.
For details on key generation and PublicKeyHash generation, refer to Generate a PKC Key List for Jetson Thor.
Prepare the Fuse Configuration File#
Create your fuse configuration directory and create a
fuse.xmlfile:cd ~/work_dir mkdir fuse_config cd fuse_config touch fuse.xml
In
fuse.xml, register the PKC hash as shown:<fuse name="PublicKeyHash" size="64" value="YOUR_PKC_HASH"/>
Fill in other fuse values as shown in the example
fuse.xml. The following XML file enables only signature verification:<genericfuse MagicId="0x45535546" version="1.0.0"> <fuse name ="PublicKeyHash" size="64" value="0xaa7ab9bf57a5747af4af080b86c1b7a651f839141da6ee80ebe3c562a5a8fb34e981d964bee653e93cd56e6b0896753d191ded586a12503fc8797d3d62ac3c08"/> <fuse name="OptInEnable" size="4" value="0x00000001"/> <fuse name="PscOemKdk0" size="32" value="0x6208e3cd81ed0cd77b214db0c875ade40c26bca09382ad82cd0e24046cc8c64e"/> <fuse name="PscOemKdk1" size="32" value="0x112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"/> <fuse name="OespOemKdk0" size="32" value="0xc008421905de5493ff350b7e69b15196028fdf073f2b3f5e41f4f58f37240f29"/> <fuse name="OespOemKdk1" size="32" value="0xbd88ccea074705402afe649806ce6c5771730dc36a676fe23054c104aa48ddc0"/> <fuse name="SbOemKdk0" size="32" value="0x8261530878e05d37b8cab1d93e44c2ab0a17190c2f7b8366721b5201595909f5"/> <fuse name="SbOemKdk1" size="32" value="0xf1429596951a75f31aa7f787b4d7d1e23799f4f4ae02b17be73cafa62b7910a8"/> <fuse name="OdmInfo" size="4" value="0x021"/> <fuse name="OdmId" size="8" value="0x0000001000218010"/> <!--13: fTPM Enable + 9: OEM key valid + 5: Revocation policy enable--> <fuse name="BootSecurityInfo" size="4" value="0x2220"/> <fuse name="SecurityMode" size="4" value="0x1"/> </genericfuse>
The following is a sample fuse.xml file for enabling encryption and signature verification.
Note
As of the current release, the SBK values in PSC, OESP, and SB must be set to the same value.
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name ="PublicKeyHash" size="64"
value="0xaa7ab9bf57a5747af4af080b86c1b7a651f839141da6ee80ebe3c562a5a8fb34e981d964bee653e93cd56e6b0896753d191ded586a12503fc8797d3d62ac3c08"/>
<fuse name="OptInEnable" size="4" value="0x00000001"/>
<fuse name="PscOemKdk0" size="32"
value="0x6208e3cd81ed0cd77b214db0c875ade40c26bca09382ad82cd0e24046cc8c64e"/>
<fuse name="PscOemKdk1" size="32"
value="0x112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"/>
<fuse name="OespOemKdk0" size="32"
value="0xc008421905de5493ff350b7e69b15196028fdf073f2b3f5e41f4f58f37240f29"/>
<fuse name="OespOemKdk1" size="32"
value="0xbd88ccea074705402afe649806ce6c5771730dc36a676fe23054c104aa48ddc0"/>
<fuse name="SbOemKdk0" size="32"
value="0x8261530878e05d37b8cab1d93e44c2ab0a17190c2f7b8366721b5201595909f5"/>
<fuse name="SbOemKdk1" size="32"
value="0xf1429596951a75f31aa7f787b4d7d1e23799f4f4ae02b17be73cafa62b7910a8"/>
<fuse name="OdmInfo" size="4" value="0x021"/>
<fuse name="OdmId" size="8" value="0x0000001000218010"/>
<fuse name="PscSecureBootKey" size="32"
value="0x123456789abcdef0fedcba987654321023456789abcdef01edcba9876543210f"/>
<fuse name="OespSecureBootKey" size="32"
value="0x123456789abcdef0fedcba987654321023456789abcdef01edcba9876543210f"/>
<fuse name="SbSecureBootKey" size="32"
value="0x123456789abcdef0fedcba987654321023456789abcdef01edcba9876543210f"/>
<!--13: fTPM Enable + 9: OEM key valid + 5: Revocation policy enable + 3: Secure boot encryption enable-->
<fuse name="BootSecurityInfo" size="4" value="0x2228"/>
<fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
Fuse the Board#
Generate your fuse blob.
Note
In this Quick Start guide, we demonstrate generating a fuse blob without using an FSKP key. The resulting fuse blob is unencrypted. You can use this method to fuse the board for experiment or development purposes.
To fuse for production, we recommend generating an encrypted fuse blob by using an FSKP key. For more information, refer to Factory Secure Key and Expansion Key Provisioning.
Note
The following command generates a dry-run fuse blob. If you use this dry-run fuse blob when executing step 2 (burn the fuse), the fuse blob is sent to the device but not actually burned.
To prepare a fuse blob that can be burned to the device, replace
--testwith--burnin the following command.cd <top>/l4t/tools/flashtools/fuseburn sudo ./fskp_fuseburn.py --board-spec thor-agx-board-spec.txt \ -f ~/work_dir/fuse_config/fuse.xml --skipfskpkey --test \ -g out/ -c 0x26 -B <top>/jetson-agx-thor-devkit.conf
Select No when prompted for continuation of the board fusing. You should now have an
outfolder in<top>/l4t/tools/flashtools/fuseburn.Burn the fuse.
Set your device to recovery mode:
cd <top> sudo ./tools/board_automation/boardctl -t topo recovery
Run the following commands to send the pre-generated fuse blob and trigger the fuse burning process:
Caution
Fuse burning is irreversible and permanently modifies your board. To actually burn the fuses, regenerate the fuse blob in step 1 (generate your fuse blob) with
--burnin place of--test, and then proceed with the following command. A fuse blob that you generate with--testis sent to the device but no fuses are burned.cd <top>/l4t/tools/flashtools/fuseburn sudo ./fskp_fuseburn.py --board-spec thor-agx-board-spec.txt -P out/ \ -c 0x26 -B <top>/jetson-agx-thor-devkit.conf
Select Yes when prompted to continue. You can verify a successful dry run or fuse burn by checking the UART output of the board for a confirmation message in the second terminal window.
Prepare the EKB Image#
To generate the EKB image, you need to use the PSC KDK key that you have just burned to the
fuse. The <auth_t264.key> is the key stored inside the Encrypted Key Blob (EKB). The
<oem_kdk1.key> burned to the PSC_OEM_KDK1 fuse is used to encrypt and sign the EKB.
<auth_t264.key> is the UEFI variable authentication key. In the current reference
implementation, this is the key used for
UEFI Variable Protection.
cd <top>/source/optee/samples/hwkey-agent/host/tool/gen_ekb
### Fill PscOemKdk1 fused content that is burned in the previous step as oem_kdk1.key
echo "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00" >oem_kdk1.key
### Generate a random number to auth_t264.key
openssl rand -rand /dev/urandom -hex 16 >auth_t264.key
### Generate eks_t264.img
python3 gen_ekb.py -chip t264 \
-oem_kdk1_key oem_kdk1.key \
-in_auth_key auth_t264.key \
-out eks_t264.img
### Copy the newly generated image into <top>/bootloader
sudo cp eks_t264.img <top>/bootloader/
For more details on these steps, refer to EKB Generation.
Secure UEFI#
Enable UEFI Secure Boot to ensure that the UEFI payload signature—specifically, the OS loader—is verified prior to execution.
For details, refer to UEFI Secure Boot.
Prepare UEFI Keys#
To generate the PK, KEK, and db RSA key pairs, their certificates, and the EFI signature list files, run the following commands:
cd <top>
mkdir uefi_keys
cd uefi_keys
GUID=$(uuidgen)
### Generate PK RSA Key Pair, Certificate, and EFI Signature List File
openssl req -newkey rsa:2048 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj \
"/CN=my Platform Key/" -out PK.crt
cert-to-efi-sig-list -g "${GUID}" PK.crt PK.esl
### Generate KEK RSA Key Pair, Certificate, and EFI Signature List File
openssl req -newkey rsa:2048 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj \
"/CN=my Key Exchange Key/" -out KEK.crt
cert-to-efi-sig-list -g "${GUID}" KEK.crt KEK.esl
### Generate db_1 RSA Key Pair, Certificate, and EFI Signature List File
openssl req -newkey rsa:2048 -nodes -keyout db_1.key -new -x509 -sha256 -days 3650 -subj \
"/CN=my Signature Database key/" -out db_1.crt
cert-to-efi-sig-list -g "${GUID}" db_1.crt db_1.esl
### Generate db_2 RSA Key Pair, Certificate, and EFI Signature List File
openssl req -newkey rsa:2048 -nodes -keyout db_2.key -new -x509 -sha256 -days 3650 -subj \
"/CN=my another Signature Database key/" -out db_2.crt
cert-to-efi-sig-list -g "${GUID}" db_2.crt db_2.esl
Create UEFI Keys and Enroll Keys to Key Configuration File#
Create a UEFI keys configuration file. If enrolling an OEM kernel key, ensure that the
UEFI_DEFAULT_DB_ESL_1entry corresponds to thedb_1.eslfile generated in the Prepare UEFI Keys section:cd <top>/uefi_keys vim uefi_keys.conf
Insert the following lines:
UEFI_DB_1_KEY_FILE="db_1.key"; # UEFI payload signing key UEFI_DB_1_CERT_FILE="db_1.crt"; # UEFI payload signing key certificate UEFI_DEFAULT_PK_ESL="PK.esl" UEFI_DEFAULT_KEK_ESL_0="KEK.esl" UEFI_DEFAULT_DB_ESL_0="db_1.esl" UEFI_DEFAULT_DB_ESL_1="db_2.esl"
Generate the
UefiDefaultSecurityKeys.dtbofile:cd <top> sudo ./tools/gen_uefi_keys_dts.sh uefi_keys/uefi_keys.conf
Flash the Jetson Thor Device#
Set the device into recovery mode:
cd <top> sudo ./tools/board_automation/boardctl -t topo recovery
Verify that the device has successfully rebooted into recovery mode by listing your USB devices on your host machine and confirming that the NVIDIA device is listed as described in Environment Setup:
lsusb
Run the following commands to flash the Jetson device:
cd <top> sudo ./l4t_initrd_flash.sh -u ~/work_dir/pkc_keys/pkc_keylist.xml [-v <sbk_keyfile>] \ --uefi-keys uefi_keys/uefi_keys.conf jetson-agx-thor-devkit internal
The optional
-v <sbk_keyfile>flag is required only when the device image is SBK-protected (encryption enabled), as in the secondfuse.xmlexample shown earlier in Prepare the Fuse Configuration File. Here<sbk_keyfile>refers to the Secure Boot Key (SBK) file that matches the device’s fused keys; you must supply the SBK key when flashing encrypted images. Omit-vfor non-SBK/non-encrypted devices.
Reboot the Jetson Thor Device#
Reset the Jetson Thor device:
cd <top> sudo ./tools/board_automation/boardctl -t topo reset
Complete the NVIDIA OOBE on the monitor (attach an HDMI connector) to configure the system and create an account and password.
Log in to your account.
Check whether Secure Boot is enabled. Enter the following commands in the second terminal window.
Use
mokutil. (Ifmokutilis missing, install it withsudo apt install mokutil.)sudo mokutil --sb-state
The output is one of the following messages:
SecureBoot enabled
SecureBoot disabled
This system doesn’t support Secure Boot
The expected output is “SecureBoot enabled.”
Read the EFI variable directly:
sudo od -An -tx1 /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
The output is five bytes:
XX XX XX XX YY. The last byte (YY) is what matters:01indicates Secure Boot enabled.00indicates Secure Boot disabled.
Also check SetupMode:
sudo od -An -tx1 /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c
Examine the last byte of the output:
00indicates user mode (PK enrolled; Secure Boot is active).01indicates setup mode (no PK; Secure Boot is disabled); can accept new keys.
Verify that the enrolled keys are actually present:
ls /sys/firmware/efi/efivars/ | grep -E '^(PK|KEK|db|dbx)-'
The expected output is similar to the following:
db-d719b2cb-3d3a-4596-a3bc-dad00e67656f dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
You should see
db...,KEK-..., andPK-...entries. To dump them, useefi-readvar. (efi-readvarcomes fromefitools:sudo apt install efitools.)sudo efi-readvar -v PK sudo efi-readvar -v KEK sudo efi-readvar -v db sudo efi-readvar -v dbx