Introduction

You want to learn to use the TPM 2.0? Then you’re right here! In this tutorial you will get an overview over the TPM using the tpm2-tools.

The TPM (Trusted Platform Module) is a cryptographic processor which is part of most modern motherboards. If your computer runs Windows 10, it certainly contains a TPM 2.0. For those unfamiliar, the TPM provides out-of-band general cryptographic, storage, policy and key management operations (among other things).

Getting Started

Of course we need a TPM. If you do not have a TPM, don’t fret. In this tutorial we will use the TPM simulator. Just download the source code and build it.

wget https://downloads.sourceforge.net/project/ibmswtpm2/ibmtpm1332.tar.gz
mkdir ibmtpm1332
cd ibmtpm1332
tar -xavf ../ibmtpm1332.tar.gz
cd src
make
# TODO install i.e. symlink to /usr/local/bin or change PATH

Now we install the other dependencies:

# TODO

Hello World

First, let’s run the TPM simulator. This will open two TCP sockets. Keep in mind that we can use Wireshark to capture the traffic if needed.

  • localhost:2321: listening for TPM commands
  • localhost:2322: listening for platform commands
tpm_server

Now, let’s run the resource manager which will connect to the TPM simulator. Note that is has to be started as user tss for security reasons. Internally, the library libtss2-tcti-tabrmd.so will be used to send commands to the resource manager.

sudo -u tss tpm2-abrmd --tcti=mssim

Now we are ready to call our first TPM tool! After each TPM power cycle (e.g. after a reboot of the PC), we need to issue the command tpm2_startup. If the TPM is already ready, we will get an error 0x00000100 which we can simply ignore.

tpm2_startup -c

The next TPM coommand is tpm2_getrandom which will give us a specified number of random bytes. These bytes are truly random.

tpm2_getrandom --hex 8

Creating Objects

All TPM 2.0 entities are referenced with a handle (e.g. 0x8000001). A subset of these entities are the so-called objects: keys or data. How to create and use objects is covered in this section.

Hierarchies

Before we create an object, it’s important to understand the concept of hierarchies. The TPM 2.0 has 4 hierarchies, they are:

  • platform - used by firmware and the OS.
  • owner - used by the owner (this is the one we will be using).
  • endorsement - used for privacy sensitive keys, these keys are known to come from a valid TPM and platform.
  • null - reset on every boot, good for transient session keys.

Each hierarchy is essentially a static seed value (with the exception of the null hierarchy, which is a new seed on every boot). This seed is used to create one or more primary objects. Since hierarchies are also TPM entities, they can be referenced by handle, as well.

Every hierarchy has its own authorization value which is basically a password. We will dive into authorization later.

Primary Objects

Under a hierarchy, one can create one or more primary objects. These objects do not persist across reboots by default. However, a primary object created with the same inputs under a given hierarchy will produce the same exact key (with the exception of the null hierarchy, the seed changes each boot). Primary objects have special attributes and thus are not intended for general purpose application. To add a primary object to a hierarchy requires the hierarchy authorization value.

Usually, one of the first thing to do after the TPM startup is creating a primary key. The following command creates an primary RSA key under the owner hierarchy (-C o) and saves the key context in the file primary.ctx. Note that this key has no parent and thus cannot exist outside the TPM.

# Create primary key
tpm2_createprimary -C o -c primary.ctx

You might ask yourself why we did not need to authorize to the owner hierarchy (i.e. pass the password). By default the auth. value of the owner hierarchy is empty for the TPM simulator. If there was a non-empty password, we would have passed -P <password>.

Creating an Object

As you might have guessed, a hierarchy is a hierarchy of objects. That means each child key is encrypted (i.e. wrapped) by its parent key. Creating parent objects requires specific attributes which will be covered shortly.

# Create parent key
tpm2_create -C primary.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth' -c parent.ctx

# Create child key
tpm2_create -C parent.ctx -c child.ctx

We can also create data objects. Again, they are encrypted (i.e. sealed) by its parent object. We can specify either a file (-i <file>) or reading from stdin (-i-). Note that data objects are indeed a part of the hierarchy they were created in.

