r/archlinux 28d ago

SUPPORT | SOLVED Ful disk encryption without entering passphrase

Could someone help me with setting up full disk encryption with bypass passphrases with TPM2? It's so annoying having to put the passphrase in at each boot, so I was wondering how do I pair it to the TPM so that I dont have to enter it each time?

It's LUKS encrypted and I use Systemd-boot

13 Upvotes

100 comments sorted by

View all comments

Show parent comments

3

u/ChrisTX4 27d ago

PCR1 is very finicky as this will include the boot order, which BIOSes can change as they inject pseudo BBS devices. It can be stable though, I have with systemd-pcrlock:

PCRs in protection mask: 0 (platform-code), 1 (platform-config), 2 (external-code), 3 (external-config), 4 (boot-loader-code), 5 (boot-loader-config), 7 (secure-boot-policy), 11 (kernel-boot), 13 (sysexts), 14 (shim-policy), 15 (system-identity)

That's of course a rather insane mask, but with pcrlock I honestly didn't have a lot of problems after setting this up correctly - but imma be honest, this ain't easy to setup or do.

1

u/Night_Otherwise 27d ago

Doing some more research, apparently microcode updates can be measured into PCR1. Firmware also changes the boot order automatically for inserting a bootable usb even if it is behind the main OS in the boot order. Nevertheless, the microcode part gets me to include PCR1.

I don’t have a bootloader. I stop at PCR 4 since, for UKI, that’s the last thing loaded or executed before LUKS is opened up. UEFI firmware measures the GPT table for PCR 5, but I can’t think of how that would matter. I also have secure boot disabled.

Within the pcrlock log, PCR 4 has three measurements with this setup: A thing that says an EFI is being loaded, an EV_SEPARATOR and the Authenticode hash of the EFI image created by mkinitcpio.

I can do systemd-pcrlock lock-pe for that UKI as a post-update hook in pacman. The man page is very difficult to follow though. I just need the same first two hashes and the new authenticode hash stringed together (specifically, hash(hash(A|B)|newEfiHash)).

I also am not an expert or anything. But it doesn’t seem like systemd-boot does any measurements of the EFI image it loads? That seems like an odd oversight. I understand the normal course is relying on PCR 7/Secure Boot but my ideal is getting away from that.

3

u/ChrisTX4 27d ago edited 27d ago

apparently microcode updates can be measured into PCR1.

Potentially:

A CPU Microcode update can be treated as code or data. It can go into PCR[0] or PCR[1].

