| #!/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 |