# Create sealed data blob
echo "my sealed data" | tpm2_create -C parent.ctx -i- -c data.ctx

To recover this data we use tpm2_unseal.

# Create sealed data blob
tpm2_unseal -c data.ctx

I explained earlier that each entity inside the TPM is represented by a handle. Well, the resource manager handles saving and loading object contexts (the data in the .ctx files) for us. That means we do not care about object handles. However, if we want, we can display information such as the internally used handle (and e.g. the hierarchy) of an object using tpm2_print.

tpm2_print -t TPMS_CONTEXT data.ctx

Object Attributes

You probably noticed that the the commands to generate objects (tpm2_createprimary, tpm2_create) print some information about the generated entity.

...
attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
  raw: 0x30072
type:
  value: rsa
  raw: 0x1
exponent: 0x0
bits: 2048
...

In this example we generated an RSA key with a length of 2048 bits. Additionally we see a number of attributes. The most important attributes are:

  • fixedtpm - key cannot be duplicated at all
  • fixedparent - key cannot be duplicated to a different parent
  • sensitivedataorigin - sensitive data (private key) is/will be generated by the TPM
  • restricted - key can only operate on special data (needed for parent keys)
  • decrypt - the private key can be used to decrypt (needed for parent keys)
  • sign - the private key can be used to sign data
  • noda - multiple failed authorization attempts will not result in the lockout mode (see Dictionary Attack)

Note: if the key is restricted, either decrypt or sign (but not both) must be set.

//TODO what’s the requirements for parent objects?

Importing Keys

Often, you want to import an externally generated key into the TPM. This can be archieved by using the tpm2_import command.

# Generate key externally
openssl genrsa -out key.pem 2048
openssl rsa -in key.pem -pubout -out key_pub.pem

# Create primary key
tpm2_createprimary -C o -c primary.ctx

# Import key
tpm2_import -G rsa -i key.pem -C primary.ctx -r import_key_priv.bin -u import_key_pub.bin
tpm2_load -C primary.ctx -r import_key_priv.bin -u import_key_pub.bin -c key.ctx

Alternatively, an externally generated key can be loaded without being encrypted (wrapped) by a parent key. Usually, this key is associated with the null hierarchy (-C n). If there is no sensitive portion (e.g. only a public key), the key can be associated with another hierarchy. However, it will never be wrapped by a parent key.

# Generate key externally
openssl genrsa -out key.pem 2048
openssl rsa -in key.pem -pubout -out key_pub.pem

# Load the key
tpm2_loadexternal -C n -G rsa -r key.pem -c key.ctx

Signature Generation and Verification

Signing using an asymmetric key is rather easy. Note however that the sign attribute of the key has to be set.

TPM only

# Create primary key and signing key
tpm2_createprimary -C o -c primary.ctx
tpm2_create -C primary.ctx -c key.ctx -u key.pub

# Creating file with the data to be signed
echo "The data to be signed" > plain.txt

# Create signature
tpm2_sign -c key.ctx -o signature.bin plain.txt

Now the signature can be verified using the same key.

tpm2_verifysignature -c key.ctx -m plain.txt -s signature.bin

Using OpenSSL

By default, the signature written to the specified file in a specific format called tss. To be able to verify this signature using other means such as OpenSSL, we need to specify another format: -f plain. Additionally, to be able to use the public key with OpenSSL, it needs to be saved in PEM format.

Signing with the TPM, verifying with OpenSSL:

# Create signature
tpm2_sign -c key.ctx -o signature.bin -f plain -g sha256 plain.txt

# Save public key in PEM format
tpm2_readpublic -c key.ctx -f pem -o key_pub.pem

# Verify signature
openssl dgst -verify key_pub.pem -sha256 -signature signature.bin plain.txt

Of couse we can also sign with OpenSSL and verify using the TPM. Note that in this case loading the public portion of the key into the TPM is enough for the signature verification. To spice things up a little bit, we use Elliptic Curve Cryptography (ECC) keys.

Signing with OpenSSL, verifying with the TPM:

