Skip to content

Commit

Permalink
Add vTPM basic tests
Browse files Browse the repository at this point in the history
This change adds tests for vTPM functionalities requiring XCP-ng 8.3 and
a Unix UEFI VM.

The first test creates and destroys a vtpm device. A second test does
basic TPM tests (like message signing) using tpm2-tools.

Nested fixtures are defined for the second test, starting a snapshotted
UEFI VM with a vTPM device attached.

Signed-off-by: Thierry Escande <[email protected]>
  • Loading branch information
tescande committed Sep 13, 2023
1 parent b80a02a commit ccc2dc2
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 0 deletions.
14 changes: 14 additions & 0 deletions jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,20 @@
"params": {},
"paths": ["tests/xen"],
},
"vtpm": {
"description": "Testing vTPM functionalities",
"requirements": [
"A XCP-ng host >= 8.3 and a Unix RPM-based or DEB-based UEFI VM with "
"tpm2-tools installable from default repositories.",
],
"nb_pools": 1,
"params": {
# The test also works on CentOS, for example, but in this job definition
# we settle for a debian VM
"--vm": "single/debian_uefi_vm",
},
"paths": ["tests/vtpm"],
},
"flaky": {
"description": "tests that usually pass, but sometimes fail unexpectedly",
"requirements": [
Expand Down
Empty file added tests/vtpm/__init__.py
Empty file.
62 changes: 62 additions & 0 deletions tests/vtpm/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import pytest
import logging

from lib.common import PackageManagerEnum

@pytest.fixture(scope='module')
def halted_uefi_unix_vm(uefi_vm, unix_vm):
assert uefi_vm.is_halted(), "The VM must be halted for these tests"
yield uefi_vm

@pytest.fixture(scope='module')
def snapshotted_halted_uefi_unix_vm(halted_uefi_unix_vm):
vm = halted_uefi_unix_vm
snapshot = vm.snapshot()

yield vm

try:
snapshot.revert()
finally:
snapshot.destroy()

@pytest.fixture(scope='module')
def unix_vm_with_vtpm(snapshotted_halted_uefi_unix_vm):
vm = snapshotted_halted_uefi_unix_vm

vm.create_vtpm()
yield vm
# Tear down
vm.destroy_vtpm()

@pytest.fixture(scope='module')
def started_unix_vm_with_vtpm(unix_vm_with_vtpm):
vm = unix_vm_with_vtpm

try:
vm.start()
vm.wait_for_os_booted()
except Exception:
vm.shutdown(force=True, verify=True)
raise Exception(f"Failed to start VM")

yield vm
# Tear down
vm.shutdown(verify=True)

@pytest.fixture(scope='module')
def unix_vm_with_tpm2_tools(started_unix_vm_with_vtpm):
vm = started_unix_vm_with_vtpm

pkg_mgr = vm.detect_package_manager()
if pkg_mgr == PackageManagerEnum.APT_GET:
pkg_mgr = 'apt-get'
elif pkg_mgr == PackageManagerEnum.RPM:
pkg_mgr = 'yum'
else:
pytest.fail("Unsupported package manager for this test. Cannot install tpm2-tools")

logging.info("Installing tpm2-tools package using '%s'" % pkg_mgr)
vm.ssh([pkg_mgr, 'install', '-y', 'tpm2-tools'])

yield vm
81 changes: 81 additions & 0 deletions tests/vtpm/test_vtpm_basic_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import logging
import pytest
import lib.commands as commands

# These tests are basic tests for vTPM devices.
# - Create / Destroy a vTPM device on a VM
# - Do some basic encryption tests using tpm2-tools package
#
# Requirements:
# - an XCP-ng host >= 8.3
# - a RPM-based or DEB-based UEFI VM with `tpm2-tools` installable
# from default repositories

vtpm_signing_test_script = """#!/bin/env bash
set -ex
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(halted_uefi_unix_vm):
vm = halted_uefi_unix_vm
assert not vm.get_vtpm_uuid(), "there must be no vTPM before we create it"
vtpm_uuid = vm.create_vtpm()
assert vtpm_uuid
assert vm.get_vtpm_uuid(), "a vTPM must be present after creation"
logging.info("vTPM created with uuid: %s" % vtpm_uuid)
vm.destroy_vtpm()
assert not vm.get_vtpm_uuid(), "there must be no vTPM after we deleted it"

@pytest.mark.small_vm
@pytest.mark.usefixtures("host_at_least_8_3")
def test_vtpm(unix_vm_with_tpm2_tools):
global vtpm_signing_test_script
vm = unix_vm_with_tpm2_tools
# Basic TPM2 tests with tpm2-tools
logging.info("Running TPM2 test script on the VM")
vm.execute_script(vtpm_signing_test_script)
2 changes: 2 additions & 0 deletions vm_data.py-dist
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ VMS = {
"small_vm_efitools": "",
# "small" Windows VM (UEFI)
"small_vm_windows": "",
# Debian VM (UEFI, no GUI)
"debian_uefi_vm": "",
},
"multi": {
# all VMs we want to run "multi_vms" tests on
Expand Down

0 comments on commit ccc2dc2

Please sign in to comment.