swtpm/tests/test_tpm2_save_load_state_2
Stefan Berger 60daa0b0d0 tests: Test verification and encryption with state from libtpms v0.9 and v0.10
Extend an existing test case using the IBM TSS2 tools and add state created
with libtpms v0.9 and v0.10 that contains signing and encryption primary
keys. Use the keys to verify a signature created by those versions of
libtpms and data encrypted by them to make sure that primary keys that are
recreated with later versions of libtpms still create the same primary
keys.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2026-04-28 20:40:58 -04:00

445 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
# For the license, see the LICENSE file in the root directory.
#set -x
if [ "${SWTPM_TEST_IBMTSS2:-0}" -eq 0 ]; then
echo "SWTPM_TEST_IBMTSS2 must be set to run this test."
exit 77
fi
if ! type -p nvdefinespace startup &>/dev/null; then
PREFIX=tss
if ! type -p ${PREFIX}nvdefinespace ${PREFIX}startup; then
echo "Could not find TPM2 tools (e.g., (tss)startup, (tss)nvdefinespace) in PATH."
exit 77
fi
fi
TOOLSPATH=$(dirname "$(type -P "${PREFIX}startup")")
ROOT=${abs_top_builddir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:-$(dirname "$0")}
TPMDIR="$(mktemp -d)" || exit 1
PID_FILE=$TPMDIR/swtpm.pid
SOCK_PATH=$TPMDIR/sock
LOGFILE=$TPMDIR/logfile
LOGFILE2=$TPMDIR/logfile2
TMPFILE=$TPMDIR/tmpfile
BINFILE=$TPMDIR/binfile
SIGFILE=$TPMDIR/sigfile
STATEFILE=${STATEFILE:-$TPMDIR/state}
STORE_PARAM="dir=$TPMDIR"
if [ "${SWTPM_TEST_LINEAR_FILE:-0}" -ne 0 ]; then
echo "Testing with linear file backend ($STATEFILE)"
STORE_PARAM="backend-uri=file://$STATEFILE"
fi
source "${TESTDIR}/test_common"
source "${TESTDIR}/common"
skip_test_no_tpm20 "${SWTPM_EXE}"
trap "cleanup" SIGTERM EXIT
function cleanup()
{
rm -rf "$TPMDIR"
# remove files from tss tools
rm -f h01*.bin nvp*.bin
if [ -n "$PID" ]; then
kill_quiet -SIGTERM "$PID" 2>/dev/null
fi
}
# Fill up the NVRAM space with 2048 bit signing keys and then an NVRAM area that
# fills it up to the last byte. We want to make sure that the OBJECTs that the
# RSA keys are creating in NVRAM can be loaded into the NVRAM again when the size
# of the OBJECT increases when for example the size of the RSA keys increases.
# This may force us to increase the NVRAM memory space in libtpms then.
function fillup_nvram()
{
local create="$1"
local check="$2"
local i sz
if [ "$create" -eq 1 ]; then
# Fill up the NVRAM space with RSA 2048 keys;
# exactly 65 have to fit
if ! "${TOOLSPATH}/${PREFIX}createprimary" -hi o -si > "$TMPFILE"; then
echo "Error: createprimary failed."
exit 1
fi
if ! grep -q 80000000 "$TMPFILE"; then
echo "Error: createprimary did not result in expected handle 80000000"
exit 1
fi
for ((i = 0x81000000; i < 0x81000100; i++)); do
"${TOOLSPATH}/${PREFIX}evictcontrol" \
-hi o \
-ho 80000000 \
-hp "$(printf "%x" "$i")" &>"$TMPFILE" || break
done
"${TOOLSPATH}/${PREFIX}getcapability" -cap 1 -pr 81000000 -pc 80 > "$TMPFILE"
# We need know we need to see '65 Handles' for state created with
# libtpms-0.6.0 and 128kb NVRAM size
if ! grep -i "65 Handles" "$TMPFILE"; then
echo "Error: Did not find '65 Handles' keyword in output"
cat "$TMPFILE"
exit 1
fi
# Fill up the rest of the NVRAM with a single NVRAM index whose size
# we now have to find;
# for reference: libtpms v0.6.0 allowed 236 bytes
for ((sz = 0; ; sz++)); do
"${TOOLSPATH}/${PREFIX}nvdefinespace" \
-hi o \
-ha 01000000 \
-sz "${sz}" > "${TMPFILE}" || break
# this worked, so lets remove it and try the next size
#echo "NVRAM space of size $sz could be created"
"${TOOLSPATH}/${PREFIX}nvundefinespace" \
-hi o \
-ha 01000000 > "${TMPFILE}"
done
if [ "$sz" -gt 0 ]; then
sz=$((sz - 1))
echo "Creating final space of size ${sz}"
if ! "${TOOLSPATH}/${PREFIX}nvdefinespace" \
-hi o \
-ha 01000000 \
-sz ${sz} > "${TMPFILE}";
then
echo "Error: Could not create final NVRAM space."
cat "${TMPFILE}"
exit 1
fi
fi
if [ $sz -eq 0 ]; then
echo "Error: NVRAM space could not be created at all; not enough space!"
exit 1
elif [ $sz -lt 236 ]; then
echo "Error: Insufficient NVRAM memory. Needed to create an NVRAM index with size 236 bytes."
exit 1
elif [ $sz -gt 236 ]; then
echo "Error: The NVRAM index is too large. Only needed 236 bytes but got $sz bytes."
exit 1
else
echo "The NVRAM index is exactly of the right size (236 bytes)."
fi
echo -n "123" > "$BINFILE"
if ! "${TOOLSPATH}/${PREFIX}sign" \
-hk 81000000 \
-if "${BINFILE}" \
-os "${SIGFILE}" > "$TMPFILE";
then
echo "Error: Could not create signature."
cat "$TMPFILE"
exit 1
fi
fi
if [ "$check" -eq 1 ]; then
"${TOOLSPATH}/${PREFIX}getcapability" -cap 1 -pr 81000000 -pc 80 > "$TMPFILE"
# We need know we need to see '65 Handles' for state created with
# libtpms-0.6.0 and 128kb NVRAM size
if ! grep -i "65 Handles" "$TMPFILE"; then
echo "Error: Did not find '65 Handles' keyword in output"
cat "$TMPFILE"
exit 1
fi
printf "Verifying signature with all the persisted keys\n"
echo -n "123" > "$BINFILE"
for ((i = 0x81000000; i < 0x81000040; i++)); do
if ! "${TOOLSPATH}/${PREFIX}verifysignature" \
-hk "$(printf "%x" "$i")" \
-is "${SIGFILE}" \
-if "${BINFILE}" > "$TMPFILE";
then
echo "Verifying signature failed for handle $(printf "%x" "$i")."
exit 1
fi
done
fi
}
export TPM_SERVER_TYPE=raw
export TPM_SERVER_NAME=127.0.0.1
export TPM_INTERFACE_TYPE=socsim
export TPM_COMMAND_PORT=${TPM_COMMAND_PORT:-65460}
export TPM_DATA_DIR=$TPMDIR
export TPM_SESSION_ENCKEY="807e2bfe898ddaed8fa6310e716a24dc" # for sessions
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "$STORE_PARAM" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (1) Socket TPM did not write pidfile."
exit 1
fi
PID="$(cat "$PID_FILE")"
# Send TPM_Init
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -i 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_INIT failed: $act"
exit 1
fi
if ! "${TOOLSPATH}/${PREFIX}startup" -c; then
echo "Error: tpm_startup clear failed."
exit 1
fi
fillup_nvram 1 1
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
if wait_process_gone "${PID}" 4; then
echo "Error: swtpm did not shut down"
exit 1
fi
echo "============================" >> "$LOGFILE"
echo "TPM was shut down"
# Store this state for later usage; use a really old version of libtpms: 0.6.0
#cp $TPMDIR/tpm2-00.permall ${TESTDIR}/data/tpm2state5;
#cp $SIGFILE ${TESTDIR}/data/tpm2state5/signature.bin
#################################################################
# Run TPM2 with the created state and verify it's the same
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "$STORE_PARAM" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (2) Socket TPM did not write pidfile."
exit 1
fi
echo "TPM re-started"
PID="$(cat "$PID_FILE")"
# Send TPM_Init
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -i 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_INIT failed: $act"
cat "$LOGFILE"
exit 1
fi
if ! "${TOOLSPATH}/${PREFIX}startup" -c; then
echo "Error: tpm_startup clear failed."
cat "$LOGFILE"
exit 1
fi
fillup_nvram 0 1
if ! "${TOOLSPATH}/${PREFIX}shutdown" -c; then
echo "Error: tpm_shutdown clear failed."
cat "$LOGFILE"
exit 1
fi
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
echo "============================" >> "$LOGFILE"
echo "TPM was shut down"
#################################################################
# Run TPM2 with previously saved state and verify it's the same
if [ "${SWTPM_TEST_LINEAR_FILE:-0}" -ne 0 ]; then
echo "Test 1 OK (skipped last with linear file)"
exit 0
fi
rm -f "$TPMDIR"/*
cp -f "${TESTDIR}/data/tpm2state5/tpm2-00.permall" "$TPMDIR/tpm2-00.permall"
cp "${TESTDIR}/data/tpm2state5/signature.bin" "$SIGFILE"
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "$STORE_PARAM,lock" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (3) Socket TPM did not write pidfile."
exit 1
fi
echo "TPM started with previously generated state"
PID="$(cat "$PID_FILE")"
# Send TPM_Init
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -i 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_INIT failed: $act"
exit 1
fi
if ! "${TOOLSPATH}/${PREFIX}startup" -c; then
echo "Error: tpm_startup clear failed."
cat "$LOGFILE"
exit 1
fi
fillup_nvram 0 1
if ! "${TOOLSPATH}/${PREFIX}shutdown" -c; then
echo "Error: tpm_shutdown clear failed."
cat "$LOGFILE"
exit 1
fi
# Start another swtpm that must fail to start due to locked storage
# The not-need-init flag causes the failure.
if $SWTPM_EXE socket \
--tpmstate "$STORE_PARAM,lock" \
--log "file=${LOGFILE2},level=1" \
--flags not-need-init \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}}; then
echo "Error: Starting 2nd instance on locked storage should have failed"
exit 1
fi
if ! grep -q "Resource temporarily unavailable" "${LOGFILE2}"; then
echo "Error: Expected error 'Resource temporarily unavailable' in logfile"
cat "${LOGFILE2}"
exit 1
fi
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
echo "Test 1 OK"
echo
rm -f "${TPMDIR}"/*
cp "${TESTDIR}/data/tpm2state8/"* "${TPMDIR}"
libtpms_versions=(v0.9 v0.10)
if ! tsscreateprimary -help | grep -q "\-rsa \[keybits\]"; then
libtpms_versions=()
fi
for version in "${libtpms_versions[@]}"; do
for profile in null default-v1; do
state="tpmstate-${version}-${profile}.bin"
statefile="${TPMDIR}/${state}"
if [ ! -r "${statefile}" ]; then
continue
fi
echo "INFO: Testing with TPM state '${state}'."
echo > "${LOGFILE}"
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "backend-uri=file://${statefile}" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
--flags not-need-init,startup-clear \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (4) Socket TPM did not write pidfile."
cat "${LOGFILE}"
exit 1
fi
if ! msg=$(tsscreateprimary -rsa 3072 -hi o -si 2>&1); then
echo "Error: Could not create 1st primary key."
echo "${msg}"
exit 1
fi
# Key has handle 80000000; was persisted at 81000000
for handle in 80000000 81000000; do
if ! tssverifysignature \
-if "${TPMDIR}/tosign.txt" \
-is "${TPMDIR}/signature-${version}-${profile}.bin" \
-hk "${handle}"; then
echo "Error: Signature verification failed with key at ${handle}."
exit 1
fi
done
echo "INFO: Signature verification passed."
if ! msg=$(tsscreateprimary -rsa 3072 -hi o -des 2>&1); then
echo "Error: Could not create 2nd primary key."
echo "${msg}"
exit 1
fi
# Key has handle 80000001; was persisted at 81000001
decrypted="${TPMDIR}/decrypted"
for handle in 80000001 81000001; do
rm -f "${decrypted}"
if ! tssencryptdecrypt \
-d \
-hk "${handle}" \
-if "${TPMDIR}/encrypted-${version}-${profile}.bin" \
-of "${decrypted}"; then
echo "Error: Decryption failed with key at ${handle}."
exit 1
fi
if [ "$(cat "${TPMDIR}/toencrypt.txt")" != "$(cat "${decrypted}")" ]; then
echo "Error: Decrypted files have different contents."
echo "Expected: $(cat "${TPMDIR}/toencrypt.txt")"
echo "Actual : $(cat "${decrypted}")"
exit 1
fi
done
echo "INFO: Decryption succeeded."
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
done
done
if [ -n "${libtpms_versions[*]}" ]; then
echo "Test 2 OK"
else
echo "Test 2: Skipped. tss tools are tool old."
fi
exit 0