#!/bin/bash -x # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: # # Copyright (c) 2019 Red Hat, Inc. # Author: Sergio Correia # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # TEST=$(basename "${0}") # Code to return to mark test as skipped. SKIP_RET_CODE=77 tpm2_available() { # Old environment variables for tpm2-tools 3.0 export TPM2TOOLS_TCTI_NAME=device export TPM2TOOLS_DEVICE_FILE= for dev in /dev/tpmrm?; do [ -e "${dev}" ] || continue TPM2TOOLS_DEVICE_FILE="${dev}" break done # New environment variable for tpm2-tools >= 3.1 export TPM2TOOLS_TCTI="${TPM2TOOLS_TCTI_NAME}:${TPM2TOOLS_DEVICE_FILE}" if [ -z "${TPM2TOOLS_DEVICE_FILE}" ]; then echo "A TPM2 device with the in-kernel resource manager is needed!" >&2 return 1 fi if ! [[ -r "${TPM2TOOLS_DEVICE_FILE}" \ && -w "${TPM2TOOLS_DEVICE_FILE}" ]]; then echo "The ${TPM2TOOLS_DEVICE_FILE} device must be readable and writable!" >&2 return 1 fi local _tpm2tools_info="$(tpm2_createprimary -v)" local _match='version="(.)\.' [[ ${_tpm2tools_info} =~ ${_match} ]] && TPM2TOOLS_VERSION="${BASH_REMATCH[1]}" if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then echo "The tpm2 pin requires a tpm2-tools version between 3 and 5" >&2 return 1 fi export TPM2TOOLS_VERSION } validate_pcrs() { local _pcr_bank="${1}" local _pcrs="${2}" [ -z "${_pcr_bank}" ] && return 1 [ -z "${_pcrs}" ] && return 0 local _fail= local _pcrs_r= case "${TPM2TOOLS_VERSION}" in 3) _pcrs_r="$(tpm2_pcrlist -L "${_pcr_bank}":"${_pcrs}" | grep -v "^${_pcr_bank}")" || _fail=$?;; 4|5) _pcrs_r=$(tpm2_pcrread "${_pcr_bank}":"${_pcrs}" | grep -v " ${_pcr_bank}") || _fail=$?;; *) _fail=1 esac if [ -n "${_fail}" ] || [ -z "${_pcrs_r}" ]; then return 1 fi return 0 } # Checking if we can run this test. if ! tpm2_available; then exit ${SKIP_RET_CODE} fi decode_jwe() { local jwe="${1}" local coded if ! coded=$(jose jwe fmt -i- <<< "${jwe}"); then return 1 fi coded=$(jose fmt -j- -g protected -u- <<< "${coded}" | tr -d '"') jose b64 dec -i- <<< "${coded}" } test_pcr_ids() { local orig="${1}" local cfg="${2}" local expected_pcr_ids="${3}" local enc if ! enc=$(echo "${orig}" | clevis encrypt tpm2 "${cfg}"); then echo "${TEST}: encrypt failed for cfg: ${cfg}" >&1 return 1 fi local pcr_ids pcr_ids=$(decode_jwe "${enc}" \ | jose fmt -j- -Og clevis -Og tpm2 -Og pcr_ids -u- 2>/dev/null) local dec dec=$(echo "${enc}" | clevis decrypt) if [ "${orig}" != "${dec}" ]; then echo "${TEST}: decoded text (${dec}) does not match original one (${orig})" >&2 return 1 fi if [ "${pcr_ids}" != "${expected_pcr_ids}" ]; then echo "${TEST}: pcr_ids (${pcr_ids}) do not match the expected (${expected_pcr_ids}) result." >&2 return 1 fi } test_enc_dec() { local cfg="${1}" output=$(echo Working | clevis encrypt tpm2 "${cfg}" | clevis decrypt) if [ "$output" != "Working" ]; then echo "Output after decrypting doesn't match: ${output} != 'Working'" return 1 fi } test_enc_dec '{}' || exit 1 test_pcr_ids "${orig}" '{}' "" || exit 1 test_pcr_ids "${orig}" '{ }' "" || exit 1 test_pcr_ids "${orig}" '{"key": "ecc"}' "" || exit 1 # Issue #103: now let's try a few different configs with both strings and # arrays and check if we get the expected pcr_ids. # Let's first make sure this would be a valid configuration. _default_pcr_bank="sha1" if validate_pcrs "${_default_pcr_bank}" "4,16"; then test_pcr_ids "${orig}" '{"pcr_ids": "16"}' "16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": ["16"]}' "16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": "4, 16"}' "4,16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": "4,16"}' "4,16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": ["4,16"]}' "4,16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": [4,16]}' "4,16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": [4, 16]}' "4,16" || exit 1 test_pcr_ids "${orig}" '{"pcr_ids": ["4","16"]}' "4,16" || exit 1 ! test_pcr_ids "${orig}" '{"pcr_ids": ["4","16"]}' "foo bar" || exit 1 else echo "Skipping tests related to issue#103 because the combination of pcr_bank and PCRs is invalid" >&2 fi # Test with policies if we have the PIN rewrite available if ! $(which clevis-pin-tpm2 >/dev/null 2>&1); then echo "No PIN rewrite available" exit 0 fi if ! $(which clevis-pin-tpm2-signtool >/dev/null 2>&1); then echo "No policy signtool available" exit 0 fi function on_exit() { popd if [ ! -d "$TMP" ] || ! rm -rf "$TMP"; then echo "Delete temporary files failed!" >&2 echo "You need to clean up: $TMP" >&2 exit 1 fi } if ! TMP="$(mktemp -d)"; then echo "Creating a temporary dir for TPM files failed!" >&2 exit 1 fi trap 'on_exit' EXIT pushd $TMP clevis-pin-tpm2-signtool >policy_working.json << EOP --- - policy_ref: steps: - PCRs: hash_algorithm: sha256 selection: - pcr_id: 21 value: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - pcr_id: 22 value: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" EOP clevis-pin-tpm2-signtool >policy_broken.json << EOP --- - policy_ref: steps: - PCRs: hash_algorithm: sha256 selection: - pcr_id: 21 value: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" - pcr_id: 22 value: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" EOP test_enc_dec '{"policy_pubkey_path":"./publickey.json", "policy_ref": "", "policy_path": "./policy_working.json"}' || exit 1 ! test_enc_dec '{"policy_pubkey_path":"./publickey.json", "policy_ref": "", "policy_path": "./policy_broken.json"}' || exit 1