Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion extract/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,64 @@ func SecureBootState(replayEvents []tcg.Event, registerCfg registerConfig) (*pb.
}, nil
}

// EfiDriverState extracts EFI Driver information from a UEFI TCG2 firmware event log.
// Obtained from section 3.3.4.3 PCR[2]-UEFI Drivers and UEFI Applications
// https://trustedcomputinggroup.org/wp-content/uploads/TCG-PC-Client-Platform-Firmware-Profile-Version-1.06-Revision-52_pub-3.pdf
func EfiDriverState(events []tcg.Event, registerCfg registerConfig) (*pb.EfiState, error) {
var (
seenSeparator bool
efiDriverStates []*pb.EfiApp
efiRuntimeDriverStates []*pb.EfiApp
)
for _, e := range events {
if e.MRIndex() != registerCfg.FirmwareDriverIdx {
continue
}

et, err := tcg.UntrustedParseEventType(uint32(e.UntrustedType()))
if err != nil {
return nil, fmt.Errorf("unrecognised event type: %v", err)
}
digestVerify := DigestEquals(e, e.RawData())
switch et {
case tcg.Separator:
if seenSeparator {
return nil, fmt.Errorf("duplicate separator at event %d", e.Num())
}
seenSeparator = true
if !bytes.Equal(e.RawData(), []byte{0, 0, 0, 0}) {
return nil, fmt.Errorf("invalid separator data at event %d: %v", e.Num(), e.RawData())
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.Num(), digestVerify)
}

case tcg.EFIBootServicesDriver:
if !seenSeparator {
// The EFI Boot Services Driver will use the EFI LoadImage service, so try loading it.
_, err := tcg.ParseEFIImageLoad(bytes.NewReader(e.RawData()))
if err != nil {
return nil, fmt.Errorf("failed parsing EFI image load at boot services driver event %d: %v", e.Num(), err)
}
efiDriverStates = append(efiDriverStates, &pb.EfiApp{Digest: e.ReplayedDigest()})
}
case tcg.EFIRuntimeServicesDriver:
if !seenSeparator {
// The EFI Runtime Services Driver will use the EFI LoadImage service, so try loading it.
_, err := tcg.ParseEFIImageLoad(bytes.NewReader(e.RawData()))
if err != nil {
return nil, fmt.Errorf("failed parsing EFI image load at boot services driver event %d: %v", e.Num(), err)
}
efiRuntimeDriverStates = append(efiRuntimeDriverStates, &pb.EfiApp{Digest: e.ReplayedDigest()})
}
}
}
return &pb.EfiState{
BootServicesDrivers: efiDriverStates,
RuntimeServicesDrivers: efiRuntimeDriverStates,
}, nil
}

// PlatformState extracts platform information from a UEFI TCG2 firmware
// event log.
func PlatformState(hash crypto.Hash, events []tcg.Event) (*pb.PlatformState, error) {
Expand Down Expand Up @@ -381,7 +439,15 @@ func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig)
// Otherwise, software further down the bootchain could extend bad
// PCR4/RTMR2 measurements.
if seenExitBootServices {
return &pb.EfiState{Apps: efiAppStates}, nil
efiDriver, err := EfiDriverState(events, registerCfg)
if err != nil {
return nil, err
}
return &pb.EfiState{
Apps: efiAppStates,
BootServicesDrivers: efiDriver.BootServicesDrivers,
RuntimeServicesDrivers: efiDriver.RuntimeServicesDrivers,
}, nil
}
return nil, nil
}
Expand Down
128 changes: 127 additions & 1 deletion extract/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package extract

