Enable Secure Boot for Pre-UEFI Phases#

You can enable end-to-end secure boot for your production NVIDIA IGX device. For an overview, see Secure Boot for IGX.

Secure boot for pre-UEFI phases uses the NVIDIA SoC fuses stored root of trust to authenticate boot codes from IGX power-up to the code end before UEFI.

To enable secure boot for pre-UEFI phases, do the following steps:

1. Request the FSKP Keys from NVIDIA#

To request the Factory Secure Key Provisioning (FSKP) keys, contact your NVIDIA representative.

2. Generate the Encrypted and Signed Blob#

To generate the encrypted and signed blob, do the following steps:

A. Generate the PKC Asymmetric Key Pairs and Key List#

IGX’s fuse supports three types of public key cryptography (PKC) keys: RSA 3K, ECDSA P-256, and ECDSA P-521. The fuse can store up to 16 hashed keys. The digest of all 16 public key digests is burned to the PublicKeyHash fuse. Any of the keys can be used to sign boot images. The information of all PKC keys is stored in a key list file in the form of an xml file.

The following example generates keys and key list by using RSA 3K.

  1. To generate the PKC private keys rsa3k-0.pem, rsa3k-1.pem, and rsa3k-2.pem, run the following code.

    openssl genrsa -out rsa3k-0.pem 3072
    openssl genrsa -out rsa3k-1.pem 3072
    openssl genrsa -out rsa3k-2.pem 3072
    
  2. To generate a PKC key list, use the following template to create your PKC key list:

    <?xml version="1.0"?>
    <entry_list>
       <bct active_index="<active_key_id>" chip_id="0x260" pcp_file="nv_combo.pcp" pcps_file="nv_combo.pcps" pcps_hash_file="nv_combo.pcps.hash" />
       <entry hash_file="<public_key_digest_filename>"  key="<private_key_filename>"  key_id="0"  mode="<key_mode>"  pub_file="<public_key_filename>" />
       <entry hash_file="<public_key_digest_filename>"  key="<private_key_filename>"  key_id="1"  mode="<key_mode>"  pub_file="<public_key_filename>" />
       <entry hash_file="<public_key_digest_filename>"  key="<private_key_filename>"  key_id="2"  mode="<key_mode>"  pub_file="<public_key_filename>" />
       <entry hash_file="<public_key_digest_filename>"  key="<private_key_filename>"  key_id="3"  mode="<key_mode>"  pub_file="<public_key_filename>" />
       . . .
       <entry hash_file="<public_key_digest_filename>"  key="<private_key_filename>"  key_id="15" mode="<key_mode>"  pub_file="<public_key_filename>" />
    </entry_list>
    
    Where the following fields are needed to be filled:
    • <active_key_id> is the index of the active key that will be used to sign images.

    • <public_key_digest_filename> is the name of the output file containing the public key digest file.

    • <private_key_filename> is the absolute path to the input file containing the private key file.

    • <public_key_filename> is the name of the output file of the public key file.

    • <key_mode> is the mode of the key. Fill pkc for RSA-3K keys, ec for ECDSA P-256 keys, and ec521 for ECDSA P-521 keys.

    Default fields in the PKC key list:
    • pcp_file is the output public cryptography parameter file of the active key.

    • pcps_file is the output file of the concatenation of all public key digests.

    • pcps_hash_file is the output file containing the digest of the pcps_file.

    Example PKC key list with three RSA-3K keys:

    <?xml version="1.0"?>
    <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="rsa3k-0.hash"  key="/path/to/rsa3k-0.pem"  key_id="0"  mode="pkc"  pub_file="rsa3k-0.pubkey" />
       <entry hash_file="rsa3k-1.hash"  key="/path/to/rsa3k-1.pem"  key_id="1"  mode="pkc"  pub_file="rsa3k-1.pubkey" />
       <entry hash_file="rsa3k-2.hash"  key="/path/to/rsa3k-2.pem"  key_id="2"  mode="pkc"  pub_file="rsa3k-2.pubkey" />
    </entry_list>
    

    Example PKC key list with a mix of RSA-3K, ECDSA P-256, and ECDSA P-521 keys:

    <?xml version="1.0"?>
    <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="rsa3k-0.hash"   key="/path/to/rsa3k-0.pem"   key_id="0"   mode="pkc"     pub_file="rsa3k-0.pubkey" />
       <entry hash_file="rsa3k-1.hash"   key="/path/to/rsa3k-1.pem"   key_id="1"   mode="pkc"     pub_file="rsa3k-1.pubkey" />
       <entry hash_file="rsa3k-2.hash"   key="/path/to/rsa3k-2.pem"   key_id="2"   mode="pkc"     pub_file="rsa3k-2.pubkey" />
       <entry hash_file="rsa3k-3.hash"   key="/path/to/rsa3k-3.pem"   key_id="3"   mode="pkc"     pub_file="rsa3k-3.pubkey" />
       <entry hash_file="ecp256-0.hash"  key="/path/to/ecp256-0.pem"  key_id="4"   mode="ec"      pub_file="ecp256-0.pubkey" />
       <entry hash_file="ecp256-1.hash"  key="/path/to/ecp256-1.pem"  key_id="5"   mode="ec"      pub_file="ecp256-1.pubkey" />
       <entry hash_file="ecp256-2.hash"  key="/path/to/ecp256-2.pem"  key_id="6"   mode="ec"      pub_file="ecp256-2.pubkey" />
       <entry hash_file="ecp256-3.hash"  key="/path/to/ecp256-3.pem"  key_id="7"   mode="ec"      pub_file="ecp256-3.pubkey" />
       <entry hash_file="ecp521-0.hash"  key="/path/to/ecp521-0.pem"  key_id="8"   mode="ecp521"  pub_file="ecp521-0.pubkey" />
       <entry hash_file="ecp521-1.hash"  key="/path/to/ecp521-1.pem"  key_id="9"   mode="ecp521"  pub_file="ecp521-1.pubkey" />
       <entry hash_file="ecp521-2.hash"  key="/path/to/ecp521-2.pem"  key_id="10"  mode="ecp521"  pub_file="ecp521-2.pubkey" />
       <entry hash_file="ecp521-3.hash"  key="/path/to/ecp521-3.pem"  key_id="11"  mode="ecp521"  pub_file="ecp521-3.pubkey" />
       <entry hash_file="ecp521-4.hash"  key="/path/to/ecp521-4.pem"  key_id="12"  mode="ecp521"  pub_file="ecp521-4.pubkey" />
       <entry hash_file="ecp521-5.hash"  key="/path/to/ecp521-5.pem"  key_id="13"  mode="ecp521"  pub_file="ecp521-5.pubkey" />
       <entry hash_file="ecp521-6.hash"  key="/path/to/ecp521-6.pem"  key_id="14"  mode="ecp521"  pub_file="ecp521-6.pubkey" />
       <entry hash_file="ecp521-7.hash"  key="/path/to/ecp521-7.pem"  key_id="15"  mode="ecp521"  pub_file="ecp521-7.pubkey" />
    </entry_list>
    

