Skip to content

Commit

Permalink
tpm-tests: streamline unit-tests that uses TPM
Browse files Browse the repository at this point in the history
Streamline all the unit-tests that uses TPM, by removing the
duplicated code and making all to use the sim tpm helper functions.

Signed-off-by: Shahriyar Jalayeri <shahriyar@zededa.com>
  • Loading branch information
shjala committed Sep 11, 2024
1 parent 2987c02 commit ad620da
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 100 deletions.
84 changes: 11 additions & 73 deletions pkg/pillar/cmd/msrv/activatecred_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,102 +10,40 @@ import (
"crypto/rsa"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"

"github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/legacy/tpm2/credactivation"
"github.com/lf-edge/eve/pkg/pillar/base"
"github.com/lf-edge/eve/pkg/pillar/cmd/msrv"
etpm "github.com/lf-edge/eve/pkg/pillar/evetpm"
"github.com/lf-edge/eve/pkg/pillar/evetpm"
"github.com/lf-edge/eve/pkg/pillar/pubsub"
"github.com/onsi/gomega"
"github.com/sirupsen/logrus"
)

const TpmSimPath = "/tmp/eve-tpm/srv.sock"

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

func waitForTpmReadyState() error {
for i := 0; i < 10; i++ {
rw, err := tpm2.OpenTPM(msrv.TPMDevicePath)
if err != nil {
return fmt.Errorf("Failed to open TPM: %w", err)
}

_, _, err = tpm2.GetCapability(rw, tpm2.CapabilityHandles, 1, uint32(tpm2.HandleTypeTransient)<<24)
if 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 isTpmAvailable(path string) bool {
_, err := os.Stat(path)
if err != nil {
log.Warnf("TPM device %s is not available: %v", msrv.TPMDevicePath, err)
return false
}
return true
}

func prepareTPM() error {
//Make sure the TPM is ready for use, swtpm takes some time to become ready
if err := waitForTpmReadyState(); err != nil {
return fmt.Errorf("Failed to wait for TPM ready state: %w", err)
}

// Create EK and AIK in case it is not already created
err := etpm.CreateKey(log, msrv.TPMDevicePath, etpm.TpmEKHdl, tpm2.HandleEndorsement, etpm.DefaultEkTemplate, false)
if err != nil {
return fmt.Errorf("error in creating Endorsement key: %w ", err)
}
err = etpm.CreateKey(log, msrv.TPMDevicePath, etpm.TpmAIKHdl, tpm2.HandleOwner, etpm.DefaultAikTemplate, false)
if err != nil {
return fmt.Errorf("error in creating Attestation key: %w ", err)
}

return nil
}

// TestTpmActivateCred contains TPM kong-fu, not for the faint of heart.
func TestTpmActivateCred(t *testing.T) {
t.Parallel()
g := gomega.NewGomegaWithT(t)

if !isTpmAvailable(msrv.TPMDevicePath) {
// no TPM device available, check for TPM socket, see if we ended up
// here from tests/tpm/prep-and-test.sh script.
if !isTpmAvailable(TpmSimPath) {
t.Skip("Neither HW or SW TPM device available, skipping the test")
}

// set the TPM device path to swtpm socket path
msrv.TPMDevicePath = TpmSimPath
// we should end up here from tests/tpm/prep-and-test.sh script,
// so set the TPM device path to swtpm socket path.
msrv.TPMDevicePath = evetpm.SimTpmPath

// make sure TPM is prepare it before running the test.
err := prepareTPM()
g.Expect(err).ToNot(gomega.HaveOccurred())
if !evetpm.SimTpmAvailable() {
t.Skip("SWTPM device not available, skipping the test")
}

// make sure TPM is prepare it before running the test.
err := evetpm.SimTpmWaitForTpmReadyState()
g.Expect(err).ToNot(gomega.HaveOccurred())

logger := logrus.StandardLogger()
log := base.NewSourceLogObject(logger, "pubsub", 1234)
log := base.NewSourceLogObject(logger, "acitavatecred_test", os.Getpid())
ps := pubsub.New(pubsub.NewMemoryDriver(), logger, log)

srv := &msrv.Msrv{
Expand Down
2 changes: 1 addition & 1 deletion pkg/pillar/cmd/vcomlink/tpmcom.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func handleTpmGetEk() ([]byte, error) {
}

func getEkPub() (string, error) {
rw, err := tpm2.OpenTPM(etpm.TpmDevicePath)
rw, err := tpm2.OpenTPM(TpmDevicePath)
if err != nil {
return "", fmt.Errorf("unable to open TPM: %w", err)
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/pillar/cmd/vcomlink/vcomlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/lf-edge/eve/pkg/pillar/agentbase"
"github.com/lf-edge/eve/pkg/pillar/agentlog"
"github.com/lf-edge/eve/pkg/pillar/base"
etpm "github.com/lf-edge/eve/pkg/pillar/evetpm"
"github.com/lf-edge/eve/pkg/pillar/pubsub"
"github.com/lf-edge/eve/pkg/pillar/types"
"github.com/sirupsen/logrus"
Expand All @@ -31,6 +32,9 @@ var (
logger *logrus.Logger
log *base.LogObject
listener SocketListener = vsockListener
// TpmDevicePath is the path to the TPM device, this is a variable so that
// it can be overridden in tests.
TpmDevicePath = etpm.TpmDevicePath
)

type vcomLinkContext struct {
Expand Down
29 changes: 16 additions & 13 deletions pkg/pillar/cmd/vcomlink/vcomlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

"github.com/lf-edge/eve/pkg/pillar/base"
"github.com/lf-edge/eve/pkg/pillar/evetpm"
"github.com/lf-edge/eve/pkg/pillar/vcom"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
Expand Down Expand Up @@ -74,15 +75,25 @@ func connect() (int, error) {
return sockfd, nil
}

func isTPMAvailable() bool {
_, err := os.Stat("/dev/tpm0")
return err == nil
}

func TestMain(m *testing.M) {
log = base.NewSourceLogObject(logrus.StandardLogger(), "vcomlink_test", os.Getpid())
res := 1

if !evetpm.SimTpmAvailable() {
fmt.Println("TPM not available, skipping test")
os.Exit(0)
}

// make sure TPM is prepare it before running the test.
err := evetpm.SimTpmWaitForTpmReadyState()
if err != nil {
fmt.Printf("Failed to wait for TPM ready state: %v", err)
os.Exit(1)
}

// use sim tpm for testing
TpmDevicePath = evetpm.SimTpmPath

// we can't connect to a vsock listener host<->host, so we use a tcp listener
// for testing.
go startVcomServer(tcpListener)
Expand All @@ -101,10 +112,6 @@ func TestMain(m *testing.M) {
}

func TestValidTPMRequest(t *testing.T) {
if !isTPMAvailable() {
t.Skip("TPM not available, skipping test")
}

request := &vcom.TpmRequest{
Base: vcom.Base{Channel: int(vcom.ChannelTpm)},
Request: uint(vcom.RequestTpmGetEk),
Expand Down Expand Up @@ -151,10 +158,6 @@ func TestValidTPMRequest(t *testing.T) {
}

func TestInvalidRequest(t *testing.T) {
if !isTPMAvailable() {
t.Skip("TPM not available, skipping test")
}

request := &vcom.Base{
Channel: math.MaxUint32,
}
Expand Down
22 changes: 9 additions & 13 deletions pkg/pillar/evetpm/tpm_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) 2020 Zededa, Inc.
// Copyright (c) 2020-2024 Zededa, Inc.
// SPDX-License-Identifier: Apache-2.0

// unit-tests for evetpm

// unit-tests for evetpm package
package evetpm

import (
Expand All @@ -22,30 +21,27 @@ import (
"github.com/sirupsen/logrus"
)

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

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

// setup variables
TpmDevicePath = "/tmp/eve-tpm/srv.sock"
TpmDevicePath = SimTpmPath
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
if !SimTpmAvailable() {
log.Warnf("TPM is not available, skipping the test.")
os.Exit(0)
}

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

Expand Down

0 comments on commit ad620da

Please sign in to comment.