From 53e693ae2b9d6b2c88ce6cd5dedd64466d0573a7 Mon Sep 17 00:00:00 2001
From: ebrig <146060912+ebrig@users.noreply.github.com>
Date: Sun, 26 Jul 2026 07:08:57 -0400
Subject: [PATCH 1/2] efi: replay pre-OS PCR2 vendor events after separator
Some firmware extends vendor-defined events into PCR2 after EV_SEPARATOR but before the initial OS loader is authorized or launched. Continue replaying those events within that pre-OS interval so generated profiles match the TPM, and apply the same boundary in preinstall validation.
---
efi/fw_load_handler.go | 37 +++-
efi/pcr2_vendor_event_test.go | 223 +++++++++++++++++++++
efi/preinstall/check_pcr2.go | 41 +++-
efi/preinstall/pcr2_vendor_event_test.go | 244 +++++++++++++++++++++++
4 files changed, 538 insertions(+), 7 deletions(-)
create mode 100644 efi/pcr2_vendor_event_test.go
create mode 100644 efi/preinstall/pcr2_vendor_event_test.go
diff --git a/efi/fw_load_handler.go b/efi/fw_load_handler.go
index 3cef06c5..23311d0e 100644
--- a/efi/fw_load_handler.go
+++ b/efi/fw_load_handler.go
@@ -392,18 +392,49 @@ func (h *fwLoadHandler) measurePlatformFirmware(ctx pcrBranchContext) error {
}
func (h *fwLoadHandler) measureDriversAndApps(ctx pcrBranchContext) error {
+ seenSeparator := false
+
for _, event := range h.log.Events {
+ // Some firmware extends vendor-defined events into PCR2 after the
+ // separator but before authorizing or launching the initial OS loader.
+ // Retain those events, but do not copy any PCR2 measurements made once
+ // OS image processing has begun.
+ if seenSeparator &&
+ ((event.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ event.EventType == tcglog.EventTypeEFIVariableAuthority) ||
+ (event.PCRIndex == internal_efi.BootManagerCodePCR &&
+ event.EventType == tcglog.EventTypeEFIBootServicesApplication)) {
+ return nil
+ }
+
if event.PCRIndex != internal_efi.DriversAndAppsPCR {
continue
}
- if event.EventType == tcglog.EventTypeSeparator {
- return h.measureSeparator(ctx, internal_efi.DriversAndAppsPCR, event)
+ if !seenSeparator {
+ if event.EventType == tcglog.EventTypeSeparator {
+ if err := h.measureSeparator(ctx, internal_efi.DriversAndAppsPCR, event); err != nil {
+ return err
+ }
+ seenSeparator = true
+ continue
+ }
+ ctx.ExtendPCR(internal_efi.DriversAndAppsPCR, event.Digests[ctx.PCRAlg()])
+ continue
+ }
+
+ if !internal_efi.IsVendorEventType(event.EventType) {
+ return fmt.Errorf(
+ "unexpected post-separator event type %v found in PCR %d",
+ event.EventType, internal_efi.DriversAndAppsPCR)
}
ctx.ExtendPCR(internal_efi.DriversAndAppsPCR, event.Digests[ctx.PCRAlg()])
}
- return errors.New("missing separator in log")
+ if !seenSeparator {
+ return errors.New("missing separator in log")
+ }
+ return errors.New("reached end of log before encountering initial OS authorization or launch")
}
func (h *fwLoadHandler) measureBootManagerCodePreOS(ctx pcrBranchContext) error {
diff --git a/efi/pcr2_vendor_event_test.go b/efi/pcr2_vendor_event_test.go
new file mode 100644
index 00000000..a31cb4de
--- /dev/null
+++ b/efi/pcr2_vendor_event_test.go
@@ -0,0 +1,223 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2026 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 efi_test
+
+import (
+ "github.com/canonical/go-tpm2"
+ "github.com/canonical/tcglog-parser"
+ . "github.com/snapcore/secboot/efi"
+ internal_efi "github.com/snapcore/secboot/internal/efi"
+ "github.com/snapcore/secboot/internal/efitest"
+ "github.com/snapcore/secboot/internal/testutil"
+ . "gopkg.in/check.v1"
+)
+
+func newLogWithPostSeparatorPCR2Event(c *C, insertedEvent *tcglog.Event) *tcglog.Log {
+ log := efitest.NewLog(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ })
+
+ var events []*tcglog.Event
+ inserted := false
+ for _, event := range log.Events {
+ events = append(events, event)
+ if event.PCRIndex == internal_efi.PlatformManufacturerPCR &&
+ event.EventType == tcglog.EventTypeSeparator {
+ events = append(events, insertedEvent)
+ inserted = true
+ }
+ }
+ c.Assert(inserted, Equals, true)
+ log.Events = events
+ return log
+}
+
+func newLogWithPCR2EventAfterOSBoundary(c *C, opts *efitest.LogOptions, insertedEvent *tcglog.Event) *tcglog.Log {
+ log := efitest.NewLog(c, opts)
+
+ var events []*tcglog.Event
+ seenPCR2Separator := false
+ inserted := false
+ for _, event := range log.Events {
+ events = append(events, event)
+
+ if event.PCRIndex == internal_efi.DriversAndAppsPCR &&
+ event.EventType == tcglog.EventTypeSeparator {
+ seenPCR2Separator = true
+ continue
+ }
+ if !seenPCR2Separator || inserted {
+ continue
+ }
+
+ if (event.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ event.EventType == tcglog.EventTypeEFIVariableAuthority) ||
+ (event.PCRIndex == internal_efi.BootManagerCodePCR &&
+ event.EventType == tcglog.EventTypeEFIBootServicesApplication) {
+ events = append(events, insertedEvent)
+ inserted = true
+ }
+ }
+ c.Assert(inserted, Equals, true)
+ log.Events = events
+ return log
+}
+
+// TestMeasureImageStartDriversAndAppsIncludesPostSeparatorVendorEvent
+// reproduces the event ordering from the HP ZBook Ultra G1a firmware:
+//
+// PCR2 EV_SEPARATOR
+// ...
+// PCR2 vendor event 0x00008401 with data 0x01
+// ...
+// PCR7 EV_EFI_VARIABLE_AUTHORITY
+// PCR4 EV_EFI_BOOT_SERVICES_APPLICATION
+//
+// The event digest must be retained in the generated PCR2 profile because the
+// firmware extends it into the TPM before launching the OS.
+func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsIncludesPostSeparatorVendorEvent(c *C) {
+ vendorDigest := testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a")
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: vendorDigest,
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ s.testMeasureImageStart(c, &testFwMeasureImageStartData{
+ log: log,
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.DriversAndAppsPCR),
+ expectedEvents: []*mockPcrBranchEvent{
+ {pcr: 2, eventType: mockPcrBranchResetEvent},
+ {pcr: 2, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
+ {pcr: 2, eventType: mockPcrBranchExtendEvent, digest: vendorDigest},
+ },
+ })
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsRejectsPostSeparatorStandardEvent(c *C) {
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: tcglog.EventTypeEFIAction,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ collector := NewVariableSetCollector(efitest.NewMockHostEnvironment(nil, nil))
+ ctx := newMockPcrBranchContext(&mockPcrProfileContext{
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.DriversAndAppsPCR),
+ }, nil, collector.Next())
+
+ handler := NewFwLoadHandler(log)
+ c.Check(
+ handler.MeasureImageStart(ctx),
+ ErrorMatches,
+ `cannot measure drivers and apps: unexpected post-separator event type EV_EFI_ACTION found in PCR 2`)
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsRejectsMissingOSBoundary(c *C) {
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ truncated := false
+ for i, event := range log.Events {
+ if (event.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ event.EventType == tcglog.EventTypeEFIVariableAuthority) ||
+ (event.PCRIndex == internal_efi.BootManagerCodePCR &&
+ event.EventType == tcglog.EventTypeEFIBootServicesApplication) {
+ log.Events = log.Events[:i]
+ truncated = true
+ break
+ }
+ }
+ c.Assert(truncated, Equals, true)
+
+ collector := NewVariableSetCollector(efitest.NewMockHostEnvironment(nil, nil))
+ ctx := newMockPcrBranchContext(&mockPcrProfileContext{
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.DriversAndAppsPCR),
+ }, nil, collector.Next())
+
+ handler := NewFwLoadHandler(log)
+ c.Check(
+ handler.MeasureImageStart(ctx),
+ ErrorMatches,
+ `cannot measure drivers and apps: reached end of log before encountering initial OS authorization or launch`)
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsStopsAtVariableAuthorityBoundary(c *C) {
+ vendorDigest := testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a")
+ log := newLogWithPCR2EventAfterOSBoundary(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ }, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: vendorDigest,
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ s.testMeasureImageStart(c, &testFwMeasureImageStartData{
+ log: log,
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.DriversAndAppsPCR),
+ expectedEvents: []*mockPcrBranchEvent{
+ {pcr: 2, eventType: mockPcrBranchResetEvent},
+ {pcr: 2, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
+ },
+ })
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsStopsAtImageLaunchBoundary(c *C) {
+ vendorDigest := testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a")
+ log := newLogWithPCR2EventAfterOSBoundary(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ SecureBootDisabled: true,
+ }, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: vendorDigest,
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ s.testMeasureImageStart(c, &testFwMeasureImageStartData{
+ log: log,
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.DriversAndAppsPCR),
+ expectedEvents: []*mockPcrBranchEvent{
+ {pcr: 2, eventType: mockPcrBranchResetEvent},
+ {pcr: 2, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
+ },
+ })
+}
diff --git a/efi/preinstall/check_pcr2.go b/efi/preinstall/check_pcr2.go
index a62bff89..8915bc33 100644
--- a/efi/preinstall/check_pcr2.go
+++ b/efi/preinstall/check_pcr2.go
@@ -57,17 +57,47 @@ func checkDriversAndAppsMeasurements(ctx context.Context, env internal_efi.HostE
var addonDrivers []*LoadedImageInfo
- // Iterate over the log until OS-present and check if there are any
- // drivers or applications loaded
+ // Iterate over the log through the initial OS authorization or launch
+ // boundary and check if there are any drivers or applications loaded.
phaseTracker := newTcgLogPhaseTracker()
for _, ev := range log.Events {
+ wasTransitioningToOSPresent := phaseTracker.phase == tcglogPhaseTransitioningToOSPresent
phase, err := phaseTracker.processEvent(ev)
if err != nil {
return nil, err
}
- if phase >= tcglogPhaseTransitioningToOSPresent {
- return addonDrivers, nil
+ switch phase {
+ case tcglogPhaseTransitioningToOSPresent:
+ // The phase tracker validates that this consists only of the
+ // remaining separators.
+ continue
+ case tcglogPhaseOSPresent:
+ if wasTransitioningToOSPresent {
+ // processEvent returns the new phase. The event that
+ // completes the transition is still one of the required
+ // separators, regardless of which PCR it belongs to.
+ continue
+ }
+
+ // Some firmware extends vendor-defined events into PCR2 after the
+ // separators but before authorizing or launching the initial OS
+ // loader. Validate the same narrow interval that profile
+ // generation retains.
+ if (ev.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ ev.EventType == tcglog.EventTypeEFIVariableAuthority) ||
+ (ev.PCRIndex == internal_efi.BootManagerCodePCR &&
+ ev.EventType == tcglog.EventTypeEFIBootServicesApplication) {
+ return addonDrivers, nil
+ }
+
+ if ev.PCRIndex == internal_efi.DriversAndAppsPCR &&
+ !internal_efi.IsVendorEventType(ev.EventType) {
+ return nil, fmt.Errorf(
+ "unexpected post-separator event type %v found in PCR %d",
+ ev.EventType, internal_efi.DriversAndAppsPCR)
+ }
+ continue
}
if ev.PCRIndex != internal_efi.DriversAndAppsPCR {
@@ -126,5 +156,8 @@ func checkDriversAndAppsMeasurements(ctx context.Context, env internal_efi.HostE
}
}
+ if phaseTracker.reachedOSPresent() {
+ return nil, errors.New("reached end of log before encountering initial OS authorization or launch")
+ }
return nil, errors.New("reached end of log before encountering transition to OS-present")
}
diff --git a/efi/preinstall/pcr2_vendor_event_test.go b/efi/preinstall/pcr2_vendor_event_test.go
new file mode 100644
index 00000000..6089dd9b
--- /dev/null
+++ b/efi/preinstall/pcr2_vendor_event_test.go
@@ -0,0 +1,244 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2026 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 (
+ "github.com/canonical/go-tpm2"
+ "github.com/canonical/tcglog-parser"
+ internal_efi "github.com/snapcore/secboot/internal/efi"
+ "github.com/snapcore/secboot/internal/efitest"
+ "github.com/snapcore/secboot/internal/testutil"
+ . "gopkg.in/check.v1"
+)
+
+func newLogWithPostSeparatorPCR2Event(c *C, insertedEvent *tcglog.Event) *tcglog.Log {
+ log := efitest.NewLog(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ })
+
+ var events []*tcglog.Event
+ inserted := false
+ for _, event := range log.Events {
+ events = append(events, event)
+ if event.PCRIndex == internal_efi.PlatformManufacturerPCR &&
+ event.EventType == tcglog.EventTypeSeparator {
+ events = append(events, insertedEvent)
+ inserted = true
+ }
+ }
+ c.Assert(inserted, Equals, true)
+ log.Events = events
+ return log
+}
+
+func newLogWithPCR2EventAfterOSBoundary(c *C, insertedEvent *tcglog.Event) *tcglog.Log {
+ log := efitest.NewLog(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ })
+
+ var events []*tcglog.Event
+ seenPCR2Separator := false
+ inserted := false
+ for _, event := range log.Events {
+ events = append(events, event)
+
+ if event.PCRIndex == internal_efi.DriversAndAppsPCR &&
+ event.EventType == tcglog.EventTypeSeparator {
+ seenPCR2Separator = true
+ continue
+ }
+ if !seenPCR2Separator || inserted {
+ continue
+ }
+ if event.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ event.EventType == tcglog.EventTypeEFIVariableAuthority {
+ events = append(events, insertedEvent)
+ inserted = true
+ }
+ }
+ c.Assert(inserted, Equals, true)
+ log.Events = events
+ return log
+}
+
+func newLogWithPCR2AsFinalSeparator(c *C, insertedEvent *tcglog.Event) *tcglog.Log {
+ log := efitest.NewLog(c, &efitest.LogOptions{
+ Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256},
+ })
+
+ var (
+ events []*tcglog.Event
+ pcr2Separator *tcglog.Event
+ inserted bool
+ )
+ for _, event := range log.Events {
+ if event.PCRIndex == internal_efi.DriversAndAppsPCR &&
+ event.EventType == tcglog.EventTypeSeparator {
+ pcr2Separator = event
+ continue
+ }
+
+ events = append(events, event)
+ if event.PCRIndex == internal_efi.PlatformManufacturerPCR &&
+ event.EventType == tcglog.EventTypeSeparator {
+ c.Assert(pcr2Separator, NotNil)
+ events = append(events, pcr2Separator, insertedEvent)
+ inserted = true
+ }
+ }
+ c.Assert(inserted, Equals, true)
+ log.Events = events
+ return log
+}
+
+// TestCheckDriversAndAppsMeasurementsAcceptsPostSeparatorVendorEvent confirms
+// that the PCR2 preinstall check accepts the HP vendor event before the OS
+// launch boundary.
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsAcceptsPostSeparatorVendorEvent(c *C) {
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ err := s.testCheckDriversAndAppsMeasurements(c, &testCheckDriversAndAppsMeasurementsParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(efitest.MockVars{}),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ })
+ c.Check(err, IsNil)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsAcceptsPCR2AsFinalSeparator(c *C) {
+ log := newLogWithPCR2AsFinalSeparator(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ err := s.testCheckDriversAndAppsMeasurements(c, &testCheckDriversAndAppsMeasurementsParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(efitest.MockVars{}),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ })
+ c.Check(err, IsNil)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsRejectsLatePCR2Separator(c *C) {
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: tcglog.EventTypeSeparator,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"),
+ },
+ Data: &tcglog.SeparatorEventData{Value: tcglog.SeparatorEventNormalValue},
+ })
+
+ err := s.testCheckDriversAndAppsMeasurements(c, &testCheckDriversAndAppsMeasurementsParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(efitest.MockVars{}),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ })
+ c.Check(err, ErrorMatches, `unexpected post-separator event type EV_SEPARATOR found in PCR 2`)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsRejectsMissingOSBoundary(c *C) {
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: 0x00008401,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ truncated := false
+ for i, event := range log.Events {
+ if (event.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ event.EventType == tcglog.EventTypeEFIVariableAuthority) ||
+ (event.PCRIndex == internal_efi.BootManagerCodePCR &&
+ event.EventType == tcglog.EventTypeEFIBootServicesApplication) {
+ log.Events = log.Events[:i]
+ truncated = true
+ break
+ }
+ }
+ c.Assert(truncated, Equals, true)
+
+ err := s.testCheckDriversAndAppsMeasurements(c, &testCheckDriversAndAppsMeasurementsParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(efitest.MockVars{}),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ })
+ c.Check(err, ErrorMatches, `reached end of log before encountering initial OS authorization or launch`)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsRejectsPostSeparatorStandardEvent(c *C) {
+ log := newLogWithPostSeparatorPCR2Event(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: tcglog.EventTypeEFIAction,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ err := s.testCheckDriversAndAppsMeasurements(c, &testCheckDriversAndAppsMeasurementsParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(efitest.MockVars{}),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ })
+ c.Check(err, ErrorMatches, `unexpected post-separator event type EV_EFI_ACTION found in PCR 2`)
+}
+
+func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsStopsAtOSBoundary(c *C) {
+ log := newLogWithPCR2EventAfterOSBoundary(c, &tcglog.Event{
+ PCRIndex: internal_efi.DriversAndAppsPCR,
+ EventType: tcglog.EventTypeEFIAction,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: testutil.DecodeHexString(c, "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"),
+ },
+ Data: tcglog.OpaqueEventData{0x01},
+ })
+
+ err := s.testCheckDriversAndAppsMeasurements(c, &testCheckDriversAndAppsMeasurementsParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(efitest.MockVars{}),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ })
+ c.Check(err, IsNil)
+}
From ef00edc4d17916bc233946b48a00ee5bfd1774c5 Mon Sep 17 00:00:00 2001
From: ebrig <146060912+ebrig@users.noreply.github.com>
Date: Sun, 26 Jul 2026 07:09:27 -0400
Subject: [PATCH 2/2] efi: support HP pre-boot DMA configuration event
HP firmware can measure enabled SVM and DMA protection settings into PCR7 as an EV_EFI_ACTION before the Secure Boot variables. Recognize only the exact protected configuration, validate its digest, replay it once in the profile, and mirror the acceptance rule in preinstall validation.
---
efi/fw_load_handler.go | 16 ++-
efi/pcr7_hp_dma_event_test.go | 148 ++++++++++++++++++++
efi/preinstall/check_pcr7.go | 30 +++--
efi/preinstall/pcr7_hp_dma_event_test.go | 165 +++++++++++++++++++++++
internal/efi/tcg_events.go | 12 ++
internal/efi/tcg_events_test.go | 48 +++++++
6 files changed, 406 insertions(+), 13 deletions(-)
create mode 100644 efi/pcr7_hp_dma_event_test.go
create mode 100644 efi/preinstall/pcr7_hp_dma_event_test.go
diff --git a/efi/fw_load_handler.go b/efi/fw_load_handler.go
index 23311d0e..474cc130 100644
--- a/efi/fw_load_handler.go
+++ b/efi/fw_load_handler.go
@@ -146,6 +146,7 @@ func (h *fwLoadHandler) measureSecureBootPolicyPreOS(ctx pcrBranchContext) error
// enabled. A firmware debugger permits an adversary with local access to control
// firmware execution, bypassing any protections offered by measuredboot or verified
// boot, and the presence of one should prevent FDE from being enabled.
+ measuredHPPreBootDMAConfig := false
for len(events) > 0 {
e := events[0]
events = events[1:]
@@ -156,14 +157,21 @@ func (h *fwLoadHandler) measureSecureBootPolicyPreOS(ctx pcrBranchContext) error
}
if e.EventType == tcglog.EventTypeEFIVariableDriverConfig {
- // This is the first secure boot configuration measurement. In most
- // circumstances, this will be the first measurement to PCR7. Only
- // in the case where the first event is a EV_EFI_ACTION "DMA Protection
- // Disabled" event will this not be true.
+ // This is the first secure boot configuration measurement. It is
+ // generally the first measurement to PCR7, although supported
+ // pre-configuration action or vendor events may precede it.
break
}
switch {
+ case internal_efi.IsHPPreBootDMAConfigEvent(e) && !measuredHPPreBootDMAConfig:
+ digest := e.Digests[ctx.PCRAlg()]
+ expectedDigest := tcglog.ComputeStringEventDigest(ctx.PCRAlg().GetHash(), string(e.Data.Bytes()))
+ if !bytes.Equal(digest, expectedDigest) {
+ return errors.New("invalid digest for HP pre-boot DMA configuration event")
+ }
+ ctx.ExtendPCR(internal_efi.SecureBootPolicyPCR, digest)
+ measuredHPPreBootDMAConfig = true
case e.EventType == tcglog.EventTypeEFIAction &&
(bytes.Equal(e.Data.Bytes(), []byte(dmaProtectionDisabled)) || bytes.Equal(e.Data.Bytes(), []byte(dmaProtectionDisabledNul))) &&
allowInsufficientDMAProtection:
diff --git a/efi/pcr7_hp_dma_event_test.go b/efi/pcr7_hp_dma_event_test.go
new file mode 100644
index 00000000..3d7753cb
--- /dev/null
+++ b/efi/pcr7_hp_dma_event_test.go
@@ -0,0 +1,148 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2026 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 efi_test
+
+import (
+ "crypto"
+
+ . "gopkg.in/check.v1"
+
+ efi "github.com/canonical/go-efilib"
+ "github.com/canonical/go-tpm2"
+ "github.com/canonical/tcglog-parser"
+
+ . "github.com/snapcore/secboot/efi"
+ internal_efi "github.com/snapcore/secboot/internal/efi"
+ "github.com/snapcore/secboot/internal/efitest"
+ "github.com/snapcore/secboot/internal/testutil"
+)
+
+const hpPreBootDMAConfigEventData = `"SVM CPU Virtualization":"Enable";"DMA protection":"Enable";"Pre-boot DMA protection":"All PCIe devices";`
+
+func newLogWithHPPreBootDMAConfigEvent(c *C) *tcglog.Log {
+ log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}})
+ data := tcglog.StringEventData(hpPreBootDMAConfigEventData)
+ event := &tcglog.Event{
+ PCRIndex: internal_efi.SecureBootPolicyPCR,
+ EventType: tcglog.EventTypeEFIAction,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: tcglog.ComputeStringEventDigest(crypto.SHA256, hpPreBootDMAConfigEventData),
+ },
+ Data: data,
+ }
+
+ var events []*tcglog.Event
+ added := false
+ for _, ev := range log.Events {
+ if ev.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ ev.EventType == tcglog.EventTypeEFIVariableDriverConfig &&
+ !added {
+ events = append(events, event)
+ added = true
+ }
+ events = append(events, ev)
+ }
+ c.Assert(added, testutil.IsTrue)
+ log.Events = events
+ return log
+}
+
+func measureSecureBootPolicyProfileError(c *C, log *tcglog.Log) error {
+ collector := NewVariableSetCollector(efitest.NewMockHostEnvironment(makeMockVars(c, withMsSecureBootConfig()), nil))
+ ctx := newMockPcrBranchContext(&mockPcrProfileContext{
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.SecureBootPolicyPCR),
+ }, nil, collector.Next())
+ return NewFwLoadHandler(log).MeasureImageStart(ctx)
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartSecureBootPolicyProfileWithHPPreBootDMAConfig(c *C) {
+ vars := makeMockVars(c, withMsSecureBootConfig())
+ s.testMeasureImageStart(c, &testFwMeasureImageStartData{
+ vars: vars,
+ log: newLogWithHPPreBootDMAConfigEvent(c),
+ alg: tpm2.HashAlgorithmSHA256,
+ pcrs: MakePcrFlags(internal_efi.SecureBootPolicyPCR),
+ expectedEvents: []*mockPcrBranchEvent{
+ {pcr: 7, eventType: mockPcrBranchResetEvent},
+ {pcr: 7, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "102a994acb0172f38fada0319cea1a2964ad15fcdb54216bcd3a0b821c8612ee")},
+ {pcr: 7, eventType: mockPcrBranchMeasureVariableEvent, varName: efi.VariableDescriptor{Name: "SecureBoot", GUID: efi.GlobalVariable}, varData: []byte{0x01}},
+ {pcr: 7, eventType: mockPcrBranchMeasureVariableEvent, varName: PK, varData: vars[PK].Payload},
+ {pcr: 7, eventType: mockPcrBranchMeasureVariableEvent, varName: KEK, varData: vars[KEK].Payload},
+ {pcr: 7, eventType: mockPcrBranchMeasureVariableEvent, varName: Db, varData: vars[Db].Payload},
+ {pcr: 7, eventType: mockPcrBranchMeasureVariableEvent, varName: Dbx, varData: vars[Dbx].Payload},
+ {pcr: 7, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
+ },
+ })
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartSecureBootPolicyProfileRejectsHPPreBootDMAConfigWithWrongDigest(c *C) {
+ log := newLogWithHPPreBootDMAConfigEvent(c)
+ for _, ev := range log.Events {
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) {
+ ev.Digests[tpm2.HashAlgorithmSHA256] = make(tpm2.Digest, tpm2.HashAlgorithmSHA256.Size())
+ }
+ }
+
+ err := measureSecureBootPolicyProfileError(c, log)
+ c.Check(err, ErrorMatches, `cannot measure secure boot policy: invalid digest for HP pre-boot DMA configuration event`)
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartSecureBootPolicyProfileRejectsDuplicateHPPreBootDMAConfig(c *C) {
+ log := newLogWithHPPreBootDMAConfigEvent(c)
+ var events []*tcglog.Event
+ for _, ev := range log.Events {
+ events = append(events, ev)
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) {
+ events = append(events, ev)
+ }
+ }
+ log.Events = events
+
+ err := measureSecureBootPolicyProfileError(c, log)
+ c.Check(err, ErrorMatches, `cannot measure secure boot policy: unexpected event type \(EV_EFI_ACTION\) found in log, before config`)
+}
+
+func (s *fwLoadHandlerSuite) TestMeasureImageStartSecureBootPolicyProfileRejectsMisplacedHPPreBootDMAConfig(c *C) {
+ log := newLogWithHPPreBootDMAConfigEvent(c)
+ var (
+ events []*tcglog.Event
+ hpEvent *tcglog.Event
+ added bool
+ )
+ for _, ev := range log.Events {
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) {
+ hpEvent = ev
+ continue
+ }
+ events = append(events, ev)
+ if ev.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ ev.EventType == tcglog.EventTypeEFIVariableDriverConfig &&
+ !added {
+ events = append(events, hpEvent)
+ added = true
+ }
+ }
+ c.Assert(hpEvent, NotNil)
+ c.Assert(added, testutil.IsTrue)
+ log.Events = events
+
+ err := measureSecureBootPolicyProfileError(c, log)
+ c.Check(err, ErrorMatches, `cannot measure secure boot policy: unexpected event type \(EV_EFI_ACTION\) found in log`)
+}
diff --git a/efi/preinstall/check_pcr7.go b/efi/preinstall/check_pcr7.go
index 1b67ef9c..be4aac66 100644
--- a/efi/preinstall/check_pcr7.go
+++ b/efi/preinstall/check_pcr7.go
@@ -429,9 +429,10 @@ func checkSecureBootPolicyMeasurementsAndObtainAuthorities(ctx context.Context,
}
var (
- db efi.SignatureDatabase // The authorized signature database from the TCG log.
- measuredSignatures tpm2.DigestList // The verification event digests measured by the firmware
- seenIBLLoadEvent bool // Whether we've seen the launch event for the OS initial boot loader
+ db efi.SignatureDatabase // The authorized signature database from the TCG log.
+ measuredSignatures tpm2.DigestList // The verification event digests measured by the firmware
+ seenIBLLoadEvent bool // Whether we've seen the launch event for the OS initial boot loader
+ seenHPPreBootDMAConfig bool // Whether we've seen HP's additional pre-boot DMA configuration event
)
phaseTracker := newTcgLogPhaseTracker()
@@ -452,7 +453,8 @@ NextEvent:
switch ev.EventType {
case tcglog.EventTypeEFIAction:
// An EV_EFI_ACTION event measured to PCR7 may indicate some degraded condition
- // that weakens device security. 2 known ones are:
+ // that weakens device security, or may record a security-relevant
+ // platform configuration. 3 known ones are:
// - "UEFI Debug Mode", which indicates the presence of a debugging endpoint.
// The TCG PC Client PFP spec says this goes before the secure boot config
// is measured.
@@ -463,15 +465,25 @@ NextEvent:
// generate a policy that includes it. However, the tianocore documentation
// doesn't specify event ordering, so we need to accommodate any possible
// ordering of events.
+ // - HP's exact additional DMA settings event, which records enabled SVM,
+ // DMA protection and pre-boot DMA protection for all PCIe devices. This
+ // is accepted once before the secure boot configuration and its digest
+ // is validated below.
//
- // The presence of an EV_EFI_ACTION event other than "DMA Protection Disabled"
- // will result in WithSecureBootPolicyProfile() creating an invalid policy,
- // because it generally doesn't emit these measurements. Just return an error
- // here to prevent the use of WithSecureBootPolicyProfile() unless it is a
- // "DMA Protection Disabled" event and it is permitted.
+ // Other EV_EFI_ACTION events will result in
+ // WithSecureBootPolicyProfile() creating an invalid policy because it
+ // generally doesn't emit these measurements. Reject them here.
//
// Note that "UEFI Debug Mode" and "DMA Protection Disabled" events are both
// caught by the host security checks, which run before this.
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) && !seenHPPreBootDMAConfig {
+ expectedDigest := tcglog.ComputeStringEventDigest(pcrAlg.GetHash(), string(ev.Data.Bytes()))
+ if !bytes.Equal(ev.Digests[pcrAlg], expectedDigest) {
+ return nil, errors.New("invalid digest for HP pre-boot DMA configuration event")
+ }
+ seenHPPreBootDMAConfig = true
+ continue NextEvent
+ }
if permitDMAProtectionDisabledEvent && (bytes.Equal(ev.Data.Bytes(), []byte(tcglog.DMAProtectionDisabled)) ||
bytes.Equal(ev.Data.Bytes(), append([]byte(tcglog.DMAProtectionDisabled), 0x00))) {
// This event is detected by the host security checks which will result in a flag
diff --git a/efi/preinstall/pcr7_hp_dma_event_test.go b/efi/preinstall/pcr7_hp_dma_event_test.go
new file mode 100644
index 00000000..b3be88eb
--- /dev/null
+++ b/efi/preinstall/pcr7_hp_dma_event_test.go
@@ -0,0 +1,165 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2026 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 (
+ "crypto"
+
+ efi "github.com/canonical/go-efilib"
+ "github.com/canonical/go-tpm2"
+ "github.com/canonical/tcglog-parser"
+ secboot_efi "github.com/snapcore/secboot/efi"
+ . "github.com/snapcore/secboot/efi/preinstall"
+ internal_efi "github.com/snapcore/secboot/internal/efi"
+ "github.com/snapcore/secboot/internal/efitest"
+ "github.com/snapcore/secboot/internal/testutil"
+ . "gopkg.in/check.v1"
+)
+
+const hpPreBootDMAConfigEventData = `"SVM CPU Virtualization":"Enable";"DMA protection":"Enable";"Pre-boot DMA protection":"All PCIe devices";`
+
+func newLogWithHPPreBootDMAConfigEvent(c *C) *tcglog.Log {
+ log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}})
+ event := &tcglog.Event{
+ PCRIndex: internal_efi.SecureBootPolicyPCR,
+ EventType: tcglog.EventTypeEFIAction,
+ Digests: tcglog.DigestMap{
+ tpm2.HashAlgorithmSHA256: tcglog.ComputeStringEventDigest(crypto.SHA256, hpPreBootDMAConfigEventData),
+ },
+ Data: tcglog.StringEventData(hpPreBootDMAConfigEventData),
+ }
+
+ var events []*tcglog.Event
+ added := false
+ for _, ev := range log.Events {
+ if ev.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ ev.EventType == tcglog.EventTypeEFIVariableDriverConfig &&
+ !added {
+ events = append(events, event)
+ added = true
+ }
+ events = append(events, ev)
+ }
+ c.Assert(added, testutil.IsTrue)
+ log.Events = events
+ return log
+}
+
+func hpPreBootDMAConfigTestVars(c *C) efitest.MockVars {
+ return efitest.MockVars{
+ {Name: "AuditMode", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeNonVolatile | efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x0}},
+ {Name: "DeployedMode", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeNonVolatile | efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x1}},
+ {Name: "SetupMode", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x0}},
+ {Name: "OsIndicationsSupported", GUID: efi.GlobalVariable}: &efitest.VarEntry{Attrs: efi.AttributeNonVolatile | efi.AttributeBootserviceAccess | efi.AttributeRuntimeAccess, Payload: []byte{0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ }.SetSecureBoot(true).SetPK(c, efitest.NewSignatureListX509(c, snakeoilCert, efi.MakeGUID(0x03f66fa4, 0x5eee, 0x479c, 0xa408, [...]uint8{0xc4, 0xdc, 0x0a, 0x33, 0xfc, 0xde})))
+}
+
+func hpPreBootDMAConfigTestIBL(c *C) secboot_efi.Image {
+ return &mockImage{
+ signatures: []*efi.WinCertificateAuthenticode{
+ efitest.ReadWinCertificateAuthenticodeDetached(c, shimUbuntuSig4),
+ },
+ digest: testutil.DecodeHexString(c, "25e1b08db2f31ff5f5d2ea53e1a1e8fda6e1d81af4f26a7908071f1dec8611b7"),
+ }
+}
+
+func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoodWithHPPreBootDMAConfig(c *C) {
+ err := s.testCheckSecureBootPolicyMeasurementsAndObtainAuthorities(c, &testCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(hpPreBootDMAConfigTestVars(c)),
+ efitest.WithLog(newLogWithHPPreBootDMAConfigEvent(c)),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ iblImage: hpPreBootDMAConfigTestIBL(c),
+ expectedFlags: SecureBootPolicyResultFlags(0),
+ expectedUsedAuthorities: []*X509CertificateID{
+ NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
+ },
+ })
+ c.Check(err, IsNil)
+}
+
+func (s *pcr7Suite) checkHPPreBootDMAConfigLog(c *C, log *tcglog.Log) error {
+ return s.testCheckSecureBootPolicyMeasurementsAndObtainAuthorities(c, &testCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesParams{
+ env: efitest.NewMockHostEnvironmentWithOpts(
+ efitest.WithMockVars(hpPreBootDMAConfigTestVars(c)),
+ efitest.WithLog(log),
+ ),
+ pcrAlg: tpm2.HashAlgorithmSHA256,
+ iblImage: hpPreBootDMAConfigTestIBL(c),
+ expectedUsedAuthorities: []*X509CertificateID{
+ NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
+ },
+ })
+}
+
+func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesRejectsHPPreBootDMAConfigWithWrongDigest(c *C) {
+ log := newLogWithHPPreBootDMAConfigEvent(c)
+ for _, ev := range log.Events {
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) {
+ ev.Digests[tpm2.HashAlgorithmSHA256] = make(tpm2.Digest, tpm2.HashAlgorithmSHA256.Size())
+ }
+ }
+
+ err := s.checkHPPreBootDMAConfigLog(c, log)
+ c.Check(err, ErrorMatches, `invalid digest for HP pre-boot DMA configuration event`)
+}
+
+func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesRejectsDuplicateHPPreBootDMAConfig(c *C) {
+ log := newLogWithHPPreBootDMAConfigEvent(c)
+ var events []*tcglog.Event
+ for _, ev := range log.Events {
+ events = append(events, ev)
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) {
+ events = append(events, ev)
+ }
+ }
+ log.Events = events
+
+ err := s.checkHPPreBootDMAConfigLog(c, log)
+ c.Check(err, ErrorMatches, `unexpected EV_EFI_ACTION event .* before config`)
+}
+
+func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesRejectsMisplacedHPPreBootDMAConfig(c *C) {
+ log := newLogWithHPPreBootDMAConfigEvent(c)
+ var (
+ events []*tcglog.Event
+ hpEvent *tcglog.Event
+ added bool
+ )
+ for _, ev := range log.Events {
+ if internal_efi.IsHPPreBootDMAConfigEvent(ev) {
+ hpEvent = ev
+ continue
+ }
+ events = append(events, ev)
+ if ev.PCRIndex == internal_efi.SecureBootPolicyPCR &&
+ ev.EventType == tcglog.EventTypeEFIVariableDriverConfig &&
+ !added {
+ events = append(events, hpEvent)
+ added = true
+ }
+ }
+ c.Assert(hpEvent, NotNil)
+ c.Assert(added, testutil.IsTrue)
+ log.Events = events
+
+ err := s.checkHPPreBootDMAConfigLog(c, log)
+ c.Check(err, ErrorMatches, `unexpected EV_EFI_ACTION event .* whilst measuring config`)
+}
diff --git a/internal/efi/tcg_events.go b/internal/efi/tcg_events.go
index b6f49e80..c35eae1a 100644
--- a/internal/efi/tcg_events.go
+++ b/internal/efi/tcg_events.go
@@ -20,6 +20,7 @@
package efi
import (
+ "bytes"
"errors"
"fmt"
@@ -27,6 +28,8 @@ import (
"github.com/canonical/tcglog-parser"
)
+const hpPreBootDMAConfigEventData = `"SVM CPU Virtualization":"Enable";"DMA protection":"Enable";"Pre-boot DMA protection":"All PCIe devices";`
+
// IsVendorEventType indicates whether the supplied event type is vendor
// defined. Officially, this applies to any event type that is not within the
// range of TCG reserved types (0x00000000-0x0000ffff and 0x80000000-0x8000ffff),
@@ -42,6 +45,15 @@ func IsVendorEventType(t tcglog.EventType) bool {
}
}
+// IsHPPreBootDMAConfigEvent indicates whether the supplied event is the exact
+// virtualization and pre-boot DMA configuration measurement produced by HP
+// firmware when "Measure Additional DMA Settings" is directed to PCR7.
+func IsHPPreBootDMAConfigEvent(ev *tcglog.Event) bool {
+ return ev.PCRIndex == SecureBootPolicyPCR &&
+ ev.EventType == tcglog.EventTypeEFIAction &&
+ bytes.Equal(ev.Data.Bytes(), []byte(hpPreBootDMAConfigEventData))
+}
+
// IsLaunchedFromFirmwareVolume indicates that the supplied event is associated
// with an image launch from a firmware volume.
func IsLaunchedFromFirmwareVolume(ev *tcglog.Event) (yes bool, err error) {
diff --git a/internal/efi/tcg_events_test.go b/internal/efi/tcg_events_test.go
index 90bb6a26..cd12a114 100644
--- a/internal/efi/tcg_events_test.go
+++ b/internal/efi/tcg_events_test.go
@@ -51,6 +51,54 @@ func (*tcgEventsSuite) TestIsVendorEventType(c *C) {
}
}
+func (*tcgEventsSuite) TestIsHPPreBootDMAConfigEvent(c *C) {
+ const hpData = `"SVM CPU Virtualization":"Enable";"DMA protection":"Enable";"Pre-boot DMA protection":"All PCIe devices";`
+
+ for _, params := range []struct {
+ event *tcglog.Event
+ expected bool
+ }{
+ {
+ event: &tcglog.Event{
+ PCRIndex: 7,
+ EventType: tcglog.EventTypeEFIAction,
+ Data: tcglog.StringEventData(hpData),
+ },
+ expected: true,
+ },
+ {
+ event: &tcglog.Event{
+ PCRIndex: 6,
+ EventType: tcglog.EventTypeEFIAction,
+ Data: tcglog.StringEventData(hpData),
+ },
+ },
+ {
+ event: &tcglog.Event{
+ PCRIndex: 7,
+ EventType: tcglog.EventTypeEFIPlatformFirmwareBlob,
+ Data: tcglog.StringEventData(hpData),
+ },
+ },
+ {
+ event: &tcglog.Event{
+ PCRIndex: 7,
+ EventType: tcglog.EventTypeEFIAction,
+ Data: tcglog.StringEventData("UEFI Debug Mode"),
+ },
+ },
+ {
+ event: &tcglog.Event{
+ PCRIndex: 7,
+ EventType: tcglog.EventTypeEFIAction,
+ Data: tcglog.StringEventData(hpData + "\x00"),
+ },
+ },
+ } {
+ c.Check(IsHPPreBootDMAConfigEvent(params.event), Equals, params.expected)
+ }
+}
+
type invalidEventData struct {
err error
}