Note

The path to private key file must be in absolute path.

  1. To generate the public key hash value from the PKC key list, run the following code.

    sudo ./tegrasign_v3.py --key <pkc_key_list> --pubkeyhash <active_pkc.pcp> <pkc_key_list.hash>
    
    Where:
    • <pkc_key_list> is the name of the PKC key list file generated in the previous step.

    • <active_pkc.pcp> is the name of the output file containing the public cryptography parameter file of the active key.

    • <pkc_key_list.hash> is the name of the output file of the public key digest of all the public keys in the PKC key list.

    Later, add the hexadecimal value shown on the screen after “tegra-fuse format (big-endian)” to the fuse configuration file as the PublicKeyHash fuse data.

Note

We highly suggest generating all 16 keys, because after the PublicKeyHash fuse is burned, there’s no way to update the key list.

B. (Optional) Generate an SBK Symmetric Key#

To further enhance secure boot, you can optionally encrypt bootloader components.

Note

You can’t encrypt bootloader components if secure boot is not enabled.

To encyrpt bootloader components, you use a symmetric SBK key fused to the IGX’s SoC. The SBK key is used as an encryption key when the bootloader components are generated. The SBK key is used as a decryption key when the IGX is booted.

The IGX SoC requires an SBK key of eight 32-bit words (32 bytes) in length. To generate a 32-byte SBK key, run the following code.

