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
next prev 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