|Hardware:||Platforms based on Intel® Core™ processors with Intel® Platform Trust Technology (Intel® PTT) or discrete TPM|
(Programming Language, tool, IDE, Framework)
|Prerequisites:||Kernel TPM2 driver, TPM 2.0 tools (tpm2-tools) 4.0 release based on TPM2 Software Stack (tpm2-tss) and TPM2 Access Broker daemon (tpm2-abrmd), and OpenSSL command-line tools
Applications running on connected devices often lack many protections that applications running in the cloud or in corporate data centers by virtue of the platform hardware being more physically accessible. This sample will demonstrate how to use Intel® Platform Trust Technology (Intel® PTT) to protect the pass phrase for an encrypted storage volume by "sealing" and "unsealing" data to the underlying platform using TPM 2.0. You will learn how to perform basic sealing and unsealing operations and then add various types of authorization policies that protect the data against different kinds of attacks such as physical disk cloning or unauthorized versions of key pieces of system software.
Encryption schemes function by virtue of keeping protected the encryption secret called a digital key. Key protection requires both physical access protection as well as restricted access to sensitive operations with the key, such as decryption and digital signing. The Trusted Computing Group (TCG) calls these two requirements root of trust for storage (RTS) and root of trust for reporting (RTR). TCG is a consortium of industry and academic expertise coming together to define specifications for a security module—the Trusted Platform Module or TPM—that achieves these objectives. TPM implementations comply with two major standard specification versions: 1.2 and 2.0. Algorithm flexibility and enhanced authorization schemes are the major improvements in version 2.0. Other notable TPM properties include: secure persistent storage, platform configuration registers (PCRs), a per-TPM unique certified key pair, and hierarchies for partitioning roles for access to TPM objects. Silicon that is purpose-built to function as a TPM is known as a discrete TPM.
Intel® Platform Trust Technology (Intel® PTT) is an integrated TPM 2.0 implementation on select Intel platforms. Intel PTT runs on the Intel® Management Engine (Intel® ME) and maintains its state separate and isolated from the the host CPU and hence the host software. The notable differences from a conventional discrete TPM are:
TPM is a resource-limited device: applications may quickly exhaust the secure persistent storage of about 14 KB and a smaller fast memory designed to support a maximum of three sessions. To address these limitations, TCG created a companion specification for a trusted software stack (TSS) to optimally function with the resource-limited TPM. The TSS specification partitions the stack into well-defined interfaces for device communication, command response buffer transport, access brokering, resource management, session management, and defines profiles for the most common use cases.
Unlike TPM1.2 where IBM TSS and tools were widely used, for TPM2 there is more than one implementation of the TSS available. Besides the one from IBM there is an implementation from Google that is mostly used for the Google Chromium* project. The one that is getting widespread adoption by distributions and Linux community at large is the tpm2-software project on GitHub. One of its notable features is that it follows the TCG guidelines on having well-defined interfaces for ESYS, SAPI, TAB/RM and the TCTI. Following are the links for the tpm2-software projects:
tpm2-tools has an dependency on the tpm2-tss project and optionally on the tpm2-abrmd project. The use cases in this sample are demonstrated using the tpm2-tools 4.0 release.
All the projects use GNU Autotools since it is widely used and highly portable across Linux distributions. For all the projects above: "bootstrap", "configure", "make" and "make install" are generally sufficient to build and install the packages. The bootstrap and configure scripts should inform the user of missing libraries and packages. The user can then install them using the package manager specific to the distribution in use.
Disk encryption prevents storage devices from being mounted on alternative operating environments controlled by the attacker where they can observe or tamper with sensitive information. This use case will take a basic unprotected loopback-mounted file system and gradually add stronger and more robust protections to it. Assume for purposes of discussion that this loopback-mounted file system is a logical disk partition automatically mounted by the operating system on boot rather than just a file-based simulation.
The sample code referenced in this article is available in the download link in the header of this article.
To demonstrate the problem, first set up a disk image without encryption and demonstrate disclosure of sensitive content.
Create a disk image and write some content:
dd if=/dev/zero of=plain.disk bs=1M count=10 mkfs.ext4 plain.disk mkdir mountpoint sudo mount plain.disk mountpoint sudo echo "This is my plain text" > mountpoint/plain.txt sudo umount mountpoint
At this point we have a disk with a file plain.txt which has the sensitive content "This is my plain text". The file system is not mounted by the operating system, yet we can demonstrate that the sensitive data is not protected. To do this simply run the following command:
The above command will disclose the file name and sensitive file content. Future sections will add protections to prevent an attacker from performing the trivial attack above.
Disk encryption is only an effective mitigation when used with other security controls. Once an encrypted volume is mounted, it is subject to the standard operating system enforced file system protections. An encrypted volume that is automatically mounted without user intervention will offer no protection if an attacker is allowed to override the kernel command line and start an interactive shell without having to log in, for example.
Most standard Linux distributions support LUKS-encrypted disk volumes. LUKS ensures data confidentiality at rest. LUKS stores setup information in the partition header to aid easy migration. LUKS volumes can be automatically mounted, and the encryption passphrase can be supplied interactively (default) and or specified as key file (command line argument). The cryptsetup user-space utility aids creating and managing LUKS volumes.
Set up a new LUKS volume with a simple passphrase as key protector:
It is suggested to run losetup -f to find the next unused loopback device and use that device in place of /dev/loop0 in the script below. Also, use /dev/hwrng (hardware random number generator) in place of /dev/urandom below if it is available.
dd if=/dev/zero of=enc.disk bs=1M count=10 dd if=/dev/urandom of=disk.key bs=1 count=32 sudo losetup /dev/loop0 enc.disk sudo cryptsetup --key-file=disk.key luksFormat /dev/loop0
At this point you have set up the LUKS volume and it should display a warning about overwriting the data. Open the LUKS volume by authenticating with the disk.key and complete the setting up the disk with a filesystem:
sudo cryptsetup --key-file=disk.key open /dev/loop0 enc_volume sudo mkfs.ext4 -j /dev/mapper/enc_volume sudo mount /dev/mapper/enc_volume mountpoint
Create a plain text file again, add user content, and unmount the volume:
sudo echo "This is my plain text" > mountpoint/plain.txt sudo umount mountpoint sudo cryptsetup remove enc_volume sudo losetup -d /dev/loop0
Observe that the sensitive data is no longer visible in the (encrypted) disk image:
strings enc.disk | grep -i plain
The biggest issue with this method of encrypting the disk volume is that LUKS protections are easily subverted if the passphrase or key file is stored in plain text on disk. Thus, many LUKS implementations interactively prompt for the passphrase. This solution is unworkable for many Internet of Things (IoT) and embedded scenarios due to the lack of a human at a keyboard. An effective alternative to human input is to seal the passphrase on a security token that anchors to the platform, like a TPM.
Since auto-mounting requires providing a passphrase or key to cryptsetup at runtime without user intervention, it must as some point be available in the clear. A method is needed to encrypt the passphrase or key until it is needed at runtime to decrypt the volume. Such a method must also protect against the case where the disk is separated from the system into which it was installed. This mechanism is TPM.
The improved solution has two steps:
Create and persist a sealing object and use it to seal a random byte sequence as the disk key:
tpm2_createprimary -Q --hierarchy=o --key-context=prim.ctx dd if=/dev/urandom bs=1 count=32 status=none | tpm2_create --hash-algorithm=sha256 --public=seal.pub --private=seal.priv --sealing-input=- --parent-context=prim.ctx tpm2_load -Q --parent-context=prim.ctx --public=seal.pub --private=seal.priv --name=seal.name --key-context=seal.ctx tpm2_evictcontrol --hierarchy=o --object-context=seal.ctx 0x81010002
Install the new key in place of the old one, and delete the old key created previously:
tpm2_unseal -Q --object-context=0x81010002 | sudo cryptsetup --key-file=disk.key luksChangeKey enc.disk shred disk.key rm -f disk.key
Mount the volume with the new authentication sealed in the TPM:
sudo losetup /dev/loop0 enc.disk tpm2_unseal -Q --object-context=0x81010002 | sudo cryptsetup --key-file=- luksOpen /dev/loop0 enc_volume sudo mount /dev/mapper/enc_volume mountpoint
Disk access is granted with the new secret:
Unmount the disk:
sudo umount mountpoint sudo cryptsetup remove enc_volume sudo losetup -d /dev/loop0
An attacker now additionally needs the TPM on the platform along with the disk to access the data since the decryption key isn't on the disk—it is safely stored on the TPM anchored to the specific platform.
This improved solution will protect against offline attacks through cloning or physical theft of the encrypted disk. It may still be possible, however, for an attacker to boot a system from alternative boot media, perform the above commands, and retrieve the secret needed to decrypt the LUKS volume. What is needed is an authentication mechanism that anchors to trusted system state. This can be achieved with a TPM and its platform configuration register (PCR) sealing provision.
TPM platform configuration registers (PCRs) are used to protect against the scenario where an attacker alters the system boot parameters or boots to an operating system of their choice in order to access sensitive data. Effective use of TPM PCRs requires the cooperation of the system firmware, boot loaders, operating system kernel, and applications. Without firmware, drivers, and software to configure the TPM, the TPM will just sit inactive on an I/O bus doing nothing.
TPM PCRs are used to measure boot components using a secure hash algorithm such as SHA-256. The TPM PCRs default to a zero value when the system is reset. As the system boots, measurements of critical system components such as the firmware, BIOS, OS loaders, et cetera are extended into PCRs as boot progresses. Extending a PCR is an append-only operation, and requires I/O to the TPM. Because it is impossible to set a PCR to a user-specified value and also impossible to "take back" I/O, the TPM PCRs can attest the system boot sequence and thus the state of the platform up to the point were PCR measurements ceased. PCR0, for example, contains measurements of the system firmware and BIOS, but not the operating system boot loader or kernel. A consequence of using PCR0 and only PCR0 is that this example would only protect against a firmware replacement attack, provided the original firmware made measurements properly, and provided the replacement firmware does not forge measurements to the TPM—an attack that requires a separate mitigation.
In practice, an effective PCR set must be sufficiently complete to attest the currently running code and any code that has run beforehand that the designer deems security-critical. The TCG PC Client Specific Implementation Specification for Conventional BIOS only specifies usage of PCRs 0-7, 16, and 23. PCR usage during and after operating system boot is operating system specific.
The further improved solution has two additional steps:
Create a PCR policy with current value of PCR0 in the sha256 bank:
tpm2_startauthsession --session=session.ctx tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:0" --policy=pcr0.sha256.policy tpm2_flushcontext session.ctx
Now replace the seal object in the TPM non-volatile memory protecting the disk encryption secret with a new one that adds the pcr policy we just created as an authentication mechanism to access the sealed secret:
tpm2_unseal --object-context=0x81010002 | tpm2_create -Q --hash-algorithm=sha256 --public=pcr_seal_key.pub --private=pcr_seal_key.priv --sealing-input=- --parent-context=prim.ctx --policy=pcr0.sha256.policy tpm2_evictcontrol --hierarchy=o --object-context=0x81010002 tpm2_load -Q --parent-context=prim.ctx --public=pcr_seal_key.pub --private=pcr_seal_key.priv --name=pcr_seal_key.name --key-context=pcr_seal_key.ctx tpm2_evictcontrol --hierarchy=o --object-context=pcr_seal_key.ctx 0x81010002
Now try to mount the encrypted disk again except this time the secret is sealed inside a TPM object whose unsealing operation can only be accessed by satisfying the PCR policy. In other words, authenticating by virtue of intended system software state being unchanged as reflected by the PCR value.
sudo losetup /dev/loop0 enc.disk tpm2_startauthsession --policy-session --session=session.ctx tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:0" --policy=pcr0.sha256.policy
At this point ideally you would want unseal the secret in memory and pipe it directly to cryptsetup like this: "tpm2_unseal --auth=session:session.ctx --object-context=0x81010002 | sudo cryptsetup luksOpen --key-file=- /dev/loop0 encvolume". However, for the purpose of demonstrating flexible PCR in a later section we will make a copy of the unsealed secret:
tpm2_unseal --auth=session:session.ctx --object-context=0x81010002 > disk_secret.bkup cat disk_secret.bkup | sudo cryptsetup --key-file=- luksOpen /dev/loop0 enc_volume tpm2_flushcontext session.ctx sudo mount /dev/mapper/enc_volume mountpoint/ ls mountpoint/
To prevent further unsealing, PCR0 will be extended. This will cause PCR0 to hold a different value, much like it would during a firmware replacement attack. This will cause a failed policy check and thus a failed unsealing attempt.
Look at the PCR state prior to extending it and then again after extending:
tpm2_pcrread --sel-list=sha256:0 tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 tpm2_pcrread --sel-list=sha256:0
Try to unseal the sealed disk encryption secret with the dirty PCR:
tpm2_startauthsession --policy-session --session=session.ctx tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:0" --policy=pcr0.sha256.policy
The following operation should result in a policy check failure preventing the unseal operation:
tpm2_unseal --auth=session:session.ctx --object-context=0x81010002 tpm2_flushcontext session.ctx
Unmount the disk:
sudo umount mountpoint sudo cryptsetup remove enc_volume sudo losetup -d /dev/loop0
The improved solution now uses TPM PCRs to attest to the system state prior to retrieving the LUKS encryption pass phrase, but there is now a new problem: updates. In this example, applying a firmware update to the system could render the encrypted data inaccessible because PCR0 represents a specific version of the firmware. In order to successfully update the system, it would be necessary to re-seal the secret against a predicted set of PCR values. But if the update was rolled-back, the old values would no longer work. Furthermore, it may be impractical to predict the post-update PCR values prior to actually performing the update—the best approach might be to apply the update on an identical system and see what PCR values would result and pre-authorize both sets of PCR values. With TPM, there is a a way to do that called "authorized PCR policy."
With LUKS, it is always possible to output the LUKS master key used for data encryption on a running system. Extending the sealing PCR does not protect the LUKS master key, as the master key is needed at runtime to encrypt/decrypt disk I/O.
Authorized PCR policy as an authentication mechanism for the TPM sealing object. Instead of using a rigid PCR policy tied to raw PCR values we now seal it to a PCR signature. The PCR sets are signed by the system designer and verified by the TPM. This is achieved in following steps:
Use the OpenSSL command-line tools to generate an RSA keypair. The private key will be stored offline. The public key will be distributed with the authorized PCR policy and used as the authorization mechanism for the LUKS encryption pass phrase.
openssl genrsa -out signing_key_private.pem 2048 openssl rsa -in signing_key_private.pem -out signing_key_public.pem -pubout
On each known good system configuration, gather the current PCR values and sign them with the RSA private key. This step may be done as many times as needed.
tpm2_startauthsession --session=session.ctx tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:0" --policy=set2.pcr.policy tpm2_flushcontext session.ctx openssl dgst -sha256 -sign signing_key_private.pem -out set2.pcr.signature set2.pcr.policy
Before sealing the LUKS encryption passphrase to the TPM, it is necessary to create a policy object that specifies the conditions under which the passphrase can be unsealed. The policy will specify that a certain set of PCRs (PCR0) must match to the values signed with a specific key (signing_key_public.pem):
tpm2_loadexternal --key-algorithm=rsa --hierarchy=o --public=signing_key_public.pem --key-context=signing_key.ctx --name=signing_key.name tpm2_startauthsession --session=session.ctx tpm2_policyauthorize --session=session.ctx --policy=authorized.policy --name=signing_key.name --input=set2.pcr.policy tpm2_flushcontext session.ctx
Seal the pass phrase to the TPM by creating a sealing object using the above policy. Note that the backup copy of the passphrase is used since since the previous example extended PCR0 to prevent re-unsealing of the pass phrase:
cat disk_secret.bkup | tpm2_create --hash-algorithm=sha256 --public=auth_pcr_seal_key.pub --private=auth_pcr_seal_key.priv --sealing-input=- --parent-context=prim.ctx --policy=authorized.policy
Replace the old persistent sealing object with the one created above:
tpm2_evictcontrol --hierarchy=o --object-context=0x81010002 tpm2_load -Q --parent-context=prim.ctx --public=auth_pcr_seal_key.pub --private=auth_pcr_seal_key.priv --name=auth_pcr_seal_key.name --key-context=auth_pcr_seal_key.ctx tpm2_evictcontrol --hierarchy=o --object-context=auth_pcr_seal_key.ctx 0x81010002
Load the public key, PCR policy, and signature, and ask the TPM to verify the signature:
tpm2_loadexternal --key-algorithm=rsa --hierarchy=o --public=signing_key_public.pem --key-context=signing_key.ctx --name=signing_key.name tpm2_verifysignature --key-context=signing_key.ctx --hash-algorithm=sha256 --message=set2.pcr.policy --signature=set2.pcr.signature --ticket=verification.tkt --format=rsassa
Ask the TPM to now verify that the PCR values match the current values, passing in a verification ticket for the signature verification. Note that only one set of PCR values can be verified at a time: the whole process must be repeated in order to attempt to verify against another set of signed PCR values:
tpm2_startauthsession --policy-session --session=session.ctx tpm2_policypcr --pcr-list="sha256:0" --session=session.ctx --policy=set2.pcr.policy tpm2_policyauthorize --session=session.ctx --input=set2.pcr.policy --name=signing_key.name --ticket=verification.tkt
Unseal the encryption pass phrase and unlock the volume:
sudo losetup /dev/loop0 enc.disk tpm2_unseal --auth=session:session.ctx --object-context=0x81010002 | sudo cryptsetup --key-file=- luksOpen /dev/loop0 enc_volume tpm2_flushcontext session.ctx sudo mount /dev/mapper/enc_volume mountpoint/ ls mountpoint/
Unmount the disk when done:
sudo umount mountpoint sudo cryptsetup remove enc_volume sudo losetup -d /dev/loop0
Intel Platform Trust Technology (Intel PTT) is a useful technology based on the TCG TPM 2.0 specifications that enables protection of secret data and keys. In the volume encryption use case, Intel PTT was used to protect an encryption key against offline attacks on encrypted storage. The use case culminated in using a PCR authorization policy that allowed the data to remain protected while also accommodating system updates. The workflow for using a PCR authorization consisted of a number of phases with steps in each phase:
Please contact firstname.lastname@example.org if you have feedback or wish to report issues with this sample.
Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice.
Notice revision #20110804