Skip to content

Commit

Permalink
efi: Add profiles for PCRs 0 and 2
Browse files Browse the repository at this point in the history
This adds profiles for PCR0 (platform firmware) and PCR2 (host
firmware that runs from adapter cards or firmware that runs on
embedded controllers)
  • Loading branch information
chrisccoulson committed May 22, 2024
1 parent 21595ba commit 14e0082
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 15 deletions.
8 changes: 5 additions & 3 deletions efi/efi.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
package efi

const (
bootManagerCodePCR = 4 // Boot Manager Code and Boot Attempts PCR
secureBootPCR = 7 // Secure Boot Policy Measurements PCR
kernelConfigPCR = 12
platformFirmwarePCR = 0 // SRTM, POST BIOS, and Embedded Drivers
driversAndAppsPCR = 2 // UEFI Drivers and UEFI Applications
bootManagerCodePCR = 4 // Boot Manager Code and Boot Attempts PCR
secureBootPCR = 7 // Secure Boot Policy Measurements PCR
kernelConfigPCR = 12
)
35 changes: 35 additions & 0 deletions efi/efi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type mockPcrBranchEventType int

const (
mockPcrBranchResetEvent mockPcrBranchEventType = iota
mockPcrBranchResetCRTMPCREvent
mockPcrBranchExtendEvent
mockPcrBranchMeasureVariableEvent
)
Expand All @@ -69,6 +70,8 @@ type mockPcrBranchEvent struct {
pcr int
eventType mockPcrBranchEventType

locality uint8

digest tpm2.Digest

varName efi.VariableDescriptor
Expand Down Expand Up @@ -120,6 +123,14 @@ func (c *mockPcrBranchContext) ResetPCR(pcr int) {
})
}

func (c *mockPcrBranchContext) ResetCRTMPCR(locality uint8) {
c.events = append(c.events, &mockPcrBranchEvent{
pcr: 0,
eventType: mockPcrBranchResetCRTMPCREvent,
locality: locality,
})
}