# Generate key externally
openssl ecparam -name prime256v1 -genkey -out key.pem
openssl ec -in key.pem -pubout -out key_pub.pem

# Create signature
openssl dgst -sign key.pem -sha256 -out signature.bin plain.txt

# Load public key
tpm2_loadexternal -G ecc -u key_pub.pem -c key.ctx

# Verify signature
tpm2_verifysignature -c key.ctx -m plain.txt -g sha256 -f ecdsa -s signature.bin

Encryption and Decryption

// TODO

Persistance

By default, every key we created (or imported) is a transient object. This means it’s in the TPM’s volatile memory and will be lost after a reboot (or a TPM reset). To take advantage of the TPM’s non-volatile memory (NV), we have to instruct the TPM to store it persistently. This enables us to use keys without having them stored on the computer’s file system (because the file system is not available in early stages of boot or because the platform is a microcontroller without any file system at all).

# Create primary key and child key
tpm2_createprimary -C o -c primary.ctx
tpm2_create -C primary.ctx -c key.ctx

# Persist the transient key
tpm2_evictcontrol -c key.ctx 0x81000000

# List the handles of all persistent objects
tpm2_getcap handles-persistent

# Evict persistent key
tpm2_evictcontrol -c 0x81000000

In this example we already chose a persistent handle: 0x81000000. This handle is used to reference the persistent onject. If we did not specify a handle, the TPM would have assigned a handle.

We also learned that there is a way of listing the handles of all persistent objects using the tpm2_getcap (get capability) tool.

NV Space

// TODO

# Create primary key and child key
tpm2_nvdefine -Q  1 -C o -s 32 -a "ownerread|policywrite|ownerwrite"

echo "please123abc" > nv.test_w

tpm2_nvwrite -Q   $nv_test_index -C o -i nv.test_w

tpm2_nvread -Q  1 -C o -s 32 -o 0

Authorization

Until now, we did not worry about authorization. Obviously we do not want our keys to be accessible by anyone. To protect our secrets, the TPM offers three kind of sessions:

  • password session
  • HMAC session
  • policy session

Authorization Roles

// TODO

Password Session

Password authorizations are the simplest authorizations. Every object has an auth. value which is by default empty. If the auth. value is specified during object creation or changed afterwards, the entity can only be used with authorization.

The TPM provides a special password session which does not need to be started and does not maintain any state. It is used to send a password in plaintext to the TPM alongside a command. For the tpm2-tools, to authorize an entity you usually pass -p <password>. If an object’s parent is to be authorized, an uppercase -P <password> is passed.

The auth. value can be set during creation of a TPM entity or later on.

Commands to set the auth. value during creation:

  • tpm2_createprimary for primary objects
  • tpm2_create for any other object
  • tpm2_nvdefine for NV indices

command to set the auth. value later:

  • tpm2_changeauth for transient and persistent objects, hierarchies and NV indices

In the following example, the authorization of a key object is demonstrated. TPM objects need to be loaded after a call of tpm2_changeauth.

# Create primary key
tpm2_createprimary -c primary.ctx

# Create child key with password 123456
tpm2_create -C primary.ctx -p 123456 -c key.ctx -u key_pub.bin -p 123456

# Use the key
echo "The data to be signed" > plain.txt
tpm2_sign -c key.ctx -o signature.bin -p 123456 plain.txt

# Change password to 000000 and load
tpm2_changeauth -c key.ctx -C primary.ctx -r key_priv.bin -p 123456 000000
tpm2_load -C primary.ctx -r key_priv.bin -u key_pub.bin -c key.ctx

# Use the key again
tpm2_sign -c key.ctx -o signature.bin -p 000000 plain.txt

HMAC Sessions

Behind the scenes

// TODO

The TPM’s memory is quite constraint. To be able to export keys to the computers file system securely i.e. as an encrypted key context, we need the Access Broker and Resource Manager (ABRM). There are two notable implementations:

  • The in-linux-kernel resource manager which is part of the TPM device driver. Typically, the driver provides a character device /dev/tpmrm0.
  • The user space tpm2-abrmd which can be used with the TPM simulator and offers some features such as easy logging etc.
# TODO