openssl rand -rand /dev/urandom -hex 32 2>&1 |tee sbk.key

Later, add the key to the fuse configuration file as the PscSecureBootKey, OespSecureBootKey, and SbSecureBootKey fuse data. PscSecureBootKey, OespSecureBootKey, and SbSecureBootKey must be identical.

We recommend that you use the Hardware Security Module (HSM) to generate a truly random number for an SBK key.

C. Prepare the KDK1 keys#

The KDK1 key is a pre-fused 256 bits symmetry key which you can use for other security applications, such as deriving the key for EKB generation. You must prepare a key, and other ODM fuse bits, as described in the documentation for the other security application.

For IGX, the fuse key name is PscOemKdk1, and the key length must be 32 bytes. To generate a 32-byte OemKdk1 key file, run the following code.

openssl rand -rand /dev/urandom -hex 32 2>&1 |tee OemKdk1

Later, add the key to the fuse configuration file.

We recommend that you use the Hardware Security Module (HSM) to generate a truly random number for OemKdk1 key.

Caution

Guard your private key files. The security of your IGX device depends on the security of your private key files.

D. Prepare the fuse Configuration file#

The fuse configuration file contains the fuse data, a list of fuses, and the value to be burned in each fuse. The FSKP tool uses the configuration file to program the fuses.

A fuse configuration file contains a <genericfuse> element, and one <fuse> element for each fuse to burn. The hash of the PKC key list is named PublicKeyHash and the names are case sensitive. The fuse SecurityMode must be the last element in the file.

Caution

After secure boot mode is enabled, the fuse is locked and it can’t be changed.

The fuse BootSecurityInfo contains a 4-byte value which you must set by using the following information:

  • Bit[3]: set to 1 if you are using an optional SBK key

  • Bit[5]: set to 1 to enable revocation policy

  • Bit[9]: set to 1 when either PublicKeyHash or PscOemKdk1 is burned

  • Bit[13]: set to 1 to enable fTPM

  • Set all other bits to 0

The following is an example of an IGX fuse configuration file. The file contains PKC, SBK Key, Kdk0, Kdk1 keys, and enable revocation policy and fTPM. For more information about Kdk0, please refer to the fTPM section in Jetson Linux Developer Guide.

<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="OespOemKdk0" size="32" value="0xac88dead695089ed2aee491b180264873e966a61b609db4977f073aea41b132b"/>
   <fuse name="SbOemKdk0" size="32" value="0x35e174674e5ab8168023b83063886ca252c018bca1015d86cfd7c0d1d09b6659"/>
   <fuse name="PscOemKdk1" size="32" value="0x112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"/>
   <fuse name="OespOemKdk1" size="32" value="0xae4052f4e52541b238d6c63123e768ef519a3cdc8ff2e7cbd759e03d68a8775e"/>
   <fuse name="SbOemKdk1" size="32" value="0x881b43f0d8985031df039339d4eff6b30513e272633b527513b358195821ee37"/>
   <fuse name="PscSecureBootKey" size="32" value="0x123456789abcdef0fedcba987654321023456789abcdef01edcba9876543210f"/>
   <fuse name="OespSecureBootKey" size="32" value="0x123456789abcdef0fedcba987654321023456789abcdef01edcba9876543210f"/>
   <fuse name="SbSecureBootKey" size="32" value="0x123456789abcdef0fedcba987654321023456789abcdef01edcba9876543210f"/>
   <fuse name="BootSecurityInfo" size="4" value="0x2228"/>
   <fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>

