grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v10 20/20] tests: Add tpm2_test


From: Gary Lin
Subject: [PATCH v10 20/20] tests: Add tpm2_test
Date: Tue, 9 Apr 2024 16:30:52 +0800

For the tpm2 module, the TCG2 command submission 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, rather than 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.

There are several test cases in the script to test various settings. Each
test case uses grub-protect to seal the LUKS password against PCR 0 and
PCR 1. Then grub-emu is launched 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       | 284 +++++++++++++++++++++++++++++++++++++++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 295 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index a0a3e2cd5..77bbdd453 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1279,6 +1279,12 @@ script = {
   common = tests/asn1_test.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..8d4e96f47
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,284 @@
+#! @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
+
+tpm2_seal_unseal() {
+    local tpm2_alg="$1"
+    local handle_type="$2"
+    local srk_test="$3"
+
+    local grub_tpm2_alg=${tpm2_alg}
+
+    local extra_opt=""
+    local extra_grub_opt=""
+
+    local persistent_handle="0x81000000"
+
+    if [ "${handle_type}" == "persistent" ]; then
+       extra_opt="--tpm2-srk=${persistent_handle}"
+    fi
+
+    # Seal the password with grub-protect
+    grub-protect ${extra_opt} \
+           --tpm2-device=${tpm2dev} \
+           --action=add \
+           --protector=tpm2 \
+           --tpm2key \
+           --tpm2-asymmetric=${tpm2_alg} \
+           --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"
+       return 1
+    fi
+
+    # Flip the asymmetric algorithm in grub.cfg to trigger fallback SRKs
+    if [ "${srk_test}" == "fallback_srk" ]; then
+        if [[ "${tpm2_alg}" == RSA* ]]; then
+            grub_tpm2_alg="ECC"
+        elif [[ "${tpm2_alg}" == ECC* ]]; then
+            grub_tpm2_alg="RSA"
+        fi
+    fi
+
+    if [ "${handle_type}" != "persistent" ]; then
+       extra_grub_opt="-a ${grub_tpm2_alg}"
+    fi
+
+    # Write the TPM unsealing script
+    cat > ${tpm2testdir}/testcase.cfg <<EOF
+loopback luks (host)${luksfile}
+tpm2_key_protector_init -T (host)${sealedkey} ${extra_grub_opt}
+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=$?
+
+    # Remove the persistent handle
+    if [ "${handle_type}" == "persistent" ]; then
+       grub-protect \
+               --tpm2-device=${tpm2dev} \
+               --protector=tpm2 \
+               --action=remove \
+               --tpm2-srk=${persistent_handle} \
+               --tpm2-evict
+    fi
+
+    return $ret
+}
+
+tpm2_seal_unseal RSA transient no_fallback_srk
+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
+
+tpm2_seal_unseal ECC transient no_fallback_srk
+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
+
+tpm2_seal_unseal RSA persistent no_fallback_srk
+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
+
+tpm2_seal_unseal ECC persistent no_fallback_srk
+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
+
+tpm2_seal_unseal RSA transient fallback_srk
+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
+
+tpm2_seal_unseal ECC transient fallback_srk
+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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]