| #!/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="<none>" |
| fi |
| |
| for profile in ${profiles}; do |
| local profileopt="" |
| |
| if [ "${profile}" = "<none>" ]; 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 |