Skip to content

Commit

Permalink
switch to swtp for running TPM tests
Browse files Browse the repository at this point in the history
Signed-off-by: Shahriyar Jalayeri <shahriyar@zededa.com>
  • Loading branch information
shjala authored and eriknordmark committed Nov 27, 2023
1 parent 3475068 commit 9589a8e
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 141 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ jobs:
run: |
make test
- name : Test (TPM Required)
timeout-minutes: 30
run: |
bash tools/tpm-tests-on-qemu.sh
bash tests/tpm/prep-and-test.sh
- name: Report test results as Annotations
if: ${{ always() }}
uses: guyarb/golang-test-annoations@v0.6.0
Expand Down
61 changes: 33 additions & 28 deletions pkg/pillar/evetpm/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ import (
)

const (
//TpmDevicePath is the TPM device file path
TpmDevicePath = "/dev/tpmrm0"

//TpmPasswdHdl is the well known TPM NVIndex for TPM Credentials
TpmPasswdHdl tpmutil.Handle = 0x1600000

Expand Down Expand Up @@ -72,25 +69,6 @@ const (
//EmptyPassword is an empty string
EmptyPassword = ""
vaultKeyLength = 32 //Bytes

// savedSealingPcrsFile is the file that holds a copy of PCR values
// at the time of generating and sealing the disk key into the TPM.
savedSealingPcrsFile = types.PersistStatusDir + "/sealingpcrs"

// measurementLogSealSuccess is files that holds a copy of event log at the time
// of generating/sealing the disk key into the TPM.
measurementLogSealSuccess = types.PersistStatusDir + "/tpm_measurement_seal_success"

// measurementLogUnsealFail is files that holds a copy of event log at the time EVE
// fails to unseal the vault key from TPM.
measurementLogUnsealFail = types.PersistStatusDir + "/tpm_measurement_unseal_fail"

// measurefsTpmEventLog is the file containing the event log from the measure-config
measurefsTpmEventLog = types.PersistStatusDir + "/measurefs_tpm_event_log"

// measurementLogFile is a kernel exposed variable that contains the
// TPM measurements and events log.
measurementLogFile = "/hostfs/sys/kernel/security/tpm0/binary_bios_measurements"
)

// PCRBank256Status stores info about support for
Expand All @@ -114,6 +92,33 @@ var (

//DiskKeySealingPCRs represents PCRs that we use for sealing
DiskKeySealingPCRs = tpm2.PCRSelection{Hash: tpm2.AlgSHA256, PCRs: []int{0, 1, 2, 3, 4, 6, 7, 8, 9, 13, 14}}

// TpmDevicePath is the TPM device file path, it is not a constant due to
// test usage.
TpmDevicePath = "/dev/tpmrm0"

// measurementLogFile is a kernel exposed variable that contains the
// TPM measurements and events log. it is not a constant due to test usage.
measurementLogFile = "/hostfs/sys/kernel/security/tpm0/binary_bios_measurements"

// savedSealingPcrsFile is the file that holds a copy of PCR values at the
// time of generating and sealing the disk key into the TPM. it is not a
// constant due to test usage.
savedSealingPcrsFile = types.PersistStatusDir + "/sealingpcrs"

// measurementLogSealSuccess is files that holds a copy of event log at the
// time of generating/sealing the disk key into the TPM. it is not a constant
// due to test usage.
measurementLogSealSuccess = types.PersistStatusDir + "/tpm_measurement_seal_success"

// measurementLogUnsealFail is files that holds a copy of event log at the
// time EVE fails to unseal the vault key from TPM. it is not a constant due
// to test usage.
measurementLogUnsealFail = types.PersistStatusDir + "/tpm_measurement_unseal_fail"

// measurefsTpmEventLog is the file containing the event log from the measure-config.
// it is not a constant due to test usage.
measurefsTpmEventLog = types.PersistStatusDir + "/measurefs_tpm_event_log"
)

// SealedKeyType holds different types of sealed key
Expand Down Expand Up @@ -641,7 +646,7 @@ func SealDiskKey(log *base.LogObject, key []byte, pcrSel tpm2.PCRSelection) erro
}