import (
"bytes"
"crypto"
"crypto/rand"
"encoding/hex"
Expand All @@ -24,10 +25,12 @@ import (
"testing"

"github.com/google/go-eventlog/internal/testutil"
pb "github.com/google/go-eventlog/proto/state"
"github.com/google/go-eventlog/register"
"github.com/google/go-eventlog/tcg"
"github.com/google/go-eventlog/testdata"
"google.golang.org/protobuf/proto"

pb "github.com/google/go-eventlog/proto/state"
)

func TestExtractFirmwareLogStateRTMR(t *testing.T) {
Expand Down Expand Up @@ -373,6 +376,129 @@ func TestGrubStateFromRTMRLogWithModifiedNullTerminator(t *testing.T) {
}
}

func TestEfiState(t *testing.T) {
tests := []struct {
name string
events func() (crypto.Hash, []tcg.Event)
registserConfig registerConfig
wantPass bool
wantEfiState *pb.EfiState
}{
{
name: "success with TPM logs",
events: func() (crypto.Hash, []tcg.Event) {
return getTPMELEvents(t)
},
registserConfig: TPMRegisterConfig,
wantPass: true,
wantEfiState: &pb.EfiState{
Apps: []*pb.EfiApp{
{
Digest: []byte("rM\xe6\x84M\xd0\xfea\x8b\xa5wl{\xca\x07(\xbe8\xa6TN$\xe4N\xf2Y\xb9\x87\xb7\xab΀"),
},
{
Digest: []byte("^\x8c\xb7Z\xcd\xf8\xe0\x9e_\xc1L\xc2\xd6\xce\x0c\"\x88\xaf \x89v\xd9s\t\x85\x1cf\x1e\x91\xec\x1e\x03"),
},
},
},
},
{
name: "success with CCEL logs",
events: func() (crypto.Hash, []tcg.Event) {
return crypto.SHA384, getCCELEvents(t)
},
registserConfig: RTMRRegisterConfig,
wantPass: true,
wantEfiState: &pb.EfiState{
Apps: []*pb.EfiApp{
{
Digest: []byte("Z\x10\x02l\x9a\xd4\x1d\x1f\x90ܜ\xfe\x88\xbc\xab\xe1\x84,\xcf\xd8T\x95\xc8\x1b\x1a\x1a\xb9&\xa9\xef#\xb5\xd2\xe6\x0e\xef\xeb\xa0A[\xbe\\\x8c2\x8a\x89\x9a\n"),
},
{
Digest: []byte("\xb1\xfb\x7fL\x06\x89\xf5\xa9 \xb8\x00\xb2`pu\xf4\x90o\x8c\x82\x82\xd4NV\xfc\x99\x1e\xc0\x1f\x1a\xda\xc1v\xd2\x04\n&\xf1E=\xf1\x12\xd7\xc4\xf4)?\xc9"),
},
},
},
},
{
name: "nil EFI state with missing ExitBootServicesInvocation event in TPM logs",
events: func() (crypto.Hash, []tcg.Event) {
hash, evts := getTPMELEvents(t)
var failedEvts []tcg.Event
for _, e := range evts {
if bytes.Equal(e.RawData(), []byte(tcg.ExitBootServicesInvocation)) {
continue
}
failedEvts = append(failedEvts, e)
}
return hash, failedEvts
},
registserConfig: TPMRegisterConfig,
wantPass: true,
wantEfiState: nil,
},
{
name: "failed with missing CallingEFIApp event in TPM logs",
events: func() (crypto.Hash, []tcg.Event) {
hash, evts := getTPMELEvents(t)
var failedEvts []tcg.Event
for _, e := range evts {
if bytes.Equal(e.RawData(), []byte(tcg.CallingEFIApplication)) {
continue
}
failedEvts = append(failedEvts, e)
}
return hash, failedEvts
},
registserConfig: TPMRegisterConfig,
wantPass: false,
wantEfiState: nil,
},
{
name: "failed with multiple separators in TPM logs",
events: func() (crypto.Hash, []tcg.Event) {
hash, evts := getTPMELEvents(t)
for i := range evts {
evts[i].Type = tcg.Separator
}
return hash, evts
},
registserConfig: TPMRegisterConfig,
wantPass: false,
wantEfiState: nil,
},
{
name: "failed with bad data in TPM logs",
events: func() (crypto.Hash, []tcg.Event) {
hash, evts := getTPMELEvents(t)
for i := range evts {
b := make([]byte, len(evts[i].Data))
if _, err := rand.Read(b); err != nil {
t.Fatal(err)
}
evts[i].Data = b
}
return hash, evts
},
registserConfig: TPMRegisterConfig,
wantPass: false,
wantEfiState: nil,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
hash, events := tc.events()
efiState, err := EfiState(hash, events, tc.registserConfig)
if gotPass := (err == nil); gotPass != tc.wantPass {
t.Errorf("EfiState returned unexpected result, gotPass %v, but want %v", gotPass, tc.wantPass)
}
if !proto.Equal(efiState, tc.wantEfiState) {
t.Errorf("EfiState returned unexpected state, got %+v, but want %+v", efiState, tc.wantEfiState)
}
})
}
}

func getTPMELEvents(t *testing.T) (crypto.Hash, []tcg.Event) {
log := testdata.Ubuntu2404AmdSevSnpEventLog
bank := testutil.MakePCRBank(pb.HashAlgo_SHA256, map[uint32][]byte{
Expand Down
9 changes: 8 additions & 1 deletion proto/state.proto
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,24 @@ message SecureBootState {

message EfiApp {
// The PE/COFF digest of the EFI application (pulled from the raw event digest).
// This can also represent digest of the EFI boot/runtime service drivers.
bytes digest = 1;
}

// The verified state of EFI Applications. Policy usage on this machine state
// The verified state of EFI Drivers and Applications. Policy usage on this machine state
// should check the entire set of EFI App digests matches, not a subset.
message EfiState {
// UEFI's OS Loader code is required to measure attempts to load and execute
// UEFI applications.
// UEFI applications are typically bootloaders such as shim and GRUB.
// These run and are measured using the UEFI LoadImage() service.
repeated EfiApp apps = 1;
// The EFI drivers,
// obtained from https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf#page=22.
// The EFI Boot Services Drivers from adapter or loaded bydriver in adapter.
repeated EfiApp boot_services_drivers = 2;
// The EFI Runtime Drivers from adapter or loaded bydriver in adapter.
repeated EfiApp runtime_services_drivers = 3;
}

// Enum values come from the TCG Algorithm Registry - v1.27 - Table 3.
Expand Down
Loading
Loading