Linux Integrity Measurement development
 help / color / mirror / Atom feed
From: Daniel Golle <daniel@makrotopia.org>
To: Jarkko Sakkinen <jarkko@kernel.org>
Cc: James Bottomley <James.Bottomley@hansenpartnership.com>,
	Christoph Anton Mitterer <calestyo@scientia.org>,
	linux-integrity@vger.kernel.org
Subject: inherit null-key across hibernate (was: Re: regression: kernel log "flooded" with tpm tpm0: A TPM error (2306) occurred attempting to create NULL primary)
Date: Tue, 16 Jun 2026 04:50:37 +0100	[thread overview]
Message-ID: <ajDIDcW_EzgLB0qX@makrotopia.org> (raw)
In-Reply-To: <D5LEQGJ9X3NF.3K3YVPNE6KQJK@kernel.org>

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

  parent reply	other threads:[~2026-06-16  3:50 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-13 14:44 regression: kernel log "flooded" with tpm tpm0: A TPM error (2306) occurred attempting to create NULL primary Christoph Anton Mitterer
2024-11-13 15:47 ` James Bottomley
2024-11-13 18:12   ` James Bottomley
2024-11-13 22:34     ` Jarkko Sakkinen
2024-11-13 22:43       ` Jarkko Sakkinen
2026-06-16  3:50       ` Daniel Golle [this message]
2024-11-13 23:56   ` Christoph Anton Mitterer
2024-11-14  2:06     ` James Bottomley
2024-11-14  2:17       ` Christoph Anton Mitterer
2024-11-14  4:57         ` Jarkko Sakkinen
2024-11-25 13:49           ` Christoph Anton Mitterer
2024-11-30  2:37             ` Jarkko Sakkinen
2024-11-14  4:56       ` Jarkko Sakkinen
2024-11-13 18:49 ` Jarkko Sakkinen
2024-11-13 18:59   ` Jarkko Sakkinen
2024-11-14  0:04   ` Christoph Anton Mitterer
2024-11-14  4:52     ` Jarkko Sakkinen
2024-11-14 23:57       ` Christoph Anton Mitterer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ajDIDcW_EzgLB0qX@makrotopia.org \
    --to=daniel@makrotopia.org \
    --cc=James.Bottomley@hansenpartnership.com \
    --cc=calestyo@scientia.org \
    --cc=jarkko@kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox