SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: LicenseRef-NvidiaProprietary

NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
property and proprietary rights in and to this material, related
documentation and any modifications thereto. Any use, reproduction,
disclosure or distribution of this material and related documentation
without an express license agreement from NVIDIA CORPORATION or
its affiliates is strictly prohibited.

# Keylime TPM Remote Attestation Test for an unfused Jetson

This README file describes how to use Keylime to verify fTPM on an
unfused Jetson device using fTPM simulation mode.


## Overview
  * Preparation for Keylime
    + Provisioning fTPM in simulation mode on an unfused Jetson device
    + Keylime Server Installation and Configuration on a host
    + Keylime Agent Installation and Configuration on the Jetson device
  * Launching Keylime Server
  * Launching Keylime Agent
  * Using Keylime Tenant to add or remove an IoT device (through TPM remote attestation)
  * Using Keylime to Monitor Device Measurement
  * PCR0 Measurement Calculation


## Preparation for Keylime

### Provisioning fTPM in simulation mode on an unfused Jetson device

  * mkdir a folder, **ftpm_tool**, on the Jetson device.
  * Copy the following fTPM simulation provisioning script to the **ftpm_tool** folder on the Jetson device.
    + ftpm_sim_provisioning_tool.sh
  * Copy the following configuration files to the **ftpm_tool/conf** folder.
    + ftpm_sim_root_ca_csr.config
    + ftpm_sim_i_ca_csr.config
    + ftpm_sim_ek_csr.config

  * Run the following commands to complete the fTPM simulation provisioning.
~~~
      # Install tpm2-tools
      sudo apt-get install tpm2-tools

      # Run the fTPM SIM provision script
      sudo ./ftpm_sim_provisioning_tool.sh ek_prov

      # To clear the fTPM SIM provision
      sudo ./ftpm_sim_provisioning_tool.sh clear
