Notes on using a TPM2 module on Linux

Here I collected some notes on using the TPM2 on Linux, specifically Arch Linux. The motherboard used is an ASRock E3C222D4U with the ASRock TPM2 module.

This article focuses on enabling the usage of the keys stored in the TPM2 by various tools using the PKCS#11 interface. The module should be configured to allow the access to these keys only after some measurement of the state of the system is done, to guarantee that it was not tampered. This part is outside of the scope of these notes. Here the protections offered to the user are the physical possession and the requirement to enter a PIN.

PKCS#11 exposes N separate slots, each containing a token. We will used a separate slot/token for each application. For the basic support under Arch Linux, the following packages need to be installed: tpm2-abrmd,tpm2-pkcs11, tpm2-tools and tpm2-tss. The latest package also sets up udev rules to allow the abrmd broker daemon to access the device /dev/tpm0. You can test if the TPM2 module is working by printing some random characters by using its random number generator:

$ tpm2_getrandom --hex 16

Note that tpm-pkcs11 needs to save some information on disk, I've set the TPM2_PKCS11_STORE environment variable to a suitable directory in my .bashrc file.

SSH in slot 0

Using the TPM2 is pretty straightforward. From here on [PIN] denotes the user PIN. Avoid saving the command lines containing it in the bash history, for example by prepending the line with a space.

$ tpm2_ptool init
action: Created
id: 1
$ tpm2_ptool addtoken --pid=1 --sopin=[PIN] --userpin=[PIN] --label=ssh_token
$ tpm2_ptool addkey --algorithm=rsa2048 --label=ssh_token --key-label=ssh_token --userpin=[PIN]
action: add
private:
CKA_ID: '33333334333132626661393734663161'
public:
CKA_ID: '33333334333132626661393734663161'

To output the public key (to be added to the authorized_keys file on the remote system):

$ ssh-keygen -D /usr/lib/pkcs11/libtpm2_pkcs11.so
ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...

And you can connect to a system by offering the key from the TPM2:

$ ssh -I /usr/lib/pkcs11/libtpm2_pkcs11.so [destination host]

GnuPG in slot 1

Supporting GnuPG is more complicated. You will need openssl for creating a certificate from the public key and the package gnupg-pkcs11-scd to interface GnuPG with the TPM2 via the PKCS#11 API. Unfortunately, there is a bug at the moment in the upstream program which doesn't correctly inform the GnuPG agent daemon about the key padding. I prepared a Pull Request for the upstream, until it is merged, you need to use the version from my github repository.

The initial part is similar to the previous case, but then you need to create a certificate from the public key to allow GnuPG to use it:

$ tpm2_ptool init
action: Created
id: 2
$ tpm2_ptool addtoken --pid=2 --label=gpg_token --sopin=[PIN] --userpin=[PIN]
$ tpm2_ptool addkey --algorithm=rsa2048 --label=gpg_token --key-label=gpg_token --userpin=[PIN]
$ openssl <<EOF
req -engine pkcs11 -new -key pkcs11:model=rls;manufacturer=Nuvoton;serial=0000000000000000;token=gpg_token;type=private;pin-value=[PIN] -keyform engine -out req.pem -text -x509 -subj /CN=[Your Common Name]
x509 -engine pkcs11 -signkey pkcs11:model=rls;manufacturer=Nuvoton;serial=0000000000000000;token=gpg_token;type=private;pin-value=[PIN] -keyform engine -in req.pem -out cert.pem
EOF
$ tpm2_ptool addcert --label gpg_token --key-label gpg_token cert.pem

Afterwards, we need to configure the SCD (smart card driver) for the GnuPG Agent which uses the PKCS#11 to work with the private key. Unfortunately, GnuPG Agent supports only a single SCD, so you cannot use both the TPM2 and a normal smart card at the same time. Also, note that the patched version of gnupg-pkcs11-scd needs to be used until upstream is fixed. Two configuration files need changes:

  • ~/.gnupg/gpg-agent.conf:
    scdaemon-program /usr/bin/gnupg-pkcs11-scd
  • scdaemon-program /usr/bin/gnupg-pkcs11-scd:
    has_padding
    providers tpm
    provider-tpm-library /usr/lib/pkcs11/libtpm2_pkcs11.so
  • for the latter, the following debug directives might be useful if you encounter any problems:
    verbose
    debug-all
    log-file /tmp/scd.log

GnuPG Agent has to be restarted and you need to trigger a reload of the smart card:

$ systemctl --user restart gpg-agent.service
$ gpg --card-status

The next step consist in identifying the keygrip for the RSA keypair stored in the TPM2 module. The procedure for creating a keypair should support automatically identifying the smartcard. Unfortunately, this doesn't seem working, so you need to manually list the available keygrips:

$ gpg-agent --server gpg-connect-agent << EOF
SCD LEARN
EOF
...
gnupg-pkcs11-scd[9221]: S KEY-FRIEDNLY 01B4D88E8F24441E1773472EFAD1CFE020072CF2 /CN=[The CN you used] on gpg_tokenchan_0 ...
...

Look for a 40 characters long string of letters and numbers after the words KEY-FRIENDLY. With this information you can proceed with the normal generation of a keypair using option 13 Existing key and entering the keygrip when prompted:

$ gpg --expert --full-generate-key
...
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
Your selection? 13
Enter the keygrip: 01B4D88E8F24441E1773472EFAD1CFE020072CF2
...

And that's it, you should have a keypair based on the RSA keys in the TPM2. Please test it by encrypting/decrypting/signing some text.

This entry was posted in Linux desktop. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*