Trusted platform module (TPM) is a secure element that can be used to securely generate and store keys. It has many possible uses, one of which is encryption of sensitive data. This article describes how to use TPM on Linux environment to encrypt different types of data, how to encrypt filesystem partitions and how to bind the encryption with device state.
Warning: when writing this article I have used an embedded Linux platform with TPM chip. Many modern PCs and laptops also include TPM. However, one should be cautious when modifying the TPM on a PC because e.g. BIOS and disk encryption may depend upon it.
Before the TPM chip can be used for encryption, its ownership needs to be taken. This step also allows to set passwords for the TPM chip itself (owner password) and for the storage root key (SRK) which is generated automatically each time the take ownership command is executed.
When testing, it is convenient to use so-called well-known passwords (20 bytes of zeros) for owner and SRK because many of the tpm-tools provide command line argument for them. However, in real production environment, other passwords should be considered.
To use the TPM commands described in this article, tcsd daemon should be running and tpm-tools package installed. To take the TPM ownership with well-known owner and SRK passwords, the following command is used.
Encrypting small files
TPM seal command allows to encrypt data using the SRK key in the TPM chip. In practice this means that data sealed with a TPM can only be unsealed (decrypted) with the exactly same TPM chip which binds the encryption to a specific device.
The following command encrypts a file named data.bin and stores it as data.enc. The -z option is a shortcut for the well-known SRK password so it won’t be prompted.
tpm_sealdata -i data.bin -o data.enc -z
To decrypt the same data later the following command is used:
tpm_unsealdata -i data.enc -o data.bin -z
Encrypting large files
The TPM chip is designed to be an inexpensive secure element, not a cryptographic accelerator so its encryption and decryption performance is very modest. Therefore it would be inefficient to seal/unseal large data files directly. Instead, it is better to seal a symmetric key (which is a small file) and use it to encrypt the actual data.
The following command generates a random key data that is used with OpenSSL to encrypt data:
# note: consider seeding the OpenSSL random pool before key generation. openssl rand 32 -hex | tpm_sealdata -z > keydata.enc
The following example shows how to encrypt a blob of data using openssl enc and the previously sealed key data. In this example AES256 algorithm is used for encryption and SHA256 for salt (see OpenSSL manual for more details).
tpm_unsealdata -i keydata.enc -z | openssl enc -aes-256-cbc -md sha256 -pass stdin -in large_data.bin -out large_data.enc
The data can then be decrypted using a similar command (note the -d option which stands for decryption).
tpm_unsealdata -i keydata.enc -z | openssl enc -d -aes-256-cbc -md sha256 -pass stdin -in large_data.enc -out large_data.bin
Note that in all examples above, the key data is directly piped to the command operating on it, so it is never stored unencrypted on the filesystem.
Encrypting full filesystem partitions can be done using a similar scheme that was described for large files. The partition can be encrypted using for instance dm-crypt subsystem, and the partition password is protected using TPM.
The following example shows how to create a new dm-crypt partition using cryptsetup utility. This step only needs to be done once when the new partition is created.
# Generate random password and seal it openssl rand 32 -hex | tpm_sealdata -z > key.enc # Create new dm-crypt partition using the password tpm_unsealdata -i key.enc -z | cryptsetup luksFormat --key-file=- /dev/mmcblk0p1
Once the dm-crypt partition has been created, it needs to be unlocked and a filesystem such as EXT4 needs to be created to it.
# Unlock the partition tpm_unsealdata -i key.enc -z | cryptsetup luksOpen --key-file=- /dev/mmcblk0p1 encrypted # Unlocking the partition creates a virtual node /dev/mapper/encrypted. # File system can now be created to it. # This step only needs to be done on the first time mkfs.ext4 /dev/mapper/encrypted -L encrypted # the filesystem can now be mounted normally mount /dev/mapper/encrypted /encrypted
The mounted filesystem can be used normally, and all files stored to it will be transparently encrypted before they are written to the physical media.
Binding encryption with device state
It is also possible to bind the encryption with device state using platform configuration registers (PCR) that are on the TPM chip. In practice this means that the sealed data can only be unsealed if the PCR registers are in the same state as they were when the data was sealed.
When a PCR register is written the TPM chip uses hashing operations to calculate its new value. Here is a practical example.
# Initial PCR23 value is all zeros # PCR-23: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 echo 1234 | tpm_extendpcr -p 23 # New value after write: # PCR-23: 84 02 D4 9A BC 9B F1 BF E9 6C D3 79 5A 01 A2 C1 38 17 77 EE # If the same 1234 value is written (extended) again, we get # PCR-23: 4F BC 58 E8 2E 6D D5 1E 71 C4 8D 6E 49 BC A1 08 E9 BC 9D C1
So just by looking at the PCR23 value, it is impossible to know what value was written to it and how many times it was written. However, it is important to notice that if the device is resetted (PCR23 goes back to reset value, zeros) and the same writes are done again, the PCR register will end up with the same value.
So if security critical parts such as bootloader extends the PCR registers, it is possible to only allow decryption of sensitive data if these parts were executed successfully. For instance, if some security measure is bypassed (PCR register is not extended), all unseal commands that have been bound to that PCR register will fail.
Binding with PCR registers is applicable to all cases described earlier. The binding is done by setting -p option one or many times when using tpm_sealdata command.