~~~

  * Note: If **ftpm_sim_provisioning_tool.sh** returns "fTPM is already provisioned" message, your Jetson device has been fTPM provisioned, and you cannot continue to run the device in the fTPM simulation mode.

  * Note: the following files in **ftpm_tool/temp_ftpm_sim_prov/cert/** folder will need to copy to the Keylime Server:
    - ftpm_sim_root_ca_csr.config
    - ftpm_sim_i_ca_csr.config
    - ftpm_sim_ek_csr.config


### Keylime Server Installation and Configuration

Keylime Server consists of **Keylime Verifier**, **Keylime Registrar** and **Keylime Tenant**.

To simply the verification steps, we install all these 3 components on one host. All following steps have been verified on Ubuntu 22.04 with Keylime v7.11.0.


#### SWTPM Installation (Optional)
Keylime verifier relies on TPM to work. If the host machine doesn’t have TPM support, you can install the SWTPM module as a TPM emulator.

How to check if your host has TPM support:

~~~
1. cd /sys
2. find ./ | grep tpm
~~~

If you can find the terms below, that means your host does have TPM support.

~~~
   * devices/platform/MSFT0101:00/tpmrm/tpmrm0 -> This is a SW TPM emulator (NOT SWTPM).
   * devices/platform/STM7364:00/tpm/tpm0 -> This is a physical DTPM.
~~~

You can continue on the section of **Install and configure Keylime Server**.

Otherwise, follow the following steps to install SWTPM module.

~~~
   # Install Prerequisite Packages for SWTPM
   sudo apt-get install tpm2-tools swtpm-tools

   # SWTPM Provisioning
   sudo mkdir /tmp/swtpm
   sudo chown tss:root /tmp/swtpm
   sudo swtpm_setup --tpmstate /tmp/swtpm --create-ek-cert --create-platform-cert --tpm2

   # SWTPM EK & EK Cert NVRAM Handles
   # 0x81010001 : RSA 2K EK
   # 0x81010016 : ECC NIST P384 EK
   # 0x01c00002 : RSA 2K EK Cert

   # Open a terminal to launch SWTPM
   sudo su
   swtpm socket --tpmstate dir=/tmp/swtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 \
          --flags not-need-init,startup-clear

   # Open another terminal to verifing SWTPM Cert Chain
   sudo su
   export TPM2TOOLS_TCTI="swtpm:port=2321"
   mkdir swtpm_cert
   cp /var/lib/swtpm-localca/swtpm-localca-rootca-cert.pem swtpm_cert/
   cp /var/lib/swtpm-localca/issuercert.pem swtpm_cert/
   cd swtpm_cert
   tpm2_nvread 0x01c00002 -o swtpm_rsa_ek_cert.der
   openssl x509 -inform DER -outform PEM -in swtpm_rsa_ek_cert.der -out swtpm_rsa_ek_cert.pem
   openssl verify -CAfile swtpm-localca-rootca-cert.pem --untrusted issuercert.pem -show_chain swtpm_rsa_ek_cert.pem
~~~

#### Install and configure Keylime Server
~~~
   # Install Prerequisite Packages for Keylime Server
   sudo apt-get install git python3-pip python3-gpg tpm2-tools
   sudo apt-get remove python3-cryptography

   # Clone Keylime Repo
   git clone --branch v7.11.0 https://github.com/keylime/keylime.git

   # Install Keylime Server Components
   cd keylime
   sudo pip3 install . -r requirements.txt
   sudo python3 setup.py install

   # Add user: keylime
   sudo useradd -g tss keylime

   # Create Keylime DIRs
   sudo mkdir -p /var/lib/keylime/tpm_cert_store
   sudo mkdir -p /var/lib/keylime/cv_ca

   # Copy ftpm simulation CA certificates from the Jetson device.
   scp ${AGENT_IP_ADDR}:ftpm_tool/temp_ftpm_sim_prov/cert/ftpm_sim_root_ca_cert.pem /var/lib/keylime/tpm_cert_store/
   scp ${AGENT_IP_ADDR}:ftpm_tool/temp_ftpm_sim_prov/cert/ftpm_sim_i_ca_cert.pem /var/lib/keylime/tpm_cert_store/

   sudo chown keylime:tss /var/lib/keylime
   sudo chown keylime:tss /var/lib/keylime/cv_ca
   sudo chown keylime:tss /var/lib/keylime/tpm_cert_store

   # Configure Keylime Verifier
   sudo mkdir -p /usr/etc/keylime
   sudo cp config/logging.conf config/verifier.conf config/ca.conf /usr/etc/keylime

   sudo vi /usr/etc/keylime/verifier.conf
   # Find and update the configs
   # ip = "${VERIFIER_IP_ADDR}"
   # registrar_ip = ${REGISTRAR_IP_ADDR}

   # Configure Keylime Registrar
   sudo cp config/logging.conf config/registrar.conf /usr/etc/keylime

   sudo vi /usr/etc/keylime/registrar.conf
   # Find and Update the configs
   # ip = "${REGISTRAR_IP_ADDR}"

   # Configure Keylime Tenant
   sudo cp config/tenant.conf /usr/etc/keylime

   sudo vi /usr/etc/keylime/tenant.conf
   # Find and update the configs
   # verifier_ip = ${VERIFIER_IP_ADDR}
   # registrar_ip = ${REGISTRAR_IP_ADDR}
   # accept_tpm_signing_algs = ['rsassa', 'ecdsa']
~~~


### Keylime Agent Installation and Configuration on Jetson

Perform the following steps on the Jetson device
(Verified on Ubuntu 22.04 with rust-keylime v0.2.6)

#### Install and configure Keylime Agent

~~~
   # Install Prerequisite Packages
   sudo apt-get install git clang libarchive-dev libssl-dev libtss2-dev libzmq3-dev rust-all build-essential

   # Clone rust-keylime Repo
   git clone --branch v0.2.6 https://github.com/keylime/rust-keylime.git

   # Build and Install rust-keylime
   cd rust-keylime
   make
   sudo make install

   # Add user keylime
   sudo useradd -g tss keylime

   # Create keylime DIRs
   sudo mkdir -p /var/lib/keylime/cv_ca
   sudo chown keylime:tss /var/lib/keylime
   sudo chown keylime:tss /var/lib/keylime/cv_ca

   # Pause here, go to steps to "Launching Keylime Server from the host"
   # The file 'cacert.crt' is from the Keylime Verifier.
   # Copy the 'cacert.crt' file from the Keylime Verifier after the Verifier is launched.
   sudo chown keylime:tss /var/lib/keylime/cv_ca/cacert.crt

   # Configure rust-keylime agent
   sudo vi /etc/keylime/agent.conf

   # Find and update the configs
   # ip = "${AGENT_IP_ADDR}"
   # contact_ip = "${AGENT_IP_ADDR}"
   # registrar_ip = "${REGISTRAR_IP_ADDR}"
~~~


### Launching Keylime Server from the host

~~~
   # Setup TPM interface on host (Choose 1 or 2 when launching Verifier or Registrar)
     - 1. SWTPM interface if SWTPM is installed in previous steps
          export TPM2TOOLS_TCTI="swtpm:port=2321"
     - 2. TPM interface supported on the host machine
          export TPM2TOOLS_TCTI="device:/dev/tpmrm0"
~~~

~~~
   # Open one terminal to launch Keylime Verifier
   sudo su
   export TPM2TOOLS_TCTI="swtpm:port=2321" or export TPM2TOOLS_TCTI="device:/dev/tpmrm0"
   keylime_verifier

   # Open another terminal to launch Keylime Registrar
   sudo su
   export TPM2TOOLS_TCTI="swtpm:port=2321" or export TPM2TOOLS_TCTI="device:/dev/tpmrm0"
   keylime_registrar

   # Open another terminal to copy the CA cert file to the Jetson device:
   scp /var/lib/keylime/cv_ca/cacert.crt ${AGENT_IP_ADDR}:/var/lib/keylime/cv_ca/

   # Go back to continue to "Install and configure Keylime Agent"
~~~


### Launching Keylime Agent on the Jetson device

~~~
   # Install tpm2-tools
   sudo apt-get install tpm2-tools

   # Insert fTPM module
   sudo su
   export TPM2TOOLS_TCTI="device:/dev/tpmrm0"

   # Keylime Agent (Using fTPM RSA EK)
   export KEYLIME_AGENT_TPM_HASH_ALG="sha256"
   export KEYLIME_AGENT_TPM_ENCRYPTION_ALG="rsa"
   export KEYLIME_AGENT_TPM_SIGNING_ALG="rsassa"
   export KEYLIME_AGENT_EK_HANDLE="0x81010001"
   export KEYLIME_AGENT_TPM_OWNERPASSWORD="owner"

   # Launching Keylime Agent (to register device to Keylime Registrar)
   RUST_LOG=debug keylime_agent
~~~


### Using Keylime Tenant to add or remove an IoT device

~~~
   # Open a terminal on the host

   # Enroll the agent for TPM remote attestation
   sudo keylime_tenant -c add -t ${AGENT_IP_ADDR} -u <uuid> --cert /var/lib/keylime/cv_ca
   (password: default)

   # Using "update" when SQL DB full
   sudo keylime_tenant -c update -t ${AGENT_IP_ADDR} -u <uuid> --cert /var/lib/keylime/cv_ca

   # Remove the agent for TPM remote attestation
   sudo keylime_tenant -c delete -t ${AGENT_IP_ADDR} -u <uuid> --cert /var/lib/keylime/cv_ca
~~~

   * Note: uuid is d432fbb3-d2f1-4a97-9ef7-75bd81c00000.

     It is set in /etc/keylime/agent.conf file:

~~~
       uuid = "d432fbb3-d2f1-4a97-9ef7-75bd81c00000"
~~~

### Using Keylime to Monitor Device Measurement

Using Keylime to monitor PCR content and protect firmware integrity is a robust approach to maintaining device security. By leveraging TPM and storing critical measurements in PCR0, we can ensure that the integrity of the device is preserved from the earliest stages of the boot process. This proactive security measure helps in safeguarding against potential threats and maintaining trust in the device's operational state.

~~~
   1. Set value of tpm_hash_alg in /etc/keylime/agent.conf at device side.

      # List of hash algorithms used for PCRs
      # Accepted values: sha512, sha384, sha256
      tpm_hash_alg = "sha256"

   2. Get PCR0 value and launch keylime_agent at device side.

      # Dump hash value of all PCR banks
      sudo tpm2_pcrread

      # Sample output
      # sha256:
      #   0 : 0x6095EAF50940A6F5DEAFAE6425DBC45C9828F4D6C99A001B7BA433D346FC5F67
      #   1 : 0x5C268A52829842D05B2AD2E7475F3FBD85D420706E129E285E891F0CC1076932
      #   2 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
      #   ........
      #   23: 0x0000000000000000000000000000000000000000000000000000000000000000
      # Get the hash value of bank 0 which is measurement of MB2/OP-TEE/fTPM/UEFI

   3. Add the device to be monitored at server side by its PCR0 hash value through keylime_tenant. PCR0 of sha256 value
      is monitored which is configured from step 1.

      sudo keylime_tenant -c add -t <device_ip> -u <uuid> --cert <ca_cert> --tpm_policy '{"0":["<hash_value_of_PCR0>"]}'”
      # -t: device ip
      # -u: uuid
      # --cert: CA certificate configured. Normally it's under /var/lib/keylime/cv_ca
      # --tpm_policy: JSON format policy to monitor TPM PCR, here we monitor PCR0 to check integrity of firmware images
        which include MB2, OP-TEE, fTPM and UEFI.

      # Sample command:
      sudo keylime_tenant -c add -t <device_ip> -u <uuid> --cert <ca_cert> --tpm_policy \
        '{"0":["6095EAF50940A6F5DEAFAE6425DBC45C9828F4D6C99A001B7BA433D346FC5F67"]}'

   4. Modify MB2 content to build a new MB2 image which results in a different PCR0 hash value.

   5. Flash the modified MB2 image and update tpm policy at server side through keylime_tenant by using "update" command
      but still with previous PCR0 hash value.

      sudo keylime_tenant -c update -t <device_ip> -u <uuid> --cert <ca_cert> --tpm_policy '{"0":["<hash_value_of_PCR0>"]}'”
      # -t: device ip
      # -u: uuid
      # --cert: CA certificate configured. Normally it's under /var/lib/keylime/cv_ca
      # --tpm_policy: JSON format policy to monitor TPM PCR, here we only monitor PCR0 to check integrity of firmware
        images - MB2/TOS/CPUBL

      # Sample command:
      sudo keylime_tenant -c update -t <device_ip> -u <uuid> --cert <ca_cert> --tpm_policy \
         '{"0":["6095EAF50940A6F5DEAFAE6425DBC45C9828F4D6C99A001B7BA433D346FC5F67"]}'”

   6. The monitored device will be revoked due to PCR0 value from device side does not match to the one at server side.

      # Log dump at keylime_verifier
      keylime.tpm - ERROR - PCR #0: 9c05e5bb9e5e00863a57b87464448ea8a445c8dfa6a3c8aed266cf14ea2c7d04 from quote (from agent d432fbb3-d2f1-4a97-9ef7-75bd81c00000) does not match expected value ['6095eaf50940a6f5deafae6425dbc45c9828f4d6c99a001b7ba433d346fc5f67']
~~~

### PCR0 Measurement Calculation

Platform Configuration Registers (PCRs) are registered in the TPM that store integrity measurements of the system's state. They are used to ensure that the system has not been tampered with and that it is in a known good state. “PCR extend” is to extend the value of a PCR with a new hash value.

~~~
   PCR_new = HASH(PCR_old || new hash)
   # PCR_old is the current PCR value
   # new hash is the value to being added
   # PCR_new is the updated PCR value
~~~

PCR0 represents the measurement of various firmware images such as MB2, OP-TEE, fTPM, UEFI. This measurement includes values computed using SHA-256, SHA-384 and SHA-512 cryptographic hash functions.

The SHA-512 value for each firmware image is embedded within the BCH header and can be directly extracted and utilized. In contrast, the SHA-256 and SHA-384 values are derived from the SHA-512 value, rather than being direct measurements of the firmware image.

The tool **ftpm_gen_measurement_list.py** can be used to generate PCR0 content from firmware images directly.

~~~
   python3 ftpm_gen_measurement_list.py \
          --platform <t234> \
          --mb2_image <MB2 image> \
          --secure_os_image <TOS image> \
          --uefi_image <UEFI image>

   # --platform: t234 is the only supported platform for now
   # --mb2_image: mb2 image file
   # --secure_os_image: secure OS image file
   # --uefi_image: UEFI image file
~~~

Command example:

~~~
    python3 ftpm_gen_measurement_list.py \
        --platform t234 \
        --mb2_image mb2_t234_with_mb2_cold_boot_bct_MB2_sigheader_encrypt.bin.signed \
        --secure_os_image tos-optee_t234_sigheader_encrypt.img.signed \
        --uefi_image uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed

    # Sample output of PCR0 content:
    INFO:
    SHA256 PCR value: 4EE357BA9620020F65F542A8F71103397F95173744F5ECAB10528D3D6D719D40
    INFO:
    SHA384 PCR value: 011E66783D35E158C0DD4D1C9AA6A308221E76B96DF020E554FD48BC19BBB6641CE2F992F94AD021EB091969B2614143
    INFO:
    SHA512 PCR value: 4977E0D73BCE8D5675B9337D6126958A64BE682B62AE3161CBFF43B8A5BA0E36A4FEAE096E314649D1905F2D39D492C021E06CEBEB3CCC7747F0262167A59848
~~~

