test: add TEST-92-TPM2-SWTPM for the software TPM fallback

Boot a VM in EFI mode without a hardware/firmware TPM and with
systemd.tpm2_software_fallback=yes, so systemd-tpm2-generator manufactures a
software TPM on the ESP in the initrd and chainloads swtpm. Assert the service
starts, the vtpm-proxy device shows up, and a systemd-creds TPM2 seal/unseal
round-trip works. Then reboot and confirm the sealed secret still unseals,
i.e. the TPM state persisted on the ESP across the reboot.

Signed-off-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
Paul Meyer
2026-06-25 09:39:55 +02:00
parent 78a5271245
commit 1b1900a6f3
3 changed files with 87 additions and 0 deletions
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
integration_tests += [
integration_test_template + {
'name' : fs.name(meson.current_source_dir()),
# The test reboots to verify TPM state persists across boots, so keep storage around.
'storage' : 'persistent',
'configuration' : integration_test_template['configuration'] + {
'wants' : '@0@ tpm2.target'.format(integration_test_template['configuration']['wants']),
'after' : '@0@ tpm2.target'.format(integration_test_template['configuration']['after']),
},
'vm' : true,
# We need to boot in EFI mode (with an initrd) so that an ESP exists for the software TPM to
# store its state on and so that systemd-tpm2-generator runs in the initrd.
'firmware' : 'auto',
# Crucially, do NOT attach a hardware/firmware TPM: that's what makes the generator fall back
# to the software TPM, which is what this test exercises.
'tpm' : false,
'cmdline' : integration_test_template['cmdline'] + [
# Opt into the software TPM fallback (off by default).
'systemd.tpm2_software_fallback=yes',
'systemd.default_device_timeout_sec=300',
],
},
]
+1
View File
@@ -104,6 +104,7 @@ foreach dirname : [
'TEST-89-RESOLVED-MDNS',
'TEST-90-RESTRICT-FSACCESS',
'TEST-91-LIVEUPDATE',
'TEST-92-TPM2-SWTPM',
]
subdir(dirname)
endforeach
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# Exercises the software TPM fallback (systemd-tpm2-swtpm.service) across a reboot. The VM boots in EFI mode
# without a hardware/firmware TPM and with "systemd.tpm2_software_fallback=yes" (see the test's meson.build),
# so systemd-tpm2-generator manufactures a software TPM on the ESP in the initrd and chainloads swtpm.
#
# boot 0: the TPM is manufactured in the initrd; seal a secret to it and stash the blob.
# boot 1: the TPM state persisted on the ESP across the reboot, so the secret still unseals.
#
# See systemd-tpm2-swtpm.service(8).
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
CRED=/var/lib/systemd-tpm2-swtpm-test.cred
PLAINTEXT="swtpm round-trip"
if [[ -n "${ASAN_OPTIONS:-}" ]]; then
# swtpm_setup is not built with sanitizers, but does NSS lookups that pull in the ASan-instrumented
# libnss_systemd.so, which aborts with "ASan runtime does not come first". Skip under sanitizers.
echo "swtpm_setup does not work under sanitizers, skipping the test" | tee --append /skipped
exit 77
fi
if [[ ! -x /usr/lib/systemd/systemd-tpm2-swtpm ]] || ! command -v swtpm >/dev/null || [[ ! -d /sys/firmware/efi ]]; then
echo "Software TPM prerequisites missing, skipping the test" | tee --append /skipped
exit 77
fi
assert_swtpm_up() {
systemctl is-active systemd-tpm2-swtpm.service
timeout 30 bash -c 'until [[ -c /dev/tpmrm0 ]]; do sleep 1; done'
test -c /dev/tpm0
# No firmware TPM here, so has-tpm2 reports "partial"; assert the software driver is present.
assert_in '\+driver' "$(systemd-analyze has-tpm2 || :)"
}
case "$REBOOT_COUNT" in
0)
assert_swtpm_up
# Seal a secret to the software TPM and keep the blob for the next boot.
echo -n "$PLAINTEXT" >/tmp/swtpm-plaintext
systemd-creds encrypt --name= --with-key=tpm2 /tmp/swtpm-plaintext "$CRED"
systemd-creds decrypt --name= "$CRED" - | cmp /tmp/swtpm-plaintext -
systemctl_final reboot
exec sleep infinity
;;
1)
assert_swtpm_up
# Persistence: the TPM state survived the reboot on the ESP, so the blob still unseals.
echo -n "$PLAINTEXT" >/tmp/swtpm-plaintext
systemd-creds decrypt --name= "$CRED" - | cmp /tmp/swtpm-plaintext -
touch /testok
;;
*)
assert_not_reached
;;
esac