// save a snapshot of current PCR values
if err := saveDiskKeySealingPCRs(savedSealingPcrsFile); err != nil {
if err := saveDiskKeySealingPCRs(); err != nil {
log.Warnf("saving snapshot of sealing PCRs failed: %s", err)
}

Expand Down Expand Up @@ -729,7 +734,7 @@ func UnsealDiskKey(pcrSel tpm2.PCRSelection) ([]byte, error) {
}

// try to find out the mismatching PCR index
mismatch, errPcrMiss := findMismatchingPCRs(savedSealingPcrsFile)
mismatch, errPcrMiss := findMismatchingPCRs()
if errPcrMiss != nil {
return nil, fmt.Errorf("UnsealWithSession failed: %w, %s, finding mismatching PCR failed: %v", err, evtLogStat, errPcrMiss)
}
Expand Down Expand Up @@ -897,7 +902,7 @@ func copyMeasurementLog(dstPath string) error {
return nil
}

func saveDiskKeySealingPCRs(pcrsFile string) error {
func saveDiskKeySealingPCRs() error {
trw, err := tpm2.OpenTPM(TpmDevicePath)
if err != nil {
return err
Expand All @@ -916,11 +921,11 @@ func saveDiskKeySealingPCRs(pcrsFile string) error {
return err
}

return fileutils.WriteRename(pcrsFile, buff.Bytes())
return fileutils.WriteRename(savedSealingPcrsFile, buff.Bytes())
}

func findMismatchingPCRs(savedPCRsFile string) ([]int, error) {
frw, err := os.Open(savedPCRsFile)
func findMismatchingPCRs() ([]int, error) {
frw, err := os.Open(savedSealingPcrsFile)
if err != nil {
return nil, err
}
Expand Down
47 changes: 47 additions & 0 deletions pkg/pillar/evetpm/tpm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"reflect"
"strings"
"testing"
"time"

"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
Expand All @@ -23,6 +24,52 @@ import (

var log = base.NewSourceLogObject(logrus.StandardLogger(), "test", 1234)

func TestMain(m *testing.M) {
log.Tracef("Setup test environment")

// setup variables
TpmDevicePath = "/tmp/eve-tpm/srv.sock"
measurementLogFile = "/tmp/eve-tpm/binary_bios_measurement"
measurefsTpmEventLog = "/tmp/eve-tpm/measurefs_tpm_event_log"
savedSealingPcrsFile = "/tmp/eve-tpm/sealingpcrs"
measurementLogSealSuccess = "/tmp/eve-tpm/tpm_measurement_seal_success"
measurementLogUnsealFail = "/tmp/eve-tpm/tpm_measurement_unseal_fail"

// check if we are running under the correct context and we end up here
// from tests/tpm/prep-and-test.sh.
_, err := os.Stat(TpmDevicePath)
if err != nil {
log.Warnf("Neither TPM device nor swtpm is available, skipping the test.")
return
}

// for some reason unknown to me, TPM might return RCRetry for the first
// few operations, so we need to wait for it to become ready.
if err := waitForTpmReadyState(); err != nil {
log.Fatalf("Failed to wait for TPM ready state: %v", err)
}

m.Run()
}

func waitForTpmReadyState() error {
for i := 0; i < 10; i++ {
if err := SealDiskKey(log, []byte("secret"), DiskKeySealingPCRs); err != nil {
// this is RCRetry, so retry
if strings.Contains(err.Error(), "code 0x22") {
time.Sleep(100 * time.Millisecond)
continue
} else {
return fmt.Errorf("Something is wrong with the TPM : %w", err)
}
} else {
return nil
}
}

return fmt.Errorf("TPM did't become ready after 10 attempts, failing the test")
}

func TestSealUnseal(t *testing.T) {
_, err := os.Stat(TpmDevicePath)
if err != nil {
Expand Down
107 changes: 107 additions & 0 deletions tests/tpm/prep-and-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

#!/bin/bash
#
# Copyright (c) 2023 Zededa, Inc.
# SPDX-License-Identifier: Apache-2.0

# add more TPM tests here
TESTS=(
"/pillar/evetpm"
)

TPM_SRV_PORT=1337
TPM_CTR_PORT=$((TPM_SRV_PORT + 1))
ENDO_SEED=0x4000000B
EK_HANDLE=0x81000001
SRK_HANDLE=0x81000002
EVE_TPM_STATE=/tmp/eve-tpm
EVE_TPM_CTRL="$EVE_TPM_STATE/ctrl.sock"
EVE_TPM_SRV="$EVE_TPM_STATE/srv.sock"

echo "[+] Installing swtpm and tpm2-tools ..."
sudo apt-get -qq update -y > /dev/null
sudo apt-get install swtpm tpm2-tools -y -qq > /dev/null

echo "[+] preparing the environment ..."
rm -rf $EVE_TPM_STATE
mkdir -p $EVE_TPM_STATE

flushtpm() {
tpm2 flushcontext -t
tpm2 flushcontext -l
tpm2 flushcontext -s
}

swtpm socket --tpm2 \
--server port="$TPM_SRV_PORT" \
--ctrl type=tcp,port="$TPM_CTR_PORT" \
--tpmstate dir="$EVE_TPM_STATE" \
--flags not-need-init,startup-clear &

PID=$!

# set Transmission Interface (TCTI) swtpm socket, so tpm2-tools use it
# instead of the default char device interface.
export TPM2TOOLS_TCTI="swtpm:host=localhost,port=$TPM_SRV_PORT"

# start fresh
tpm2 clear

# create Endorsement Key
tpm2 createek -c ek.ctx

# this setup seems very fragile, and quickly errors out with
# "out of memory for object contexts", so flush everything to be safe.
flushtpm

# create Storage Root Key
tpm2 startauthsession --policy-session -S session.ctx
tpm2 policysecret -S session.ctx -c $ENDO_SEED
tpm2 create -C ek.ctx -P "session:session.ctx" -G rsa2048 -u srk.pub -r srk.priv \
-a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth'
tpm2 flushcontext session.ctx
flushtpm

# load the srk
tpm2 startauthsession --policy-session -S session.ctx
tpm2 policysecret -S session.ctx -c $ENDO_SEED
tpm2 load -C ek.ctx -P "session:session.ctx" -u srk.pub -r srk.priv -c srk.ctx
tpm2 flushcontext session.ctx
flushtpm

# make persisted know-good-handles out of ek and srk
tpm2 evictcontrol -C o -c ek.ctx $EK_HANDLE
tpm2 evictcontrol -C o -c srk.ctx $SRK_HANDLE

# clean up
rm session.ctx ek.ctx srk.pub srk.priv srk.ctx

# just dump persistent handles, good for debugging§
tpm2 getcap handles-persistent

# kill swtpm
kill $PID

# start swtpm again, but this time with unix sockets for tests to use.
# in case we need to debug this, cat the log file.
swtpm socket --tpm2 \
--flags startup-clear \
--server type=unixio,path="$EVE_TPM_SRV" \
--ctrl type=unixio,path="$EVE_TPM_CTRL" \
--tpmstate dir="$EVE_TPM_STATE" \
--log file="$EVE_TPM_STATE/swtpm.log"&

# copy test data, so it is accessible from the go tests
cp "tests/tpm/testdata/binary_bios_measurement" $EVE_TPM_STATE
cp "tests/tpm/testdata/measurefs_tpm_event_log" $EVE_TPM_STATE

# give swtpm time to start and init the TPM
sleep 1

# run tests
echo "[+] Running tests ..."
echo "========================================================"
for T in "${TESTS[@]}"; do
name=$(basename "$T")
cd pkg$T && go test -v -coverprofile="$name.coverage.txt" -covermode=atomic
done
Binary file added tests/tpm/testdata/binary_bios_measurement
Binary file not shown.
Binary file added tests/tpm/testdata/measurefs_tpm_event_log
Binary file not shown.
Loading

0 comments on commit 9589a8e

Please sign in to comment.