tris.fyi > blog >

TPM2-backed SSH keys on NixOS

posted 2022-08-18

Table of Contents

Introduction

If you are here, I am assuming you already know why it's useful to store keys on a TPM, so I won't write about my rationale here.

There are a few components to understand here:

Installation

Let's make sure those components are all installed and configured appropriately:

security.tpm2 = {
  enable = true;
  pkcs11.enable = true;
  abrmd.enable = true;
};

environment.systemPackages = [
  # your other desired systemPackages
  tpm2-tools
];

Configuring the TPM

Now we can initialize the TPM and generate our keys.

First, initialize an object hierarchy within the TPM, store that information in tpm2-pkcs11's database, and return a handle that we can use in future commands.

% tpm2_ptool init
action: Created
id: 1

The id returned is that of the "primary object" of the hierarchy. We will need this number in future commands. It's probably 1, but change the --pid option in later commands if you need to.

Next, we'll create a PKCS11 token. You have to pass a sopin ("system operator" PIN, for recovery/admin purposes) and a userpin (the PIN you will usually use to unlock the keys of this token) on the command line in this step, so consider export HISTFILE=/dev/null so those don't get stored in your shell history.

The label can be anything you want, and both PINs can contain non-numeric characters if you like.

$ tpm2_ptool addtoken --pid=1 --label=ftpmtoken1 --sopin=youradminpassword --userpin=youruserpassword

Next, create a key on the newly created token. --label and --userpin must match what you used before. Multiple algorithms are available (tpm2_ptool addkey --help) but not all of them are necessarily supported by your TPM.

To see if your TPM supports a specific ECC curve, try tpm2_getcap ecc-curves. To see if your TPM supports a specific RSA key size, try tpm2_testparms rsa[bits], like tpm2_testparams rsa4096. If no error is returned, you can use that key size.

$ tpm2_ptool addkey --algorithm=ecc256 --label=ftpmtoken1 --userpin=youruserpassword

Configuring OpenSSH

Now we can get our public key from the TPM:

$ ssh-keygen -D /run/current-system/sw/lib/libtpm2_pkcs11.so
WARNING: Listing FAPI token objects failed: "fapi:A parameter has a bad value"
Please see https://github.com/tpm2-software/tpm2-pkcs11/blob/master/docs/FAPI.md for more details
WARNING: FAPI backend was not initialized.
ecdsa-sha2-nistp256 [...]

You can safely ignore the FAPI warnings, if any appear for you. Install the SSH public key on whatever hosts you need to access, then:

$ ssh -o IdentityAgent=none -o PKCS11Provider=/run/current-system/sw/lib/libtpm2_pkcs11.so user@host

You should be prompted for your token PIN here.

You only need to set IdentityAgent=none to bypass using your usual SSH agent (in my case, this is gpg-agent configured to use keys stored on a YubiKey, so I'm skipping the "insert token" prompt this way).

You may configure these options in your ~/.ssh/config as well, to save on typing.

Potential future work

References