func (c *mockPcrBranchContext) ExtendPCR(pcr int, digest tpm2.Digest) {
c.events = append(c.events, &mockPcrBranchEvent{
pcr: pcr,
Expand Down Expand Up @@ -652,3 +663,27 @@ func (s *mockSecureBootNamespaceRules) AddAuthorities(certs ...*x509.Certificate
func (mockSecureBootNamespaceRules) NewImageLoadHandler(image PeImageHandle) (ImageLoadHandler, error) {
return nil, errors.New("not implemented")
}

type mockErrLogData struct {
err error
}

func (d *mockErrLogData) String() string {
return fmt.Sprintf("Invalid event data: %v", d.err)
}

func (d *mockErrLogData) Bytes() []byte {
panic("not implemented")
}

func (d *mockErrLogData) Write(w io.Writer) error {
panic("not implemented")
}

func (d *mockErrLogData) Error() string {
return d.err.Error()
}

func (d *mockErrLogData) Unwrap() error {
return d.err
}
2 changes: 2 additions & 0 deletions efi/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
// Export constants for testing
const (
BootManagerCodeProfile = bootManagerCodeProfile
DriversAndAppsProfile = driversAndAppsProfile
GrubChainloaderUsesShimProtocol = grubChainloaderUsesShimProtocol
KernelConfigProfile = kernelConfigProfile
PlatformFirmwareProfile = platformFirmwareProfile
SecureBootPolicyProfile = secureBootPolicyProfile
ShimFixVariableAuthorityEventsMatchSpec = shimFixVariableAuthorityEventsMatchSpec
ShimHasSbatRevocationManagement = shimHasSbatRevocationManagement
Expand Down
64 changes: 61 additions & 3 deletions efi/fw_load_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,56 @@ func (h *fwLoadHandler) measureSecureBootPolicyPreOS(ctx pcrBranchContext) error
return nil
}

func (h *fwLoadHandler) measurePlatformFirmware(ctx pcrBranchContext) error {
donePcrReset := false

for _, event := range h.log.Events {
if event.PCRIndex != platformFirmwarePCR {
continue
}
if event.EventType == tcglog.EventTypeNoAction {
if err, isErr := event.Data.(error); isErr {
return fmt.Errorf("cannot decode EV_NO_ACTION event data: %w", err)
}
if loc, isLoc := event.Data.(*tcglog.StartupLocalityEventData); isLoc {
if donePcrReset {
return errors.New("log for PCR0 has an unexpected StartupLocality event")
}
ctx.ResetCRTMPCR(loc.StartupLocality)
donePcrReset = true
}
continue
}

if !donePcrReset {
ctx.ResetPCR(platformFirmwarePCR)
donePcrReset = true
}

ctx.ExtendPCR(platformFirmwarePCR, tpm2.Digest(event.Digests[ctx.PCRAlg()]))
if event.EventType == tcglog.EventTypeSeparator {
break
}
}

return nil
}

func (h *fwLoadHandler) measureDriversAndApps(ctx pcrBranchContext) {
ctx.ResetPCR(driversAndAppsPCR)

for _, event := range h.log.Events {
if event.PCRIndex != driversAndAppsPCR {
continue
}

ctx.ExtendPCR(driversAndAppsPCR, tpm2.Digest(event.Digests[ctx.PCRAlg()]))
if event.EventType == tcglog.EventTypeSeparator {
break
}
}
}

func (h *fwLoadHandler) measureBootManagerCodePreOS(ctx pcrBranchContext) {
ctx.ResetPCR(bootManagerCodePCR)

Expand Down Expand Up @@ -189,14 +239,22 @@ func (h *fwLoadHandler) MeasureImageStart(ctx pcrBranchContext) error {
return errors.New("the TCG event log does not have the requested algorithm")
}

if ctx.Flags()&secureBootPolicyProfile > 0 {
if err := h.measureSecureBootPolicyPreOS(ctx); err != nil {
return xerrors.Errorf("cannot measure secure boot policy: %w", err)
if ctx.Flags()&platformFirmwareProfile > 0 {
if err := h.measurePlatformFirmware(ctx); err != nil {
return fmt.Errorf("cannot measure platform firmware policy: %w", err)
}
}
if ctx.Flags()&driversAndAppsProfile > 0 {
h.measureDriversAndApps(ctx)
}
if ctx.Flags()&bootManagerCodeProfile > 0 {
h.measureBootManagerCodePreOS(ctx)
}
if ctx.Flags()&secureBootPolicyProfile > 0 {
if err := h.measureSecureBootPolicyPreOS(ctx); err != nil {
return xerrors.Errorf("cannot measure secure boot policy: %w", err)
}
}
if ctx.Flags()&kernelConfigProfile > 0 {
ctx.ResetPCR(kernelConfigPCR)
}
Expand Down
125 changes: 122 additions & 3 deletions efi/fw_load_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
package efi_test

import (
"fmt"
"io"

. "gopkg.in/check.v1"

efi "github.com/canonical/go-efilib"
Expand Down Expand Up @@ -58,6 +61,9 @@ func (s *fwLoadHandlerSuite) testMeasureImageStart(c *C, data *testFwMeasureImag
handler := NewFwLoadHandler(efitest.NewLog(c, data.logOptions))
c.Check(handler.MeasureImageStart(ctx), IsNil)
c.Check(ctx.events, DeepEquals, data.expectedEvents)
for _, event := range ctx.events {
c.Logf("pcr:%d, type:%v, digest:%#x", event.pcr, event.eventType, event.digest)
}
c.Check(collector.More(), testutil.IsFalse)
return ctx.FwContext()
}
Expand Down Expand Up @@ -193,21 +199,77 @@ func (s *fwLoadHandlerSuite) TestMeasureImageStartSecureBootPolicyAndBootManager
alg: tpm2.HashAlgorithmSHA256,
flags: BootManagerCodeProfile | SecureBootPolicyProfile,
expectedEvents: []*mockPcrBranchEvent{
{pcr: 4, eventType: mockPcrBranchResetEvent},
{pcr: 4, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba")},
{pcr: 4, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
{pcr: 7, eventType: mockPcrBranchResetEvent},
{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")},
{pcr: 4, eventType: mockPcrBranchResetEvent},
{pcr: 4, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba")},
{pcr: 4, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
},
})
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartPlatformFirmwareProfile(c *C) {
s.testMeasureImageStart(c, &testFwMeasureImageStartData{
logOptions: &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA1}},
alg: tpm2.HashAlgorithmSHA256,
flags: PlatformFirmwareProfile,
expectedEvents: []*mockPcrBranchEvent{
{pcr: 0, eventType: mockPcrBranchResetEvent},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "d0ff5974b6aa52cf562bea5921840c032a860a91a3512f7fe8f768f6bbe005f6")},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "aef237d4703e8936530141636186a9f249fa39e194f02f668cd328bd5902cf03")},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "8b0eec99d3cccc081edb98c3a2aa74b99a02b785bd74513e1cf7401e99121e80")},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
},
})
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartPlatformFirmwareProfileSL3(c *C) {
s.testMeasureImageStart(c, &testFwMeasureImageStartData{
logOptions: &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA1}, StartupLocality: 3},
alg: tpm2.HashAlgorithmSHA256,
flags: PlatformFirmwareProfile,
expectedEvents: []*mockPcrBranchEvent{
{pcr: 0, eventType: mockPcrBranchResetCRTMPCREvent, locality: 3},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "d0ff5974b6aa52cf562bea5921840c032a860a91a3512f7fe8f768f6bbe005f6")},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "aef237d4703e8936530141636186a9f249fa39e194f02f668cd328bd5902cf03")},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "8b0eec99d3cccc081edb98c3a2aa74b99a02b785bd74513e1cf7401e99121e80")},
{pcr: 0, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
},
})
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsProfile(c *C) {
s.testMeasureImageStart(c, &testFwMeasureImageStartData{
logOptions: &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA1}},
alg: tpm2.HashAlgorithmSHA256,
flags: DriversAndAppsProfile,
expectedEvents: []*mockPcrBranchEvent{
{pcr: 2, eventType: mockPcrBranchResetEvent},
{pcr: 2, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
},
})
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartDriversAndAppsProfile2(c *C) {
s.testMeasureImageStart(c, &testFwMeasureImageStartData{
logOptions: &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA1}, IncludeDriverLaunch: true},
alg: tpm2.HashAlgorithmSHA256,
flags: DriversAndAppsProfile,
expectedEvents: []*mockPcrBranchEvent{
{pcr: 2, eventType: mockPcrBranchResetEvent},
{pcr: 2, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "1e94aaed2ad59a4409f3230dca2ad8c03ef8e3fde77cc47dc7b81bb8b242f3e6")},
{pcr: 2, eventType: mockPcrBranchExtendEvent, digest: testutil.DecodeHexString(c, "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")},
},
})
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog1(c *C) {
// Insert a second EV_SEPARATOR event into PCR7
collector := NewRootVarsCollector(efitest.NewMockHostEnvironment(makeMockVars(c, withMsSecureBootConfig()), nil))
ctx := newMockPcrBranchContext(&mockPcrProfileContext{
alg: tpm2.HashAlgorithmSHA256,
Expand All @@ -231,6 +293,7 @@ func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog1(c *C) {
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog2(c *C) {
// Prepend a verification event into PCR7 before the EV_SEPARATOR
collector := NewRootVarsCollector(efitest.NewMockHostEnvironment(makeMockVars(c, withMsSecureBootConfig()), nil))
ctx := newMockPcrBranchContext(&mockPcrProfileContext{
alg: tpm2.HashAlgorithmSHA256,
Expand All @@ -257,6 +320,7 @@ func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog2(c *C) {
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog3(c *C) {
// Append a configuration event into PCR7 after the EV_SEPARATOR
collector := NewRootVarsCollector(efitest.NewMockHostEnvironment(makeMockVars(c, withMsSecureBootConfig()), nil))
ctx := newMockPcrBranchContext(&mockPcrProfileContext{
alg: tpm2.HashAlgorithmSHA256,
Expand All @@ -283,6 +347,7 @@ func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog3(c *C) {
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog4(c *C) {
// Insert an unexpected event type into PCR7
collector := NewRootVarsCollector(efitest.NewMockHostEnvironment(makeMockVars(c, withMsSecureBootConfig()), nil))
ctx := newMockPcrBranchContext(&mockPcrProfileContext{
alg: tpm2.HashAlgorithmSHA256,
Expand All @@ -308,6 +373,60 @@ func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog4(c *C) {
c.Check(handler.MeasureImageStart(ctx), ErrorMatches, `cannot measure secure boot policy: unexpected event type \(EV_EFI_BOOT_SERVICES_APPLICATION\) found in log`)
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog5(c *C) {
// Insert an invalid StartupLocality event data into the log
collector := NewRootVarsCollector(efitest.NewMockHostEnvironment(nil, nil))
ctx := newMockPcrBranchContext(&mockPcrProfileContext{
alg: tpm2.HashAlgorithmSHA256,
flags: PlatformFirmwareProfile}, nil, collector.Next())

log := efitest.NewLog(c, &efitest.LogOptions{
Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA1},
StartupLocality: 3})
for i, event := range log.Events {
if event.PCRIndex == 0 && event.EventType == tcglog.EventTypeNoAction {
if _, isLoc := event.Data.(*tcglog.StartupLocalityEventData); !isLoc {
continue
}
// Overwrite the event data with a mock error event
log.Events[i].Data = &mockErrLogData{fmt.Errorf("cannot decode StartupLocality data: %w", io.EOF)}
break
}
}

handler := NewFwLoadHandler(log)
c.Check(handler.MeasureImageStart(ctx), ErrorMatches, `cannot measure platform firmware policy: cannot decode EV_NO_ACTION event data: cannot decode StartupLocality data: EOF`)
}

func (s *fwLoadHandlerSuite) TestMeasureImageStartErrBadLog6(c *C) {
// Insert an extra StartupLocality event data into the log
collector := NewRootVarsCollector(efitest.NewMockHostEnvironment(nil, nil))
ctx := newMockPcrBranchContext(&mockPcrProfileContext{
alg: tpm2.HashAlgorithmSHA256,
flags: PlatformFirmwareProfile}, nil, collector.Next())

log := efitest.NewLog(c, &efitest.LogOptions{
Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256, tpm2.HashAlgorithmSHA1},
StartupLocality: 3})
for i, event := range log.Events {
if event.PCRIndex == 0 && event.EventType == tcglog.EventTypeNoAction {
if _, isLoc := event.Data.(*tcglog.StartupLocalityEventData); !isLoc {
continue
}
events := log.Events[:i]
events = append(events, event, event)
if len(log.Events) > i+1 {
events = append(events, log.Events[i+1:]...)
}
log.Events = events
break
}
}

handler := NewFwLoadHandler(log)
c.Check(handler.MeasureImageStart(ctx), ErrorMatches, `cannot measure platform firmware policy: log for PCR0 has an unexpected StartupLocality event`)
}

type testFwMeasureImageLoadData struct {
alg tpm2.HashAlgorithmId
flags PcrProfileFlags
Expand Down
9 changes: 8 additions & 1 deletion efi/pcr_branch_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type pcrBranchContext interface {
ShimContext() *shimContext // access the shim state for this branch

ResetPCR(pcr int) // reset the specified PCR for this branch
ResetCRTMPCR(locality uint8) // reset the S-CRTM PCR (0) from the specified locality
ExtendPCR(pcr int, digest tpm2.Digest) // extend the specified PCR for this branch
MeasureVariable(pcr int, guid efi.GUID, name string, data []byte) // measure the specified variable for this branch
}
Expand Down Expand Up @@ -90,7 +91,13 @@ func (c *pcrBranchCtx) ShimContext() *shimContext {
}

func (c *pcrBranchCtx) ResetPCR(pcr int) {
c.branch.AddPCRValue(c.PCRAlg(), pcr, make(tpm2.Digest, c.PCRAlg().Size()))
c.branch.AddPCRValue(c.PCRAlg(), pcr, make([]byte, c.PCRAlg().Size()))
}

func (c *pcrBranchCtx) ResetCRTMPCR(locality uint8) {
value := make([]byte, c.PCRAlg().Size())
value[len(value)-1] = locality
c.branch.AddPCRValue(c.PCRAlg(), platformFirmwarePCR, value)
}

func (c *pcrBranchCtx) ExtendPCR(pcr int, digest tpm2.Digest) {
Expand Down
Loading

0 comments on commit 14e0082

Please sign in to comment.