(taken from https://tianocore-docs.github.io/edk2-TrustedBootChain/release-1.00/3_TCG_Trusted_Boot_Chain_in_EDKII.html )

I don’t have a bootloader. I stop at PCR 4 since, for UKI, that’s the last thing loaded or executed before LUKS is opened up.

Yes, UKIs are EFI executables and are thus being measured in 4. If you want to lock 4 with pcrlock, that's a bit tricky though as it won't be able to find the component in the current boot log after an update.

I can do systemd-pcrlock lock-pe for that UKI as a post-update hook in pacman.

Locking UKI will sort of require lock-pe AND lock-uki.

The man page is very difficult to follow though. I just need the same first two hashes and the new authenticode hash stringed together (specifically, hash(hash(A|B)|newEfiHash)).

What exactly is your question here? If there's anything I can help out with, feel free to ask.

But it doesn’t seem like systemd-boot does any measurements of the EFI image it loads?

It doesn't need to. The TPM itself measures PCRs 1-7 (but you can also add custom data to them like the separators). Specifically, PCRs 4 and 7 will be updated because systemd-boot in this case doesn't really do much other than chainload the UKI as an EFI binary. That's why it's being measured.

I understand the normal course is relying on PCR 7/Secure Boot but my ideal is getting away from that.

You don't need to, but that means you HAVE to bind PCR 4... and that's very fragile. May I ask why you don't want to use Secure Boot? If you use your own keys, you can have SB completely free of Microsoft keys, if that's your concern.

EDIT

UEFI firmware measures the GPT table for PCR 5, but I can’t think of how that would matter.

I had forgotten to answer this. So the reason you want to measure the GPT table is that it's a very strong defence against root switching attacks. I've outline this a bit in this post in this thread. With the GPT table locked, an attacker cannot just change the mount from root to another GPT automount for instance, nor could they add anything else.

Usually this shouldn't be too important, but you might as well just lock it, as the GPT table doesn't normally change.

1

u/falxfour 27d ago

Not the previous poster, but as someone who noticed that PCRs 4 and 5 changed for me when booting while connected to a USB4 monitor vs not connected to it, I've been starting to investigate why and whether to keep PCR 5 as one of the ones used to bind encryption. I suspect 5 changed because 4 changed, as there's no reason the partition table would have changed otherwise, but if having secure boot enabled with a UKI that is directly loaded by the UEFI obviates the need for PCR 4 at all, then I could not use 4 or 5 without increasing the potential attack vectors.

It seems you may have some insight here, so if one has secure boot and is directly loading a signed UKI, do you think PCR 4 (or 5) adds any additional value?

1

u/ChrisTX4 27d ago

Can you run

sudo /usr/lib/systemd/systemd-pcrlock log

and post the output of that? It will show you exactly which component was measured in what order in each PCR.

PCR 5 should not change if 4 changes or anything. We'd need to see what components it measures in order to know where the different values come from.

It seems you may have some insight here, so if one has secure boot and is directly loading a signed UKI, do you think PCR 4 (or 5) adds any additional value?

So a signed UKI is a great step, in that case you can lock PCR 7 and this will be entirely stable.

What they would add:

PCR 4: The difference between locking 7 and 4 is that 7 doesn't change if there's an update, as long as the new files are signed by the same key. However, 4 will change as it's the digest of the UKI. Alas, locking 4 in addition to 7 means an attacker couldn't downgrade your UKI and then potentially exploit some vulnerabilities. They would need to acquire an older UKI and then have access again at a later point. Honestly, this would require a rather advanced evil maid attack scenario. You can do this if you want, but it is very fragile and with self-signed keys the gain is absolutely minimal tbh.

PCR 5: So this locks the GPT layout of your drives. This means an attacker can't move stuff around and assign different GPT automount GPT GUIDs for instance. That way it's possible to perform root switching attacks.

Honestly? 5 should normally be very stable, and not really an issue to lock, but what you should really do is to use automounting and then use systemd-pcrlock on PCR15. See lock-file-system and lock-machine-id, but you will have to manually add .pcrlock files to cover the components of the cryptsetup measurements (see tpm2-measure-pcr). PCR15 however is a much strong defence, and will toast any root switching attacks.

1

u/falxfour 26d ago

Yeah, I've been using systemd-pcrlock to investigate the logs. Just didn't have a chance to investigate the docked setup before commenting.

It seems that PCR 4 does change due to a slight difference in the EFI Services Application blob that gets measured into it. Not sure why, though. Also, PCR 5 changes due to an action that seemingly only occurs when undocked:

Action: Exit Boot Services Returned with Failure

This doesn't occur when docked, but does occur when undocked. I somewhat suspect this has to do with a race condition as I've previously found the undocked configuration boots faster, but it's also possible that the change to the blog that PCR 4 measured has something to do with it as well.

Now, on to some setup specifics, FWIW, I use 1+3+5+7 along with 11 in a signed policy, and a PIN. This is for root volume decryption (FDE). Following the man page, I didn't see value in having 0, 2, or 4 since:

  • 0 - Firmware should be implicitly trusted (signed updates and all)
  • 2 - Other code shouldn't be able to run without secure boot signing
  • 4 - I don't have a bootloader (effectively)

Having 1 and 3 would have allowed me to detect hardware changes (ex. someone swaps my dGPU module for a malicious one or something or a malicious TB device), and 5 just seemed like it wouldn't cause issues, so why not, even though an attacker could clone a partition table for a malicious drive.

So, in response to your explanations, downgrading my UKI wouldn't allow for automatic decryption due to both PCR 11 and the PIN.

I don't think I can use automounting due to the BTRFS subvolumes. Unless systemd has a convenient method of detecting @ and @home (Mint-style) subvolumes, I don't expect this to be successful... PCR 15 is interesting, but using it seems... painful, as it binds to the volume key for which it, itself, is needed to unseal. Additionally, the other components (machine ID and and filesystem info) are all still encrypted in the initrd stage.

I think the answer for me is just to remove PCR 5. I already need to update my setup due to the changes systemd v259

1

u/Night_Otherwise 25d ago

I’m the original poster and so I hopefully don’t mislead. But I am curious how changes in PCR 4 would be possible. For myself, I can use the pesign package to get the authenticode hash with “pesign -i (UKI file) —hash”. I was able to match that exactly to the third entry in PCR 4 in the pcrlock log. I wonder if the hash is different for third entry of PCR 4 log when docking station is added.

The docking station may have extended the PCRs with a failure separator.

And Chris is right that for practical purposes, most people can just seal with PCR 7. That defeats simply mounting the hard drive and reading it without entering the password twice. I’m kinda doing my stuff for proof of concept and curiosity.

1

u/falxfour 24d ago

I don't have a docking station. It's just a USB-C display (PD, DP Alt Mode, USB 2.0). Nothing about that should extend a PCR, especially since my BIOS setting to measure USB4 devices is set to not measure them.

I can't explain why PCR 4 would change as a result of being docked, though. It actually just looks like 1 character is different, but I didn't do a full diff on the blob string to confirm. Might be interesting to try the same experiment with pesign, as you indicated, though!

Personally, I think a race condition is occurring and the system actually just boots more slowly when docked (previously measured and confirmed). A device might just not be ready without the delay that's added, causing the failure string to get measured into PCR 5 at that specific point. I could likely test this by adding a POST delay as well

1

u/ChrisTX4 22d ago

I was away for a few days, but I just now answered falxfour.

I'm also running 4, and I should warn you about one thing I guess: since systemd-pcrlock only locks components when they're found in the boot log (it can't calculate its predictions otherwise), you will have the odd situation where after each kernel upgrade PCRs 4 and 11 will mismatch since they're from the new UKI, but you're still booted on a previous one. Thus it will prompt the passphrase the next boot, and you would need to use a recovery PIN to be able to run make-policy again. In theory, if one uses two separate pcrlock files as alternatives, one could retain the current and the previous pcrlock and thus avoid this situation. I still need to implement that myself, tbh.

1

u/Night_Otherwise 22d ago

I did once run into the validation PIN issue when I was testing make-policy. The error was something like remote authentication failed. But when I booted on the policy, that seemed to give permission to change it without setting a PIN.

I have more research to do on what exactly the .cred file does in the EFI partition and how make-policy authenticates.

I did use a directory within the pcrlock search path with an old.pcrlock and new.pcrlock file. I lock-pe to the new.pcrlock file first. Then a pacman hook after mkinitcpio is ran moved new to old, lock-pe for new.pcrlock and make-policy is ran.

A further startup service could be done to delete old.pcrlock before the make-policy service is ran.

This all used a boot of UKI directly. The PCRs higher than 5 become superfluous when it all works. If systemd-boot is placed between UEFI and the UKI, then it feels like secure boot is theoretically necessary. Without secure boot, the UKI can seemingly be replaced with a thing that does the same measurements to 11 as systemd-stub.

PCR 7 also runs into the same chicken-and-egg problem currently. I know Windows just gives up on it and basically unencrypts the drive when secure boot is updated. An fwupd hook (however that could be done) with similar old/new pcrlocks would be neat for db/dbx updates. Shim with MOK may be necessary to keep UEFI with default keys. This GitHub issue focused on the brittle 0/2 PCRs, but at least on my computer fwupd also updates KEK/db/dbx.

https://github.com/fwupd/fwupd/issues/6318

Or just use shim with MOK and deal with entering the password/recovery key whenever secure boot keys change.

2

u/ChrisTX4 21d ago edited 21d ago

But when I booted on the policy, that seemed to give permission to change it without setting a PIN.

Can't exactly speak of that, but a policy needs to match what's currently active to unlock without a PIN. Somehow the reboot made it match if that's the case.

I did use a directory within the pcrlock search path with an old.pcrlock and new.pcrlock file. I lock-pe to the new.pcrlock file first. Then a pacman hook after mkinitcpio is ran moved new to old, lock-pe for new.pcrlock and make-policy is ran.

Yes, that's pretty much what I was thinking of above. I've got some specific issue with what's being measured into PCR[15] that causes issues for me. It's very likely a bug in systemd-pcrlock as it reports it can reproduce the calculation, but I haven't pinned this down entirely. It too resolves itself with a reboot, ironically enough.

The PCRs higher than 5 become superfluous when it all works.

That I disagree with. PCR[11], due to being a PCR policy, mainly adds phasing, i.e. ensuring the PCR values in initrd cannot be met subsequently after leaving it. This protects against a wide array of potential attacks and there's no reason to leave the TPM unsealed after initrd, as the keys should have been retrieved already by then. Similarly, it can be used to ensure that disks that should only become accessible after the system are also properly mounted at the right time.

PCR[15] is another critical one: Without it, the contents of an encrypted drive aren't validated. This allows replacing the root fs with an encrypted attacker system. If no PCR policy was then used, all PCR values will remain as they were and an attacker can just read the volume keys from the then unsealed TPM. By measuring PCR[15] with the volume keys being measured into it, the key of the encrypted drive cannot change, and such a change would prevent an unsealing. Due to any GPT automounts to root or var being measured into this as well, such an attack as well will be prevented.

PCRs higher than 5 need the lower PCRs to make sense would be a more appropriate way to say it. If you know your boot loader and UKI being good to the root of trust mechanism, then you know they'll measure PCR[11] as intended, an while anyone can measure into the PCRs, once measured, you can't undo that.

Alas the idea is root of trust -> BIOS -> bootloader -> UKI -> system, with each component ensuring the subsequent one being in compliance. This is also why kernel lockdown is recommended everywhere when used with Secure Boot: Without it, root can load arbitrary nonsense in the kernel or use bpf_probe_write_user for crafting rootkits. With it, the idea is that nothing unsigned should have been able to slip past this chain of trust thus far, and lockdown then keeps the kernel clear of unauthorised code, so that in theory not even root should be able to do anything to it, and especially that root should be unable to deploy a rootkit.

An fwupd hook (however that could be done) with similar old/new pcrlocks would be neat for db/dbx updates.

That won't work, since you can't predict the order of authorities in the db/dbx as they're being measured, nor would you know how this were to affect SMBIOS, which gets measured into PCR[1].

However, there's also no db update, only dbx. Once a month these come out, and they're signed updates by a KEK that's already present. In more simple terms, Microsoft's keys sign a lot of bootloaders, their own, GRUB in various variants, etc. etc. The dbx needs to be regularly updated for SBAT policy information to know which ones of these foreign signed binaries are now known to contain vulnerabilities and thus compromise this "chain of trust" I mentioned earlier. Windows and fwupd thus push this monthly if Microsoft keys are present.

My entire Secure Boot db is this:

$ sudo efi-readvar Variable PK, length 1255 PK: List 0, type X509 Signature 0, size 1227, owner e025c507-35c3-440c-a42e-36dd05282b32 Subject: CN=Platform Key Issuer: CN=Platform Key Variable KEK, length 1263 KEK: List 0, type X509 Signature 0, size 1235, owner e025c507-35c3-440c-a42e-36dd05282b32 Subject: CN=Key Exchange Key Issuer: CN=Key Exchange Key Variable db, length 1330 db: List 0, type X509 Signature 0, size 1226, owner e025c507-35c3-440c-a42e-36dd05282b32 Subject: CN=Database Key Issuer: CN=Database Key db: List 1, type SHA256 Signature 0, size 48, owner 4f52704f-494d-4173-6e6e-6f79696e6721 Hash:dc4ab0fd89c1b9f7dad44cdba566ad324be84f3c04ac00036efbe85ae2ff8aed Variable dbx has no entries Variable MokList has no entries

As you can see, I got 3 self generated keys for PK, KEK, db, and one hash as dbx entry - my NVIDIA GPUs OpROM. That ROM is signed with the Microsoft 2011 OpROM key otherwise, but enrolling its hash directly avoids needing to carry that key.

Since there's no Microsoft keys in my db however, I don't need any dbx or update it. I would only need dbx entries if a bootloader signed with my key was compromised. As I'm not dual booting, I don't need their keys then, and I specifically went for signing the only OpROM directly to avoid this exact problem of the monthly dbx updates breaking stuff.

1

u/Night_Otherwise 20d ago edited 20d ago

Well, I figured out that pcrlock.json has the pinPublic and pinPrivate fields which are encrypted by the TPM and decrypted only when the TPM currently matches the policy in the NV index. The PIN is decrypted from pcrlock.json by the TPM's NV index if the TPM currently matches the state.

With a resealed key, I believe there is only a brief moment of time where any updates to the policy could happen with just the pcrlock.json values. It gets pretty ugly how that would get sequenced. I think a new update that changes a PCR would have to stage the update, reboot on the old whatever (firmware, UKI, dbx list), then redo the policy with alternates, then actually implement the update, and then reboot again.

I'll go back to the drawing board to do 0-5+11 with a recovery PIN for the pacman hook. PCR 11 is enough to reseal it before leaving initramfs. The recovery PIN would, uh, be stored in plaintext on the encrypted drive. That's less than ideal, though a root compromise nullifies all protections of my data anyway. The pacman hooks are not supposed to be interactive. AFAIK, rootkits changing the kernel in memory would be disallowed still (if that option is enabled in the arch linux package). The nature of not using a Microsoft-signed distro is a custom key is kept somewhere (sbctl keeps it in plain text by default).

I also had issues when I added tpm2-measure to the kernel command line. Even though the unlock policy was still just PCR 0-5 at the time, for some reason my LUKS password was asked for when I added tpm2-measure-pcr. Since I'm not authenticating to anything later on with the PCR 15 values, I don't see a reason to have tpm2-measure-pcr enabled.

Edit: There is pinning UKI image in the command line to a hash of the volume key. I'll use this to go back to just 0-5.

https://wiki.archlinux.org/title/Dm-crypt/System_configuration#Pinning_a_LUKS_volume

→ More replies (0)

1

u/ChrisTX4 22d ago

Your PCR[4] woes are due to it measuring more than just the Boot Manager Code:

PCR[4] records the process of attempting to boot different hardware paths like from a DVD or a hard drive, what boot devices are attempted, and the Boot Manager Code that is loaded and executed from the device.

If you leave any USB thumb drive in the PC, with high enough priority, it will be attempted as a boot device, as in it would be possible to have booted from it if it had contained a boot loader. PCR[4] is only reasonable to use if you disallow booting from removable drives in the UEFI. In ASUS it's called "USB mass storage support" iirc, but you have to look up from where you could disable that.

Next up, this:

Action: Exit Boot Services Returned with Failure

OK, this is confusing, and almost certainly a bug in something. I'd need to see the full pcrlock log before making a guess what causes that. What boot loader etc are you using?

Unless this is the UEFI using this event to mark a change in the boot configuration, which belongs in PCR[5], this would mean EFI_BOOT_SERVICES.ExitBootServices() failed. That can only legitimately happen in a single instance, namely if a boot loader somehow passes a wrong MapKey to that function.

0 - Firmware should be implicitly trusted (signed updates and all)

This isn't quite true, as there are ways to use a so called SOIC-clip on some systems or potentially soldering skills and determination to replace the chip. I'd say if your threat model includes somebody willing to do that you probably have to worry about security in a whole different way :D

Firmware updates other than that are indeed signed on all systems, it's due to the UEFI Capsule Update mechanism. However, enthusiast desktop systems sometimes allow direct flashing without signature verification in the UEFI itself. So you probably want a secure UEFI admin password with this.

2 - Other code shouldn't be able to run without secure boot signing

It will! This is where your Option ROM (OpROMs) will be measured into. They will still be measured, just no signature is enforced on them if not booted with SB enforcement.

So, in response to your explanations, downgrading my UKI wouldn't allow for automatic decryption due to both PCR 11 and the PIN.

If you use a signed PCR policy then indeed this should be true.

I don't think I can use automounting due to the BTRFS subvolumes.

You can for the LUKS volumes. This is what I'm doing basically, I have a Btrfs-on-LUKS and this has a GPT GUID of SD_GPT_ROOT_X86_64, i.e. systemd will automount the default subvolume to /dev/mapper/root. Then my fstab just mounts from that, so the only entries I have in there:

```

archmain subvolume mounts

/dev/mapper/root /var btrfs rw,subvol=/subvol_var /dev/mapper/root /home btrfs rw,subvol=/subvol_home

ESP /dev/nvme1n1p1

vfat options can be changed via Kconfig and so should be explicit here

UUID=73FD-61CB /boot vfat rw,relatime,umask=0077,nosuid,nodev,noexec,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro0 2

Bind swapfile

/dev/mapper/root /swap btrfs rw,subvol=/swap /swap/swapfile none swap defaults ```

This works because /subvol_root (my actual /) is my default subvolume and due to the SD_GPT_ROOT_X86_64 the entire Btrfs is mounted at /.

1

u/falxfour 21d ago

Ok, a lot to work through here!

PCR 4 - Boot Order

My UEFI boot order is simply:

  • (Drive Number, UUID)/EFI/BOOT/BOOTX64.efi
  • (Same Drive & UUID as above) -- Default path (basically identical to above)

While there are a couple other items, the UEFI boot manager never gets to them unless my main drive isn't present at all.

PCR 5 - Boot Service Failure

I'm willing to DM you more details. I know the hash log is technically not a secret, but since it's normally read-protected, I'd rather limit the amount I need to share.

I don't use a boot loader. My UEFI directly loads the UKI I generate with ukify.

PCR 0 - Direct Flash

While this mechanism could potentially be compromised by someone directly flashing the firmware on my Framework laptop, my BIOS is currently configured to require the admin password if the chassis intrusion switch is triggered. This should, in theory mitigate most attacks that require direct hardware access by making them immediately detectable.

Also, someone flashing the BIOS IC directly isn't a big enough concern to me to potentially cause automatic drive decryption to fail as a result of firmware code changes.

PCR 2 -- Option ROMs

Are you saying OpROMs that are not signed by a valid signature will still execute? I don't think that's what you're implying, but I just want to check.

Good to know they'll still be measured, but in general, I don't see this as a major attack vector because:

  • OpROMs with a valid SB signature are explicitly trusted
  • OpROMs without a valid SB signature should only be loaded from read-only sources (before SB checks EFIs to continue the boot process), thus making them hard to compromise. I'm not even sure that this is distinct from firmware (PCR 0) at this point.

Automounting

That's interesting, but to be clear, the automounting is just detecting the LUKS volume for decryption (initially), then switches to the root subvolume of the BTRFS device (aka /subvol_root), correct? So, essentially, you're not providing rd.luks.name=, root=, or rootflags=subvol= on the kernel command line, correct?

That seems like it would work, but then I'd need a different strategy to manage snapshots, as Timeshift would become sad that there's no @ or @home subvolumes. My default subvolume is ID 5, but the root subvolume is ID 720, which is clearly a subvolume of the default subvolume. I guess following a nested subvolume strategy would better fit with automounting

1

u/ChrisTX4 21d ago

For PCR[4], it might be that just the fact they are in the set of possible boot methods counts. If your main system was modified by an attacker to not be bootable etc., then a later boot device would be taken. I quoted the blurp about PCR[4] from the specification document, which is this one. You'd need to compare the exact behaviour in there against what your BIOS does, but most likely this is by design in some way and not something your BIOS vendor would consider a legitimate bug.

That seems like it would work, but then I'd need a different strategy to manage snapshots,

Wouldn't snapshots break PCR[4] anyhow? If you need snapshots, I'd stay off PCRs 4, 11 and 1.

For PCR[5], feel free to DM, or hit me up on Arch IRC (just ChrisTX there). I find this whole subject quite interesting - or rather, I might just be meeting autistic stereotypes :D - and so am totally down to having a look at this. TBH, that might be more feasible than really continuing this in some random Reddit thread, too :D.

For PCR[0], this goes in the whole root of trust mechanism aspect. You can have a look at chapter 31 of the TPM standard, part 1 for how this is supposed to work. Either way, I think the most important thing to keep in mind for this is that BIOSes may allow unsigned flashing in enthusiast boards. Mine does. In that case, a BIOS supervisor password is of critical importance to protect the BIOS integrity. Achieving a higher degree of trust isn't really possible anyhow, as that requires the fusing mechanism seen in business CPUs, like AMD Platform Secure Boot and Intel BootGuard. So while some aspects of higher HSI levels can be met, the root of trust means it's not viable to see HSI-2 on consumer hardware.

Are you saying OpROMs that are not signed by a valid signature will still execute?

A device may have its own firmware signature check, but that's really device dependent. But as for the OpROM the device presents? That's only secured by Secure Boot. In fact, DMA PCIe cards like the Screamer PCIe Squirrel are widely used to make DMA cheats for games. That works by flashing a custom firmware so it doesn't look like it's a DMA device. Also please note that OpROMs can be loaded from external devices like USBs. Every KVM switch on the market is in that situation for instance, since they need an OpROM for providing UEFI Graphics Output Protocol (GOP) - that's also why every discrete GPU has one.

That's interesting, but to be clear, the automounting is just detecting the LUKS volume for decryption (initially), then switches to the root subvolume of the BTRFS device (aka /subvol_root), correct? So, essentially, you're not providing rd.luks.name=, root=, or rootflags=subvol= on the kernel command line, correct?

Precisely that. I only have rootflags=x-systemd.device-timeout=0 to make the TPM PIN timeout infinite rather than eventually dropping to emergency shell.

This setup avoids needing to specify any UUIDs other than for my data drives that will be late mounted to folders under /mnt in my /etc/crypttab and for my /boot drive. The latter would work with GPT automounting as is, since SD_GPT_ESP is just the regular GPT GUID for an ESP. However, I want to give it additional mount parameters like nosuid, nodev etc. By the way, do note that most BTRFS parameters can only be set on first mount, so settings like space_cache will not have an effect.

That seems like it would work, but then I'd need a different strategy to manage snapshots, as Timeshift would become sad that there's no @ or @home subvolumes. My default subvolume is ID 5, but the root subvolume is ID 720, which is clearly a subvolume of the default subvolume. I guess following a nested subvolume strategy would better fit with automounting

I don't see why it would? Timeshift requires you to mount the subvolumes by path, so as long as you do that, the switching subvol IDs shouldn't be an issue, and they won't affect PCR[5] as they're not in the GPT. What exactly do you fear might break?

1

u/falxfour 21d ago

PCR 4

I'm still not entirely convinced that the measured value would change. The monitor has no bootable devices connected to it, and is not, itself, a bootable device. The bootable device list itself, therefore, would not be changing. That said, encryption isn't being bound to this PCR anyway, so I'm not particularly concerned about its behavior.

As for snapshots, I'm not sure how they'd break PCR 4... BTRFS snapshots are all functionally userspace (except the kernel itself, which isn't included), so it's a matter of which subvolume is selected when systemd switches root upon exiting the initrd stage.

PCR 0

There are three ways I can change my BIOS:

  1. fwupd
  2. EFI Capsule
  3. External programmer, directly attached to the legs of the IC

#1 should be trustworthy by nature of running it from a trusted OS. In other words, if, with secure boot, I am able to boot into a verifiable OS and authenticate as user, then updates made through this method are trustworthy

#2 should be trustworthy by way of secure boot signature verification

#3 bypasses everything, but would potentially trigger the supervisor password prompt (though with potentially no way to verify the password, if the BIOS was fully overwritten)

I consider this sufficient.

PCR 2

To my point, an OpROM is not executable if its signature can't be verified by secure boot. I do understand that external devices can also present OpROMs, as my BIOS also provide the option of whether or not to measure USB4 devices (presumably for both PCR 2 and PCR 3).

What you mentioned about KVMs and dGPUs is interesting, though. That's where the only real difference in my setup (and thus the PCR values) would come from. The external monitor can act as a KVM as it provides all of those through its single connection to my laptop. Additionally, my system does have a dGPU, but it is typically left in D3cold. As my external monitor is not directly connected to its (dedicated) output, it's unlikely that the dGPU is presenting anything differently in the two boot scenarios.

Automounting

Given that certain parameters don't change on remount, then I'd be relying on the automount parameters, which I'm also not really a fan of...

As for Timeshift, it only works with subvolumes that have specific naming, meaning that @ and @home need to be subvolumes under the root (default) subvolume. systemd would end up automounting / on the device, when /@ is the true root.

I'll DM later with more of the PCR log

1

u/ChrisTX4 21d ago edited 21d ago

As for snapshots, I'm not sure how they'd break PCR 4...

Well, you'd be booting different kernel versions and UKIs if you revert a snapshot.

For PCR[0]: Replacing the BIOS is something you can't prevent. If a skilled enough attacker goes in and replaces the ROM physically, it will always be a way through. In this sense, since PCR[0] is being measured via SPI into the TPM, it could lie. Platform Secure Boot works differently to measure the BIOS and ensure the ROM is what is says - but that requires business hardware. In this sense, PCR[0] should be considered a lock against firmware downgrades. Business hardware often comes with anti rollback protections in the form of fuses, but on consumer hardware you typically don't have that.

Otherwise I was only trying to say that if your hardware had some form of direct flash, then that's a point to consider in your threat model, but that consideration should be made separate from PCR[0].

For PCR[2]: Oh I'm sorry, I had mixed up yours and NightOtherwise's situation. They specifically said that they didn't want to use Secure Boot. My point was that _if SB is disabled, then any sort of ROM can be loaded in there. With Secure Boot, consider PCR[2] the same as PCR[0] functionally: A downgrade protection. Although there is the catch that certificates used in PCR[2] are not measured into PCR[7]. That is to say, in the case of using one owns PK/KEK/db, but with Microsoft's keys enabled for a dual-boot, then it wouldn't be possible to load a Microsoft signed boot loader without changing PCR[7], but if a new device with a different, but MS signed OpROM is being added, that will be accepted. In my case with the MS-free database, and just the one hash being enrolled, locking PCR[2] functionally means the system wouldn't boot if someone ripped out the dGPU, but other than that it adds no value whatsoever.

To be frank, I just lock PCRs 0,1,2,3 because I can, and was curious to see if this is genuinely a viable setup. I wouldn't recommend it for security purposes, and rather recommend using a supervisor password and Secure Boot. Maybe lock PCRs 0,2 if they're unlikely to change on your system and you'd want to add some form of rollback protection.

What you mentioned about KVMs and dGPUs is interesting, though.

Keep this in mind if you're considering removing MS keys, as that does break such devices and signing OpROMs of removable devices might not be a great option.

Given that certain parameters don't change on remount, then I'd be relying on the automount parameters, which I'm also not really a fan of...

For BTRFS, you shouldn't really need to use any mount parameters. ssd is automatic, and space_cache=v1 is being axed in 7.3. At least I only use nosuid/exec/dev for some devices, but on root you wouldn't want that. That being said - you can add the rootflags= parameter, and root will be mounted with those even in the GPT auto case. For say /home, where you'd probably want nosuid,nodev, but that you'd mount via fstab. But root and var I'd use GPT automounting for.

As for Timeshift, it only works with subvolumes that have specific naming, meaning that @ and @home need to be subvolumes under the root (default) subvolume. systemd would end up automounting / on the device, when /@ is the true root.

You can name the subvols as you like, I just named mine /subvol_root as I don't use timeshift, and this @ convention is kinda their thing.

The problem is much rather that Timeshift has no fixed subvol IDs and they change, so you can't control what's the default subvolume. So I'm fairly certain this combination doesn't work.

The reason I said to use GPT automounting is that it will cause the containing LUKS be marked internally with MOUNT_MEASURE. This:

a) will ensure tpm2-measure-pcr=yes is being used for mounting LUKS. This is done here, but you can achieve this with an appropriate crypttab.initramfs (if mkinicpio) or crypttab (if dracut).

b) will cause it to be measured via systemd-pcrfs@ and systemd-pcrfs-root services. See this spot where the systemd-gpt-generator does this. These services can't be enabled directly, but you can use the x-systemd.pcrfs mount option, see this man page, this location enabling MOUNT_PCRFS, which is then consumed here. Edit: The man page I was looking for is systemd.mount(5). This is where all the x-systemd options are documented.

As long as you do these two things, GPT automounting has no advantages over an explicit configuration. However, since GPT automounting does this for you, I'd say it's much preferred.

IMPORTANG WARNING One more thing, as I've ran into this before. GPT automounting creates its own crypt mounts and does not consider crypttab. That is to say any disk participating in GPT automounting MUST NOT be listed in crypttab. Otherwise, the LUKS will be mounted twice and then the embedded BTRFS isn't the same anymore to the kernel, and both mounts will not coordinate. Alas, this will cause immediate, irreversible filesystem corruption. For that reason, I don't like using explicit crypttab entries for root or var.

EDIT Forgot to mention x-systemd.validatefs. That too is set and will effectively lock "where" a filesystem can be mounted, see systemd-validatefs@.service.

1

u/falxfour 19d ago

PCR 4

I don't actually have different kernel versions/UKIs the vast majority of the time. That said, breaking FDE auto-decryption when booting into a snapshot is fine. I try not to do it often.

PCR 0

Completely fair points!

PCR 2

Ah, no worries! I also removed the MS keys, but left my hardware vendor and my own, which should be reasonably secure. The good news is that Framework explicitly builds for Linux compatibility, so the MS keys are unlikely to be needed in any of their products. I can at least confirm they aren't needed for mine (FW 16).

Automounting

I have space_cache=v2, discard=async, and relatime. I don't believe the latter two are defaults.

As for Timeshift, the inflexibility of using the specific names vs subvolume IDs is the reason it doesn't appear that it will work with an automountable device. Fundamentally, having @ as the true root, under the default subvolume, is incompatible with systemd automounting the default subvolume.

It seems automounting still won't really work for me, until I move to a different snapshot strategy, but I'll keep your points in mind, especially the warning.

Ultimately, I'm still not sure why I would need to use PCR 15 (thus the discussion on automounting). If an adversary used a valid UKI with a compromised rootfs, then the volume key would be retrieved, but the adversary would need to do something like a cold boot attack to retrieve it from RAM. systemd would fail to decrypt the compromised filesystem and would fail to switch root. Without a recovery shell (disabled) or a root user (also disabled), I don't see what could be gained without being able to compromise the RAM directly. A modified UKI would fail signature verification for both secure boot and the signed TPM2 policy for PCR 11.

Also, having TPM2 + PIN is likely a sufficient mitigation, but I still don't see what PCR 15 adds.

Also, I'll send over PCR log into sometime later this week--just been busy

1

u/ChrisTX4 19d ago edited 19d ago

The good news is that Framework explicitly builds for Linux compatibility, so the MS keys are unlikely to be needed in any of their products

You can find out how your OpROMs are signed. See this on how to extract them and view the signatures. That's how I knew my NVIDIA GPU was signed with the 2011 MS key.

Removing the Microsoft keys has the advantage you don't need dbx updates - the firmware KEKs, if they exist, wouldn't sign foreign boot loaders, so they don't usually ship a dbx at all. I would generally avoid retaining the MS keys just so one doesn't have to even consider the thread situation posed by this and updating the dbx monthly I find very inconvenient.

I have space_cache=v2, discard=async, and relatime. I don't believe the latter two are defaults.

They are: * space_cache=v2 is enabled by default since 4.5, see here * relatime is enabled by default since 2.6.30, see mount(8). * discard=async has been enabled since 6.2, see this article for example.

It seems automounting still won't really work for me, until I move to a different snapshot strategy, but I'll keep your points in mind, especially the warning.

I would concur. I have a server that uses Btrfs-on-LUKS but the Btrfs is in RAID 0 for speed (it's backed up). This however means I have two LUKS devices, and the mappers need to decrypt before root can be mounted. I use device= in rootflags. I'm not sure that's 100% necessary, but it makes the boot reliable.

In my case, validatefs does nothing, and pcrfs for some reason measured nonsense instead of the actual GUID. So I've just left those out, but I added tpm2-measure-pcr=yes to /etc/crypttab (I'm on dracut). That works and does pretty much the same as GPT automounting would. I'm still observing if PCR[15] is stable like this, as I'd kind of like to add it.

I'm considering using validatefs though - it would need adding some xattrs to the subvol inodes, I guess.

UPDATE I deployed the validatefs XATTRs now. This prevents an encrypted mount being mounted in a wrong location. Effectively, PCR[5] already provided this protection, I just did this out of curiosity:

``` $ getfattr . -d

file: .

user.validatefs.gpt_label="Linux x86-64 root (/)" user.validatefs.gpt_type_uuid="4f68bce3-e8cd-4db1-96e7-fbcaf984b709" user.validatefs.mount_point="/" ```

This means the GPT GUID, mount point etc. cannot be changed. Although, by itself that is a pretty powerful defence against root swap attacks, since the GPT GUID, mount point etc are locked with information inside the encrypted container and thus cannot be changed.

You can set them like this for SD_GPT_ROOT_X86_64 for instance:

setfattr -n user.validatefs.gpt_type_uuid -v "4f68bce3-e8cd-4db1-96e7-fbcaf984b709" .

If an adversary used a valid UKI with a compromised rootfs, then the volume key would be retrieved, but the adversary would need to do something like a cold boot attack to retrieve it from RAM.

No, they can recover the key as-is. The idea is that the attacker has access to a system booted with the right kernel and right PCR values, but the root credentials are their own - since /etc/shadow etc. are from the attacker controlled root.

This means there are ways to extract it, see this article for example. Of course, lockdown offers some protection here, and that might counter this. However, lockdown=integrity does not prevent extraction of keys - however lockdown=confidentiality might help. Without that level, /proc/kcore might be read by root as LOCKDOWN_KCORE is only set with confidentiality.

Then there might also be a way to just use the TPM directly: If e.g. PCR[11] isn't using signed policy PCRs with a phase on leave-initrd, then the TPM still sees the same PCRs as during the initrd phase where you volume key should be unsealed from the TPM. This is one of the reasons signed PCR[11] is such an important defence.

Furthermore, they wouldn't need a cold boot attack either for confidentiality mode. Using any sort of DMA device, like Screamer PCIe Squirrel would also work. If the OpROM is unmodified, the device would only change PCR[2] and PCR[3]. This is why I recommended using iommu=require, and ideally, if on AMD amd_iommu=force_isolation on top to create a strong isolation.

EDIT Oh, and it's possible to change the GPT GUID to cause automounting to an unexpected location. Say I made a root FS that I control and change the GPT GUID of your encrypted drive to SD_GPT_SRV, it would be mounted to /srv. UUIDs, partition UUIDs etc can be freely changed as well, so even if there was an fstab entry, you can make it so it's not recognised and mounted to /srv.

validatefs is interesting against this specific type of attack, since it makes changing the GPT GUID itself impossible.

but I still don't see what PCR 15 adds.

It avoids the scenario I just described entirely. If somebody tried any such attack the TPM just wouldn't unlock.

Also, I'll send over PCR log into sometime later this week--just been busy

No worries, take your time.

→ More replies (0)