mirror of
https://github.com/stefanberger/swtpm.git
synced 2025-08-22 19:04:35 +00:00

Skip the test_tpm2_libtpms_versions_profiles since it requires that swtpm is built from a git checkout so that various versions of swtpm can be built. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
940 lines
25 KiB
Bash
Executable File
940 lines
25 KiB
Bash
Executable File
#!/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
|