Secure Storage of Cryptographic Keys

About This Task

The eLxr bianca kernel supports TPM-backed secure key storage through the kernel keyring, using trusted and encrypted keys, and userspace tools such as clevis and systemd-cryptenroll. This enables use cases such as disk encryption with keys sealed to the TPM.

The following kernel options are enabled in the eLxr bianca configuration:

CONFIG_KEYS=y
CONFIG_TRUSTED_KEYS=m
CONFIG_TRUSTED_KEYS_TPM=y
CONFIG_ENCRYPTED_KEYS=y

These provide two key types:

  • Trusted keys: keys sealed by the TPM, or another trust source.

  • Encrypted keys: keys encrypted by a trusted or user key.

Before You Begin

Creating TPM-Backed Trusted Keys with keyctl

  1. Prepare the TPM by creating a persistent Storage Root Key (SRK).

    # SRK_HANDLE=0x81000001
    # if ! tpm2_readpublic -c "$SRK_HANDLE" >/dev/null 2>&1; then
          tpm2_createprimary -C owner -G rsa2048 -g sha256 -c srk.ctx
          tpm2_evictcontrol -C owner -c srk.ctx "$SRK_HANDLE"
      fi
    
  2. Create a trusted key.

    # printf 'new 32 keyhandle=0x81000001\n' | keyctl padd trusted kmk @u
    
  3. Create an encrypted key sealed by the trusted key.

    # keyctl add encrypted evm 'new trusted:kmk 32' @u
    

Binding LUKS Disk Encryption to the TPM

  1. Install the required packages.

    # apt install cryptsetup clevis clevis-luks clevis-tpm2
    
  2. Create a LUKS volume and bind it to TPM PCRs 0 and 7.

    # dd if=/dev/zero of=disk.img bs=1M count=64
    # loopdev=$(losetup -f --show disk.img)
    # printf 'passphrase' > luks-pass
    # cryptsetup luksFormat "$loopdev" luks-pass
    # clevis luks bind -y -k luks-pass -d "$loopdev" tpm2 '{"pcr_ids":"0,7","pcr_bank":"sha256"}'
    
  3. Unlock the volume using the TPM.

    # clevis luks unlock -d "$loopdev" -n myvolume
    

    The volume can also be unlocked at boot by adding a clevis initramfs hook or by configuring /etc/crypttab with a _netdev or keyscript that invokes clevis luks unlock.

Sealing Arbitrary Data to PCRs

  1. Seal data to TPM PCRs with clevis.

    # echo 'secret' | clevis encrypt tpm2 '{"pcr_ids":"10","pcr_bank":"sha256"}' > sealed.jwe
    # clevis decrypt < sealed.jwe
    
  2. Confirm that decryption fails if the PCR values change.

    # tpm2_pcrextend 10:sha256=0000000000000000000000000000000000000000000000000000000000000000
    # clevis decrypt < sealed.jwe   # fails
    

Results

Cryptographic keys are sealed to the TPM through the kernel keyring and clevis, so that keys and sealed data are released only when the TPM is present and the bound PCR values match the expected state. For fully set up devices, you can verify the TPM state remotely using Remote Attestation.