Hi Jarkko, Hi James, first of all, sorry for hijacking a thread from 2 years ago.
On Thu, Nov 14, 2024 at 12:34:30AM +0200, Jarkko Sakkinen wrote: > On Wed Nov 13, 2024 at 8:12 PM EET, James Bottomley wrote: > > > I think we might have to expect the NULL name to change on actual > > > hibernation because unlike suspend to ram it does power off the TPM. > > > > I checked the code: we're coming in on the correct path to renew the > > null seed after hibernation, so it should all work. The problem seems > > to be that your TPM itself is doing something invalid because the name > > we calculate for the primary key doesn't match what your TPM says it > > should be. Absent some form of attack or bus integrity problem, that > > shouldn't ever happen, so I'm even more curious to know why it worked > > in 6.11.5 and before and whether current upstream works. > > > > I haven't found it yet, but I think the every 10s signature is because > > the hibernation path is trying to restart the TPM device and won't take > > no for an answer. > > My fix returned the behavior how it was before my earlier fix in this > corner case (i.e. disable TPM). The issue has gone unnoticed before > since it has emitted only a single klog entry. > > On suspend this has not happened to me so obvious deduction is that > hibernate resets the null seed. > > Hibernate needs an addition a fix to disable bus encryption from kernel > command-line completely, i.e. tpm.disable_integrity following the > convention from my earlier fix [1]. I'd like to offer a way it might be resolvable with the null key after all, without provisioning a persistent NV key -- by changing the question from "re-derive the null primary and compare" to "inherit the trust the resume has already established". Resume-from-hibernation is a TPM Restart (Shutdown(STATE) -> Startup(CLEAR)), i.e. a firmware cold-init of the (f)TPM, after which the boot/initramfs kernel establishes a fresh, genuine null primary. In the common configuration (FDE with the resume/swap device inside a TPM-sealed LUKS2 container) that same TPM has, moments earlier and *before* the hibernation image is restored, cryptographically attested itself by unsealing the resume device. A substituted or interposed TPM cannot produce that unseal. So rather than letting the resumed kernel re-derive the null name, find a mismatch and disable the chip, the boot kernel's freshly-established and unseal-validated null primary could be inherited by the resumed image. The existing null-seed TOFU model is preserved; nothing new is provisioned. The gate is the unseal, and the adversary case shows why it is the right gate: Malice swaps (or interposes on) the TPM while the machine is hibernated, then leaves. Alice powers on. The initramfs attempts the TPM unseal of the resume device; with a foreign TPM it fails, so systemd-cryptsetup falls back to the passphrase, which Alice -- seeing a prompt -- types. The disk opens and the system resumes. If trust were re-established here, Alice would have personally vouched for Malice's TPM. But the unseal *failed*, so under this scheme nothing is inherited and the chip stays fail-closed exactly as today. The passphrase proves a human is present; it never proves the TPM is the genuine one. Hence: unseal succeeded -> inherit the validated null primary; passphrase fallback -> trust is lost, stay disabled. This keeps the property the null-seed design wants -- an in-session reset is not on the hibernate-restore path and is still caught -- while removing the false positive only where the platform has already re-attested the TPM. The hard parts, and where I'd value direction: - systemd-cryptsetup would need to signal "the resume device was unsealed by the TPM this boot" (vs. the passphrase fallback). This is per-resume runtime state; a static command-line parameter (and obviously build-time config as well) cannot represent it. - the validated null primary has to cross the boot -> resumed memory discontinuity (the initramfs kernel's state is overwritten by the restored image). Boot and image kernel are the same binary, so patching chip->null_key_name in the restored image is mechanically possible; a small reserved/nosave hand-off area may be cleaner. I don't know the hibernate path well enough to say which is right. It is admittedly cross-subsystem (tpm + pm/hibernate + systemd/cryptsetup), which is presumably why it hasn't been done. Compared with the persistent NV-key route (tpm.integrity_key=<handle>): that avoids the carry-across but needs the key provisioned and managed, and a persistent key's name no longer changes on a genuine reset, so the implicit reset detection has to be reconstructed. The null-key-inherit approach keeps the existing model and defers "is this the same TPM?" to the unseal that has already happened. Does this seem viable, or is there a reason the unseal-as-attestation gate does not hold that I'm missing? (For motivation: on a firmware TPM -- Intel PTT here -- there is no external bus to interpose, so the protection has no benefit on this class of hardware at all, yet the legitimate hibernation power-cycle still trips the disable. For fTPMs specifically, not enabling the feature is arguably the better answer; but for discrete TPMs that hibernate, a real solution seems a good idea if doable.) Cheers Daniel
