Skip to content

Commit

Permalink
Merge pull request #299 from chrisccoulson/add-pcr0-and-pcr2-profiles
Browse files Browse the repository at this point in the history
efi: Add profiles for PCRs 0 and 2.

This adds profiles for PCR0 (platform firmware) and PCR2 (host firmware that
runs from adapter cards or firmware that runs on embedded controllers)

The goal is that an eventual PR that implements the pre-install checks will return
an options that contains the optimal set of TCG defined PCRs, which may or
may not include these new profiles depending on the device configuration. There
will be some additional options to control this, such as selecting "optimal"
configuration or "most secure".
  • Loading branch information
chrisccoulson authored May 30, 2024
2 parents 68d5cc0 + 14e0082 commit 121a1da
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 121a1da

Please sign in to comment.