[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v8 22/22] tests: Add tpm2_test
From: |
Gary Lin |
Subject: |
[PATCH v8 22/22] tests: Add tpm2_test |
Date: |
Tue, 16 Jan 2024 17:21:00 +0800 |
For TPM 2.0 TSS stack, the TCG2 command sending function is the only
difference between the a QEMU instance and grub-emu. To test TPM key
unsealing with a QEMU instance, it requires an extra OS image to invoke
grub-protect to seal the LUKS key, not only a simple grub-shell rescue
CD image. On the other hand, grub-emu can share the emulated TPM device
with the host, so that we can seal the LUKS key on host and test key
unsealing with grub-emu.
This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM device is created by
swtpm_cuse and PCR 0 and 1 are extended. The LUKS password is sealed by
grub-protect against PCR 0 and 1. The last step is to launch grub-emu to
load the LUKS image, try to mount the image with tpm2_key_protector_init
and cryptomount, and verify the result.
Based on the idea from Michael Chang.
Cc: Michael Chang <mchang@suse.com>
Signed-off-by: Gary Lin <glin@suse.com>
---
Makefile.util.def | 6 ++
tests/tpm2_test.in | 179 +++++++++++++++++++++++++++++++++++++++
tests/util/grub-shell.in | 6 +-
3 files changed, 190 insertions(+), 1 deletion(-)
create mode 100644 tests/tpm2_test.in
diff --git a/Makefile.util.def b/Makefile.util.def
index 89ceeec0e..dd6c18f76 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1279,6 +1279,12 @@ script = {
common = tests/test_asn1.in;
};
+script = {
+ testcase = native;
+ name = tpm2_test;
+ common = tests/tpm2_test.in;
+};
+
program = {
testcase = native;
name = example_unit_test;
diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
new file mode 100644
index 000000000..8edc49780
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,179 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# Copyright (C) 2024 Free Software Foundation, Inc.
+#
+# GRUB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# GRUB is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x$grub_modinfo_platform != xemu ]; then
+ exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:$PATH"
+export PATH
+
+if [ "x$EUID" = "x" ] ; then
+ EUID=`id -u`
+fi
+
+if [ "$EUID" != 0 ] ; then
+ echo "not root; cannot test tpm2."
+ exit 99
+fi
+
+if ! which cryptsetup >/dev/null 2>&1; then
+ echo "cryptsetup not installed; cannot test tpm2."
+ exit 99
+fi
+
+if ! which swtpm >/dev/null 2>&1; then
+ echo "swtpm not installed; cannot test tpm2."
+ exit 99
+fi
+
+if ! which tpm2_startup >/dev/null 2>&1; then
+ echo "tpm2-tools not installed; cannot test tpm2."
+ exit 99
+fi
+
+tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XXXXXXXXXX"`" ||
exit 20
+
+disksize=20M
+
+luksfile=$tpm2testdir/luks.disk
+lukskeyfile=${tpm2testdir}/password.txt
+
+# Choose a low iteration number to reduce the time to decrypt the disk
+csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
+
+tpm2devname="vtpm-test0"
+tpm2statedir=${tpm2testdir}/tpm
+tpm2dev="/dev/${tpm2devname}"
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=$tpm2testdir/testoutput
+
+vtext="TEST VERIFIED"
+
+# Create the password file
+echo -n "top secret" > ${lukskeyfile}
+
+# Setup LUKS2 image
+truncate -s ${disksize} ${luksfile} || exit 21
+cryptsetup luksFormat -q ${csopt} ${luksfile} ${lukskeyfile} || exit 22
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+ if [ -e "$tpm2dev" ]; then
+ swtpm_ioctl -s ${tpm2dev}
+ fi
+ if [ "${RET:-1}" -eq 0 ]; then
+ rm -rf "$tpm2testdir" || :
+ fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+# Shutdown the previous swtpm instance if exists
+if [ -c "${tpm2dev}" ]; then
+ swtpm_ioctl -s ${tpm2dev}
+fi
+
+# Create the swtpm cuse instannce
+swtpm_cuse -n ${tpm2devname} --tpm2 --tpmstate dir=${tpm2statedir}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+ exit $ret
+fi
+
+# Initialize swtpm device
+swtpm_ioctl -i ${tpm2dev}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+ exit $ret
+fi
+
+# Export the TCTI variable for tpm2-tools
+export TPM2TOOLS_TCTI="device:${tpm2dev}"
+
+# Send the startup command
+tpm2_startup -c
+ret=$?
+if [ "$ret" -ne 0 ]; then
+ exit $ret
+fi
+
+# Extend PCR 0
+tpm2_pcrextend 0:sha256=$(sha256sum <<< "test0" | cut -d ' ' -f 1)
+ret=$?
+if [ "$ret" -ne 0 ]; then
+ exit $ret
+fi
+
+# Extend PCR 1
+tpm2_pcrextend 1:sha256=$(sha256sum <<< "test1" | cut -d ' ' -f 1)
+ret=$?
+if [ "$ret" -ne 0 ]; then
+ exit $ret
+fi
+
+# Seal the password with grub-protect
+grub-protect \
+ --tpm2-device=${tpm2dev} \
+ --action=add \
+ --protector=tpm2 \
+ --tpm2key \
+ --tpm2-asymmetric=RSA2048 \
+ --tpm2-bank=sha256 \
+ --tpm2-pcrs=0,1 \
+ --tpm2-keyfile=${lukskeyfile} \
+ --tpm2-outfile=${sealedkey}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+ echo "Failed to seal the secret key"
+ exit 1
+fi
+
+# Write the TPM unsealing script
+cat > ${tpm2testdir}/testcase.cfg <<EOF
+loopback luks (host)${luksfile}
+tpm2_key_protector_init -T (host)${sealedkey}
+if cryptomount -a --protector tpm2; then
+ echo "${vtext}"
+fi
+EOF
+
+# Test TPM unsealing with the same PCR
+${grubshell} --timeout=$timeout --grub-emu-opts="-t ${tpm2dev}"
${tpm2testdir}/testcase.cfg > ${testoutput} || ret=$?
+
+if [ "$ret" -eq 0 ]; then
+ if ! grep -q "^${vtext}$" "$testoutput"; then
+ echo "error: test not verified [`cat $testoutput`]" >&2
+ exit 1
+ fi
+else
+ echo "grub-emu exited with error: $ret" >&2
+ exit $ret
+fi
+
+exit $ret
diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in
index 496e1bab3..f8642543d 100644
--- a/tests/util/grub-shell.in
+++ b/tests/util/grub-shell.in
@@ -75,6 +75,7 @@ work_directory=${WORKDIR:-`mktemp -d
"${TMPDIR:-/tmp}/grub-shell.XXXXXXXXXX"`} |
. "${builddir}/grub-core/modinfo.sh"
qemuopts=
+grubemuopts=
serial_port=com0
serial_null=
halt_cmd=halt
@@ -281,6 +282,9 @@ for option in "$@"; do
--qemu-opts=*)
qs=`echo "$option" | sed -e 's/--qemu-opts=//'`
qemuopts="$qemuopts $qs" ;;
+ --grub-emu-opts=*)
+ qs=`echo "$option" | sed -e 's/--grub-emu-opts=//'`
+ grubemuopts="$grubemuopts $qs" ;;
--disk=*)
dsk=`echo "$option" | sed -e 's/--disk=//'`
if [ ${grub_modinfo_platform} = emu ]; then
@@ -576,7 +580,7 @@ elif [ x$boot = xemu ]; then
cat >"$work_directory/run.sh" <<EOF
#! @BUILD_SHEBANG@
SDIR=\$(realpath -e \${0%/*})
-exec "$(realpath -e "${builddir}")/grub-core/grub-emu" -m
"\$SDIR/${device_map##*/}" --memdisk "\$SDIR/${roottar##*/}" -r memdisk -d
"/boot/grub"
+exec "$(realpath -e "${builddir}")/grub-core/grub-emu" -m
"\$SDIR/${device_map##*/}" --memdisk "\$SDIR/${roottar##*/}" -r memdisk -d
"/boot/grub" ${grubemuopts}
EOF
else
cat >"$work_directory/run.sh" <<EOF
--
2.35.3
- [PATCH v8 12/22] util/grub-protect: Add new tool, (continued)
- [PATCH v8 12/22] util/grub-protect: Add new tool, Gary Lin, 2024/01/16
- [PATCH v8 14/22] tpm2: Add more marshal/unmarshal functions, Gary Lin, 2024/01/16
- [PATCH v8 15/22] tpm2: Implement more TPM2 commands, Gary Lin, 2024/01/16
- [PATCH v8 16/22] tpm2: Support authorized policy, Gary Lin, 2024/01/16
- [PATCH v8 18/22] cryptodisk: Fallback to passphrase, Gary Lin, 2024/01/16
- [PATCH v8 17/22] protectors: Implement NV index, Gary Lin, 2024/01/16
- [PATCH v8 19/22] cryptodisk: wipe out the cached keys from protectors, Gary Lin, 2024/01/16
- [PATCH v8 20/22] diskfilter: look up cryptodisk devices first, Gary Lin, 2024/01/16
- [PATCH v8 22/22] tests: Add tpm2_test,
Gary Lin <=
- [PATCH v8 21/22] tpm2: Enable tpm2 module for grub-emu, Gary Lin, 2024/01/16