#!/usr/bin/env bash #set -x # shellcheck disable=SC2090 # For the license, see the LICENSE file in the root directory. # This test case builds various versions of libtpms + swtpm and generates # TPM 2 state using swtpm_setup with profiles, if supported. After that # it starts the built versions of swtpm and tries to use the state that # was created possibly by another version of libtpms and runs tests with it. # Currently this only works with the null profile whose state can be created # and read either with libtpms v0.9 or v0.10. In the future tests can be # covering the default-v1 profile as well that must be exchageable with # libtpms >v0.10. if [ "${SWTPM_TEST_EXPENSIVE:-0}" -eq 0 ]; then echo "SWTPM_TEST_EXPENSIVE must be set to run this test." exit 77 fi if [ "${SWTPM_TEST_IBMTSS2:-0}" -eq 0 ]; then echo "SWTPM_TEST_IBMTSS2 must be set to run this test." exit 77 fi ROOT=${abs_top_builddir:-$(dirname "$0")/..} TESTDIR=${abs_top_testdir:-$(dirname "$0")} SRCDIR=${abs_top_srcdir:-$(dirname "$0")/..} source "${TESTDIR}/common" workdir="$(mktemp -d)" || exit 1 export TPM_PATH=${workdir} SWTPM_SERVER_PORT=65478 SWTPM_CTRL_PORT=65479 SWTPM_SERVER_NAME=127.0.0.1 SWTPM_INTERFACE="socket+socket" LIBTPMS_URL=https://github.com/stefanberger/libtpms LIBTPMS_INITIAL_BRANCH=master SWTPM_URL=https://github.com/stefanberger/swtpm SWTPM_DEFAULT_BRANCH=master # during development change to local branch cat <<_EOF_ > "${workdir}/swtpm-localca.options" --tpm-manufacturer IBM --tpm-model swtpm-libtpms --tpm-version 2 --platform-manufacturer "Fedora XYZ" --platform-version 2.1 --platform-model "QEMU A.B" _EOF_ cat <<_EOF_ > "${workdir}/swtpm_setup.conf" create_certs_tool=\${SWTPM_LOCALCA} create_certs_tool_config=${workdir}/swtpm-localca.conf create_certs_tool_options=${workdir}/swtpm-localca.options _EOF_ # Copy swtpm source tree to workdir pushd "${SRCDIR}" &>/dev/null || exit 1 if [ ! -d ".git" ]; then echo "SKIP: This test must be run from a git checkout" exit 77 fi mkdir -p "${workdir}/swtpm" cp -rp . "${workdir}/swtpm" cd "${workdir}/swtpm" || exit 1 chmod -R 0755 . # when using 'distcheck' if [ -n "$(git status --porcelain --untracked-files=no)" ]; then # store all current modifications in a temp patch git config --local user.name test git config --local user.email test@test.test git add --all . >/dev/null git commit -m "temp" >/dev/null fi git clean -xdf &>/dev/null popd &>/dev/null || exit 1 function cleanup() { rm -rf "${workdir}" if kill_quiet -0 "${SWTPM_PID}"; then kill_quiet -9 "${SWTPM_PID}" fi } trap "cleanup" SIGTERM EXIT function nv_storefile() { local nvindex="$1" local filename="$2" local sz if ! sz=$(get_filesize "${filename}"); then return 1 fi if ! tssnvdefinespace -ha "${nvindex}" -hi o -pwdn nv -sz "${sz}" 1>/dev/null; then return 1 fi if ! tssnvwrite -ha "${nvindex}" -pwdn nv -if "${filename}"; then return 1 fi return 0 } function nv_savetofile() { local nvindex="$1" local filename="$2" local sz if ! tssnvread -ha "${nvindex}" -pwdn nv -of "${filename}" >/dev/null; then return 1 fi sz=$(get_filesize "${filename}") if [ "${sz}" -eq 0 ]; then # older versions required the -sz parameter sz=$(tssnvreadpublic -ha "${nvindex}" | sed -n 's/.*data size //p') if ! tssnvread -ha "${nvindex}" -pwdn nv -sz "${sz}" -of "${filename}" >/dev/null; then return 1 fi fi return 0 } function create_tpm_state() { local workdir="$1" local inputfile="${workdir}/input.txt" local encfile="${workdir}/enc" local signature="${workdir}/signature.bin" local contextfile="${workdir}/context.bin" local aespub="${workdir}/aespub.bin" local aespriv="${workdir}/aespriv.bin" local cc rsasize ecdsaparam echo "input" > "${inputfile}" export TPM_COMMAND_PORT=${SWTPM_SERVER_PORT} export TPM_PLATFORM_PORT=${SWTPM_CTRL_PORT} export TPM_SERVER_NAME=${SWTPM_SERVER_NAME} export TPM_INTERFACE_TYPE=socsim export TPM_SERVER_TYPE=raw if tsscreateprimary --help | grep rsa | grep -q keybits; then rsasize="3072" fi # Test signing with RSA 3072 key if ! tsscreateprimary -rsa ${rsasize:+${rsasize}} -hi o -pwdk ooo -si 1>/dev/null; then return 1 fi if ! tsssign -hk 80000000 -pwdk ooo -if "${inputfile}" -os "${signature}"; then return 1 fi if ! nv_storefile 01000000 "${signature}"; then return 1 fi # Save the key as contextfile if ! tsscontextsave -ha 80000000 -of "${contextfile}" 1>/dev/null; then return 1 fi if ! nv_storefile 01000001 "${contextfile}"; then return 1 fi # Flush all keys if ! tssflushcontext -ha 80000000; then return 1 fi # Test HMAC # tsssign -salg was added in a later version only; Ubuntu Jammy does not have it if tsssign -h | grep -q "salg"; then if ! tsscreateprimary -rsa ${rsasize:+${rsasize}} -hi o -pwdk ooo 1>/dev/null; then return 1 fi if ! tsscreate -hp 80000000 -pwdp ooo -kh -kt f -kt p -opu "${aespub}" -opr "${aespriv}" -halg sha256; then return 1 fi if ! nv_storefile 01000002 "${aespub}" || ! nv_storefile 01000003 "${aespriv}" ; then return 1 fi if ! tssload -hp 80000000 -pwdp ooo -ipu "${aespub}" -ipr "${aespriv}" 1>/dev/null; then return 1 fi if ! tsssign -hk 80000001 -if "${inputfile}" -salg hmac -scheme hmac -os "${signature}"; then return 1 fi if ! nv_storefile 01000004 "${signature}"; then return 1 fi if ! tsscontextsave -ha 80000000 -of "${contextfile}" 1>/dev/null || \ ! nv_storefile 01000005 "${contextfile}" || \ ! tsscontextsave -ha 80000001 -of "${contextfile}" 1>/dev/null || \ ! nv_storefile 01000006 "${contextfile}"; then return 1 fi # Flush all keys if ! tssflushcontext -ha 80000000 || ! tssflushcontext -ha 80000001; then return 1 fi fi # Encryption/Decryption if ! tsscreateprimary -hi o -des 1>/dev/null; then return 1 fi if ! tssencryptdecrypt -hk 80000000 -if "${inputfile}" -of "${encfile}"; then return 1 fi if ! nv_storefile 01000007 "${encfile}"; then return 1 fi if ! tsscontextsave -ha 80000000 -of "${contextfile}" 1>/dev/null || \ ! nv_storefile 01000008 "${contextfile}"; then return 1 fi if ! tssflushcontext -ha 80000000; then return 1 fi # Signing with a NIST P256 key if ! tsscreateprimary -ecc nistp256 -hi o -pwdk ooo -si 1>/dev/null; then return 1 fi ecdsaparam="-scheme ecdsa" # older tools had: [-ecc (ECDSA scheme)] if tsssign --help | grep ecc | grep -q scheme; then ecdsaparam="-ecc" fi if ! tsssign ${ecdsaparam:+${ecdsaparam}} -hk 80000000 -pwdk ooo -if "${inputfile}" -os "${signature}"; then return 1 fi if ! nv_storefile 01000009 "${signature}"; then return 1 fi # Save the key as contextfile if ! tsscontextsave -ha 80000000 -of "${contextfile}" 1>/dev/null; then return 1 fi if ! nv_storefile 0100000a "${contextfile}"; then return 1 fi # Flush all keys if ! tssflushcontext -ha 80000000; then return 1 fi # Test setting command audit if available; Ubuntu Jammy does not have it if type -P tsssetcommandcodeauditstatus >/dev/null; then # Set a couple of commands to be audited for cc in 11f 12a 133 13a 140 147 150 15c 16e 17a 187 190 197; do if ! tsssetcommandcodeauditstatus -hi o -set $cc; then return 1 fi done fi return 0 } function check_tpm_state() { local workdir="$1" local is_fullresume="$2" local inputfile="${workdir}/input.txt" local encfile="${workdir}/enc" local decfile="${workdir}/dec" local signature="${workdir}/signature.bin" local contextfile="${workdir}/context.bin" local aespub="${workdir}/aespub.bin" local aespriv="${workdir}/aespriv.bin" local cc rsasize echo "input" > "${inputfile}" export TPM_COMMAND_PORT=${SWTPM_SERVER_PORT} export TPM_PLATFORM_PORT=${SWTPM_CTRL_PORT} export TPM_SERVER_NAME=${SWTPM_SERVER_NAME} export TPM_INTERFACE_TYPE=socsim export TPM_SERVER_TYPE=raw if tsscreateprimary --help | grep rsa | grep -q keybits; then rsasize="3072" fi # Test RSA 3072 signing key if ! tsscreateprimary -rsa ${rsasize:+${rsasize}} -hi o -pwdk ooo -si 1>/dev/null; then return 1 fi if ! nv_savetofile 01000000 "${signature}"; then return 1 fi if ! tssverifysignature -hk 80000000 -if "${inputfile}" -is "${signature}"; then return 1 fi echo "INFO: Verified signature with RSA key" if ! tssflushcontext -ha 80000000; then return 1 fi # Test with the key stored in context; this only works with save/restore of all state if [ "${is_fullresume}" -ne 0 ]; then if ! nv_savetofile 01000001 "${contextfile}"; then return 1 fi if ! tsscontextload -if "${contextfile}" 1>/dev/null; then return 1 fi if ! tssverifysignature -hk 80000000 -if "${inputfile}" -is "${signature}"; then return 1 fi echo "INFO: Verified signature with RSA key and restored key context" if ! tssflushcontext -ha 80000000; then return 1 fi fi # HMAC test # tsssign -salg was added in a later version only if tsssign -h | grep -q "salg"; then if ! tsscreateprimary -rsa ${rsasize:+${rsasize}} -hi o -pwdk ooo 1>/dev/null; then return 1 fi if ! nv_savetofile 01000002 "${aespub}" \ || ! nv_savetofile 01000003 "${aespriv}" \ || ! nv_savetofile 01000004 "${signature}"; then return 1 fi if ! tssload -hp 80000000 -pwdp ooo -ipu "${aespub}" -ipr "${aespriv}" 1>/dev/null; then return 1 fi if ! tssverifysignature -hk 80000001 -if "${inputfile}" -is "${signature}"; then return 1 fi echo "INFO: Verified HMAC" if ! tssflushcontext -ha 80000000 || ! tssflushcontext -ha 80000001; then return 1 fi # Test with the keys stored in contexts; this only works with save/restore of all state if [ "${is_fullresume}" -ne 0 ]; then if ! nv_savetofile 01000005 "${contextfile}" || \ ! tsscontextload -if "${contextfile}" 1>/dev/null; then return 1 fi if ! nv_savetofile 01000006 "${contextfile}" || \ ! tsscontextload -if "${contextfile}" 1>/dev/null; then return 1 fi if ! tssverifysignature -hk 80000001 -if "${inputfile}" -is "${signature}"; then return 1 fi echo "INFO: Verified HMAC with restored key context" if ! tssflushcontext -ha 80000000 || ! tssflushcontext -ha 80000001; then return 1 fi fi fi # Encryption/Decryption if ! tsscreateprimary -hi o -des 1>/dev/null; then return 1 fi if ! nv_savetofile 01000007 "${encfile}"; then return 1 fi if ! tssencryptdecrypt -hk 80000000 -d -if "${encfile}" -of "${decfile}"; then return 1 fi if [ "$(get_sha1_file "${inputfile}")" != "$(get_sha1_file "${decfile}")" ]; then return 1 fi echo "INFO: Verified decryption" if ! tssflushcontext -ha 80000000; then return 1 fi # Test with the key stored in context; this only works with save/restore of all state if [ "${is_fullresume}" -ne 0 ]; then if ! nv_savetofile 01000008 "${contextfile}" || \ ! tsscontextload -if "${contextfile}" 1>/dev/null; then return 1 fi if ! tssencryptdecrypt -hk 80000000 -d -if "${encfile}" -of "${decfile}"; then return 1 fi if [ "$(get_sha1_file "${inputfile}")" != "$(get_sha1_file "${decfile}")" ]; then return 1 fi echo "INFO: Verified decryption with restored key context" if ! tssflushcontext -ha 80000000; then return 1 fi fi # Test NIST p256 signing key if ! tsscreateprimary -ecc nistp256 -hi o -pwdk ooo -si 1>/dev/null; then return 1 fi if ! nv_savetofile 01000009 "${signature}"; then return 1 fi if ! tssverifysignature -ecc -hk 80000000 -if "${inputfile}" -is "${signature}"; then return 1 fi echo "INFO: Verified signature with RSA key" if ! tssflushcontext -ha 80000000; then return 1 fi # Test with the key stored in context; this only works with save/restore of all state if [ "${is_fullresume}" -ne 0 ]; then if ! nv_savetofile 0100000a "${contextfile}"; then return 1 fi if ! tsscontextload -if "${contextfile}" 1>/dev/null; then return 1 fi if ! tssverifysignature -ecc -hk 80000000 -if "${inputfile}" -is "${signature}"; then return 1 fi echo "INFO: Verified signature with RSA key and restored key context" if ! tssflushcontext -ha 80000000; then return 1 fi fi # Test the audited commands is command was available to set audited commands if [ "${is_fullresume}" -ne 0 ]; then if type -P tsssetcommandcodeauditstatus >/dev/null; then for cc in 11f 12a 133 13a 140 147 150 15c 16e 17a 187 190 197; do if ! tssgetcapability -cap 4 | grep -q 00000${cc}; then echo "Error: Audit not set for command 0000${cc}" tssgetcapability -cap 4 return 1 fi done echo "INFO: Verified audited commands" fi fi return 0 } function build_swtpm() { local srcdir="$1" local cflags="$2" local destdir="$3" local swtpm_git_version="$4" echo -e "\nBuilding swtpm ${swtpm_git_version} with CFLAGS=${cflags} ..." pushd "${srcdir}" &>/dev/null || exit 1 git clean -xdf &>/dev/null git reset --hard HEAD if ! git checkout "${swtpm_git_version}"; then # Travis needs this git remote add upstream "${SWTPM_URL}" git fetch upstream "${swtpm_git_version}" git checkout "upstream/${swtpm_git_version}" fi PKG_CONFIG_PATH="${destdir}" CFLAGS="${cflags}" \ ./autogen.sh --without-cuse --without-selinux --without-seccomp make -j "$(nproc)" || return 1 cp \ src/swtpm/.libs/swtpm src/swtpm/.libs/libswtpm*.so* \ src/swtpm_setup/swtpm_setup \ src/swtpm_ioctl/swtpm_ioctl \ "${destdir}" popd &>/dev/null || exit 1 } # Copy a compiled libtpms version to its destination directory; the destination # directory will be created # @param1: Destination directory's parent # @param2: The version of libtpms, e.g. '0.9' or 'master' function copy_libtpms() { local dest="$1" local version="$2" local destdir="${dest}/libtpms-${version}" mkdir -p "${destdir}" cp libtpms.pc src/.libs/libtpms.so* "${destdir}" return 0 } # Build various versions of libtpms starting with libtpms 0.9 up to 'master' # Build swtpm for each one of these versions as well. function build_libtpms_and_swtpm() { local workdir="$1" local tmp major maj min local libtpmsdir="${workdir}/libtpms" pushd "${workdir}" 2>&1 || return 1 git clone "${LIBTPMS_URL}" libtpms pushd libtpms 2>&1 || return 1 git checkout "${LIBTPMS_INITIAL_BRANCH}" echo -e "\nBuilding libtpms ${INITIAL_BRANCH} ..." CFLAGS="-g -O2" ./autogen.sh --with-tpm2 --without-tpm1 make -j "$(nproc)" || return 1 copy_libtpms "${workdir}" "master" # Determine major version of what master built tmp=$(ls src/.libs/libtpms.so.*.*) major=$(echo "${tmp}" | sed -n 's/.*.so\.\([^\.]\+\)\.\([^\.]\+\)\..*/\1/p') if ! build_swtpm \ "${workdir}/swtpm" \ "-I${libtpmsdir}/include -L${workdir}/libtpms-master" \ "${workdir}/libtpms-master" \ "${SWTPM_DEFAULT_BRANCH}"; then echo "Error: Could not build swtpm" return 1 fi for ((maj=0; maj<=major; maj++)) { min=0 [ "${maj}" = 0 ] && min=9 # start with v0.9 for ((;; min++)) { git checkout "origin/stable-${maj}.${min}" &>/dev/null || break # clean directory containing libtpms.so.* rm -rf src/.libs echo -e "\nBuilding libtpms ${maj}.${min}..." make -j "$(nproc)" || return 1 copy_libtpms "${workdir}" "${maj}.${min}" if ! build_swtpm \ "${workdir}/swtpm" \ "-I${libtpmsdir}/include -L${workdir}/libtpms-${maj}.${min}" \ "${workdir}/libtpms-${maj}.${min}" \ stable-0.9; then echo "Error: Could not build swtpm" return 1 fi } } popd &>/dev/null || return 1 #ls -l libtpms-* #PATH="${workdir}/libtpms-master" LD_LIBRARY_PATH=${PATH} ./libtpms-master/swtpm_setup --help #PATH="${workdir}/libtpms-0.9" LD_LIBRARY_PATH=${PATH} ./libtpms-0.9/swtpm_setup socket --help popd &>/dev/null || return 1 return 0 } # Run swtpm_setup on all versions >= v0.10 of libtpms. The state is written # into directories with the name patterns of state--${branch}--${profile}, # which leads to names like state--0.10--null for null profile and libtpms v0.10 function swtpm_setup_create_profile_state() { local workdir="$1" local branch profiles libtpmsdir statedir err=0 has_option echo -e "INFO: Creating state with various profiles\n" pushd "${workdir}" &>/dev/null || return 1 # Setup swtpm-0.10 and later with null profile and check that swtpm-0.9 works with it for libtpmsdir in libtpms-master libtpms-*.*; do branch=$(echo "${libtpmsdir}" | sed -n 's/.*-//p') export LD_LIBRARY_PATH="${workdir}/${libtpmsdir}" export SWTPM_IOCTL=./${libtpmsdir}/swtpm_ioctl echo has_option=$(PATH="${workdir}/${libtpmsdir}" \ swtpm socket --help | grep -E "\-profile") if [ -n "${has_option}" ]; then # get a space-separated list of profile names profiles=$(PATH="${workdir}/${libtpmsdir}" \ swtpm socket --tpm2 --print-capabilities \ | sed -n \ -e "s/.* \"profiles\": { \"names\": \[ \(.*\) \].*/\1/" \ -e 's/[ "]//g' -e 's/,/ /gp') else profiles="" fi for profile in ${profiles}; do local profileopt="" if [ "${profile}" = "" ]; then profile=null else # shellcheck disable=SC2089 profileopt="--profile {\"Name\":\"${profile}\"}" fi statedir="state--${branch}--${profile}" mkdir "${statedir}" echo PATH="${workdir}/${libtpmsdir}" \ swtpm -v echo "state dir:$statedir, libtpms dir:$libtpmsdir, profile: $profile" if ! PATH="${workdir}/${libtpmsdir}" \ "./${libtpmsdir}/swtpm_setup" \ --tpm2 \ --tpmstate "${statedir}" \ --config "${workdir}/swtpm_setup.conf" \ --log "${statedir}/swtpm_setup.log" \ --tpm "${workdir}/${libtpmsdir}/swtpm socket" \ ${profileopt:+${profileopt}}; then echo "Error: Could not run swtpm_setup with libtpms from branch ${branch} with profile ${profile}" err=1 break fi SWTPM_PID="" if ! SWTPM_SERVER_NO_DISCONNECT=1 \ SWTPM_EXE=./${libtpmsdir}/swtpm \ run_swtpm "${SWTPM_INTERFACE}" \ --tpm2 \ --tpmstate "dir=${workdir}/${statedir}" \ --flags not-need-init,startup-clear; then echo "Error: Could not start swtpm with null profile state and libtpms version '${libtpmsbranch}'." err=1 break fi if ! create_tpm_state "${workdir}"; then echo "Error: Creating TPM-internal state failed" err=1 break fi echo "INFO: Successfully created TPM-internal state" if ! run_swtpm_ioctl "${SWTPM_INTERFACE}" --save permanent "${workdir}/${statedir}/permanent" \ || ! run_swtpm_ioctl "${SWTPM_INTERFACE}" --save volatile "${workdir}/${statedir}/volatile"; then echo "Error: Could not save permanent or volatile state" err=1 break fi echo "INFO: Successfully saved permanent and volatile state to ${workdir}/${statedir}/" if ! check_tpm_state "${workdir}" 1; then echo "Error: Checking TPM-internal state failed" err=1 break fi echo "INFO: Successfully checked proper functioning of TPM-internal state" # Use kill here rather than a shutdown to be able to compare the # size of the state later on. A shutdown would generate a bigger # tpm2-00.permall file that cannot be compare with while the TPM 2 # is running. kill_quiet -9 "${SWTPM_PID}" SWTPM_PID="" echo "INFO: Sizes of state in ${workdir}/${statedir}" ls -l "${workdir}/${statedir}" done if [ -n "${SWTPM_PID}" ]; then kill_quiet -9 "${SWTPM_PID}" fi if [ "${err}" -ne 0 ]; then break fi done unset LD_LIBRARY_PATH popd &>/dev/null || return 1 echo -e "INFO: Done creating state with various profiles\n\n" return "${err}" } # This function tests that the NULL profile state, that was created by a certain # version of libtpms, can be used by swtpm with libtpms v0.9 and others. function swtpm_run_with_null_profile_state() { local workdir="$1" local statedir branch libtpmsbranch resp exp fsize err=0 echo "INFO: Running with NULL profile states using different version of libtpms" pushd "${workdir}" &>/dev/null || return 1 for statedir in state--*--null; do branch=$(echo "${statedir}" | sed -n 's/.*--\(.*\)--.*/\1/p') for libtpmsdir in libtpms-master libtpms-*.*; do libtpmsbranch=$(echo "${libtpmsdir}" | sed -n 's/.*-//p') fsize=$(get_filesize "${workdir}/${statedir}/tpm2-00.permall") export LD_LIBRARY_PATH="${workdir}/${libtpmsdir}" export SWTPM_IOCTL="./${libtpmsdir}/swtpm_ioctl" echo -e "\nINFO: Using swtpm in ${libtpmsdir} with TPM state in ${workdir}/${statedir}" if ! SWTPM_SERVER_NO_DISCONNECT=1 \ SWTPM_EXE=./${libtpmsdir}/swtpm \ run_swtpm "${SWTPM_INTERFACE}" \ --tpm2 \ --tpmstate "dir=${workdir}/${statedir}" \ --flags not-need-init,startup-clear; then echo "Error: Could not start swtpm with null profile state and libtpms version '${libtpmsbranch}'." err=1 break fi # Read PCR 10 exp=' 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ' resp=$(tsspcrread -ha 10 -halg sha256 | grep " 00" | tr -d "\n" | tr -s " ") if [ "${resp}" != "${exp}" ]; then echo "Error: Did not get expected result from TPM2_PCRRead(10)" echo "expected: ${exp}" echo "received: ${resp}" err=1 break fi echo " PCR 10 is good: profile: null libtpms: ${libtpmsbranch} state written by: ${branch}" if [ "$(get_filesize "${workdir}/${statedir}/tpm2-00.permall")" != "${fsize}" ]; then echo "Error: Size of tpm2 state file has changed" echo "expected: ${fsize}" echo "actual : $(get_filesize "${workdir}/${statedir}/tpm2-00.permall")" ls -l "${workdir}/${statedir}/" err=1 break fi if ! check_tpm_state "${workdir}" 0; then echo "Error: Checking TPM-internal state failed: profile: null libtpms: ${libtpmsbranch} state written by: ${branch}" err=1 break fi echo "INFO: Successfully checked proper functioning of TPM-internal state" # restore permanent and volatile state and run tests again if ! run_swtpm_ioctl "${SWTPM_INTERFACE}" --stop \ || ! run_swtpm_ioctl "${SWTPM_INTERFACE}" --load permanent "${workdir}/${statedir}/permanent" \ || ! run_swtpm_ioctl "${SWTPM_INTERFACE}" --load volatile "${workdir}/${statedir}/volatile" \ || ! run_swtpm_ioctl "${SWTPM_INTERFACE}" -i; then echo "Error: Could not load permanent or volatile state" err=1 break fi echo "INFO: Successfully restored permanent and volatile state from ${workdir}/${statedir}/" if ! check_tpm_state "${workdir}" 1; then echo "Error: Checking TPM-internal state failed (after resume): profile: null libtpms: ${libtpmsbranch} state written by: ${branch}" err=1 break fi echo "INFO: Successfully checked proper functioning of TPM-internal state after state resume" kill_quiet -9 "${SWTPM_PID}" echo " INFO: Passed with libtpms ${libtpmsbranch}" done if [ ${err} -ne 0 ]; then kill_quiet -9 "${SWTPM_PID}" break fi done unset LD_LIBRARY_PATH popd &>/dev/null || return 1 echo -e "INFO: Done running with NULL profile states using different version of libtpms\n\n" return "${err}" } # This function tests that a default profile state with 'stateFormatLevel = 2' can be used # and remains at this level. function swtpm_run_with_default_profile_state() { local workdir="$1" local statefile="$2" local libtpmsdir libtpmsbranch output err=0 exp mkdir "${workdir}/tmp" cp "${statefile}" "${workdir}/tmp/" echo -e "\n\nINFO: Testing with previously created default profile state" pushd "${workdir}" &>/dev/null || return 1 for libtpmsdir in libtpms-master libtpms-*.*; do libtpmsbranch=$(echo "${libtpmsdir}" | sed -n 's/.*-//p') [ "${libtpmsbranch}" = "0.9" ] && continue export LD_LIBRARY_PATH="${workdir}/${libtpmsdir}" export SWTPM_IOCTL="./${libtpmsdir}/swtpm_ioctl" if ! SWTPM_EXE="./${libtpmsdir}/swtpm" \ run_swtpm "${SWTPM_INTERFACE}" \ --tpm2 \ --tpmstate "dir=${workdir}/tmp" \ --flags not-need-init,startup-clear; then echo "Error: Could not start swtpm with 'default' profile state and libtpms version '${libtpmsbranch}'." err=1 break fi if ! output=$(run_swtpm_ioctl "${SWTPM_INTERFACE}" --info 0x20); then echo "Error: Could not run swtpm_ioctl: ${output}" err=1 break fi exp=',"StateFormatLevel":2,' if ! echo "${output}" | grep -q "${exp}"; then echo "Error: Could not find '${exp}' in swtpm_ioctl output." echo " output: ${output}" err=1 break fi echo " INFO: Found proper default state in libtpms ${libtpmsbranch}" run_swtpm_ioctl "${SWTPM_INTERFACE}" -s done if [ ${err} -ne 0 ]; then run_swtpm_ioctl "${SWTPM_INTERFACE}" -s fi unset LD_LIBRARY_PATH popd &>/dev/null || return 1 echo -e "INFO: Done testing with previously created default profile state\n\n" rm -rf "${workdir}/tmp/" return "${err}" } if ! build_libtpms_and_swtpm "${workdir}"; then echo "Error: Building libtpms and/or swtpm failed" exit 1 fi if ! swtpm_setup_create_profile_state "${workdir}"; then exit 1 fi if ! swtpm_run_with_null_profile_state "${workdir}"; then exit 1 fi if ! swtpm_run_with_default_profile_state "${workdir}" "${TESTDIR}/data/tpm2state7/tpm2-00.permall"; then exit 1 fi exit 0