diff --git a/efi/preinstall/check_pcr2.go b/efi/preinstall/check_pcr2.go
new file mode 100644
index 00000000..7ed1c734
--- /dev/null
+++ b/efi/preinstall/check_pcr2.go
@@ -0,0 +1,75 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2024 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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 .
+ *
+ */
+
+package preinstall
+
+import (
+ "errors"
+
+ "github.com/canonical/tcglog-parser"
+ internal_efi "github.com/snapcore/secboot/internal/efi"
+)
+
+type checkDriversAndAppsMeasurementsResult int
+
+const (
+ noDriversAndAppsPresent checkDriversAndAppsMeasurementsResult = iota
+ driversAndAppsPresent
+)
+
+// checkDriversAndAppsMeasurements performs minimal checks on PCR 2, which is where
+// addon code from value-added-retailer components such as option ROMs and UEFI
+// drivers are measured.
+//
+// It returns whether the PCR indicates that there is code from value-added-retailer
+// components executing.
+//
+// As efi.WithDriversAndAppsProfile just copies events from the log and does no
+// prediction for this PCR, this function doesn't do any more extensive testing, such
+// as ensuring that the PCR only contains events of the expected type, the event data
+// for each event is of an expected type, and for events where the digest is a tagged
+// hash of the event data, that the digest is consistent with the event data.
+func checkDriversAndAppsMeasurements(log *tcglog.Log) (checkDriversAndAppsMeasurementsResult, error) {
+ // Iterate over the log until OS-present and check if there are any
+ // drivers or applications loaded
+ phaseTracker := newTcgLogPhaseTracker()
+ for _, ev := range log.Events {
+ phase, err := phaseTracker.processEvent(ev)
+ if err != nil {
+ return noDriversAndAppsPresent, err
+ }
+
+ if phase >= tcglogPhaseTransitioningToOSPresent {
+ return noDriversAndAppsPresent, nil
+ }
+
+ if ev.PCRIndex != internal_efi.DriversAndAppsPCR {
+ // Not PCR2
+ continue
+ }
+
+ switch ev.EventType {
+ case tcglog.EventTypeEFIBootServicesApplication, tcglog.EventTypeEFIBootServicesDriver, tcglog.EventTypeEFIRuntimeServicesDriver,
+ tcglog.EventTypeEFIPlatformFirmwareBlob, tcglog.EventTypeEFIPlatformFirmwareBlob2, tcglog.EventTypeEFISPDMFirmwareBlob:
+ return driversAndAppsPresent, nil
+ }
+ }
+
+ return noDriversAndAppsPresent, errors.New("internal error: reached end of log before encountering transition to OS-present")
+}
diff --git a/efi/preinstall/check_pcr2_test.go b/efi/preinstall/check_pcr2_test.go
new file mode 100644
index 00000000..3e054f68
--- /dev/null
+++ b/efi/preinstall/check_pcr2_test.go
@@ -0,0 +1,87 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2024 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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 .
+ *
+ */
+
+package preinstall_test
+
+import (
+ "errors"
+
+ "github.com/canonical/go-tpm2"
+ "github.com/canonical/tcglog-parser"
+ . "github.com/snapcore/secboot/efi/preinstall"
+ "github.com/snapcore/secboot/internal/efitest"
+ . "gopkg.in/check.v1"
+)
+
+type pcr2Suite struct{}
+
+var _ = Suite(&pcr2Suite{})
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsGoodNonePresent(c *C) {
+ log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}})
+ result, err := CheckDriversAndAppsMeasurements(log)
+ c.Check(err, IsNil)
+ c.Check(result, Equals, NoDriversAndAppsPresent)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsGoodDriversPresent(c *C) {
+ log := efitest.NewLog(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ IncludeDriverLaunch: true,
+ })
+ result, err := CheckDriversAndAppsMeasurements(log)
+ c.Check(err, IsNil)
+ c.Check(result, Equals, DriversAndAppsPresent)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsLogError(c *C) {
+ log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}})
+ events := log.Events
+ for len(events) > 0 {
+ ev := events[0]
+ events = events[1:]
+
+ if ev.EventType != tcglog.EventTypeSeparator {
+ continue
+ }
+
+ ev.Data = &invalidEventData{errors.New("some error")}
+ break
+ }
+
+ _, err := CheckDriversAndAppsMeasurements(log)
+ c.Check(err, ErrorMatches, `invalid event data for EV_SEPARATOR event in PCR 7: some error`)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsLogNoTransitionToOSPresent(c *C) {
+ log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}})
+ events := log.Events
+ for len(events) > 0 {
+ ev := events[0]
+ if (ev.PCRIndex >= 0 && ev.PCRIndex < 7) && ev.EventType == tcglog.EventTypeSeparator {
+ break
+ }
+ events = events[1:]
+ }
+ // Truncate the log
+ log.Events = log.Events[:len(log.Events)-len(events)]
+
+ _, err := CheckDriversAndAppsMeasurements(log)
+ c.Check(err, ErrorMatches, `internal error: reached end of log before encountering transition to OS-present`)
+}
diff --git a/efi/preinstall/export_test.go b/efi/preinstall/export_test.go
index 53508d68..985ba7e8 100644
--- a/efi/preinstall/export_test.go
+++ b/efi/preinstall/export_test.go
@@ -20,10 +20,11 @@
package preinstall
type (
- CheckTPM2DeviceFlags = checkTPM2DeviceFlags
- CpuVendor = cpuVendor
- DetectVirtResult = detectVirtResult
- MeVersion = meVersion
+ CheckDriversAndAppsMeasurementsResult = checkDriversAndAppsMeasurementsResult
+ CheckTPM2DeviceFlags = checkTPM2DeviceFlags
+ CpuVendor = cpuVendor
+ DetectVirtResult = detectVirtResult
+ MeVersion = meVersion
)
const (
@@ -33,16 +34,19 @@ const (
CpuVendorAMD = cpuVendorAMD
DetectVirtNone = detectVirtNone
DetectVirtVM = detectVirtVM
+ DriversAndAppsPresent = driversAndAppsPresent
MeFamilyUnknown = meFamilyUnknown
MeFamilySps = meFamilySps
MeFamilyTxe = meFamilyTxe
MeFamilyMe = meFamilyMe
MeFamilyCsme = meFamilyCsme
+ NoDriversAndAppsPresent = noDriversAndAppsPresent
)
var (
CalculateIntelMEFamily = calculateIntelMEFamily
CheckCPUDebuggingLockedMSR = checkCPUDebuggingLockedMSR
+ CheckDriversAndAppsMeasurements = checkDriversAndAppsMeasurements
CheckFirmwareLogAndChoosePCRBank = checkFirmwareLogAndChoosePCRBank
CheckForKernelIOMMU = checkForKernelIOMMU
CheckPlatformFirmwareProtections = checkPlatformFirmwareProtections