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-dev for USB debug port support.

  • openssh-server package for OpenSSL.

  • efitools package for the cert-to-efi-sig-list utility.

  • 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.tbz2

  • Tegra_Linux_Sample-Root-Filesystem_<VERSION>_aarch64.tbz2

  • host_overlay_fskp_tools_<VERSION>_aarch64.tbz2

  • public_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#

  1. Create a directory for your PKC keys:

    cd ~/work_dir
    mkdir pkc_keys
    cd pkc_keys
    touch pkc_keylist.xml
    
  2. Generate PKC keys.

    Tip

    We recommend that you generate all 16 keys because you can burn keys only once.

  3. Add all keys to pkc_keylist.xml. Ensure that all keys are listed in pkc_keylist.xml as 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 key in each entry must be the absolute path to the key.

  4. 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.xml as 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#

  1. Create your fuse configuration directory and create a fuse.xml file:

    cd ~/work_dir
    mkdir fuse_config
    cd fuse_config
    touch fuse.xml
    
  2. In fuse.xml, register the PKC hash as shown:

    <fuse name="PublicKeyHash" size="64" value="YOUR_PKC_HASH"/>
    
  3. 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#

  1. 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 --test with --burn in 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 out folder in <top>/l4t/tools/flashtools/fuseburn.

  2. Burn the fuse.

    1. Set your device to recovery mode:

      cd <top>
      sudo ./tools/board_automation/boardctl -t topo recovery
      
    2. 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 --burn in place of --test, and then proceed with the following command. A fuse blob that you generate with --test is 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#

  1. Create a UEFI keys configuration file. If enrolling an OEM kernel key, ensure that the UEFI_DEFAULT_DB_ESL_1 entry corresponds to the db_1.esl file 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"
    
  2. Generate the UefiDefaultSecurityKeys.dtbo file:

    cd <top>
    sudo ./tools/gen_uefi_keys_dts.sh uefi_keys/uefi_keys.conf
    

Flash the Jetson Thor Device#

  1. Set the device into recovery mode:

    cd <top>
    sudo ./tools/board_automation/boardctl -t topo recovery
    
  2. 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
    
  3. 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 second fuse.xml example 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 -v for non-SBK/non-encrypted devices.

Reboot the Jetson Thor Device#

  1. Reset the Jetson Thor device:

    cd <top>
    sudo ./tools/board_automation/boardctl -t topo reset
    
  2. Complete the NVIDIA OOBE on the monitor (attach an HDMI connector) to configure the system and create an account and password.

  3. Log in to your account.

  4. Check whether Secure Boot is enabled. Enter the following commands in the second terminal window.

    • Use mokutil. (If mokutil is missing, install it with sudo 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:

      • 01 indicates Secure Boot enabled.

      • 00 indicates 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:

      • 00 indicates user mode (PK enrolled; Secure Boot is active).

      • 01 indicates setup mode (no PK; Secure Boot is disabled); can accept new keys.

  5. 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-..., and PK-... entries. To dump them, use efi-readvar. (efi-readvar comes from efitools: sudo apt install efitools.)

    sudo efi-readvar -v PK
    sudo efi-readvar -v KEK
    sudo efi-readvar -v db
    sudo efi-readvar -v dbx