Skip to content

Latest commit

 

History

History
417 lines (321 loc) · 13.7 KB

tcti.md

File metadata and controls

417 lines (321 loc) · 13.7 KB

TPM Command Transmission Interface

What is a tcti?

In a strict sense, the TPM Command Transmission Interface (TCTI) is the API for the lowest layer of the TSS. However, we are a bit sloppy with our terminology, here, so we will call any library which implements the TCTI just that: a tcti.

flowchart TD
    sapi(SAPI) -->|TCTI API| tcti(tcti)
    tcti <-.-> tpm{{TPM}}
    style tpm stroke-dasharray: 3, 3
Loading

For example, the tcti-device is a library (libtss2-tcti-device.so) for interacting with e.g. /dev/tpmrm0.

As you can see in this example, a tcti is responsible for communicating with the TPM. Typically, it sends TPM commands to the TPM and reads responses from the TPM (as bytes). The path to /dev/tpmrm0 is configured via the conf parameter when initializing the tcti.

flowchart TD
    sapi(SAPI) -->|"conf=&quot/dev/tpmrm0&quot" | tcti_device(tcti-device)
    tcti_device -.->|write| tpm{{/dev/tpmrm0}}
    tpm -.->|read| tcti_device
    style tpm stroke-dasharray: 3, 3
Loading

tcti Loader

Most of the time, you will see that the TCTI is specified via a string, like "device:/dev/tpmrm0" (e.g. with the tpm2-tools argument --tcti=... or the FAPI config param "tcti": "..."). This indicates that the tcti is loaded dynamically using the tctildr (tcti loader).

The tctildr is a tcti itself. It dynamically loads and configures a tcti for you. For instance, it can load tcti-device:

flowchart TD
    invis[ ] -->|"conf=&quotdevice:/dev/tpmrm0&quot"| tctildr(tctildr)
    tctildr -->|"conf=&quot/dev/tpmrm0&quot"| tcti_device(tcti-device)
    tcti_device -.->|write| tpm{{/dev/tpmrm0}}
    tpm -.->|read| tcti_device
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tpm stroke-dasharray: 3, 3
Loading

Another example for tcti-swtpm:

flowchart TD
    invis[ ] -->|"conf=&quotswtpm:host=localhost,port=2321&quot"| tctildr(tctildr)
    tctildr -->|"conf=&quothost=localhost,port=2321&quot"| tcti_swtpm(tcti-swtpm)
    tcti_swtpm <-.->|localhost:2321| tpm{{"swtpm (TPM simulator)"}}
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tpm stroke-dasharray: 3, 3
Loading

Parameters

conf

  • <child_name>, e.g., device (child_conf will be NULL) OR
  • <child_name>:<child_conf>, e.g., device:/dev/tpmrm0 OR
  • NULL, tctildr will attempt to load a child tcti in the following order:
    1. libtss2-tcti-default.so
    2. libtss2-tcti-tabrmd.so
    3. libtss2-tcti-device.so.0:/dev/tpmrm0
    4. libtss2-tcti-device.so.0:/dev/tpm0
    5. libtss2-tcti-device.so.0:/dev/tcm0
    6. libtss2-tcti-swtpm.so
    7. libtss2-tcti-mssim.so

Where:

child_name

  • If not empty, tctildr will try to dynamically load the child tcti library in the following order:
    1. <child_name>
    2. libtss2-tcti-<child_name>.so.0
    3. libtss2-tcti-<child_name>.so
    4. libtss2-<child_name>.so.0
    5. libtss2-<child_name>.so

child_conf

  • conf param to be passed to the child tcti

tcti-device

To put it simply, tcti-device writes to and reads from a file, typically /dev/tpm0 or /dev/tpmrm0 or /dev/tcm0. The character devices are provided by the Linux kernel module tpm_tis. If no files like these are present, verify that the kernel module is loaded (lsmod) and load it if necessary (modprobe tpm_tis).

flowchart TD
    invis[ ] -->|"conf=&quotdevice:/dev/tpmrm0&quot"| tctildr(tctildr)
    tctildr -->|"conf=&quot/dev/tpmrm0&quot"| tcti_device(tcti-device)
    tcti_device -.->|write| tpm{{/dev/tpmrm0}}
    tpm -.->|read| tcti_device
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tpm stroke-dasharray: 3, 3
Loading

Parameters

conf

  • path to the character device, typically /dev/tpm0 or /dev/tpmrm0 or /dev/tcm0

tcti-tbs

The tcti-tbs is used for communicating to the TPM via the TPM Base Services (TBS) on Windows. There might be limitations, especially if you do not have admin rights.

flowchart TD
    invis[ ] -->|"conf=&quottbs&quot"| tctildr(tctildr)
    tctildr --> tcti_tbs(tcti-tbs)
    tcti_tbs <-.->|"Tbsip_Submit_Command()"| tbs{{"TPM Base Services (TBS)"}}
    tbs -.- tpm{{TPM}}
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tbs stroke-dasharray: 3, 3
    style tpm stroke-dasharray: 3, 3