Here is the Reference Fuse Configuration File:

<genericfuse MagicId="0x45535546" version="1.0.0">
   <!-- <fuse name="OdmId" size="8" value="0xFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="OdmInfo" size="4" value="0xFFFF"/> -->
   <!-- <fuse name="ReservedOdm0" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm1" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm2" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm3" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm4" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm5" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm6" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="ReservedOdm7" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="OptInEnable" size="4" value="0x00000001"/> -->
   <!-- <fuse name="PublicKeyHash" size="64" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="PscOemKdk0" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="OespOemKdk0" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="SbOemKdk0" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="PscOemKdk1" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="OespOemKdk1" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="SbOemKdk1" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="PscSecureBootKey" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="OespSecureBootKey" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="SbSecureBootKey" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
   <!-- <fuse name="BootSecurityInfo" size="4" value="0xFFFFFFFF"/> -->
   <!-- <fuse name="SecurityMode" size="4" value="0x1"/> -->
</genericfuse>

Note

The three SBK fuses, PscSecureBootKey, OespSecureBootKey and SbSecureBootKey, must be programmed with the same value.

Note

Although the fuse configuration file contains XML, it does not need the <?xml ?> prolog defined by the XML standard. If you want to run general purpose XML utilities on your configuration file, you can add a prolog.

E. Generate the Encrypted and Signed Blob#

To generate the encrypted and signed blob with FSKP key, run the following code. Replace fuseblob.xml with the name of your fuse configuration file.

  1. Download the following packages:

    • host_overlay_fskp_tools_<VERSION>_aarch64.tbz2

    • Jetson_Linux_<VERSION>_aarch64.tbz2

  2. Install the Jetson Linux release package

  3. Untar FSKP tools package:

    cd <top>/../
    tar xvjf host_overlay_fskp_tools_<VERSION>_aarch64.tbz2
    cd <top>/l4t/tools/flashtools/fuseburn/
    
  4. Make sure the board specification file igx-thor-board-spec.txt matches with your board. The variables can be retrieved by flash.sh with the --read-info command option.

    • For example, sudo ./flash.sh --read-info igx-thor-devkit internal:

      Board ID(3834) version(400) sku(0008) revision(H.1) Platform ID() Board Boot Device()
      RAMCODE is 12
      Chip SKU(00:00:00:A0) ramcode(12) fuselevel(fuselevel_production) board_FAB(400)
      
    • The corresponding board specification variables will be:

      BOARDID=3834
      FAB=400
      BOARDSKU=0008
      CHIP_SKU=00:00:00:A0
      RAMCODE_ID=12
      
  5. Run the following command to generate signed and encrypted blob:

    • To burn the fuses from x86 host:

      # Get the FSKP select
      FSKP_SELECT=$(cat fskp_select.txt)
      
      # Blob generation
      sudo ./fskp_fuseburn.py --board-spec igx-thor-board-spec.txt -f <FUSE_CONFIG_FILE> -i $FSKP_SELECT -t \
         -k <FSKP_KEY>  -g out/ -c 0x26 -B <top>/igx-thor-devkit.conf
      
    • To burn the fuses from BMC:

      cd <top>
      
      # Get the FSKP select
      FSKP_SELECT=$(cat fskp_select.txt)
      
      # Blob and BMC fuse package generation
      sudo ./l4t/tools/flashtools/fuseburn/bmc_fuse/package_bmc_fskp.sh -c 0x26 -f <FUSE_CONFIG_FILE> -i $FSKP_SELECT -t \
         -k <FSKP_KEY>
      

Note

The example uses the -t option for testing purposes, which performs only a dry run. Use the -b option instead if you want to perform actual fuse burning.

3. Burn the Fuses#

