In measured boot, each component of the boot process is "measured" (ie, hashed and that hash recorded) in a register in the Trusted Platform Module (TPM) build into the system. The TPM has several different registers (Platform Configuration Registers, or PCRs) which are typically used for different purposes - for instance, PCR0 contains measurements of various system firmware components, PCR2 contains any option ROMs, PCR4 contains information about the partition table and the bootloader. The allocation of these is defined by the PC Client working group of the Trusted Computing Group. However, once the boot loader takes over, we're outside the spec[1].
One important thing to note here is that the TPM doesn't actually have any ability to directly interfere with the boot process. If you try to boot modified code on a system, the TPM will contain different measurements but boot will still succeed. What the TPM can do is refuse to hand over secrets unless the measurements are correct. This allows for configurations where your disk encryption key can be stored in the TPM and then handed over automatically if the measurements are unaltered. If anybody interferes with your boot process then the measurements will be different, the TPM will refuse to hand over the key, your disk will remain encrypted and whoever's trying to compromise your machine will be sad.
The problem here is that a lot of things can affect the measurements. Upgrading your bootloader or kernel will do so. At that point if you reboot your disk fails to unlock and you become unhappy. To get around this your update system needs to notice that a new component is about to be installed, generate the new expected hashes and re-seal the secret to the TPM using the new hashes. If there are several different points in the update where this can happen, this can quite easily go wrong. And if it goes wrong, you're back to being unhappy.
Is there a way to improve this? Surprisingly, the answer is "yes" and the people to thank are Microsoft. Appendix A of a basically entirely unrelated spec defines a mechanism for storing the UEFI Secure Boot policy and used keys in PCR 7 of the TPM. The idea here is that you trust your OS vendor (since otherwise they could just backdoor your system anyway), so anything signed by your OS vendor is acceptable. If someone tries to boot something signed by a different vendor then PCR 7 will be different. If someone disables secure boot, PCR 7 will be different. If you upgrade your bootloader or kernel, PCR 7 will be the same. This simplifies things significantly.
I've put together a (not well-tested) patchset for Shim that adds support for including Shim's measurements in PCR 7. In conjunction with appropriate firmware, it should then be straightforward to seal secrets to PCR 7 and not worry about things breaking over system updates. This makes tying things like disk encryption keys to the TPM much more reasonable.
However, there's still one pretty major problem, which is that the initramfs (ie, the component responsible for setting up the disk encryption in the first place) isn't signed and isn't included in PCR 7[2]. An attacker can simply modify it to stash any TPM-backed secrets or mount the encrypted filesystem and then drop to a root prompt. This, uh, reduces the utility of the entire exercise.
The simplest solution to this that I've come up with depends on how Linux implements initramfs files. In its simplest form, an initramfs is just a cpio archive. In its slightly more complicated form, it's a compressed cpio archive. And in its peak form of evolution, it's a series of compressed cpio archives concatenated together. As the kernel reads each one in turn, it extracts it over the previous ones. That means that any files in the final archive will overwrite files of the same name in previous archives.
My proposal is to generate a small initramfs whose sole job is to get secrets from the TPM and stash them in the kernel keyring, and then measure an additional value into PCR 7 in order to ensure that the secrets can't be obtained again. Later disk encryption setup will then be able to set up dm-crypt using the secret already stored within the kernel. This small initramfs will be built into the signed kernel image, and the bootloader will be responsible for appending it to the end of any user-provided initramfs. This means that the TPM will only grant access to the secrets while trustworthy code is running - once the secret is in the kernel it will only be available for in-kernel use, and once PCR 7 has been modified the TPM won't give it to anyone else. A similar approach for some kernel command-line arguments (the kernel, module-init-tools and systemd all interpret the kernel command line left-to-right, with later arguments overriding earlier ones) would make it possible to ensure that certain kernel configuration options (such as the iommu) weren't overridable by an attacker.
There's obviously a few things that have to be done here (standardise how to embed such an initramfs in the kernel image, ensure that luks knows how to use the kernel keyring, teach all relevant bootloaders how to handle these images), but overall this should make it practical to use PCR 7 as a mechanism for supporting TPM-backed disk encryption secrets on Linux without introducing a hug support burden in the process.
[1] The patchset I've posted to add measured boot support to Grub use PCRs 8 and 9 to measure various components during the boot process, but other bootloaders may have different policies.
[2] This is because most Linux systems generate the initramfs locally rather than shipping it pre-built. It may also get rebuilt on various userspace updates, even if the kernel hasn't changed. Including it in PCR 7 would entirely break the fragility guarantees and defeat the point of all of this.
One important thing to note here is that the TPM doesn't actually have any ability to directly interfere with the boot process. If you try to boot modified code on a system, the TPM will contain different measurements but boot will still succeed. What the TPM can do is refuse to hand over secrets unless the measurements are correct. This allows for configurations where your disk encryption key can be stored in the TPM and then handed over automatically if the measurements are unaltered. If anybody interferes with your boot process then the measurements will be different, the TPM will refuse to hand over the key, your disk will remain encrypted and whoever's trying to compromise your machine will be sad.
The problem here is that a lot of things can affect the measurements. Upgrading your bootloader or kernel will do so. At that point if you reboot your disk fails to unlock and you become unhappy. To get around this your update system needs to notice that a new component is about to be installed, generate the new expected hashes and re-seal the secret to the TPM using the new hashes. If there are several different points in the update where this can happen, this can quite easily go wrong. And if it goes wrong, you're back to being unhappy.
Is there a way to improve this? Surprisingly, the answer is "yes" and the people to thank are Microsoft. Appendix A of a basically entirely unrelated spec defines a mechanism for storing the UEFI Secure Boot policy and used keys in PCR 7 of the TPM. The idea here is that you trust your OS vendor (since otherwise they could just backdoor your system anyway), so anything signed by your OS vendor is acceptable. If someone tries to boot something signed by a different vendor then PCR 7 will be different. If someone disables secure boot, PCR 7 will be different. If you upgrade your bootloader or kernel, PCR 7 will be the same. This simplifies things significantly.
I've put together a (not well-tested) patchset for Shim that adds support for including Shim's measurements in PCR 7. In conjunction with appropriate firmware, it should then be straightforward to seal secrets to PCR 7 and not worry about things breaking over system updates. This makes tying things like disk encryption keys to the TPM much more reasonable.
However, there's still one pretty major problem, which is that the initramfs (ie, the component responsible for setting up the disk encryption in the first place) isn't signed and isn't included in PCR 7[2]. An attacker can simply modify it to stash any TPM-backed secrets or mount the encrypted filesystem and then drop to a root prompt. This, uh, reduces the utility of the entire exercise.
The simplest solution to this that I've come up with depends on how Linux implements initramfs files. In its simplest form, an initramfs is just a cpio archive. In its slightly more complicated form, it's a compressed cpio archive. And in its peak form of evolution, it's a series of compressed cpio archives concatenated together. As the kernel reads each one in turn, it extracts it over the previous ones. That means that any files in the final archive will overwrite files of the same name in previous archives.
My proposal is to generate a small initramfs whose sole job is to get secrets from the TPM and stash them in the kernel keyring, and then measure an additional value into PCR 7 in order to ensure that the secrets can't be obtained again. Later disk encryption setup will then be able to set up dm-crypt using the secret already stored within the kernel. This small initramfs will be built into the signed kernel image, and the bootloader will be responsible for appending it to the end of any user-provided initramfs. This means that the TPM will only grant access to the secrets while trustworthy code is running - once the secret is in the kernel it will only be available for in-kernel use, and once PCR 7 has been modified the TPM won't give it to anyone else. A similar approach for some kernel command-line arguments (the kernel, module-init-tools and systemd all interpret the kernel command line left-to-right, with later arguments overriding earlier ones) would make it possible to ensure that certain kernel configuration options (such as the iommu) weren't overridable by an attacker.
There's obviously a few things that have to be done here (standardise how to embed such an initramfs in the kernel image, ensure that luks knows how to use the kernel keyring, teach all relevant bootloaders how to handle these images), but overall this should make it practical to use PCR 7 as a mechanism for supporting TPM-backed disk encryption secrets on Linux without introducing a hug support burden in the process.
[1] The patchset I've posted to add measured boot support to Grub use PCRs 8 and 9 to measure various components during the boot process, but other bootloaders may have different policies.
[2] This is because most Linux systems generate the initramfs locally rather than shipping it pre-built. It may also get rebuilt on various userspace updates, even if the kernel hasn't changed. Including it in PCR 7 would entirely break the fragility guarantees and defeat the point of all of this.
no subject
Date: 2017-07-18 11:19 am (UTC)no subject
Date: 2017-07-18 03:56 pm (UTC)no subject
Date: 2019-03-26 02:13 pm (UTC)This seems in many ways simpler, and much more robust. Am I missing something?
no subject
Date: 2019-03-26 04:32 pm (UTC)no subject
Date: 2019-04-04 11:35 am (UTC)If you're using FDE in an unattended context where passphrase decryption isn't involved, then the initramfs doesn't need to be involved in passphrase entry, and unlocking FDE earlier could allow simplifying things like partition layout, since GRUB could read the kernel and initramfs directly from the encrypted filesystem. Indeed this would also allow eliminating one of the remaining reasons why a separate /boot is often required, namely that at the moment if you're using FDE and don't have a separate /boot then you have to enter your passphrase twice.
no subject
Date: 2017-07-18 02:32 pm (UTC)The TPM isn't the only place we want secrets from at early boot. Additionally, we may want to use TPM as a piece of a large policy involving other sources.
The clevis[0] project already does this. We also ship in RHEL 7.4 and Fedora 26+. Although this initial release doesn't support TPM, we already have an active pull request for TPM support which will land in the next version.
The main challenge I see with this proposal is bringing up additional hardware (such as network). Dracut already supports this. But I assume that by "small" you are also implying "not dracut."
Another alternative is to perform all our key recovery in UEFI, which already has (hit or miss) network support and pass the key to the (trusted) kernel from there.
However, we would very much like to be in the loop of whatever you're planning.
[0]: https://github.com/latchset/clevis
Nervous about attack surface
Date: 2017-07-18 03:02 pm (UTC)Generate a public/private key pair, store the private key on the encrypted drive (or even elsewhere, for infrastructure), sign the initramfs (or the kernel too, for that matter, if they're building it themselves), and have it all get authenticated using the same mechanism?
Re: Nervous about attack surface
Date: 2017-07-18 04:03 pm (UTC)UEFI Secure Boot policy
Date: 2017-07-18 10:33 pm (UTC)what does this policy actually include? The settings and keys/certificates?
Re: UEFI Secure Boot policy
Date: 2017-07-19 01:48 pm (UTC)- JC [MSFT]
a crazy idea?
Date: 2017-12-19 09:52 pm (UTC)your security is still dependant on the hash being collision resistant (if I understand correctly), but what your measuring in becomes less variable.
(I just took a glance at the referenced Appendix A and it seems to be about the same idea)
Re: a crazy idea?
Date: 2018-05-25 12:52 pm (UTC)At that point, if you're already measuring the GRUB software/configuration/cert, doesn't seem like you would need to extend with anything else. If your GRUB software and configuration is getting measured, and the logic declares boot this kernel on correct sig verification or exit on failure, then tying your secrets to those measurements and later receiving them from the TPM means you've already verified that you booted a signed kernel, and what you're measuring is still less volatile.
How to make PCR7 the same after upgrade bootloader or kernel
Date: 2019-08-28 02:14 am (UTC)But after reading Appendix A, it is just the regular step to measure UEFI variable into PCR7.
So how to make PCR7 the same after upgrade bootloader or kernel?
Thanks.
Re: How to make PCR7 the same after upgrade bootloader or kernel
Date: 2019-08-28 02:21 am (UTC)Re: How to make PCR7 the same after upgrade bootloader or kernel
Date: 2019-08-30 01:40 am (UTC)But upgrade bootloader will change DB, DBX, where save image hash or image signature.
Then PCR[7] will change accordingly.
So how to make PCR[7] the same when upgrade bootloader?
Re: How to make PCR7 the same after upgrade bootloader or kernel
Date: 2019-08-31 11:23 am (UTC)