#!/usr/bin/env bash # For the license, see the LICENSE file in the root directory. ROOT=${abs_top_builddir:-$(dirname "$0")/..} TESTDIR=${abs_top_testdir:-$(dirname "$0")} source "${TESTDIR}/common" skip_test_no_tpm20 "${SWTPM_EXE}" workdir="$(mktemp -d)" || exit 1 export TPM_PATH=${workdir} SWTPM_SERVER_PORT=65480 SWTPM_CTRL_PORT=65481 SWTPM_SERVER_NAME=127.0.0.1 SWTPM_INTERFACE="socket+socket" trap "cleanup" SIGTERM EXIT 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_ function cleanup() { rm -rf "${workdir}" if kill_quiet -0 "${SWTPM_PID}"; then kill_quiet -9 "${SWTPM_PID}" fi } test_swtpm_setup_profile() { local workdir="${1}" local profile="${2}" local exp_response="${3}" # expected 'ActiveProfile' response; optional local disabled_algos="${4}" # disabled algorithms; optional local tpm2request="${5}" # TPM 2 request to send; optional local exp_tpm2response="${6}" # expected response; optional local exp_fail="${7}" # swtpm_setup is expexted to fail when '1' local response rm -f "${workdir}/logfile" if ! $SWTPM_SETUP \ --tpm2 \ --tpmstate "${workdir}" \ --config "${workdir}/swtpm_setup.conf" \ --log "${workdir}/logfile" \ --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ --overwrite \ ${profile:+--profile ${profile}} then if [ "${exp_fail}" -eq 1 ]; then # swtpm_setup failed as expected return 0 fi echo "Test failed: Error: Could not run $SWTPM_SETUP." echo "Setup Logfile:" cat "${workdir}/logfile" exit 1 fi if [ -n "${exp_fail}" ] && [ "${exp_fail}" -eq 1 ]; then echo "Error: swtpm_setup did not fail as expected" exit 1 fi run_swtpm "${SWTPM_INTERFACE}" \ --tpm2 \ --flags not-need-init,startup-clear if ! kill_quiet -0 "${SWTPM_PID}"; then echo "Error: ${SWTPM_INTERFACE} TPM did not start." exit 1 fi if [ -n "${exp_response}" ]; then response=$(run_swtpm_ioctl "${SWTPM_INTERFACE}" --info 0x20) if ! [[ "${response}" =~ ${exp_response} ]]; then echo "Error: Response does not match expected response regular expression" echo "Actual : ${response}" echo "Expected : ${exp_response}" exit 1 fi fi if [ -n "${disabled_algos}" ]; then response=$(run_swtpm_ioctl "${SWTPM_INTERFACE}" --info 0x8 | sed -n 's/.*"Disabled":"\([^"]*\).*/\1/p') if [ "${response}" != "${disabled_algos}" ]; then echo "Error: Response for disabled algorithms does not match expected response" echo "Actual : ${response}" echo "Expected : ${disabled_algos}" exit 1 fi fi if [ -n "${tpm2request}" ]; then response=$(swtpm_cmd_tx "${SWTPM_INTERFACE}" "${tpm2request}") if [ "${response}" != "${exp_tpm2response}" ]; then echo "Error: TPM 2 response does not match expected response" echo "Actual : ${response}" echo "Expected : ${exp_tpm2response}" exit 1 fi fi if ! run_swtpm_ioctl "${SWTPM_INTERFACE}" -s; then echo "Error: Could not shut down the ${SWTPM_INTERFACE} TPM." exit 1 fi if wait_process_gone "${SWTPM_PID}" 4; then echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore." exit 1 fi # Expecting same results from swtpm directly if [ -n "${exp_response}" ]; then response=$(${SWTPM_EXE} socket \ --tpmstate "dir=${workdir}" \ --tpm2 \ --print-info 0x20) if ! [[ "${response}" =~ ${exp_response} ]]; then echo "Error: Response does not match expected response regular expression (swtpm)" echo "Actual : ${response}" echo "Expected : ${exp_response}" exit 1 fi fi } # Get available profiles and algorithms that are implemented and those that can be disabled run_swtpm "${SWTPM_INTERFACE}" \ --tpm2 \ --flags not-need-init if ! kill_quiet -0 "${SWTPM_PID}"; then echo "Error: ${SWTPM_INTERFACE} TPM did not start." exit 1 fi profiles=$(run_swtpm_ioctl "${SWTPM_INTERFACE}" --info 0x40 | sed $'s/{"Name/\\\n{"Name/g' | sed -n 's/^{"Name":"\([^"]*\)".*/\1/p') canbedisabled_algos_str=$(run_swtpm_ioctl "${SWTPM_INTERFACE}" --info 0x8 | sed -n 's/.*"CanBeDisabled":"\([^"]*\).*/\1/p') implemented_algos_str=$(run_swtpm_ioctl "${SWTPM_INTERFACE}" --info 0x8 | sed -n 's/.*"Implemented":"\([^"]*\).*/\1/p') if ! run_swtpm_ioctl "${SWTPM_INTERFACE}" -s; then echo "Error: Could not shut down the ${SWTPM_INTERFACE} TPM." exit 1 fi if wait_process_gone "${SWTPM_PID}" 4; then echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore." exit 1 fi # Test with no profile exp_response=$(echo "^\{\"ActiveProfile\":\{" \ "\"Name\":\"null\"," \ "\"StateFormatLevel\":[0-9]+,"\ "\"Commands\":\"[,x[:xdigit:]-]+\","\ "\"Algorithms\":\"[,=[:alnum:]-]+\","\ "\"Description\":\"[[:print:]]+\""\ "\}\}\$"| tr -d " ") test_swtpm_setup_profile \ "${workdir}" "" "${exp_response}" \ "" "" "" "" echo "Test without profile passed" # Test-setup each profile that is implemented for profile in ${profiles}; do exp_response=$(echo "^\{\"ActiveProfile\":\{" \ "\"Name\":\"${profile}\"," \ "\"StateFormatLevel\":[0-9]+,"\ "\"Commands\":\"[,x[:xdigit:]-]+\","\ "\"Algorithms\":\"[,=[:alnum:]-]+\","\ "(\"Attributes\":\"[,=[:alnum:]-]+\",)?"\ "\"Description\":\"[[:print:]]+\""\ "\}\}\$"| tr -d " ") test_swtpm_setup_profile \ "${workdir}" "{\"Name\":\"${profile}\"}" "${exp_response}" "" "" "" "" echo "Test with profile '${profile}' passed" done # Setup the custom profile with an increasing set of disabled algorithms # that swtpm_ioctl has to show then. IFS="," read -r -a canbedisabled_algos <<< "${canbedisabled_algos_str}" IFS="," read -r -a implemented_algos <<< "${implemented_algos_str}" algos="${implemented_algos[*]}" disabled=() for todisable in "${canbedisabled_algos[@]}"; do # Remove algorithm ${todisable} from list of implemented algorithms algos=$(echo "${algos}" | sed "s/${todisable}//" | tr -s ' ') disabled+=("${todisable}") # Format profile a=$(echo "${algos}" | sed -e 's/ /,/g' -e 's/,$//') profile="{\"Name\":\"custom\",\"Algorithms\":\"${a}\"}" d=${disabled[*]} d=${d// /,} test_swtpm_setup_profile \ "${workdir}" "${profile}" "" "${d}" "" "" "" echo "Test with custom profile and disabled algorithms '${d}' passed" done # Test the custom profiles with disabled commands and various StateFormatLevels test_custom_profile_sfls() { local cmds="$1" local sfls_list="$2" local msg="$3" local sfl exp_sfl exp_fail profile exp_response for sfls in ${sfls_list}; do sfl=$(cut -d":" -f1 <<< "${sfls}") exp_sfl=$(cut -d":" -f2 <<< "${sfls}") exp_fail=$(cut -d":" -f3 <<< "${sfls}") profile="{\"Name\":\"custom:test-sfl-${sfl}\",\"Commands\":\"${cmds}\",\"StateFormatLevel\":${sfl}}" exp_response=".*,\"StateFormatLevel\":${exp_sfl},.*" test_swtpm_setup_profile \ "${workdir}" "${profile}" "${exp_response}" "" "" "" "${exp_fail}" echo "Test with custom profile and commands${msg} passed with StateFormatLevel ${sfl}." done } # because of 0x199-0x19a we must have StateFormatLevel >=3 for it to be acceptable cmds="0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x199-0x19a" test_custom_profile_sfls "${cmds}" "1:f:1 2:f:1 3:3:0 4:4:0 5:5:0 6:6:0 7:7:0" " (with 0x199-0x19a)" # because of 0x19b we must have StateFormatLevel >=5 for it to be acceptable cmds="0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x199-0x19a,0x19b" test_custom_profile_sfls "${cmds}" "1:f:1 2:f:1 3:f:1 4:f:1 5:5:0 6:6:0 7:7:0" " (with 0x199-0x19b)" # Without given StateFormatLevel it should go up to StateFormatLevel '4' (due to AES-192 being selected) profile="{\"Name\":\"custom\",\"Commands\":\"${cmds}\"}" exp_response=".*,\"StateFormatLevel\":[[:digit:]]+,.*" test_swtpm_setup_profile \ "${workdir}" "${profile}" "${exp_response}" "" "" "" "0" # with cmds 0x199-0x19a missing it can have many StateFormatLevels >= 2 cmds="0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193" # StateFormatLevel 3 enables 'nothing' additional but will be kept # StateFormatLevel 4 enables AES-192 and will remain at '4' test_custom_profile_sfls "${cmds}" "1:f:1 2:2:0 3:3:0 4:4:0 5:5:0 6:6:0 7:7:0" "" # Without given StateFormatLevel it should go up to StateFormatLevel '4' (due to AES-192 being selected) profile="{\"Name\":\"custom\",\"Commands\":\"${cmds}\"}" exp_response=".*,\"StateFormatLevel\":[[:digit:]]+,.*" test_swtpm_setup_profile \ "${workdir}" "${profile}" "${exp_response}" "" "" "" "${exp_fail}" # Setup the TPM 2 with custom profile and sha1 removed # Check that the GetCapability command returns the proper set of PCR banks algos="${implemented_algos[*]}" algos=$(echo "${algos}" | sed "s/sha1//" | tr -s ' ') # Format profile a=$(echo "${algos}" | sed -e 's/ /,/g' -e 's/,$//') profile="{\"Name\":\"custom:no-sha1\",\"Algorithms\":\"${a}\"}" # Check PCR bank sha1 is not available: tssgetcapability -cap 5 test_swtpm_setup_profile \ "${workdir}" \ "${profile}" \ "" \ "" \ "\x80\x01\x00\x00\x00\x16\x00\x00\x01\x7a\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x40" \ " 80 01 00 00 00 25 00 00 00 00 00 00 00 00 05 00 00 00 03 00 0b 03 ff ff ff 00 0c 03 00 00 00 00 0d 03 00 00 00" \ "" echo "Test with custom profile and 'sha1' disabled algorithms passed" # Check that swtpm adjusts key sizes and sets 'Attributes' when # '--profile-remove-disabled fips-host' is passed profile="{\"Name\":\"custom\",\"Description\":\"Test\"} --profile-remove-disabled fips-host" exp_response="\{\"ActiveProfile\":\{\"Name\":\"custom\",.*,\"Algorithms\":\".*,rsa-min-size=2048,.*,ecc-min-size=224,.*\",\"Attributes\":\"no-sha1-signing,no-sha1-verification,no-unpadded-encryption\",\"Description\":\"Test\"\}\}" test_swtpm_setup_profile "${workdir}" "${profile}" "${exp_response}" "" "" "" "0" profile="{\"Name\":\"custom\",\"Attributes\":\"no-sha1-signing\",\"Description\":\"Test\"} --profile-remove-disabled fips-host" test_swtpm_setup_profile "${workdir}" "${profile}" "${exp_response}" "" "" "" "0" profile="{\"Name\":\"custom:test\",\"Attributes\":\"fips-host\",\"Description\":\"Test\"} --profile-remove-disabled fips-host" exp_response="\{\"ActiveProfile\":\{\"Name\":\"custom:test\",.*,\"Algorithms\":\".*,rsa-min-size=2048,.*,ecc-min-size=224,.*\",\"Attributes\":\"fips-host\",\"Description\":\"Test\"\}\}" test_swtpm_setup_profile "${workdir}" "${profile}" "${exp_response}" "" "" "" "0" # --profile-remove-disabled must not have any effect on other profiles for p in null default-v1; do profile="{\"Name\":\"${p}\"} --profile-remove-disabled fips-host" exp_response="\{\"ActiveProfile\":\{\"Name\":\"${p}\",.*,\"Algorithms\":\".*,rsa-min-size=1024,.*,ecc-min-size=192,.*\",.*\}\}" test_swtpm_setup_profile "${workdir}" "${profile}" "${exp_response}" "" "" "" "0" done echo "Tests with custom profile and --profile-remove-disabled option passed" # Copy TPM 2 state written by libtpms v0.9 and have the TPM read and re-write the state. # The size of the state file must not change since we don't want the profile to be # written into it. cp "${TESTDIR}/data/tpm2state6/tpm2-00.permall" "${workdir}" before=$(get_filesize "${workdir}/tpm2-00.permall") # Avoid swptm sending TPM2_Shutdown(SU_STATE) and adding savestate to the state file rm -f "${workdir}/logfile" run_swtpm "${SWTPM_INTERFACE}" \ --tpm2 \ --flags not-need-init,startup-clear,disable-auto-shutdown \ --log "file=${workdir}/logfile" if ! kill_quiet -0 "${SWTPM_PID}"; then echo "Error: ${SWTPM_INTERFACE} TPM did not start." exit 1 fi # Run ChangeEPS so the state is re-written: tsschangeeps cmd='\x80\x02\x00\x00\x00\x1b\x00\x00\x01\x24\x40\x00\x00\x0c\x00\x00\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00' RES=$(swtpm_cmd_tx "${SWTPM_INTERFACE}" "${cmd}") exp=' 80 02 00 00 00 13 00 00 00 00 00 00 00 00 00 00 01 00 00' if [ "$RES" != "$exp" ]; then echo "Error: Did not get expected result from TPM2_ChangeEPS" echo "expected: $exp" echo "received: $RES" exit 1 fi if ! run_swtpm_ioctl "${SWTPM_INTERFACE}" -s; then echo "Error: Could not shut down the ${SWTPM_INTERFACE} TPM." exit 1 fi if wait_process_gone "${SWTPM_PID}" 4; then echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore." exit 1 fi after=$(get_filesize "${workdir}/tpm2-00.permall") if [ "${after}" -ne "${before}" ]; then echo "Error: The size of the TPM 2 state file has changed" echo "Actual : ${after}" echo "Expected : ${before}" exit 1 fi echo "Test with state written by libtpms v0.9 passed" # Check that swtpm emitted log message with OPENSSL_ENABLE_SHA1_SIGNATURES=1 on # RHEL and CentOS; libtpms 0.9 supported SHA1 signatures case $(get_distro_name) in RHEL) [ "$(get_rhel_version)" -ge 904 ] && exp=1;; CentOS) [ "$(get_centos_version)" -ge 900 ] && exp=1;; *) exp=0;; esac if [ "${exp}" -eq 1 ]; then if ! grep -q OPENSSL_ENABLE_SHA1_SIGNATURES "${workdir}/logfile"; then echo "Missing reference to OPENSSL_ENABLE_SHA1_SIGNATURES in logfile" exit 1 fi echo "Test checking for reference to OPENSSL_ENABLE_SHA1_SIGNATURES in logfile passed" fi # If the user passes the null profile in then libtpms has to write state # at the level of libtpms v0.9 and the size of the state file has to be # the same as the one created with libtpms v0.9 rm -f "${workdir}"/tpm2-00.* if ! $SWTPM_SETUP \ --tpm2 \ --tpmstate "${workdir}" \ --config "${workdir}/swtpm_setup.conf" \ --log "${workdir}/logfile" \ --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ --profile-file <(echo '{"Name":"null"}') then echo "Test failed: Error: Could not run $SWTPM_SETUP and set null-profile." echo "Setup Logfile:" cat "${workdir}/logfile" exit 1 fi after=$(get_filesize "${workdir}/tpm2-00.permall") if [ "${after}" -ne "${before}" ]; then echo "Error: The size of the TPM 2 state file is different than if created by libtpms v0.9" echo "Actual : ${after}" echo "Expected : ${before}" exit 1 fi echo "Test of writing state at the level of libtpms v0.9 passed" # Test enablement of PCR banks rm -f "${workdir}/logfile" algorithms="rsa,rsa-min-size=1024,hmac,aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,null,rsassa,rsapss,oaep,ecdsa,kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-nist,symcipher,cmac,ctr,ofb,cbc,cfb,ecb,ecdh" profile="{\"Name\":\"custom\",\"Algorithms\":\"${algorithms}\"}" if ! $SWTPM_SETUP \ --tpm2 \ --tpmstate "${workdir}" \ --config "${workdir}/swtpm_setup.conf" \ --log "${workdir}/logfile" \ --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ --profile "${profile}" \ --pcr-banks sha256 \ --overwrite; then echo "Error: swtpm_setup failed to run:" cat "${workdir}/logfile" exit 1 fi if ! grep -q "PCR banks sha256 among sha256,sha384." "${workdir}/logfile"; then echo "Error: Did not get expected output from activation of SHA256 PCR bank." cat "${workdir}/logfile" exit 1 fi # sha512 enablement must fail rm -f "${workdir}/logfile" if $SWTPM_SETUP \ --tpm2 \ --tpmstate "${workdir}" \ --config "${workdir}/swtpm_setup.conf" \ --log "${workdir}/logfile" \ --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ --profile "${profile}" \ --pcr-banks sha512 \ --overwrite; then echo "Error: Enablement of SHA512 bank should have failed." cat "${workdir}/logfile" exit 1 fi echo "Test of activation of sha256 bank when only sha256 & sha384 banks are available passed" exit 0