To burn the fuses, run the following code.

  • From x86 host:

    1. Put Tegra to recovery mode

    2. Run the following code:

    # Burn fuse
    sudo ./fskp_fuseburn.py --board-spec igx-thor-board-spec.txt -P ./out -c 0x26 -B <top>/igx-thor-devkit.conf
    
  • From BMC:

    # Copy the tarball to the BMC
    scp bmc_fuse_<VERSION>.tbz2 root@<bmc_ip>:/tmp
    # Log into the BMC with its credentials
    ssh root@<bmc_ip>
    
    cd /tmp
    tar -xvf bmc_fuse_<VERSION>.tbz2
    
    # Burn fuse
    bash bmcfuse/bmcfuse.sh
    

To read the fuse values through the Linux kernel, use the script /usr/sbin/nv_fuse_read.sh.

To list the supported fuses, run the following code.

sudo nv_fuse_read.sh -l

To read the value of a fuse, run the following code.

sudo nv_fuse_read.sh <fuse name>

For example, use the following code to get the exclusive chip identification (ECID) of the IGX board.

sudo nv_fuse_read.sh ecid

To read all fuse values, run the following code.

sudo nv_fuse_read.sh

4. Sign and Flash QSPI Boot Firmware Images#

To generate the encrypted and signed blob, do the following steps:

A. Generate a New EKB File#

Generate the EKB file after you program the IGX fuse, and before you flash the QSPI image. There are four keys included in the EKB file. The OemKdk1 key is fused to IGX fuse. The other three keys are generated by the NVIDIA-provided script example.sh. Any time you sign and flash the QSPI image, copy the eks_t264.img file to the <Linux_fo_Tegra>/bootloader directory.

You need the following prerequisites:

  • Python 3.9 or newer

  • pip3 install cryptography

  • pip3 install pycryptodome

  1. Download public_sources.tbz2 from the IGX Download Center.

    wget https://developer.nvidia.com/downloads/igx/v2.1.0/public_sources.tbz2
    
  2. Untar public_source.tbz2, change to the folder Linux_for_Tegra/source, untar nvidia-jetson-optee-source.tbz2.

    tar -xvf public_source.tbz2
    cd Linux_for_Tegra/source
    tar -xvf nvidia-jetson-optee-source.tbz2
    
  3. Change to the folder optee/samples/hwkey-agent/host/tool/gen_ekb/.

    cd ./optee/samples/hwkey-agent/host/tool/gen_ekb/
    
  4. Open the file example.sh with your text editor and make the following changes to the file.

    1. Go to the [T264 example] section and uncomment #echo "0000000000000000000000000000000000000000000000000000000000000000" > oem_kdk1.key.

    2. Replace 0000000000000000000000000000000000000000000000000000000000000000 with your fused Oem Kdk1 key.

    3. Uncomment the following lines.

      # openssl rand -rand /dev/urandom -hex 32 > sym_t264.key
      # openssl rand -rand /dev/urandom -hex 16 > sym2_t264.key
      # openssl rand -rand /dev/urandom -hex 16 > auth_t264.key
      
  5. Save your changes to example.sh and exit the file.

  6. Run ./example.sh to generate the file eks_t264.img.

  7. Copy eks_t264.img to the folder <LINUX_FOR_TEGRA>/bootloader.

B. Flash QSPI#

We recommend that you enable UEFI secure boot with QSPI secure boot at the same time. For details, see Enable UEFI Secure Boot at Flash Time.

If you want to enable QSPI secure boot only, use the following procedure.

  1. Navigate to the directory where you installed Jetson BSP.

  2. Put the IGX device into recovery mode. For details, see Put the system into recovery mode.

  3. To sign QSPI with PKC key, choose the signing key index, active_index, in PKC key list and run the following code. To optionally encrypt the payload with an SBK key, include the -v option and the sbk.key file argument.

    sudo ./flash.sh -u pkc_key_list.xml --qspi-only igx-thor-devkit external
    
    — Or —
    
    sudo ./flash.sh -u pkc_key_list.xml -v sbk.key --qspi-only igx-thor-devkit external
    

Next Steps#

After you complete the steps in this documentation, see Enable Secure Boot for UEFI.