grub-devel
[Top][All Lists]
Advanced

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

Re: [PATCH v10 20/20] tests: Add tpm2_test


From: Stefan Berger
Subject: Re: [PATCH v10 20/20] tests: Add tpm2_test
Date: Fri, 12 Apr 2024 12:09:36 -0400
User-agent: Mozilla Thunderbird



On 4/9/24 04:30, Gary Lin via Grub-devel wrote:
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.

My concern here would be that distros for example don't build swtpm with the CUSE interface but build it with the socket and chardev interfaces. If you don't want users to have to build their own version of swtpm then I would suggest to use either 'swtpm chardev' with the vtpm_proxy module (it is quite commonly available on recent distros) or 'swtpm socket'.

The chardev usage is for example described here and should be least involved to convert to:

https://github.com/stefanberger/swtpm/wiki/Using-the-Intel-TSS-with-swtpm#character-device-using-tpm_vtpm_proxy

You could start it like this with UnixIO control port (needs adjustment to swtpm_ioctl in your code).

swtpm chardev --vtpm-proxy --tpmstate dir=/tmp/myvtpm --tpm2 --ctrl type=unixio,path=/tmp/myvtpm/ctrl --flags startup-clear --daemon > logfile

No need to run 'swtpm_ioctl -i' and tpm2_startup with the startup-clear passed.

One of my test cases determines the created device (/dev/tpmXYZ) like this:

for ((i = 0; i < 200; i ++)); do
        if [ -z "${TPM_DEVICE}" ]; then
TPM_DEVICE=$(sed -n 's,.*\(/dev/tpm[0-9]\+\).*,\1,p' "logfile")
                if [ -n "${TPM_DEVICE}" ]; then
                        echo "Using ${TPM_DEVICE}."
                fi
        fi
        if [ -n "${TPM_DEVICE}" ]; then
                [ -c "${TPM_DEVICE}" ] && break
        fi
        sleep 0.1
done

shutdown: sudo swtpm_ioctl -s --unix /tmp/myvtpm/ctrl


Otherwise you could use the socket version described here:

https://github.com/stefanberger/swtpm/wiki/Using-the-Intel-TSS-with-swtpm#socket-interface

It needs the swtpm tcti as a dependency, though.

   Stefan


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



reply via email to

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