Loading

tcti-cmd

The tcti-cmd spawns a process and connects to its stdin and stdout. This enables some advanced shenanigans like sending TPM traffic over the network via ssh.

The following example makes the TPM communication unnecessary complex, but shows how tcti-cmd works. Here, commands are piped into tpm2_send and responses are read from its stdout.

flowchart TD
    invis[ ] -->|"conf=&quotcmd:tpm2_send --tcti='device:/dev/tpmrm0'&quot"| tctildr1(tctildr)
    tctildr1 -->|"conf=&quottpm2_send --tcti='device:/dev/tpmrm0'&quot"| tcti_cmd(tcti-cmd)
    tcti_cmd -.->|stdin| process{{tpm2_send --tcti='device:/dev/tpmrm0'}}
    process -.->|stdout| tcti_cmd
    process -->|"conf=&quotdevice:/dev/tpmrm0&quot"| tctildr2(tctildr)
    tctildr2 -->|"conf=&quot/dev/tpmrm0&quot"| tcti_device{{tcti-device}}
    tcti_device -.->|write| tpm{{/dev/tpmrm0}}
    tpm -.->|read| tcti_device

    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style process stroke-dasharray: 3, 3
    style tpm stroke-dasharray: 3, 3
Loading

Now for a real-world example. We can communicate with a remote TPM by invoking tpm2_send on another host via ssh.

flowchart TD
    invis[ ] -->|"conf=&quotcmd:ssh 192.168.178.123 tpm2_send --tcti='device:/dev/tpmrm0'&quot"| tctildr1(tctildr)
    tctildr1 -->|"conf=&quotssh 192.168.178.123 tpm2_send --tcti='device:/dev/tpmrm0'&quot"| tcti_cmd(tcti-cmd)
    tcti_cmd -.->|stdin| ssh{{ssh}}
    ssh -.->|stdout| tcti_cmd

    ssh -.-|network| sshd{{sshd}}
    sshd -.- process{{tpm2_send -tcti='device:/dev/tpmrm0'}}
    process -->|"conf=&quotdevice:/dev/tpmrm0&quot"| tctildr2(tctildr)
    tctildr2 -->|"conf=&quot/dev/tpmrm0&quot"| tcti_device{{tcti-device}}
    tcti_device -.->|write| tpm{{/dev/tpmrm0}}
    tpm -.->|read| tcti_device

    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style ssh stroke-dasharray: 3, 3
    style sshd stroke-dasharray: 3, 3
    style process stroke-dasharray: 3, 3
    style tpm stroke-dasharray: 3, 3
Loading

Parameters

conf

  • Command to execute. Actually, /bin/sh -c '<conf>' will be called.

tcti-pcap

The tcti-pcap is used for logging. It is used by prepending any tctildr conf string with pcap:, e.g. pcap:device:/dev/tpmrm0. Then, tcti-pcap will log into a file specified by the environment variable TCTI_PCAP_FILE )(default: tpm2_log.pcap). This file can be opened by e.g. wireshark or tpmstream.

Internally, tcti-pcap delegates to tctildr, again.

flowchart TD
    invis2[ ] -.->|"TCTI_PCAP_FILE=&quottpm2_log.pcap&quot"| tcti_pcap
    tcti_pcap -.->|logging| file{{tpm2_log.pcap}}
    invis[ ] -->|"conf=&quotpcap:device:/dev/tpmrm0&quot"| tctildr1(tctildr)
    tctildr1 -->|"conf=&quotdevice:/dev/tpmrm0&quot"| tcti_pcap(tcti-pcap)
    tcti_pcap -->|"conf=&quotdevice:/dev/tpmrm0&quot"| tctildr2(tctildr)
    tctildr2 -->|"conf=&quot/dev/tpmrm0&quot"| tcti_device(tcti-device)
    tcti_device -.->|write| tpm{{/dev/tpmrm0}}
    tpm -.->|read| tcti_device
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style invis2  fill:#FFFFFF00, stroke:#FFFFFF00;
    style file stroke-dasharray: 3, 3
    style tpm stroke-dasharray: 3, 3
Loading

Parameters

conf

  • conf which will be passed to tctildr, e.g. device:/dev/tpmrm0

tcti-spi-ftdi

The tcti-spi-ftdi is used for communicating with a SPI-based TPM if there is no TPM driver present (or no OS at all).

For information, see tcti-spi-ftdi.md.

tcti-i2c-ftdi

The tcti-i2c-ftdi is used for communicating with a I2C-based TPM if there is no TPM driver present (or no OS at all).

For information, see tcti-i2c-ftdi.md.

tcti-spi-ltt2go

The tcti-spi-ltt2go is used specifically for communicating to the LetsTrust-TPM2Go. The LetsTrust-TPM2Go is basically a USB stick which houses a SPI-based TPM and connects that to the host via libusb.

