Production Workflow#
This section provides the tools, templates, and step-by-step procedures for fTPM production when a single entity plays both the ODM and OEM roles. It covers everything from BSP installation through factory flashing.
For the conceptual design and the architectural tolerance that allows ODM and OEM to be two separate entities, refer to fTPM Production Flow.
Jetson BSP Installation#
Download the latest BSP from Jetson Linux Archive.
The Jetson BSP:
Jetson_Linux_${rel_ver}_aarch64.tbz2The sample root filesystem:
Tegra_Linux_Sample-Root-Filesystem_${rel_ver}_aarch64.tbz2The public source tarball:
public_sources.tbz2The FSKP tools package:
host_overlay_fskp_tools_${rel_ver}_aarch64.tbz2The toolchain (optional):
x-tools.tbz2
Create a
${BSP_TOP}folder.mkdir ${BSP_TOP} cd ${BSP_TOP}
Install the BSP.
Untar the BSP package.
tar jvxf ~/Downloads/Jetson_Linux_${rel_ver}_aarch64.tbz2
Untar the BSP source package.
tar jvxf ~/Downloads/public_sources.tbz2
Untar the sample RootFS.
cd Linux_for_Tegra/rootfs sudo tar jvxpf ~/Downloads/Tegra_Linux_Sample-Root-Filesystem_${rel_ver}_aarch64.tbz2
Apply the BSP packages.
cd ${BSP_TOP}/Linux_for_Tegra sudo ./apply_binaries.sh
Install the FSKP packages.
cd ${BSP_TOP} tar jvxf ~/Downloads/host_overlay_fskp_tools_${rel_ver}_aarch64.tbz2
Untar the ATF and OP-TEE source tarball.
cd Linux_for_Tegra/source/ tar jvxf atf_src.tbz2 tar jvxf nvidia-jetson-optee-source.tbz2 cd ../
Server Setup for fTPM Production Scripts#
Install the required Python modules:
sudo apt-get update
sudo apt-get install -y git python3-pip python3-venv
python3 -m venv ${BSP_TOP}/ftpm-venv
source ${BSP_TOP}/ftpm-venv/bin/activate
${BSP_TOP}/ftpm-venv/bin/python -m pip install --upgrade pip
${BSP_TOP}/ftpm-venv/bin/python -m pip install git+https://github.com/wbond/asn1crypto.git@master
${BSP_TOP}/ftpm-venv/bin/python -m pip install git+https://github.com/wbond/oscrypto.git@master
${BSP_TOP}/ftpm-venv/bin/python -m pip install cryptography ecdsa numpy pyaes pycryptodomex pyyaml
This virtual environment is for the host that runs the fTPM production scripts. Activate it on the host before running the Python-based tools in this workflow.
Prerequisites for Enabling fTPM#
The KDK0 is the root key. The combined KDK0 and Device_SN values must be unique to each device, and the KDK0 itself is a 256-bit secret value. The FUSE_BOOT_SECURITY_INFO must be burned with the following bits.
FUSE_BOOT_SECURITY_INFO for Jetson Thor Series
Bits |
Description |
|---|---|
[3] |
Secure boot encryption. Must be 1 to enable SBK. |
[5] |
Revocation policy. Must be 1 to enable the revocation policy. |
[9] |
OEM key valid. Must be 1. |
[13] |
fTPM enable. Must be 1 to enable fTPM. |
FUSE_BOOT_SECURITY_INFO for Jetson Orin Series
Bits |
Description |
|---|---|
[2:0] |
Authentication scheme field. Cannot be zero or greater than 011b. Must be a valid PKC authentication scheme. |
[3] |
Secure boot encryption. Must be 1 to enable SBK. |
[9] |
OEM key valid. Must be 1. |
[11] |
OEM key function. Must be 1 to enable KDF of the OEM fuse key. |
[13] |
OEM key function. Must be 1 to enable KDF of the Silicon ID generation. |
KDK Database and Fuseblob Generation#
Generating kdk-db and Silicon ID Public Keys#
From ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool, run ./kdk_gen.py to generate the KDK database and corresponding Silicon ID public keys.
cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
# Command line interface
./kdk_gen.py [-h] [--chip ${CHIP}] [--oem_id ${OEM_ID}] [--sn ${SN}] [--num_devices ${NUM_OF_DEVICES}] [--preset ${FILE} | --fixed --preset ${FILE}]
Example of five KDK entries with random KDK values:
cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
# Jetson Thor series
./kdk_gen.py --chip t264 --oem_id 0x102 --sn 0x100000002 --num_devices 5
# Jetson Orin series
./kdk_gen.py --chip t234 --oem_id 0x102 --sn 0x100000002 --num_devices 5
Output files:
ftpm_kdk/kdk_db_0102-0000000100000002-5.csvftpm_kdk/pubkey_db_0102-0000000100000002-5.csv
KDK database contents (random KDK values):
cat ftpm_kdk/kdk_db_0102-0000000100000002-5.csv
0102 0000000100000002 681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6
0102 0000000100000003 bfec6db0ddab599b2a34d01ab9e641e3b735d7ead9649579689ed535c73d2053
0102 0000000100000004 801575a8ecd51955f8efcc840af0b6f94a4a0d1e2745f97122d46873c83bd751
0102 0000000100000005 a8d49348cbbf34814b387c7189187a1c98fe03a254503e879232765651dcf0ee
0102 0000000100000006 a8e20c2e0309a6564d0c694c75fcce9b21b846c54dc3b6343a8c276bbbd2dca5
The first two columns are oem-id and SN; the last column is the randomly generated KDK value.
Example of three KDK entries with a fixed KDK value:
cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
# Store the fixed KDK value in a file
echo "681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6" > preset_kdk.txt
# Jetson Thor series
./kdk_gen.py --chip t264 --oem_id 0x102 --sn 0x100000002 --num_devices 3 --fixed --preset preset_kdk.txt
# Jetson Orin series
./kdk_gen.py --chip t234 --oem_id 0x102 --sn 0x100000002 --num_devices 3 --fixed --preset preset_kdk.txt
Note
The KDK database is used to generate fuseblobs.
Important
The Silicon ID public key database can be used by the fTPM manufacturer CA to verify device identity during provisioning.
cat ftpm_kdk/pubkey_db_0102-0000000100000002-5.csv
0102 0000000100000002 435ec556a1e23e9a676b8471ff2b2c25ca262bbc7f581abd69de025198cb56d157882fc257c4f544b206796dadf8257d5c4638e70bf8a2f13d9a2b7a30b57c0a
0102 0000000100000003 c25aef9b683cc4a2579bf634e35d42ee40b5e31dccef5dad7a16ab8b860dc2ec25a20ed6391edd8aa871cfa999acf57af1f81f1226e74569b2f3fff549bc0809
# The first two columns are oem-id and SN; the last column is the silicon-id public key.
Fuse Configuration Templates#
For the template fuse-configuration XML file, refer to Fuse Configuration File.
Create a template fuse-configuration XML file named fuse_temp.xml. The ODM and OEM use different template files. The following examples use sample values for the shared fuse contents; only KDK0, OEM_ID, and SN remain symbolic because they vary per device.
Fuse Configuration for the Jetson Thor Series#
ODM Fuse Configuration
Fuse Name |
Value |
|---|---|
|
|
|
|
|
|
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="PublicKeyHash" size="64" value="0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"/>
<fuse name="OptInEnable" size="4" value="0x1"/>
<fuse name="PscOemKdk0" size="32" value="0x<32-byte-value-PscOemKdk0>"/>
<fuse name="OespOemKdk0" size="32" value="0x111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000"/>
<fuse name="SbOemKdk0" size="32" value="0x0000ffffeeeeddddccccbbbbaaaa999988887777666655554444333322221111"/>
<fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
<fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
<fuse name="PscSecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
<fuse name="OespSecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
<fuse name="SbSecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
<fuse name="ArmJtagDisable" size="4" value="0x1"/>
<fuse name="BootSecurityInfo" size="4" value="0x00002228"/>
<fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
OEM Fuse Configuration
Fuse Name |
Value |
|---|---|
|
The root key of the EKB encryption and authentication key. |
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="PscOemKdk1" size="32" value="0x0f1e2d3c4b5a69780f1e2d3c4b5a69780f1e2d3c4b5a69780f1e2d3c4b5a6978"/>
<fuse name="OespOemKdk1" size="32" value="0x89abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567"/>
<fuse name="SbOemKdk1" size="32" value="0x76543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba98"/>
</genericfuse>
Fuse Configuration for the Jetson Orin Series#
ODM Fuse Configuration
Fuse Name |
Value |
|---|---|
|
|
|
|
|
|
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
<fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
<fuse name="Kdk0" size="32" value="0x<32-byte-value-Kdk0>"/>
<fuse name="PublicKeyHash" size="64" value="0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"/>
<fuse name="PkcPubKeyHash1" size="64" value="0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"/>
<fuse name="PkcPubKeyHash2" size="64" value="0xffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100"/>
<fuse name="OptInEnable" size="4" value="0x1"/>
<fuse name="SecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
<fuse name="ArmJtagDisable" size="4" value="0x1"/>
<fuse name="BootSecurityInfo" size="4" value="0x00002a09"/>
<fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
OEM Fuse Configuration
Fuse Name |
Value |
|---|---|
|
The root key of the EKB encryption and authentication key. |
|
The root key of the UEFI PV key. |
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="OemK1" size="32" value="0x13579bdf2468ace013579bdf2468ace013579bdf2468ace013579bdf2468ace0"/>
<fuse name="OemK2" size="32" value="0x02468ace13579bdf02468ace13579bdf02468ace13579bdf02468ace13579bdf"/>
<fuse name="PscOdmStatic" size="4" value="0x00000060"/>
</genericfuse>
Note
Replace the sample hash, key, and board-specific fuse values with the values for your platform before running fskp_fuseburn.py. Replace KDK0, OEM_ID, and SN with the per-device data from your KDK database.
Generating Fuseblobs#
Run fskp_fuseburn.py to generate fuseblobs.
ODM example—five fuseblobs using the kdk-db:
# Jetson Thor series
sudo ./fskp_fuseburn.py -f fuse_temp_odm.xml \
--test \
--skipfskpkey \
--multi-blob ftpm_kdk/kdk_db_0102-0000000100000002-5.csv \
-g odm_out/ \
-c 0x26 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series
sudo ./fskp_fuseburn.py -f fuse_temp_odm.xml \
--test \
--skipfskpkey \
--multi-blob ftpm_kdk/kdk_db_0102-0000000100000002-5.csv \
-g odm_out/ \
-c 0x23 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
# Package for delivery
tar cf odm_out_0102-0000000100000002-5.tar odm_out/
OEM example:
# Jetson Thor series
sudo ./fskp_fuseburn.py -f fuse_temp_oem.xml \
--test \
--skipfskpkey \
-g oem_out/ \
-c 0x26 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series
sudo ./fskp_fuseburn.py -f fuse_temp_oem.xml \
--test \
--skipfskpkey \
-g oem_out/ \
-c 0x23 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
# Package for delivery
tar cf oem_out.tar oem_out/
Command-line options:
-f <fuse_temp.xml>: Fuse template file. A list of corresponding fuse configurations is generated in theftpm_kdkdirectory (for example,fusexml_0102-0000000100000002.xmlthroughfusexml_0102-0000000100000006.xml).--test: Generate fuseblobs for dry run only. To burn fuses, replace with-b.--skipfskpkey: No FSKP key used (fuseblobs are unencrypted). For production, replace--skipfskpkey:For Jetson Thor series:
-i <fskp_select> --key <fskp_key.bin>. The value for-icomes fromfskp_select.txt.For Jetson Orin series:
--key-exp <fskp_ak.bin> <fskp_ek.bin> --fskpcfg <fskp_conf.txt>
--multi-blob <kdk_db>: Use the pre-generated kdk-db to fill in the template fuse configuration.-g <out_dir>/<fuse_blob_prefix>: Output folder for generated fuseblobs. For example,-g fuse/outproducesfuse/out_0102-0000000100000002.-c <chip ID>: NVIDIA Tegra SoC chip ID.--board-spec <board_spec>: Board specification (such as BOARDID or SKU).-B <board_conf>: Board configuration file for low-level boot components.
EKB Generation#
ODM EKB Generation#
The ODM EKB Gen tool uses the following library modules for EK CSR generation and certificate signing:
lib/ek_csr_gen.py: Generates RSA and EC EK CSRs from the device EPS.lib/ca_signing.py: Provides theCAInterfaceabstract class and the built-inSimulatorCAimplementation. To integrate your production CA or HSM-backed CA server, subclassCAInterface.
# Command-line interface
odm_ekb_gen.py [-h] [--chip <CHIP>] [--kdk_db ${KDK_DB}]
# Jetson Thor series
odm_ekb_gen.py --chip t26x --kdk_db ftpm_kdk/kdk_db_0102-0000000100000002-5.csv
# Jetson Orin series
odm_ekb_gen.py --chip t234 --kdk_db ftpm_kdk/kdk_db_0102-0000000100000002-5.csv
The output ODM EKB DB is stored in the odm_out folder. Package and deliver to the OEM.
OEM EKB Generation#
The OEM unpacks the ODM EKB DB and generates the final EKB DB—per-device EKS images authenticated and encrypted by OEM fuse derivation keys.
The OEM EKB Gen tool is a wrapper of the Gen EKB script (gen_ekb.py) from the hwkey-agent sample application.
Move the Gen EKB script to the same folder as the OEM EKB Gen tool.
Run
oem_ekb_gen.py:# Command-line interface oem_ekb_gen.py [-h] [-chip <CHIP>] [-oem_k1_key <oem_k1.key>] [-oem_k2_key <oem_k2.key>] [-oem_kdk1_key <oem_kdk1.key>] [-in_sym_key <sym.key>] [-in_sym_key2 <sym2.key>] [-in_auth_key <auth.key>] [-in_ftpm_odm_ekb <IN_FTPM_ODM_EKB>] # Jetson Thor series oem_ekb_gen.py -chip t264 \ -oem_kdk1_key oem_kdk1.key \ -in_sym_key sym_t264.key \ -in_sym_key2 sym2_t264.key \ -in_auth_key auth_t264.key \ -in_ftpm_odm_ekb odm_out # Jetson Orin series oem_ekb_gen.py -chip t234 \ -oem_k1_key oem_k1.key \ -in_sym_key sym_t234.key \ -in_sym_key2 sym2_t234.key \ -in_auth_key auth_t234.key \ -in_ftpm_odm_ekb odm_out
Command-line options:
-chip <CHIP>: Jetson SoC chip name (t234for Orin,t264for Thor).-oem_k1_key <oem_k1.key>: Root key of EKB encryption and authentication (Orin series).-oem_kdk1_key <oem_kdk1.key>: Root key of EKB encryption and authentication (Thor series).-in_sym_key <sym.key>: User-defined key for UEFI payloads encryption.-in_sym_key2 <sym2.key>: User-defined key for disk encryption support.-in_auth_key <auth.key>: User-defined key for UEFI variable authentication.-in_ftpm_odm_ekb <IN_FTPM_ODM_EKB>: Path to the EKB_ftpm_db.
The generated per-device EKS images are stored in oem_out. Package and deliver to the ODM.
Image Generation#
Signing and Encrypting the EKB Final DB#
Run
l4t_sign_image.sh. For detailed usage, refer to the help option.Unpack the EKB_final_db to the
oem_outfolder.The encrypted and signed files are in
oem_out/signed.
cd Linux_for_Tegra
./l4t_sign_image.sh --chip <CHIP_ID> \
--key <PKC_KEY> \
--encrypt_key <SBK_KEY> \
--mass-ekb oem_out \
--type <"data" | "eks"> \
--split False
# Package the signed EKBs
tar cf eks_<OEM_ID>-<SN>-<num_of_devices>.tar --directory=oem_out/signed/ .
Generating the ODM QSPI Image for Lower Boot Components (LBC)#
Run l4t_initrd_flash.sh with --odm-image-gen, using PKC and SBK as signing/encryption keys to generate lbc_odm.tar.gz.
Refer to ${BSP_TOP}/Linux_for_Tegra/tools/kernel_flash/README_initrd_flash.txt for detailed usage.
cd Linux_for_Tegra
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
--no-flash \
-u <PKC_KEY> \
-v <SBK_KEY> \
<BOARD_NAME> \
internal
Generating the OEM User Partition Image#
Generate the user partition image (UPI). To generate the UEFI key configuration file, refer to ${BSP_TOP}/Linux_for_Tegra/tools/README_uefi_secureboot.txt. For the Jetson Orin series, also provide the UEFI payload encryption key.
cd Linux_for_Tegra
sudo ./tools/gen_uefi_keys_dts.sh <UEFI_KEYS_CONF_FILE>
# Jetson Thor series:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
--no-flash \
--uefi-keys <UEFI_KEYS_CONF_FILE> \
<BOARD_NAME> \
internal
# Jetson Orin series:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
--no-flash \
--uefi-keys <UEFI_KEYS_CONF_FILE> \
--uefi-enc <UEFI_PAYLOAD_ENCRYPTION_KEY> \
<BOARD_NAME> \
internal
Factory Package Assembly#
cd Linux_for_Tegra
# Untar the LBC and UPI tarballs
sudo tar xvzf lbc_odm.tar.gz
sudo tar xvzf upi_oem.tar.gz
cd tools/kernel_flash/images/internal/
# Untar EKB_final_db received from ODM
sudo mkdir ekb_db
cd ekb_db
sudo tar xf ${ODM}/eks_<OEM_ID>-<SN>-<num_of_devices>.tar
# Package the factory tarball
cd ${BSP_TOP}/Linux_for_Tegra
sudo tar zvcf factory.tar.gz \
tools/kernel_flash/initrdflashparam.txt \
tools/kernel_flash/initrdflashimgmap.txt \
tools/kernel_flash/images/
Factory Operations#
Fuse Burning#
Install L4T BSP and FSKP package on the fuse-burning host at the factory.
Untar the fuseblob.
Burn the OEM fuseblob first; then burn the ODM fuseblob.
Single device:
# Burn OEM fuseblob:
# Jetson Thor series:
sudo ./fskp_fuseburn.py -P ./oem_out \
-c 0x26 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series:
sudo ./fskp_fuseburn.py -P ./oem_out \
-c 0x23 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
# Burn ODM fuseblob:
# Jetson Thor series:
sudo ./fskp_fuseburn.py -P ./odm_out \
-c 0x26 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series:
sudo ./fskp_fuseburn.py -P ./odm_out \
-c 0x23 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
Important
The ODM fuseblob must be burned after the OEM fuseblob because the ODM fuseblob contains the Security Mode fuse bit. Once burned, no other fuses can be burned.
Multiple devices:
Note
All devices must be connected to the same host and in recovery mode.
# Burn OEM mass fuseblobs:
# Jetson Thor series:
sudo ./fskp_massfuseburn.py --skipconfirmation \
--burnfuse \
-P ./oem_out \
-c 0x26 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series:
sudo ./fskp_massfuseburn.py --skipconfirmation \
--burnfuse \
-P ./oem_out \
-c 0x23 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
# Burn ODM mass fuseblobs:
# Jetson Thor series:
sudo ./fskp_multiblobfuse.py --out ./odm_out \
--oem_id 0x0102 \
--sn 0x0000000100000002 \
--num_devices 5 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series:
sudo ./fskp_multiblobfuse.py --out ./odm_out \
--oem_id 0x0102 \
--sn 0x0000000100000002 \
--num_devices 5 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
Flashing Devices#
Install L4T BSP on the host.
Untar the factory package.
Flash the devices.
cd ${BSP_TOP}/Linux_for_Tegra sudo tar zvxf factory.tar.gz cp tools/kernel_flash/images/rcmboot/* bootloader/ # Flash: sudo ./l4t_initrd_flash.sh --flash-only \ --ekb-pair \ <BOARD_NAME> \ internal
Board-specific flash commands:
AGX Thor with UFS:
jetson-agx-thor-devkit internalAGX Orin with eMMC:
jetson-agx-orin-devkit internalAGX Orin with NVMe:
jetson-agx-orin-devkit nvme0n1p1Orin Nano with NVMe:
jetson-orin-nano-devkit nvme0n1p1
End-to-End Walkthrough#
This walkthrough shows the end-to-end commands for a single-entity fTPM production flow on Jetson AGX Thor and Jetson AGX Orin platforms. The UEFI PV key feature is not used; the PKC key signs both low-level firmware and the UEFI image.
Preparation#
Complete the Jetson BSP Installation.
Collect the following keys and files:
ODM keys:
# Jetson Thor series: ~/odm_keys/t264_pkc_key_list.xml ~/odm_keys/sbk-32.key # Jetson Orin series: ~/odm_keys/rsa3k.pem ~/odm_keys/sbk-32.key
ODM FSKP fusing files:
# Jetson Thor series: ~/odm_fskp/odm_fuse_template.xml ~/odm_fskp/fskp_select.txt ~/odm_fskp/fskp_key.bin # Jetson Orin series: ~/odm_fskp/odm_fuse_template.xml ~/odm_fskp/fskp_ak.bin ~/odm_fskp/fskp_ek.bin ~/odm_fskp/fskp_conf.txt
OEM user-defined keys (for EKB generation):
# Jetson Thor series: ~/oem_keys/oem_kdk1.key ~/oem_keys/sym_t264.key ~/oem_keys/sym2_t264.key ~/oem_keys/auth_t264.key # Jetson Orin series: ~/oem_keys/oem_k1.key ~/oem_keys/sym_t234.key ~/oem_keys/sym2_t234.key ~/oem_keys/auth_t234.key
OEM UEFI keys and, for Jetson Orin series, the UEFI payload encryption key:
# Jetson Thor series: ~/oem_keys/uefi_keys/uefi_keys.conf # Jetson Orin series: ~/oem_keys/uefi_keys/uefi_keys.conf ~/oem_keys/uefi_keys/uefi_enc.key
OEM fuse template:
~/oem_keys/oem_fuse_template.xml
EKB Generation#
Generate the EKB DB with KDK-based EK certificates:
cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool echo "681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6" > preset_kdk.txt # Jetson Thor series: ./kdk_gen.py --chip t264 --oem_id 0x21 --sn 0x1000218000 --num_devices 2 --fixed --preset preset_kdk.txt ./odm_ekb_gen.py --chip t264 --kdk_db ftpm_kdk/kdk_db_0021-0000001000218000-2.csv # Jetson Orin series: ./kdk_gen.py --chip t234 --oem_id 0x21 --sn 0x1000218000 --num_devices 2 --fixed --preset preset_kdk.txt ./odm_ekb_gen.py --chip t234 --kdk_db ftpm_kdk/kdk_db_0021-0000001000218000-2.csv
Add user keys to EKB:
cp ../../../hwkey-agent/host/tool/gen_ekb/gen_ekb.py ./ # Jetson Thor series: ./oem_ekb_gen.py -chip t264 \ -oem_kdk1_key ~/oem_keys/oem_kdk1.key \ -in_sym_key ~/oem_keys/sym_t264.key \ -in_sym_key2 ~/oem_keys/sym2_t264.key \ -in_auth_key ~/oem_keys/auth_t264.key \ -in_ftpm_odm_ekb odm_out # Jetson Orin series: ./oem_ekb_gen.py -chip t234 \ -oem_k1_key ~/oem_keys/oem_k1.key \ -in_sym_key ~/oem_keys/sym_t234.key \ -in_sym_key2 ~/oem_keys/sym2_t234.key \ -in_auth_key ~/oem_keys/auth_t234.key \ -in_ftpm_odm_ekb odm_out cp -r oem_out/ ${BSP_TOP}
Sign and generate the final
ekb_db:cd ${BSP_TOP}/Linux_for_Tegra # Jetson Orin series: ./l4t_sign_image.sh --chip 0x23 \ --key ~/odm_keys/rsa3k.pem \ --encrypt_key ~/odm_keys/sbk-32.key \ --mass-ekb ${BSP_TOP}/oem_out \ --type data \ --split False # Jetson Thor series: ./l4t_sign_image.sh --chip 0x26 \ --key ~/odm_keys/t264_pkc_key_list.xml \ --encrypt_key ~/odm_keys/sbk-32.key \ --mass-ekb ${BSP_TOP}/oem_out \ --type eks \ --split False
The encrypted and signed final ekb_db images are stored in ${BSP_TOP}/oem_out/signed.
Fuseblob Generation#
Create a merged fuse template (odm_fuse_template.xml) by combining the ODM and OEM fuse templates. Place the OEM content before the ODM content so that SecurityMode remains the last entry.
Jetson Thor series merged template:
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="PublicKeyHash" size="64" value="0x<64-byte-value-PubKeyHash>"/>
<fuse name="OptInEnable" size="4" value="0x1"/>
<fuse name="PscOemKdk0" size="32" value="0x<32-byte-value-PscOemKdk0>"/>
<fuse name="PscOemKdk1" size="32" value="0x<32-byte-value-PscOemKdk0>"/>
<fuse name="OespOemKdk0" size="32" value="0x<32-byte-value-OespOemKdk0>"/>
<fuse name="OespOemKdk1" size="32" value="0x<32-byte-value-OespOemKdk1>"/>
<fuse name="SbOemKdk0" size="32" value="0x<32-byte-value-SbOemKdk0>"/>
<fuse name="SbOemKdk1" size="32" value="0x<32-byte-value-SbOemKdk1>"/>
<fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
<fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
<fuse name="PscSecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
<fuse name="OespSecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
<fuse name="SbSecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
<fuse name="ArmJtagDisable" size="4" value="0x1"/>
<fuse name="BootSecurityInfo" size="4" value="0x<4-byte-value-SECURITY_INFO>"/>
<fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
Jetson Orin series merged template:
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
<fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
<fuse name="Kdk0" size="32" value="0x<32-byte-value-Kdk0>"/>
<fuse name="PublicKeyhash" size="64" value="0x<64-byte-value-PubKeyHash>"/>
<fuse name="PkcPubKeyHash1" size="64" value="0x<64-byte_value-PubKeyHash1>"/>
<fuse name="PkcPubKeyHash2" size="64" value="0x<64-byte_value-PubKeyHash2>"/>
<fuse name="OptInEnable" size="4" value="0x1"/>
<fuse name="SecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
<fuse name="ArmJtagDisable" size="4" value="0x1"/>
<fuse name="OemK1" size="32" value="0x<32-byte-value-key>"/>
<fuse name="OemK2" size="32" value="0x<32-byte-value-key>"/>
<fuse name="PscOdmStatic" size="4" value="0x00000060"/>
<fuse name="BootSecurityInfo" size="4" value="0x<4-byte_value-SECURITY_INFO>"/>
<fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
cd ${BSP_TOP}/Linux_for_Tegra
cd l4t/tools/flashtools/fuseburn/
# Jetson Thor series:
sudo ./fskp_fuseburn.py -f ~/odm_fskp/odm_fuse_template.xml \
--multi-blob ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool/ftpm_kdk/kdk_db_0021-0000001000218000-2.csv \
--test \
-i <fskp_select> \
--key ~/odm_fskp/fskp_key.bin \
-g fuseblob/odm_out \
-c 0x26 \
--board-spec thor-agx-board-spec.txt \
-B ../../../../jetson-agx-thor-devkit.conf
# Jetson Orin series:
sudo ./fskp_fuseburn.py -f ~/odm_fskp/odm_fuse_template.xml \
--multi-blob ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool/ftpm_kdk/kdk_db_0021-0000001000218000-2.csv \
--test \
-i 63 \
--key-exp ~/odm_fskp/fskp_ak.bin ~/odm_fskp/fskp_ek.bin \
--fskpcfg ~/odm_fskp/fskp_conf.txt \
-g fuseblob/odm_out \
-c 0x23 \
--board-spec orin-agx-board-spec.txt \
-B ../../../../jetson-agx-orin-devkit.conf
tar cf fuseblob_odm_out_0021-0000001000218000-2.tar fuseblob/
Note
The parameters in the preceding example are for reference only. Replace them with your FSKP keys and board-specific values.
The --test option generates fuseblobs for dry run only. To burn fuses, replace --test with --burnfuse. Follow the Fuse Burning procedure.
Flash Image Generation#
The following are example commands for various target configurations. Note the following about flash image generation:
The UEFI image generated by the LBC command (with
--odm-image-gen) is the final UEFI image. The--uefi-keysoption must be provided so that all UEFI keys are built in.The UEFI image is encrypted and signed by the SBK key (
-v) and PKC key (-u).In the Jetson Orin series UPI commands, the
--uefi-enckey is thesym_t234.keyused in EKB generation.To enable UEFI secure boot, run the following commands:
cd ${BSP_TOP}/Linux_for_Tegra sudo ./tools/gen_uefi_keys_dts.sh ~/oem_keys/uefi_keys/uefi_keys.conf
AGX Thor with UFS:
cd ${BSP_TOP}/Linux_for_Tegra
# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
--no-flash \
-u ~/odm_keys/t264_pkc_key_list.xml \
-v ~/odm_keys/sbk-32.key \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
jetson-agx-thor-devkit \
internal
# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
--no-flash \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
jetson-agx-thor-devkit \
internal
AGX Orin with eMMC:
cd ${BSP_TOP}/Linux_for_Tegra
# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
--no-flash \
-u ~/odm_keys/rsa3k.pem \
-v ~/odm_keys/sbk-32.key \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
-p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
jetson-agx-orin-devkit \
internal
# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
--no-flash \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
--uefi-enc ~/oem_keys/uefi_keys/uefi_enc.key \
jetson-agx-orin-devkit \
internal
The output files from the preceding commands are lbc_odm.tar.gz and upi_oem.tar.gz.
AGX Orin with NVMe:
cd ${BSP_TOP}/Linux_for_Tegra
# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
--no-flash \
-u ~/odm_keys/rsa3k.pem \
-v ~/odm_keys/sbk-32.key \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
-p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
jetson-agx-orin-devkit \
internal
# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
--no-flash \
--external-device nvme0n1p1 \
--external-only \
-c ./tools/kernel_flash/flash_l4t_t234_nvme.xml \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
--uefi-enc ~/oem_keys/uefi_keys/uefi_enc.key \
jetson-agx-orin-devkit \
nvme0n1p1
The output files from the preceding commands are lbc_odm.tar.gz and upi_oem.tar.gz.
Orin Nano with NVMe:
cd ${BSP_TOP}/Linux_for_Tegra
# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
--no-flash \
-u ~/odm_keys/rsa3k.pem \
-v ~/odm_keys/sbk-32.key \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
-p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
jetson-orin-nano-devkit \
internal
# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
--no-flash \
--external-device nvme0n1p1 \
--external-only \
-c ./tools/kernel_flash/flash_l4t_t234_nvme.xml \
--uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
--uefi-enc ~/oem_keys/uefi_keys/uefi_enc.key \
jetson-orin-nano-devkit \
nvme0n1p1
The output files from the preceding commands are lbc_odm.tar.gz and upi_oem.tar.gz.
Factory Tarball Generation#
cd ${BSP_TOP}/Linux_for_Tegra
sudo tar zvxf lbc_odm.tar.gz
sudo tar zvxf upi_oem.tar.gz
sudo mkdir -p tools/kernel_flash/images/internal/ekb_db
sudo cp ${BSP_TOP}/oem_out/signed/* tools/kernel_flash/images/internal/ekb_db
sudo tar zvcf factory.tar.gz \
tools/kernel_flash/initrdflashparam.txt \
tools/kernel_flash/initrdflashimgmap.txt \
tools/kernel_flash/images/
Send factory.tar.gz to the factory. Follow the Flashing Devices procedure.