RidgeRun Platform Security Manual/Getting Started/Secure Boot: Difference between revisions

From RidgeRun Developer Wiki
No edit summary
 
(5 intermediate revisions by one other user not shown)
Line 12: Line 12:
== Secure Boot ==
== Secure Boot ==


Secure Boot on an NVIDIA Jetson system is activated to block the execution of code coming from untrusted sources. To achieve it, NVIDIA Jetson SoCs use [https://en.wikipedia.org/wiki/Public-key_cryptography public key cryptography] and the Root of Trust. The private key is used to sign the codes that are going to be executed when the board is initially booting and the public key is going to be stored in devices called fuses inside the board. To begin the booting process on an NVIDIA Jetson Xavier/Orin board, first a BootROM code is executed, which is the first element of the [https://developer.ridgerun.com/wiki/index.php/RidgeRun_Platform_Security_Manual/Platform_Security/Root_of_Trust Root of Trust] for these systems. It loads and autheticates the first codes that are going to be executed in the booting process. It authenticates by generating a public key hash from the private key digital signature embedded in each code, and compares it to the public key hash stored in the devices called fuses. If the keys match, the code comes from a trusted source and can be executed, otherwise it is not and the booting process is halted. So then, to activate a secure boot on an NVIDIA Jetson SoC, it is necessary to generate a PKC (Public Key Cryptography) key pair, store the public key hash on the fuses and sign the boot codes with the corresponding private key. As a relevant note, '''a fuse device can only be written once to''', so it is important to do it right. Check out the [[https://developer.ridgerun.com/wiki/index.php/RidgeRun_Platform_Security_Manual/Platform_Security/Secure_Boot Secure boot]] general page in this wiki for more information. In this guide we are going to take a look at how to activate this feature on a Jetson Orin Nano. Below is a general diagram of the process:
Secure Boot on an NVIDIA Jetson system is activated to block the execution of code coming from untrusted sources. To achieve it, NVIDIA Jetson SoCs use [https://en.wikipedia.org/wiki/Public-key_cryptography public key cryptography] and the Root of Trust. The private key is used to sign the codes that will be executed when the board is initially booting, and the public key will be stored in devices called fuses inside the board. To begin the booting process on an NVIDIA Jetson Xavier/Orin board, first, a BootROM code is executed, which is the first element of the [https://developer.ridgerun.com/wiki/index.php/RidgeRun_Platform_Security_Manual/Platform_Security/Root_of_Trust Root of Trust] for these systems. It loads and autheticates the first codes that are going to be executed in the booting process. It authenticates by generating a public key hash from the private key digital signature embedded in each code, and compares it to the public key hash stored in the devices called fuses. If the keys match, the code comes from a trusted source and can be executed, otherwise it is not and the booting process is halted. So then, to activate a secure boot on an NVIDIA Jetson SoC, it is necessary to generate a PKC (Public Key Cryptography) key pair, store the public key hash on the fuses and sign the boot codes with the corresponding private key. As a relevant note, '''a fuse device can only be written once to''', so it is important to do it right. Check out the [[https://developer.ridgerun.com/wiki/index.php/RidgeRun_Platform_Security_Manual/Platform_Security/Secure_Boot Secure boot]] general page in this wiki for more information. In this guide, we will look at how to activate this feature on a Jetson Orin Nano. Below is a general diagram of the process:


* This guide was tested using a Jetson Orin Nano Developer Kit, but with slight modifications, can be applied to Jetson Orin NX series, Jetson AGX Orin series, the Jetson Xavier NX series, and the Jetson AGX Xavier series. Specifically, the commands for generating the fuse blob are platform dependent, as well as flash commands.
* This guide was tested using a Jetson Orin Nano Developer Kit, but with slight modifications, can be applied to Jetson Orin NX series, Jetson AGX Orin series. Specifically, the commands for generating the fuse blob are platform dependent, as well as flash commands.


* This guide was tested using Ubuntu 20.04 LTS on the host(laptop where JetPack is installed used to flash the NVIDIA Jetson Orin/Xavier board).  
* This guide was tested using Ubuntu 20.04 LTS on the host (a laptop with NVIDIA SDK installed that was used to flash the NVIDIA Jetson Orin/Xavier board).  
 
{{review|can you elaborate which those modifications are?|lleon}}


====Factory Secure Key and Expansion Key Provisioning====
====Factory Secure Key and Expansion Key Provisioning====


Factory Secure Key and Expansion Key Provisioning (FSKP) is a technique to improve the security when burning fuses on an SoC. For reference, "burning" the fuses refers to storing a public key hash in them, which can only be done once. Before, a script named odmfuse was used to do this process, and it was purely managed by the OEM (Original Equipment Manufacturer), a.k.a. the person or team building the final product. Now, the additional security measure that FSKP brings, is that it encrypts the data passed to the SoC for fuse burning. This adds a layer of protection to the process. The fuse data is encrypted and sent to the SoC and later decrypted inside it. It will be encrypted with expansion keys provided by an NVIDIA representative, and decrypted with a key already on the IROM (internal Read Only Memory) on the SoC.
Factory Secure Key and Expansion Key Provisioning (FSKP) is a technique to improve the security when burning fuses on an SoC. For reference, "burning" the fuses refers to storing a public key hash in them, which can only be done once. Before, a script named odmfuse was used to do this process, and it was purely managed by the OEM (Original Equipment Manufacturer), a.k.a. the person or team building the final product. Now, the additional security measure that FSKP brings is that it encrypts the data passed to the SoC for fuse burning. This adds a layer of protection to the process. The fuse data is encrypted, sent to the SoC, and decrypted inside. It will be encrypted with expansion keys provided by an NVIDIA representative, and decrypted with a key already on the IROM (internal Read Only Memory) on the SoC.


[[File:FSKPdiagram.png|5px|frame|center|Fig 1. FSKP process diagram ]]
[[File:FSKPdiagram.png|5px|frame|center|Fig 1. FSKP process diagram ]]
Line 48: Line 46:
</syntaxhighlight>
</syntaxhighlight>


This passphrase is like a password for the key. This helps to protect the private key if someone gets access to the file that holds it. It encrypts it so that the only way to read it is by knowing this passphrase.
This passphrase is like a password for the key. This helps to protect the private key if someone gets access to the file that holds it. It encrypts it so that knowing this passphrase is the only way to read it.


Generate RSA x509 based Certification:
Generate RSA x509 based Certification:
Line 61: Line 59:
* key oem_rsa_priv.pem: Specifies the private key file to use for signing the certificate.
* key oem_rsa_priv.pem: Specifies the private key file to use for signing the certificate.
* out oem_publickey.cer: Specifies the output file name for the generated certificate. The certificate will be saved to a file named oem_publickey.cer. Although the extension is ".cer" which is common, the -outform PEM option means the file will contain PEM encoded data.
* out oem_publickey.cer: Specifies the output file name for the generated certificate. The certificate will be saved to a file named oem_publickey.cer. Although the extension is ".cer" which is common, the -outform PEM option means the file will contain PEM encoded data.
* days 365: Specifies the validity period of the certificate in days. This value should be set accordingly to the security policies of the OEM.  
* days 365: Specifies the validity period of the certificate in days. This value should be set accordingly to the security policies of the OEM. It is also worth noting that this key is only used to encrypt and decrypt the FSKP keys. After this, it won't be used anymore, so a relatively low time for the certificate should work, but it is up to every OEM's necessities.
 
* NVIDIA recommends these configurations in its documentation. A self-signed certificate and the PEM format work for this case because the OEM is the one creating the private key, and the PEM format is suitable for sending via email.
{{review|how many days are recommended?|lleon}}
 
{{review|is it recommended to use other configurations or just this one?|lleon}}


The output is going to ask you for the passphrase to read the private key and generate the certificate. It is going to also ask for information about your organization to add on the certificate:
The output will ask you for the passphrase to read the private key and generate the certificate. It is also going to ask for information about your organization to add on the certificate:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 74: Line 69:
into your certificate request.
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
There are quite a few fields, but you can leave some blank
For some fields there will be a default value,
For some fields, there will be a default value,
If you enter '.', the field will be left blank.
If you enter '.', the field will be left blank.
-----
-----
Line 87: Line 82:
</syntaxhighlight>
</syntaxhighlight>


RSA 4096 bits is used by NVIDIA Recommendation. Email this CER file to the NVIDIA key custodian to get the expansion keys back and some other files to complete the process. The files needed are going to come in a zip file called '''Results.zip'''. There are two expansion keys, the AK expansion key (FSKP_AK) and the EK expansion key (FSKP_EK). Below are the contents of the Results.zip file with a basic explanation for each.
The NVIDIA guide uses RSA 4096 bits. Email this CER file to the NVIDIA key custodian to get the expansion keys back and some other files to complete the process. The files needed will come in a zip file called Results.zip. There are two expansion keys: the AK expansion key (FSKP_AK) and the EK expansion key (FSKP_EK). Below are the contents of the Results.zip file, with a basic explanation for each.


* fskp_ak.bin.rsa_wrap: The FSKP_AK wrapped with the RSA public key (binary). This is the file to be decrypted to obtain the AK key as binary data.
* fskp_ak.bin.rsa_wrap: The FSKP_AK wrapped with the RSA public key (binary). This file will be decrypted to obtain the AK key as binary data.
* fskp_ak.bin.rsa_wrap.info: metadata, Label, and String/Context used in KDF (Key Derivation Function), and KCV (Key Check Value) of FSKP_AK.  
* fskp_ak.bin.rsa_wrap.info: metadata, Label, and String/Context used in KDF (Key Derivation Function), and KCV (Key Check Value) of FSKP_AK.  
* fskp_ek.bin.rsa_wrap: FSKP_EK wrapped with RSA public key (binary). This is the file to be decrypted to obtain the EK key as binary data.
* fskp_ek.bin.rsa_wrap: FSKP_EK wrapped with RSA public key (binary). This file will be decrypted to obtain the EK key as binary data.
* fskp_ek.bin.rsa_wrap.info: metadata, Label, and String/Context used in KDF (Key Derivation Function), and the KCV (Key Check Value) of FSKP_EK.
* fskp_ek.bin.rsa_wrap.info: metadata, Label, and String/Context used in KDF (Key Derivation Function), and the KCV (Key Check Value) of FSKP_EK.
* fskp_conf.txt: includes the AK and EK strings to encrypt the fuse data.
* fskp_conf.txt: includes the AK and EK strings to encrypt the fuse data.


As reference for some concepts previously mentioned:
As a reference for some concepts previously mentioned:


* KDF or Key Derivation Function: A function to generate the expansion keys from a root key. In this case, the root key is the Expansion key. This is the key that the NVIDIA representative has access to and is reference in the SoC with key index 62, and is stored in the IROM.
* KDF or Key Derivation Function: This function generates the expansion keys from a root key. In this case, the root key is the Expansion key. The NVIDIA representative has access to this key, which is referenced in the SoC with key index 62 and stored in the IROM.
* KCV or Key Check Value: A Key Check Value is a short cryptographic checksum of a cryptographic key. It's used to verify the integrity of a key, ensuring that it hasn't been altered or corrupted.
* KCV or Key Check Value: A Key Check Value is a short cryptographic checksum of a cryptographic key. It's used to verify the integrity of a key, ensuring that it hasn't been altered or corrupted.


Line 104: Line 99:
=====Prepare the necessary directories=====
=====Prepare the necessary directories=====


To begin the encryption of the fuse data, we need to set up the necessary directories. First install the NVIDIA's board support package by downloading the Driver Package (BSP) and Sample Root Filesystem from the drivers section on the following [https://docs.nvidia.com/jetson/archives/r36.4/DeveloperGuide/SD/Security/SecureBoot.html#uefi-secure-boot link]. Extract the downloaded files with the following commands inside a work directory (nvidia jetson in our case):
To begin the encryption of the fuse data, we need to set up the necessary directories. First, install the NVIDIA board support package by downloading the Driver Package (BSP) and Sample Root Filesystem from the drivers section on the following [https://docs.nvidia.com/jetson/archives/r36.4/DeveloperGuide/SD/Security/SecureBoot.html#uefi-secure-boot link]. Extract the downloaded files with the following commands inside a work directory (nvidia-jetson in our case):


<syntaxhighlight lang=bash>
<syntaxhighlight lang=bash>
Line 184: Line 179:
* FAB
* FAB


You can get the information of these variables by running the following command. This is for an NVIDIA Jetson Orin Nano with an SD Card. Remember to keep the board connected in recovery mode and with a USB-C to USB cable:
You can get the information on these variables by running the following command. This is for an NVIDIA Jetson Orin Nano with an SD Card. Remember to keep the board connected in recovery mode and with a USB-C to USB cable:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 199: Line 194:
</syntaxhighlight>
</syntaxhighlight>


Take the values obtained from the previous output and write on the board specification file inside the l4t/ tools/flashtools/fuseburn. In our case it is named '''orinnano-board-spec.txt'''. With the output obtained for the board we are using, the file modified should look like below:
Take the values from the previous output and write them in the specification file inside the l4t/ tools/flashtools/fuseburn. In our case, it is named '''orinnano-board-spec.txt'''. With the output obtained for the board we are using, the file modified should look like below:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 255: Line 250:
</syntaxhighlight>
</syntaxhighlight>


Which as we can see has zeros in all of them because we have not write to them. Once a 1 has been written to one of the bits of the fuses, it cannot be reversed. The next step to activate secure boot is to generate the keys that are going to be stored in the fuses. For this example we are going to just use the PublicKeyHash fuse and SecureBootKey fuse. The Public key is use for boot code authentication and the secure boot key is used for boot codes encryption and decryption.
Which, as we can see, has zeros in all of them because we have not written to them. Once a 1 has been written to one of the bits of the fuses, it cannot be reversed. The next step to activate secure boot is to generate the keys that will be stored in the fuses. For this example, we are going to just use the PublicKeyHash fuse and SecureBootKey fuse. The Public key is used for boot code authentication, and the secure boot key is used for boot code encryption and decryption.


Starting with the Public key used for authenticating the boot codes, it can be generated as an RSA 3K key, ECDSA P-256 or ECDSA P-521 key. For reference, these are two types of algorithms for genetring a PKC pair and the numbers are just the size of the keys. In terms of security RSA 3K and ECDSA P-256 provide a similar level of security but ECDSA is more efficient because it needs less storage. ECDSA P-521 provides a higher security level the the previous algorithms explained and has a lower size than RSA 3K. RSA 3K is a more known/applied/compatible algorithm. For this guide, an RSA 3K key is going to be used. To generated, create a keys folder inside the Linux for Tegra directory:
Starting with the Public key used for authenticating the boot codes, it can be generated as an RSA 3K key, ECDSA P-256 or ECDSA P-521 key. For reference, these are two algorithms for generating a PKC pair, and the numbers are just the size of the keys. In terms of security, RSA 3K and ECDSA P-256 provide a similar level of security, but ECDSA is more efficient because it needs less storage. ECDSA P-521 provides a higher security level than the previous algorithms explained and is smaller than RSA 3K. RSA 3K is a more known/applied/compatible algorithm. An RSA 3K key will be used for this guide. To generate, create a keys folder inside the Linux for Tegra directory:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 268: Line 263:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Generating RSA private key, 3072 bit long modulus (2 primes)
Generating RSA private key, 3072-bit long modulus (2 primes)
.................................................................++++
.................................................................++++
...................................................................++++
...................................................................++++
Line 274: Line 269:
</syntaxhighlight>
</syntaxhighlight>


A file named rsa_priv.pem should be in the directory. To generate the Public key hash, the tegrasign_v3.py script can be used to generate it, use this command from the keys directory:
A file named rsa_priv.pem should be in the directory. To generate the Public key hash, the tegrasign_v3.py script can be used. Use this command from the keys directory:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 280: Line 275:
</syntaxhighlight>
</syntaxhighlight>


the output looks like the following:
The output looks like the following:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 289: Line 284:
</syntaxhighlight>
</syntaxhighlight>


make sure to save the result because it is going to be need on the fuse configuration file explained below.
Make sure to save the result, as it will be needed in the fuse configuration file explained below.


Now, the secure boot key is randomly generated with a size of 32 bits. Specifically, the Orin SoC requires an SBK key of eight 32-bit words. You can use the following commands to generate it:
Now, the secure boot key is randomly generated with a size of 32 bits. Specifically, the Orin SoC requires an SBK key of eight 32-bit words. You can use the following commands to generate it:
Line 311: Line 306:
A sbk.key and a sbk_xml.key file are now in the directory. the second one has the value that is going to be used in the fuse configuration file.
A sbk.key and a sbk_xml.key file are now in the directory. the second one has the value that is going to be used in the fuse configuration file.


In this case we use openssl to generate the 8 random 32 byte words. It is used because it's a widely adopted, open-source cryptographic toolkit that provides robust functionality for securing communications and data. But you can use the tool of your choice.
In this case, we use OpenSSL to generate the eight random 32-byte words. It is used because it's a widely adopted, open-source cryptographic toolkit that provides robust functionality for securing communications and data. But you can use the tool of your choice.


The next step is to create the fuse configuration file. The fuse configuration file is a xml file that states that which fuse is burned with x value. It has three main tags which are name, size and value. In the same order, the tags are for the fuse name, its size in bytes and the value to be burned on to the fuse. There is a list of fuses on the Orin SoCs, but we are going to use only the PublicKeyHash, SecureBootKey and BootSecurityInfo fuses. For reference, below is the Jetson Orin reference configuration file for your reference:
The next step is to create the fuse configuration file. The fuse configuration file is an XML file that states which fuse is burned with the x value. It has three main tags: name, size, and value. In the same order, the tags are for the fuse name, its size in bytes and the value to be burned onto the fuse. There is a list of fuses on the Orin SoCs, but we will use only the PublicKeyHash, SecureBootKey and BootSecurityInfo fuses. For reference, below is the Jetson Orin reference configuration file:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 351: Line 346:
It is also important to check the [https://developer.download.nvidia.com/assets/embedded/secure/jetson/agx_orin/Jetson-Orin-Fuse-Specification_DA-10877-001v1.4.pdf?__token__=exp=1742448417~hmac=d2e6139780a4fc1fed9eb010099f9002929c35437006cd2a532ce03d5c41cbfd&t=eyJscyI6ImdzZW8iLCJsc2QiOiJodHRwczovL3d3dy5nb29nbGUuY29tLyJ9 Jetson Orin Fuse Specification: Application Note] to get information of the fuses being burned.
It is also important to check the [https://developer.download.nvidia.com/assets/embedded/secure/jetson/agx_orin/Jetson-Orin-Fuse-Specification_DA-10877-001v1.4.pdf?__token__=exp=1742448417~hmac=d2e6139780a4fc1fed9eb010099f9002929c35437006cd2a532ce03d5c41cbfd&t=eyJscyI6ImdzZW8iLCJsc2QiOiJodHRwczovL3d3dy5nb29nbGUuY29tLyJ9 Jetson Orin Fuse Specification: Application Note] to get information of the fuses being burned.


Create and open a fuse_config.xml file with your favourite text editor and add the fuse burning data. In our case it looks like the following. As a note MagicId of “0x45535546” is used by the target-binary and must not be changed.:  
Create and open a fuse_config.xml file with your favourite text editor and add the fuse burning data. In our case, it looks like the following. As a note MagicId of “0x45535546” is used by the target-binary and must not be changed.:  


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 361: Line 356:
</syntaxhighlight>
</syntaxhighlight>


With the fuse data generated, we can proceed with the encryption of it. In this process we will also activate the decryption of the fuse blob (fuse data encrypted) and the fuse burning in the board. To generate the fuse blob use the following command:
With the fuse data generated, we can proceed with its encryption. In this process, we will also activate the decryption of the fuse blob (fuse data encrypted) and the fuse burning on the board. To generate the fuse blob, use the following command:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 373: Line 368:
* --fskpcfg fskp_conf.txt: Specifies the fskp configuration file that has the expansion keys string. This file is included with the expansion keys sent by the NVIDIA representative.
* --fskpcfg fskp_conf.txt: Specifies the fskp configuration file that has the expansion keys string. This file is included with the expansion keys sent by the NVIDIA representative.
* -g out/: Specifies the output directory for the fuse blob.
* -g out/: Specifies the output directory for the fuse blob.
* -c 0x23: Specifies the chip id, which for Jetson Orin SoCs is 0x23.
* -c 0x23: Specifies the chip ID, which for Jetson Orin SoCs is 0x23.
* -B ~/nvidia-jetson/Linux_for_Tegra/jetson-orin-nano-devkit.conf: Specifies the board configuration file. It is already in the Linux_for_Tegra folder. Choose according to the board you are using.
* -B ~/nvidia-jetson/Linux_for_Tegra/jetson-orin-nano-devkit.conf: Specifies the board configuration file. It is already in the Linux_for_Tegra folder. Choose according to the board you are using.
* --board-spec orinnano-board-spec.txt: Specifies the board specification file previously edited.
* --board-spec orinnano-board-spec.txt: Specifies the board specification file previously edited.
Line 397: Line 392:
</syntaxhighlight>
</syntaxhighlight>


You should get the successful message at the end of the command's output. To actually burn the fuses run the following command.
You should get the successful message at the end of the command's output. To burn the fuses, run the following command.


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 422: Line 417:
Downloading FSKP blob to target
Downloading FSKP blob to target


WARNING!! Target will automatically reset once burning fuses is complete.
WARNING!! Target will automatically reset once the burning fuses are complete.
           If you are going to continue doing secure NOR provisioning, please DO NOT power off the system
           If you are going to continue doing secure NOR provisioning, please DO NOT power off the system
FSKP execution successful
FSKP execution successful
Line 428: Line 423:
</syntaxhighlight>
</syntaxhighlight>


It asks if you are sure about the operation because it is irreversible, write yes if you are. If you have the UART output from the board You should look for the following messages:
It asks if you are sure about the operation because it is irreversible, write yes if you are. If you have the UART output from the board, you should look for the following messages:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 453: Line 448:
</syntaxhighlight>  
</syntaxhighlight>  


Secure boot is succesfully enabled. From now on, all the images and boot codes you load to the board have to be signed with the private keys created in this process.  
Secure boot has been successfully enabled. From now on, all the images and boot codes you load to the board must be signed with the private keys created in this process.  


=== UEFI Secure Boot ===
=== UEFI Secure Boot ===


UEFI secure boot is a process to prevent the execution of the codes it loads, that are not from a trusted source, a more in depth explanation is found in the UEFI Secure Boot section in the NVIDIA Jetson example case from the general [[https://developer.ridgerun.com/wiki/index.php/RidgeRun_Platform_Security_Manual/Platform_Security/Secure_Boot| Secure Boot]] page on this wiki. It is a compliment to the secure boot previously explained since it authenticates the codes that follow initial boot process prior to the execution of the UEFI bootloader.
UEFI secure boot is a process that prevents executing code that is not from a trusted source. A more in-depth explanation is found in the UEFI Secure Boot section in the NVIDIA Jetson example case from the general [[https://developer.ridgerun.com/wiki/index.php/RidgeRun_Platform_Security_Manual/Platform_Security/Secure_Boot| Secure Boot]] page on this wiki. It complements the secure boot previously explained since it authenticates the codes that follow the initial boot process before executing the UEFI bootloader.


==== How does UEFI Secure Boot Work? ====
==== How does UEFI Secure Boot Work? ====
Line 467: Line 462:
* Signature Database (db) : Contains keys to sign UEFI payloads.
* Signature Database (db) : Contains keys to sign UEFI payloads.


These keys and the keys database are first saved as a UEFI authenticated variable. This is done so that when UEFI payloads are loaded, they are verified looking for the associated certificate/key and comparing it to the ones on db (key database). The boot process will not finish as expected if they do not have the right certificate/key. The UEFI payloads are:
These keys and the keys database are first saved as a UEFI authenticated variable. This is done so that when UEFI payloads are loaded, they are verified, and the associated certificate/key is looked for and compared to the ones on the database (key database). The boot process will not finish as expected if they do not have the right certificate/key. The UEFI payloads are:


* extlinux.conf
* extlinux.conf
Line 483: Line 478:
* efitools: It allows you to generate, sign, and manage cryptographic keys used for UEFI Secure Boot.
* efitools: It allows you to generate, sign, and manage cryptographic keys used for UEFI Secure Boot.


* uuid-runtime: Is a package that provides utilities for generating and managing Universally Unique Identifiers (UUIDs). UUIDs are 128-bit numbers used to uniquely identify information in computer systems.  
* uuid-runtime: This package provides utilities for generating and managing Universally Unique Identifiers (UUIDs). UUIDs are 128-bit numbers used to identify information in computer systems uniquely.  


To install these dependencies, run the following command:
To install these dependencies, run the following command:
Line 493: Line 488:
==== Generate RSA key pairs, certificates and EFI signature list File ====
==== Generate RSA key pairs, certificates and EFI signature list File ====


{{review|do we need another certificate? Please, clarify|lleon}}
In order to activate UEFI Secure Boot, first, let's create a folder for the UEFI keys inside the Linux for Tegra folder and generate the Platform Key(PK) and certificate:
 
In order to activate UEFI Secure Boot, first, let's create a folder for the uefi keys inside the Linux for Tegra folder and generate the Platform Key(PK) and certificate:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 505: Line 498:
</syntaxhighlight>
</syntaxhighlight>


* -newkey rsa:2048: new RSA private key with a key length of 2048 bits.
* -newkey rsa:2048: new RSA private key with a key length of 2048 bits.  
* -nodes: Tells openssl not to encrypt the private key.
* -nodes: Tells openssl not to encrypt the private key.
* -keyout PK.key: This specifies the output file for the private key, named PK.key.
* -keyout PK.key: This specifies the output file for the private key, named PK.key.
Line 602: Line 595:
* db_2.crt: This is the input file, which is the X.509 certificate that you want to convert.  
* db_2.crt: This is the input file, which is the X.509 certificate that you want to convert.  
* db_2.esl: This is the output file, which will be the EFI signature list. The .esl extension is commonly used for EFI signature lists.
* db_2.esl: This is the output file, which will be the EFI signature list. The .esl extension is commonly used for EFI signature lists.
'''NOTE:''' The generated .crt files are self-signed certificates for demonstration purposes only. For production, follow your official certificate generation procedure. Also, the keys generated use the RSA technique with a size of 2048 bits. There is no restriction to using a bigger size key as long as it is consistent throughout the commands used. In regard to other algorithms, such as ECDSA, this has not been tested yet.


Output should look like the following:
Output should look like the following:


<syntaxhighlight lang="bash>
<syntaxhighlight lang="bash>
Generating a RSA private key
Generating an RSA private key
.............................+++++
.............................+++++
.....+++++
.....+++++
Line 627: Line 624:
db_1.crt  db_1.key  db_2.esl  KEK.crt  KEK.key  PK.esl  db_1.esl  db_2.crt  db_2.key  KEK.esl  PK.crt  PK.key
db_1.crt  db_1.key  db_2.esl  KEK.crt  KEK.key  PK.esl  db_1.esl  db_2.crt  db_2.key  KEK.esl  PK.crt  PK.key
</syntaxhighlight>
</syntaxhighlight>
{{review|can you tell if these settings are safe to change?|lleon}}


==== Create the UEFI Keys Config File ====
==== Create the UEFI Keys Config File ====
Line 655: Line 650:
==== Generate the UEFI Secure Boot DTBO ====
==== Generate the UEFI Secure Boot DTBO ====


This device tree blob overlay is used to store the security keys in the form of UEFI authenticated variable. It works by adding a modification to the device tree image to generate the UEFI authenticated variable with the database. To generate it, first, move to the Linux for Tegra folder:
This device tree blob overlay stores the security keys as a UEFI authenticated variable. It works by modifying the device tree image to generate the UEFI authenticated variable with the database. To generate it, first, move to the Linux for Tegra folder:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 686: Line 681:
==== Flash the board with the --uefi_keys option ====
==== Flash the board with the --uefi_keys option ====


To activate UEFI Secure Boot, run the flash command with the uefi-keys option. Remember to put the board in recovery mode and connect it to the host. For an NVIDIA Jetson Orin Nano with an SD Card, the command is:
To activate UEFI Secure Boot, run the flash command using the uefi-keys option. Remember to put the board in recovery mode and connect it to the host. For an NVIDIA Jetson Orin Nano with an SD Card, the command is:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 692: Line 687:
</syntaxhighlight>  
</syntaxhighlight>  


Now the board has the UEFI Secure Boot activated.
Now the board has the UEFI Secure Boot activated. To verify it, run the following command inside the board:
 
<syntaxhighlight lang="bash">
efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-SecureBoot
</syntaxhighlight>
 
And the output should be 01.


<noinclude>
<noinclude>

Latest revision as of 19:47, 26 March 2025


NVIDIA partner logo NXP partner logo






Secure Boot

Secure Boot on an NVIDIA Jetson system is activated to block the execution of code coming from untrusted sources. To achieve it, NVIDIA Jetson SoCs use public key cryptography and the Root of Trust. The private key is used to sign the codes that will be executed when the board is initially booting, and the public key will be stored in devices called fuses inside the board. To begin the booting process on an NVIDIA Jetson Xavier/Orin board, first, a BootROM code is executed, which is the first element of the Root of Trust for these systems. It loads and autheticates the first codes that are going to be executed in the booting process. It authenticates by generating a public key hash from the private key digital signature embedded in each code, and compares it to the public key hash stored in the devices called fuses. If the keys match, the code comes from a trusted source and can be executed, otherwise it is not and the booting process is halted. So then, to activate a secure boot on an NVIDIA Jetson SoC, it is necessary to generate a PKC (Public Key Cryptography) key pair, store the public key hash on the fuses and sign the boot codes with the corresponding private key. As a relevant note, a fuse device can only be written once to, so it is important to do it right. Check out the [Secure boot] general page in this wiki for more information. In this guide, we will look at how to activate this feature on a Jetson Orin Nano. Below is a general diagram of the process:

  • This guide was tested using a Jetson Orin Nano Developer Kit, but with slight modifications, can be applied to Jetson Orin NX series, Jetson AGX Orin series. Specifically, the commands for generating the fuse blob are platform dependent, as well as flash commands.
  • This guide was tested using Ubuntu 20.04 LTS on the host (a laptop with NVIDIA SDK installed that was used to flash the NVIDIA Jetson Orin/Xavier board).

Factory Secure Key and Expansion Key Provisioning

Factory Secure Key and Expansion Key Provisioning (FSKP) is a technique to improve the security when burning fuses on an SoC. For reference, "burning" the fuses refers to storing a public key hash in them, which can only be done once. Before, a script named odmfuse was used to do this process, and it was purely managed by the OEM (Original Equipment Manufacturer), a.k.a. the person or team building the final product. Now, the additional security measure that FSKP brings is that it encrypts the data passed to the SoC for fuse burning. This adds a layer of protection to the process. The fuse data is encrypted, sent to the SoC, and decrypted inside. It will be encrypted with expansion keys provided by an NVIDIA representative, and decrypted with a key already on the IROM (internal Read Only Memory) on the SoC.

Fig 1. FSKP process diagram

To begin the process, first generate an RSA key pair and a certificate from it. This is done to send the certificate to the NVIDIA representative and get the expansion keys encrypted.

Generate 4096 bits RSA public and private keys:

* aes-256-cbc: 256 bit keys to encrypt RSA key with AES (Advanced Encryption Standard).
* out oem_rsa_priv.pem 4096: Specifies the output file name and the size of the RSA key.

This command is going to ask for a passphrase to encrypt. Keep it in mind for the next steps. The output looks like the following:

<syntaxhighlight lang="bash">
Generating RSA private key, 4096 bit long modulus (2 primes)
....................++++
........++++
e is 65537 (0x010001)
Enter pass phrase for oem_rsa_priv.pem:
Verifying - Enter pass phrase for oem_rsa_priv.pem:

This passphrase is like a password for the key. This helps to protect the private key if someone gets access to the file that holds it. It encrypts it so that knowing this passphrase is the only way to read it.

Generate RSA x509 based Certification:

openssl req -new -x509 –outform PEM -key oem_rsa_priv.pem -out oem_publickey.cer -days 365
  • new: Indicates that a new certificate should be generated.
  • x509: Specifies that a self-signed certificate should be generated, rather than a CSR (Certificate Sign Request). A self-signed certificate is one that is signed by its own private key.
  • outform PEM: Specifies the output format of the certificate as PEM (Privacy Enhanced Mail), which is a common format for storing certificates.
  • key oem_rsa_priv.pem: Specifies the private key file to use for signing the certificate.
  • out oem_publickey.cer: Specifies the output file name for the generated certificate. The certificate will be saved to a file named oem_publickey.cer. Although the extension is ".cer" which is common, the -outform PEM option means the file will contain PEM encoded data.
  • days 365: Specifies the validity period of the certificate in days. This value should be set accordingly to the security policies of the OEM. It is also worth noting that this key is only used to encrypt and decrypt the FSKP keys. After this, it won't be used anymore, so a relatively low time for the certificate should work, but it is up to every OEM's necessities.
  • NVIDIA recommends these configurations in its documentation. A self-signed certificate and the PEM format work for this case because the OEM is the one creating the private key, and the PEM format is suitable for sending via email.

The output will ask you for the passphrase to read the private key and generate the certificate. It is also going to ask for information about your organization to add on the certificate:

Enter pass phrase for oem_rsa_priv.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields, but you can leave some blank
For some fields, there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CR
State or Province Name (full name) [Some-State]:San Jose
Locality Name (eg, city) []:San Jose
Organization Name (eg, company) [Internet Widgits Pty Ltd]:RidgeRun
Organizational Unit Name (eg, section) []:RidgeRun
Common Name (e.g. server FQDN or YOUR name) []:RidgeRun
Email Address []:ridgerun_inquiries@ridgerun.com

The NVIDIA guide uses RSA 4096 bits. Email this CER file to the NVIDIA key custodian to get the expansion keys back and some other files to complete the process. The files needed will come in a zip file called Results.zip. There are two expansion keys: the AK expansion key (FSKP_AK) and the EK expansion key (FSKP_EK). Below are the contents of the Results.zip file, with a basic explanation for each.

  • fskp_ak.bin.rsa_wrap: The FSKP_AK wrapped with the RSA public key (binary). This file will be decrypted to obtain the AK key as binary data.
  • fskp_ak.bin.rsa_wrap.info: metadata, Label, and String/Context used in KDF (Key Derivation Function), and KCV (Key Check Value) of FSKP_AK.
  • fskp_ek.bin.rsa_wrap: FSKP_EK wrapped with RSA public key (binary). This file will be decrypted to obtain the EK key as binary data.
  • fskp_ek.bin.rsa_wrap.info: metadata, Label, and String/Context used in KDF (Key Derivation Function), and the KCV (Key Check Value) of FSKP_EK.
  • fskp_conf.txt: includes the AK and EK strings to encrypt the fuse data.

As a reference for some concepts previously mentioned:

  • KDF or Key Derivation Function: This function generates the expansion keys from a root key. In this case, the root key is the Expansion key. The NVIDIA representative has access to this key, which is referenced in the SoC with key index 62 and stored in the IROM.
  • KCV or Key Check Value: A Key Check Value is a short cryptographic checksum of a cryptographic key. It's used to verify the integrity of a key, ensuring that it hasn't been altered or corrupted.

You should also receive an FSKP tool package containing the instruments needed for the fuse burning process.

Prepare the necessary directories

To begin the encryption of the fuse data, we need to set up the necessary directories. First, install the NVIDIA board support package by downloading the Driver Package (BSP) and Sample Root Filesystem from the drivers section on the following link. Extract the downloaded files with the following commands inside a work directory (nvidia-jetson in our case):

mkdir nvidia-jetson

tar xf Jetson_Linux_R36.4.0_aarch64.tbz2 -C nvidia-jetson/
sudo tar xpf Tegra_Linux_Sample-Root-Filesystem_R36.4.0_aarch64.tbz2 -C \
nvidia-jetson/Linux_for_Tegra/rootfs

Run the following scripts inside the Linux for tegra directory:

cd nvidia-jetson/Linux_for_Tegra

sudo ./tools/l4t_flash_prerequisites.sh

sudo ./apply_binaries.sh

Output should look like the following:

.
.
.
L4T BSP package installation completed!
Disabling NetworkManager-wait-online.service
Disable the ondemand service by changing the runlevels to 'K'
Success!

(Optional) Create a Default User

 sudo ./tools/l4t_create_default_user.sh -u <user_name> -p <password>

With NVIDIA's board support package installed, untar the FSKP tool package inside the Linux for Tegra directory. Assuming the FSKP tool package tar file was downloaded to the nvidia-jetson directory, use the following command:

tar xvjf ../fskp_partner_t234_R3x.x.0_aarch64.tbz2

The resulting directory is named l4t. Unzip the Results.zip file inside l4t/tools/flashtools/fuseburn/Results directory. Assuming you downloaded the Results.zip file inside the nvidia-jetson directory:

cp ../Results.zip l4t/tools/flashtools/fuseburn/Results

cd l4t/tools/flashtools/fuseburn/Results

unzip Results.zip

Decrypt AK and EK expansion keys binary data:

openssl rsautl -decrypt -inkey oem_rsa_priv.pem -in fskp_ak.bin.rsa_wrap > fskp_ak.bin

openssl rsautl -decrypt -inkey oem_rsa_priv.pem -in fskp_ek.bin.rsa_wrap > fskp_ek.bin

Move these files from the Results directory to the fuseburn directory (one level above):

mv fskp_ak.bin ..
mv fskp_ek.bin ..
mv fskp_conf.txt ..
Prepare and encrypt the fuse data

The encrypted fuse binary data is called a fuse blob. This file is going to be decrypted in the SoC with the IROM stored key. Before going over how to prepare it, make sure the board specifications and configuration files have the right information about the board. The main variables to take into account are:

  • BOARDID
  • CHIP_SKU
  • BOARDSKU
  • RAMCODE_ID
  • FAB

You can get the information on these variables by running the following command. This is for an NVIDIA Jetson Orin Nano with an SD Card. Remember to keep the board connected in recovery mode and with a USB-C to USB cable:

sudo ./flash.sh --read-info jetson-orin-nano-devkit internal

Almost at the end of the output, there should be a similar text to the following with the values we are looking for.

Board ID(3767) version(300) sku(0005) revision(K.2)
Preset RAMCODE is 2
Chip SKU(00:00:00:D5) ramcode(2) fuselevel(fuselevel_production) board_FAB(300)
ECID is 0x80012344705DE5196C000000100102C0

Take the values from the previous output and write them in the specification file inside the l4t/ tools/flashtools/fuseburn. In our case, it is named orinnano-board-spec.txt. With the output obtained for the board we are using, the file modified should look like below:

Linux_for_Tegra/l4t/tools/flashtools/fuseburn$ cat orinnano-board-spec.txt 
BOARDID=3767
BOARDSKU=0004
FAB=000
CHIP_SKU=00:00:00:D6
FUSELEVEL=fuselevel_production
RAMCODE_ID=4

The most common values for Orin boards are in the following table:

Board Orin AGX Orin NX Orin Nano
BOARDID 3701 3767 3767
CHIP_SKU 00:00:00:D0 00:00:00:D3 00:00:00:D6
BOARD_SKU 0004 0000 0004
RAMCODE_ID 0 0 4
FAB 000 000 000

There should also be information about the current state of the fuses, similar to the following:

PublicKeyHash: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
PkcPubkeyHash1: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
PkcPubkeyHash2: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
BootSecurityInfo: 00000000
ArmJtagDisable: 00000000
SecurityMode: 00000000
SwReserved: 00000000
DebugAuthentication: 00000000
OdmInfo: 00000000
OdmId: 0000000000000000
OdmLock: 00000000
ReservedOdm0: 00000000
ReservedOdm1: 00000000
ReservedOdm2: 00000000
ReservedOdm3: 00000000
ReservedOdm4: 00000000
ReservedOdm5: 00000000
ReservedOdm6: 00000000
ReservedOdm7: 00000000
Sku: 000000d5
Uid: c00201100000006c19e55d7004000000
OptEmcDisable: 0000000c

Which, as we can see, has zeros in all of them because we have not written to them. Once a 1 has been written to one of the bits of the fuses, it cannot be reversed. The next step to activate secure boot is to generate the keys that will be stored in the fuses. For this example, we are going to just use the PublicKeyHash fuse and SecureBootKey fuse. The Public key is used for boot code authentication, and the secure boot key is used for boot code encryption and decryption.

Starting with the Public key used for authenticating the boot codes, it can be generated as an RSA 3K key, ECDSA P-256 or ECDSA P-521 key. For reference, these are two algorithms for generating a PKC pair, and the numbers are just the size of the keys. In terms of security, RSA 3K and ECDSA P-256 provide a similar level of security, but ECDSA is more efficient because it needs less storage. ECDSA P-521 provides a higher security level than the previous algorithms explained and is smaller than RSA 3K. RSA 3K is a more known/applied/compatible algorithm. An RSA 3K key will be used for this guide. To generate, create a keys folder inside the Linux for Tegra directory:

mkdir keys
cd keys
openssl genrsa -out rsa_priv.pem 3072

the output should look like the following:

Generating RSA private key, 3072-bit long modulus (2 primes)
.................................................................++++
...................................................................++++
e is 65537 (0x010001)

A file named rsa_priv.pem should be in the directory. To generate the Public key hash, the tegrasign_v3.py script can be used. Use this command from the keys directory:

./tegrasign_v3.py --pubkeyhash rsa_priv.pubkey rsa_priv.hash --key rsa_priv.pem

The output looks like the following:

Key size is 384 bytes
Saving pkc public key in rsa_priv.pubkey
Sha saved in pcp.sha
tegra-fuse format (big-endian): 0x513a1d8764dbd5d700932d18db4f48da4c13094b4986660c4525d69ef2307ceaba65ddec675bacc8bbeb2c91d31f1e7fb98be4472a354010ff7dea49510b7b48

Make sure to save the result, as it will be needed in the fuse configuration file explained below.

Now, the secure boot key is randomly generated with a size of 32 bits. Specifically, the Orin SoC requires an SBK key of eight 32-bit words. You can use the following commands to generate it:

SBK_0=$(openssl rand -hex 4)
SBK_1=$(openssl rand -hex 4)
SBK_2=$(openssl rand -hex 4)
SBK_3=$(openssl rand -hex 4)
SBK_4=$(openssl rand -hex 4)
SBK_5=$(openssl rand -hex 4)
SBK_6=$(openssl rand -hex 4)
SBK_7=$(openssl rand -hex 4)
SBK_KEY=$(echo "0x${SBK_0} 0x${SBK_1} 0x${SBK_2} 0x${SBK_3} 0x${SBK_4} 0x${SBK_5} 0x${SBK_6}
0x${SBK_7}")
echo "${SBK_KEY}" > sbk.key
SBK_KEY_XML=$(echo "0x${SBK_0}${SBK_1}${SBK_2}${SBK_3}${SBK_4}${SBK_5}${SBK_6}${SBK_7}")
echo "${SBK_KEY_XML}" > sbk_xml.key

A sbk.key and a sbk_xml.key file are now in the directory. the second one has the value that is going to be used in the fuse configuration file.

In this case, we use OpenSSL to generate the eight random 32-byte words. It is used because it's a widely adopted, open-source cryptographic toolkit that provides robust functionality for securing communications and data. But you can use the tool of your choice.

The next step is to create the fuse configuration file. The fuse configuration file is an XML file that states which fuse is burned with the x value. It has three main tags: name, size, and value. In the same order, the tags are for the fuse name, its size in bytes and the value to be burned onto the fuse. There is a list of fuses on the Orin SoCs, but we will use only the PublicKeyHash, SecureBootKey and BootSecurityInfo fuses. For reference, below is the Jetson Orin reference 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="ArmJtagDisable" size="4" value="0x1"/> -->
    <!-- <fuse name="DebugAuthentication" size="4" value="0x1F"/> -->
    <!-- <fuse name="CcplexDfdAccessDisable" size="4" value="0x1"/> -->
    <!-- <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="OdmLock" size="4" value="0xF"/> -->
    <!-- <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="0x1"/> -->
    <!-- <fuse name="SwReserved" size="4" value="0xFFFFFF"/> -->
    <!-- <fuse name="BootDevInfo" size="4" value="0xFFFFFF"/> -->
    <!-- <fuse name="ZeroizeDis" size="4" value="0x1"/> -->
    <!-- <fuse name="PublicKeyHash" size="64" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="PkcPubkeyHash1" size="64" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="PkcPubkeyHash2" size="64" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="EndorseKey" size="68" value="0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="SecureBootKey" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="Kdk0" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="PscOdmStatic" size="4" value="0xFFFFFFFF"/> -->
    <!-- <fuse name="OemK1" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="OemK2" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> -->
    <!-- <fuse name="BootSecurityInfo" size="4" value="0xFFFFFFFF"/> -->
    <!-- <fuse name="SecurityMode" size="4" value="0x1"/> -->
</genericfuse>

It is also important to check the Jetson Orin Fuse Specification: Application Note to get information of the fuses being burned.

Create and open a fuse_config.xml file with your favourite text editor and add the fuse burning data. In our case, it looks like the following. As a note MagicId of “0x45535546” is used by the target-binary and must not be changed.:

<genericfuse MagicId="0x45535546" version="1.0.0">
    <fuse name="PublicKeyHash" size="64" value="0x99a6b7d25ffd5d7cc49bf2612d01d7fe58b5121f9c473748728232bc114c25ae2415d56666157c79fc9bf0e3b4445344ff8af51a64f334289912cdff7414fa00"/>
    <fuse name="SecureBootKey" size="32" value="0xf4d8f0c3180f8b2b430d89e1eb0d600c01f99be7a3e9045d4b27d621de571d57"/>
    <fuse name="BootSecurityInfo" size="4" value="0x9"/>
</genericfuse>

With the fuse data generated, we can proceed with its encryption. In this process, we will also activate the decryption of the fuse blob (fuse data encrypted) and the fuse burning on the board. To generate the fuse blob, use the following command:

sudo ./fskp_fuseburn.py -b -f ~/nvidia-jetson/Linux_for_Tegra/keys/fuse_config.xml -i 63 --key-exp fskp_ak.bin fskp_ek.bin --fskpcfg fskp_conf.txt -g out/ -c 0x23 -B ~/work/devdir/security-features-RnD/nvidia-jetson4/Linux_for_Tegra/jetson-orin-nano-devkit.conf --board-spec orinnano-board-spec.txt -v
  • -b: Performs fuse burning. In this command, this option is used to generate a real fuse blob but the next command is the one used for actually burning the fuses.
  • -f ~/nvidia-jetson/Linux_for_Tegra/keys/fuse_config.xml: specifies the fuse configuration file to generate the fuse blob.
  • -i 63: Specifies the key index. This is used to select the key from the internal ROM to decrypt the information. The expansion keys are generated from this key by the NVIDIA representative, and they are the "public key " in this case and the key from the internal ROM is the private one. Key index 63 is used in this case because this is a key for debugging purposes but behaves equally to the one used for production purposes, which is key 62.
  • --key-exp fskp_ak.bin fskp_ek.bin: Specifies the expansion keys.
  • --fskpcfg fskp_conf.txt: Specifies the fskp configuration file that has the expansion keys string. This file is included with the expansion keys sent by the NVIDIA representative.
  • -g out/: Specifies the output directory for the fuse blob.
  • -c 0x23: Specifies the chip ID, which for Jetson Orin SoCs is 0x23.
  • -B ~/nvidia-jetson/Linux_for_Tegra/jetson-orin-nano-devkit.conf: Specifies the board configuration file. It is already in the Linux_for_Tegra folder. Choose according to the board you are using.
  • --board-spec orinnano-board-spec.txt: Specifies the board specification file previously edited.

The output should look similar to the following:

FSKP execution started 2025-03-14 12:59:55.523192
fskp_fuseburn.py script version 0.2
Parsing input arguments
fskp_fuseburn.py script version 0.2
Parsing input arguments
Setting up default paths
Setup host environment
fskp enviroment internal: False
Creating t234 fuse blob
.
.
.
Not burning fuses, exiting...
FSKP execution successful
FSKP execution time 0:00:09.341467

You should get the successful message at the end of the command's output. To burn the fuses, run the following command.

sudo ./fskp_fuseburn.py --board-spec orinnano-board-spec.txt -P ./out -b -c 0x23 -B ~/work/devdir/security-features-RnD/nvidia-jetson4/Linux_for_Tegra/jetson-orin-nano-devkit.conf

The option added in this command is the -P option. It has the argument out/ and it is used to specify that it is no required to generate a new folder with the fuse blob but to use the out directory, that already has it. You should get an output like the following:

FSKP execution started 2025-03-14 13:43:33.137784
fskp_fuseburn.py script version 0.2
Parsing input arguments
fskp_fuseburn.py script version 0.2
Parsing input arguments
Setting up default paths
Setup host environment
Found NVIDIA device ID 0x7523
Getting target details
BR_CID: 0x80012344705DE5196C000000100102C0
ECID: 0x4705DE5196C000000100102C0
WARNING!! Burning Fuses option is selected, this operation is permanent and irreversible
if you are not sure, try with --test or -t command line option
do you want to continue burning fuses (Yes/No) yes
Downloading FSKP blob to target

WARNING!! Target will automatically reset once the burning fuses are complete.
          If you are going to continue doing secure NOR provisioning, please DO NOT power off the system
FSKP execution successful
FSKP execution time 0:00:07.774871

It asks if you are sure about the operation because it is irreversible, write yes if you are. If you have the UART output from the board, you should look for the following messages:

I> Task: Burn fuses                                                                        
I> Index : 1    PublicKeyHash    size: 64                                                  
I> Index : 2    SecureBootKey    size: 32                                                  
I> Index : 3    BootSecurityInfo    size: 4                                                
I> Fuse Blob found                                                                         
I>                                                                                         
I> Burning fuses                                                                           
I> 1. Start PublicKeyHash burn                                                             
I> 1. PublicKeyHash burnt successfully                                                     
W> No handling of CRC-32 for PublicKeyHash                                                 
I>                                                                                         
I> 2. Start SecureBootKey burn                                                             
I> 2. SecureBootKey burnt successfully                                                     
W> No handling of CRC-32 for SecureBootKey                                                 
I>                                                                                         
I> 3. Start BootSecurityInfo burn                                                          
I> 3. BootSecurityInfo burnt successfully                                                  
W> No handling of CRC-32 for BootSecurityInfo                                              
I>                                                                                         
I> Successfully burnt fuses as per fuse info

Secure boot has been successfully enabled. From now on, all the images and boot codes you load to the board must be signed with the private keys created in this process.

UEFI Secure Boot

UEFI secure boot is a process that prevents executing code that is not from a trusted source. A more in-depth explanation is found in the UEFI Secure Boot section in the NVIDIA Jetson example case from the general [Secure Boot] page on this wiki. It complements the secure boot previously explained since it authenticates the codes that follow the initial boot process before executing the UEFI bootloader.

How does UEFI Secure Boot Work?

UEFI Secure Boot employs RSA digital signatures to authenticate and verify the integrity of the code it loads during startup. Below are the main components used:

  • Platform Key (PK) : Top-level key, is used to sign KEK.
  • Key Exchange Key (KEK) : Keys used to sign Signatures Database.
  • Signature Database (db) : Contains keys to sign UEFI payloads.

These keys and the keys database are first saved as a UEFI authenticated variable. This is done so that when UEFI payloads are loaded, they are verified, and the associated certificate/key is looked for and compared to the ones on the database (key database). The boot process will not finish as expected if they do not have the right certificate/key. The UEFI payloads are:

  • extlinux.conf
  • initrd
  • kernel images (in rootfs, and kernel and recovery partitions)
  • kernel-dtb images (in rootfs, and kernel-dtb and recovery-dtb partitions)
  • BOOTAA64.efi

To enable UEFI Secure Boot, first install the following dependencies:

  • openssl: Needed to generate keys, digital signatures and certificates and use them to sign codes.
  • device-tree-compiler: To compile device tree sources that are going to be used in the boot process to enroll keys for code authentication.
  • efitools: It allows you to generate, sign, and manage cryptographic keys used for UEFI Secure Boot.
  • uuid-runtime: This package provides utilities for generating and managing Universally Unique Identifiers (UUIDs). UUIDs are 128-bit numbers used to identify information in computer systems uniquely.  

To install these dependencies, run the following command:

sudo apt install openssl device-tree-compiler efitools uuid-runtime

Generate RSA key pairs, certificates and EFI signature list File

In order to activate UEFI Secure Boot, first, let's create a folder for the UEFI keys inside the Linux for Tegra folder and generate the Platform Key(PK) and certificate:

mkdir uefi_keys

cd uefi_keys

openssl req -newkey rsa:2048 -nodes -keyout PK.key  -new -x509 -sha256 -days 3650 -subj "/CN=my Platform Key/" -out PK.crt
  • -newkey rsa:2048: new RSA private key with a key length of 2048 bits.
  • -nodes: Tells openssl not to encrypt the private key.
  • -keyout PK.key: This specifies the output file for the private key, named PK.key.
  • -new: Creates a new certificate signing request (in this case, used to create a self signed certificate).
  • -x509: tells openssl to generate a self-signed X.509 certificate instead of a CSR (Certificate Signing Request).
  • -sha256: This specifies the SHA-256 hash algorithm for the certificate's signature.
  • -days 3650: Sets the validity period of the certificate to 3650 days (10 years).
  • -subj "/CN=my Platform Key/": Sets the subject of the certificate, which contains information about the certificate's owner.
  • -out PK.crt: This specifies the output file for the certificate, named PK.crt

Generate the PK EFI signature list from X.509 certificate. As reference, the EFI signature list contains the digital signatures in the UEFI bootloader.

cert-to-efi-sig-list -g "${GUID}" PK.crt PK.esl
  • -g "${GUID}": Specifies the GUID (Globally Unique Identifier) that will be associated with the signature list. GUIDs are used to identify different types of signatures and policies in UEFI Secure Boot.
  • PK.crt: This is the input file, which is the X.509 certificate that you want to convert.
  • PK.esl: This is the output file, which will be the EFI signature list. The .esl extension is commonly used for EFI signature lists.

Generate the KEK RSA key and certificate:

openssl req -newkey rsa:2048 -nodes -keyout KEK.key  -new -x509 -sha256 -days 3650 -subj "/CN=my Key Exchange Key/" -out KEK.crt
  • -newkey rsa:2048: new RSA private key with a key length of 2048 bits.
  • -nodes: Tells openssl not to encrypt the private key.
  • -keyout KEK.key: This specifies the output file for the private key, named KEK.key.
  • -new: Creates a new certificate signing request (in this case, used to create a self signed certificate).
  • -x509: tells openssl to generate a self-signed X.509 certificate instead of a CSR (Certificate Signing Request).
  • -sha256: This specifies the SHA-256 hash algorithm for the certificate's signature.
  • -days 3650: Sets the validity period of the certificate to 3650 days (10 years).
  • -subj "/CN= Key Exchange Key/": Sets the subject of the certificate, which contains information about the certificate's owner.
  • -out KEK.crt: This specifies the output file for the certificate, named KEK.crt

Generate the KEK EFI signature list from X.509 certificate.

cert-to-efi-sig-list -g "${GUID}" KEK.crt KEK.esl
  • -g "${GUID}": Specifies the GUID (Globally Unique Identifier) that will be associated with the signature list. GUIDs are used to identify different types of signatures and policies in UEFI Secure Boot.
  • KEK.crt: This is the input file, which is the X.509 certificate that you want to convert.
  • KEK.esl: This is the output file, which will be the EFI signature list. The .esl extension is commonly used for EFI signature lists.

Generate the db_1 RSA Key and certificate.

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
  • -newkey rsa:2048: new RSA private key with a key length of 2048 bits.
  • -nodes: Tells openssl not to encrypt the private key.
  • -keyout db_1.key: This specifies the output file for the private key, named db_1.key.
  • -new: Creates a new certificate signing request (in this case, used to create a self signed certificate).
  • -x509: tells openssl to generate a self-signed X.509 certificate instead of a CSR (Certificate Signing Request).
  • -sha256: This specifies the SHA-256 hash algorithm for the certificate's signature.
  • -days 3650: Sets the validity period of the certificate to 3650 days (10 years).
  • -subj "/CN= my Signature Database key/": Sets the subject of the certificate, which contains information about the certificate's owner.
  • -out db_1.crt: This specifies the output file for the certificate, named db_1.crt

Generate the db_1 EFI signature list from X.509 certificate.

cert-to-efi-sig-list -g "${GUID}" db_1.crt db_1.esl
  • -g "${GUID}": Specifies the GUID (Globally Unique Identifier) that will be associated with the signature list. GUIDs are used to identify different types of signatures and policies in UEFI Secure Boot.
  • db_1.crt: This is the input file, which is the X.509 certificate that you want to convert.
  • db_1.esl: This is the output file, which will be the EFI signature list. The .esl extension is commonly used for EFI signature lists.

Generate the db_2 RSA Key and certificate.

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
  • -newkey rsa:2048: new RSA private key with a key length of 2048 bits.
  • -nodes: Tells openssl not to encrypt the private key.
  • -keyout db_2.key: This specifies the output file for the private key, named db_2.key.
  • -new: Creates a new certificate signing request (in this case, used to create a self signed certificate).
  • -x509: tells openssl to generate a self-signed X.509 certificate instead of a CSR (Certificate Signing Request).
  • -sha256: This specifies the SHA-256 hash algorithm for the certificate's signature.
  • -days 3650: Sets the validity period of the certificate to 3650 days (10 years).
  • -subj "/CN= My another Signature Database/": Sets the subject of the certificate, which contains information about the certificate's owner.
  • -out db_2.crt: This specifies the output file for the certificate, named db_2.crt

Generate the db_2 EFI signature list from X.509 certificate.

cert-to-efi-sig-list -g "${GUID}" db_2.crt db_2.esl
  • -g "${GUID}": Specifies the GUID (Globally Unique Identifier) that will be associated with the signature list. GUIDs are used to identify different types of signatures and policies in UEFI Secure Boot.
  • db_2.crt: This is the input file, which is the X.509 certificate that you want to convert.
  • db_2.esl: This is the output file, which will be the EFI signature list. The .esl extension is commonly used for EFI signature lists.


NOTE: The generated .crt files are self-signed certificates for demonstration purposes only. For production, follow your official certificate generation procedure. Also, the keys generated use the RSA technique with a size of 2048 bits. There is no restriction to using a bigger size key as long as it is consistent throughout the commands used. In regard to other algorithms, such as ECDSA, this has not been tested yet.


Output should look like the following:

Generating an RSA private key
.............................+++++
.....+++++
writing new private key to 'PK.key'
-----
Generating a RSA private key
......+++++
.+++++
writing new private key to 'db_2.key'
-----

and you should get three files (.crt .esl .key) for each component:

uefi_keys$ ls
db_1.crt  db_1.key  db_2.esl  KEK.crt  KEK.key  PK.esl  db_1.esl  db_2.crt  db_2.key  KEK.esl  PK.crt   PK.key

Create the UEFI Keys Config File

Open a file named uefi_keys.conf with your preferred text editor and add these 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"

where:

  • UEFI_DB_1_KEY_FILE and UEFI_DB_1_CERT_FILE are the key and certificate used to sign UEFI payloads
  • UEFI_DEFAULT_PK_ES is the Platform Key EFI Signature list.
  • UEFI_DEFAULT_KEK_ESL_0 is the Key encryption key EFI Signature list.
  • UEFI_DEFAULT_DB_ESL_0 is the EFI Signature List of the signature database (db).
  • UEFI_DEFAULT_DB_ESL_1 is the EFI Signature List of the signature database for known untrusted code signatures (dbx).

Generate the UEFI Secure Boot DTBO

This device tree blob overlay stores the security keys as a UEFI authenticated variable. It works by modifying the device tree image to generate the UEFI authenticated variable with the database. To generate it, first, move to the Linux for Tegra folder:

cd ..

Use the gen_uefi_keys_dts script to generate it:

sudo tools/gen_uefi_keys_dts.sh uefi_keys/uefi_keys.conf

You should get a similar output to the following:

Linux_for_Tegra$ sudo tools/gen_uefi_keys_dts.sh uefi_keys/uefi_keys.conf
Info: generating default keys dtbo.
Info: adding node PKDefault.
Info: adding node KEKDefault.
Info: adding node dbDefault.
Info: dts file is generated to UefiDefaultSecurityKeys.dts
Info: dtbo file is generated to UefiDefaultSecurityKeys.dtbo
Info: UefiDefaultSecurityKeys.dts and corresponding dtbo are generated.
Info: generating update keys dtbo.
removed 'UefiUpdateSecurityKeys.dts'
Info: no update key dtbo is generated due to no update keys are provided.
Info: generating dtbo is done.

Flash the board with the --uefi_keys option

To activate UEFI Secure Boot, run the flash command using the uefi-keys option. Remember to put the board in recovery mode and connect it to the host. For an NVIDIA Jetson Orin Nano with an SD Card, the command is:

sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device mmcblk0p1 -c tools/kernel_flash/flash_l4t_external.xml -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" --uefi-keys uefi_keys/uefi_keys.conf --showlogs --network usb0 jetson-orin-nano-devkit internal

Now the board has the UEFI Secure Boot activated. To verify it, run the following command inside the board:

efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-SecureBoot

And the output should be 01.