From 9a42e99690b08eb95f2f68b6114adb3b7689acdc Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 18 Aug 2023 17:27:31 +0200 Subject: [PATCH] Add vTPM basic tests This change adds tests for vtpm functionalities. The tests require XCP-ng 8.3 and a Debian UEFI VM. The first test creates and destroys a vtpm device. A second test does basic TPM tests (like message signing) using tpm2-tools. The second test uses a locally defined fixture called unix_vm_with_vtpm requiring an halted UEFI Unix VM. The fixture creates a vtpm device associated to the vm. The vtpm device is destroyed once the tests are done. The fixture also does a snapshot of the VM and revert it afterward. This is because the script needs the package tpm2-tools to be installed. Signed-off-by: Thierry Escande --- tests/vtpm/__init__.py | 0 tests/vtpm/conftest.py | 28 ++++++ tests/vtpm/test_vtpm_basic_operations.py | 105 +++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 tests/vtpm/__init__.py create mode 100644 tests/vtpm/conftest.py create mode 100644 tests/vtpm/test_vtpm_basic_operations.py diff --git a/tests/vtpm/__init__.py b/tests/vtpm/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/vtpm/conftest.py b/tests/vtpm/conftest.py new file mode 100644 index 000000000..a2c33af06 --- /dev/null +++ b/tests/vtpm/conftest.py @@ -0,0 +1,28 @@ +import pytest + +@pytest.fixture(scope='module') +def uefi_unix_halted_vm(uefi_vm, unix_vm, halted_vm): + yield uefi_vm + +@pytest.fixture(scope='module') +def unix_vm_with_vtpm(uefi_unix_halted_vm): + vm = uefi_unix_halted_vm + vtpm_uuid = None + + if vm.get_vtpm() is None: + vtpm_uuid = vm.create_vtpm() + + snapshot = vm.snapshot() + + vm.start() + + yield vm + + # Tear down + if not vm.is_halted(): + vm.shutdown(verify=True) + + snapshot.destroy() + + if vtpm_uuid is not None: + vm.destroy_vtpm() diff --git a/tests/vtpm/test_vtpm_basic_operations.py b/tests/vtpm/test_vtpm_basic_operations.py new file mode 100644 index 000000000..54d013f97 --- /dev/null +++ b/tests/vtpm/test_vtpm_basic_operations.py @@ -0,0 +1,105 @@ +import logging +import pytest +import lib.commands as commands + +# These test are basic tests for vTPM devices. +# - Create / Destroy a vTPM device against a VM +# - Do some basic encryption tests using tpm2-tools package +# +# Requirements: +# - an XCP-ng host >= 8.3 +# - a Debian based UEFI VM + +from lib.common import PackageManagerEnum + +vtpm_signing_test_script = """#!/bin/env bash + +set -ex + +apt-get install -y tpm2-tools > /dev/null + +tpm2_selftest --fulltest + +tpm2_getrandom 32 > /dev/null + +TMPDIR=`mktemp -d` + +# Create an Endorsement primary key +tpm2_createprimary --hierarchy e --key-context ${TMPDIR}/primary.ctx > /dev/null + +# Create key objects +tpm2_create --key-algorithm rsa --public ${TMPDIR}/rsa.pub --private ${TMPDIR}/rsa.priv --parent-context \ + ${TMPDIR}/primary.ctx > /dev/null + +# Load keys into the TPM +tpm2_load --parent-context ${TMPDIR}/primary.ctx --public ${TMPDIR}/rsa.pub --private ${TMPDIR}/rsa.priv \ + --key-context ${TMPDIR}/rsa.ctx > /dev/null + +# Delete loaded key files +rm -f ${TMPDIR}/rsa.pub ${TMPDIR}/rsa.priv + +# Message to sign +echo 'XCP-ng Rulez' > ${TMPDIR}/message.dat + +# Sign the message +tpm2_sign --key-context ${TMPDIR}/rsa.ctx --hash-algorithm sha256 --signature ${TMPDIR}/sig.rssa \ + ${TMPDIR}/message.dat > /dev/null + +# Verify signature +tpm2_verifysignature --key-context ${TMPDIR}/rsa.ctx --hash-algorithm sha256 --message ${TMPDIR}/message.dat \ + --signature ${TMPDIR}/sig.rssa > /dev/null + +# Verify with another message +echo "XCP-ng Still Rulez" > ${TMPDIR}/message.dat + +# Verify signature !!!!! THIS MUST FAIL !!!!! +if tpm2_verifysignature --key-context ${TMPDIR}/rsa.ctx --hash-algorithm sha256 --message ${TMPDIR}/message.dat \ + --signature ${TMPDIR}/sig.rssa > /dev/null 2>&1; then + echo "Should not succeed" + exit 1 +fi + +rm -rf ${TMPDIR} +""" + +@pytest.mark.small_vm +@pytest.mark.usefixtures("host_at_least_8_3") +def test_create_and_destroy_vtpm(uefi_unix_halted_vm): + vm = uefi_unix_halted_vm + + vtpm_uuid = vm.get_vtpm() + if vtpm_uuid is not None: + logging.warning("This vm is already associated with a vtpm. Destroying it first") + vm.destroy_vtpm() + + vtpm_uuid = vm.create_vtpm() + + assert vtpm_uuid is not None + + logging.info("vtpm created with uuid: %s" % vtpm_uuid) + + vm.destroy_vtpm() + +@pytest.mark.small_vm +@pytest.mark.usefixtures("host_at_least_8_3") +def test_vtpm(unix_vm_with_vtpm): + global vtpm_signing_test_script + vm = unix_vm_with_vtpm + + # unix_vm_with_vtpm fixture starts the VM and stops it at teardown + # No need to call vm.start() here + + # Also used to retreive the VM IP address + vm.wait_for_os_booted() + + pkg_mgr = vm.detect_package_manager() + if pkg_mgr != PackageManagerEnum.APT_GET: + pytest.fail("This test requires a Debian based VM") + + # Basic TPM2 tests with tpm2-tools + try: + logging.info("Running TPM2 test script on the VM") + out = vm.execute_script(vtpm_signing_test_script) + logging.debug("*****\n%s\n*****" % out) + except commands.SSHCommandFailed as e: + pytest.fail("%s" % str(e))