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 }