flowchart TD
    invis[ ] -->|"conf=&quotlibtpms&quot"| tcti_spi_ltt2go(tcti-spi-ltt2go)
    tcti_spi_ltt2go <-.->|libusb| cy7c65211a{{"USB2.0 SPI bridge"}}
    cy7c65211a -.-|SPI| tpm{{"TPM"}}
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style cy7c65211a stroke-dasharray: 3, 3
    style tpm stroke-dasharray: 3, 3
Loading

tcti-spidev

The tcti-spidev is used for communicating to a TPM that is connected via a spidev device. On a Raspberry Pi for example this happens when enabling the device tree overlay spi0-cs2.

TPM Simulator tctis

There are multiple tctis used for testing.

tcti-libtpms

The tcti-libtpms is a simple TPM simulator based on libtpms, a library implementing TPM behavior. No second process is needed.

If no state file is passed via conf, all state is held in RAM and discarded when the process dies.

flowchart TD
    invis[ ] -->|"conf=&quotlibtpms&quot"| tctildr(tctildr)
    tctildr --> tcti_libtpms(tcti-libtpms)
    tcti_libtpms -.->|dynamically loads| tpm{{"libtpms.so (TPM simulator)"}}
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tpm stroke-dasharray: 3, 3
Loading

If multiple processes need to work on the simulated TPM, state must be saved to the filesystem and loaded again. This can be achieved by passing a path via conf.

flowchart TD
    invis[ ] -->|"conf=&quotlibtpms:tpm_state.libtpms&quot"| tctildr(tctildr)
    tctildr -->|"conf=&quottpm_state.libtpms&quot"| tcti_libtpms(tcti-libtpms)
    tcti_libtpms <-.->|loads/saves state| file{{tpm_state.libtpms}}
    tcti_libtpms -.->|dynamically loads| tpm{{"libtpms.so (TPM simulator)"}}
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style file stroke-dasharray: 3, 3
    style tpm stroke-dasharray: 3, 3
Loading

Parameters

conf

  • Path to a state file. Will be loaded on startup or created if it does not exist.

tcti-swtpm

The tcti-swtpm connects to swtpm, a TPM simulator based on libtpms and socket communication.

Just like mssim, there is a primary socket (default: 2321) used for TPM commands/responses and a secondary socket (default: 2322) for controlling the simulator, here called control channel. While the primary socket is identical with that of mssim, the secondary one is incompatible.

flowchart TD
    invis[ ] -->|"conf=&quotswtpm:host=localhost,port=2321&quot"| tctildr(tctildr)
    tctildr -->|"conf=&quothost=localhost,port=2321&quot"| tcti_swtpm(tcti-swtpm)
    tcti_swtpm <-.->|TPM commands/responses| port2321{{"localhost:2321"}}
    tcti_swtpm <-.->|control channel| port2322{{"localhost:2322"}}
    port2321 -.- tpm{{"swtpm (TPM simulator)"}}
    port2322 -.- tpm
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tpm stroke-dasharray: 3, 3
    style port2321 stroke-dasharray: 3, 3
    style port2322 stroke-dasharray: 3, 3
Loading

Parameters

conf

  • host=<host>,port=<port>, e.g., host=192.168.178.123,port=5000
  • host=<host>, e.g., host=192.168.178.123
  • port=<port>, e.g. port=5000

Where:

host

  • Hostname or IP address to the simulator, default: localhost

port

  • Port to the simulator, default: 2321. The control channel will be <port> + 1

tcti-mssim

The tcti-mssim connects to the Microsoft TPM simulator mssim, repackaged by IBM.

Like with swtpm, there is a primary socket (default: 2321) used for TPM commands/responses and a secondary socket (default: 2322) for sending so-called platform commands which control the simulator. While the primary socket is identical with that of swtpm, the secondary one is incompatible.

flowchart TD
    invis[ ] -->|"conf=&quotmssim:host=localhost,port=2321&quot"| tctildr(tctildr)
    tctildr -->|"conf=&quothost=localhost,port=2321&quot"| tcti_mssim(tcti-mssim)
    tcti_mssim <-.->|TPM commands/responses| port2321{{"localhost:2321"}}
    tcti_mssim <-.->|platform commands| port2322{{"localhost:2322"}}
    port2321 -.- tpm{{"mssim (TPM simulator)"}}
    port2322 -.- tpm
    style invis  fill:#FFFFFF00, stroke:#FFFFFF00;
    style tpm stroke-dasharray: 3, 3
    style port2321 stroke-dasharray: 3, 3
    style port2322 stroke-dasharray: 3, 3
Loading

Parameters

conf

  • host=<host>,port=<port>, e.g., host=192.168.178.123,port=5000
  • host=<host>, e.g., host=192.168.178.123
  • port=<port>, e.g. port=5000

Where:

host

  • Hostname or IP address to the simulator, default: localhost

port

  • Port to the simulator, default: 2321. The control channel will be <port> + 1