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
11 changes: 8 additions & 3 deletions extract/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ type Opts struct {
// AllowEmptySBVar allows the SecureBoot variable to be empty in addition to length 1 (0 or 1).
// This can be used when the SecureBoot variable is not initialized.
AllowEmptySBVar bool
// AllowEFIAppBeforeCallingEvent skips a check that requires
// EV_EFI_BOOT_SERVICES_APPLICATION to occur after a
// "Calling EFI Application from Boot Option". This option is useful when
// the host platform loads EFI Applications unrelated to OS boot.
AllowEFIAppBeforeCallingEvent bool
}

// FirmwareLogState extracts event info from a verified TCG PC Client event
Expand Down Expand Up @@ -88,7 +93,7 @@ func FirmwareLogState(events []tcg.Event, hash crypto.Hash, registerCfg register
if err != nil {
joined = errors.Join(joined, err)
}
efiState, err := EfiState(hash, events, registerCfg)
efiState, err := EfiState(hash, events, registerCfg, opts)

if err != nil {
joined = errors.Join(joined, err)
Expand Down Expand Up @@ -343,7 +348,7 @@ func PlatformState(hash crypto.Hash, events []tcg.Event) (*pb.PlatformState, err

// EfiState extracts EFI app information from a UEFI TCG2 firmware
// event log.
func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig) (*pb.EfiState, error) {
func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig, opts Opts) (*pb.EfiState, error) {
// We pre-compute various event digests, and check if those event type have
// been modified. We only trust events that come before the
// ExitBootServices() request.
Expand Down Expand Up @@ -393,7 +398,7 @@ func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig)
}

if evtType == tcg.EFIBootServicesApplication {
if !seenCallingEfiApp {
if !opts.AllowEFIAppBeforeCallingEvent && !seenCallingEfiApp {
return nil, fmt.Errorf("found EFIBootServicesApplication in %s%d before CallingEFIApp event", registerCfg.Name, index)
}
efiAppStates = append(efiAppStates, &pb.EfiApp{Digest: event.ReplayedDigest()})
Expand Down
21 changes: 20 additions & 1 deletion extract/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ func TestEfiState(t *testing.T) {
registserConfig registerConfig
wantPass bool
wantEfiState *pb.EfiState
opts Opts
}{
{
name: "success with TPM logs",
Expand All @@ -401,6 +402,9 @@ func TestEfiState(t *testing.T) {
},
},
},
opts: Opts{
AllowEFIAppBeforeCallingEvent: false,
},
},
{
name: "success with CCEL logs",
Expand All @@ -419,6 +423,9 @@ func TestEfiState(t *testing.T) {
},
},
},
opts: Opts{
AllowEFIAppBeforeCallingEvent: false,
},
},
{
name: "nil EFI state with missing ExitBootServicesInvocation event in TPM logs",
Expand All @@ -436,6 +443,9 @@ func TestEfiState(t *testing.T) {
registserConfig: TPMRegisterConfig,
wantPass: true,
wantEfiState: nil,
opts: Opts{
AllowEFIAppBeforeCallingEvent: false,
},
},
{
name: "failed with missing CallingEFIApp event in TPM logs",
Expand All @@ -453,6 +463,9 @@ func TestEfiState(t *testing.T) {
registserConfig: TPMRegisterConfig,
wantPass: false,
wantEfiState: nil,
opts: Opts{
AllowEFIAppBeforeCallingEvent: false,
},
},
{
name: "failed with multiple separators in TPM logs",
Expand All @@ -466,6 +479,9 @@ func TestEfiState(t *testing.T) {
registserConfig: TPMRegisterConfig,
wantPass: false,
wantEfiState: nil,
opts: Opts{
AllowEFIAppBeforeCallingEvent: false,
},
},
{
name: "failed with bad data in TPM logs",
Expand All @@ -483,12 +499,15 @@ func TestEfiState(t *testing.T) {
registserConfig: TPMRegisterConfig,
wantPass: false,
wantEfiState: nil,
opts: Opts{
AllowEFIAppBeforeCallingEvent: false,
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
hash, events := tc.events()
efiState, err := EfiState(hash, events, tc.registserConfig)
efiState, err := EfiState(hash, events, tc.registserConfig, tc.opts)
if gotPass := (err == nil); gotPass != tc.wantPass {
t.Errorf("EfiState returned unexpected result, gotPass %v, but want %v", gotPass, tc.wantPass)
}
Expand Down
2 changes: 2 additions & 0 deletions testdata/eventlog_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ var (
Cos101AmdSevEventLog []byte
//go:embed eventlogs/tpm/cos-121-amd-sev.bin
Cos121AmdSevEventLog []byte
//go:embed eventlogs/tpm/gdc-host.bin
GdcHost []byte
)

// Kernel command lines from event logs.
Expand Down
Binary file added testdata/eventlogs/tpm/gdc-host.bin
Binary file not shown.
79 changes: 60 additions & 19 deletions tpmeventlog/replay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ type eventLog struct {
ExpectedEFIAppDigests map[pb.HashAlgo][]string
}

// The Arch Linux event log has two known failures due to our parser's strict checks.
var archLinuxKnownParsingFailures = []string{
"SecureBoot data len is 0, expected 1",
"found EFIBootServicesApplication in PCR4 before CallingEFIApp event",
}

// Agile Event Log from a RHEL 8 GCE instance with Secure Boot enabled
var Rhel8GCE = eventLog{
RawLog: testdata.Rhel8EventLog,
Expand Down Expand Up @@ -555,31 +549,59 @@ var COS121AmdSev = eventLog{
},
}

var GdcHost = eventLog{
RawLog: testdata.GdcHost,
Banks: []register.PCRBank{
testutil.MakePCRBank(pb.HashAlgo_SHA256, map[uint32][]byte{
0: decodeHex("dab77c454bd12c27ff6b6ce1f9adca90b7a330c1cef0b5cd01cb89fb3bd0dffa"),
1: decodeHex("e9c706539943b2d9770715914f9b3946fab0265327bace4c479913acb9014051"),
2: decodeHex("7fde57284c6a0eabdc9b829db4e2ab0bb565c4189410de2474dd116bc18bafcc"),
3: decodeHex("3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"),
4: decodeHex("ded8b5d91a09c328b9859d8c9db5a346f1065224616b0ba66d6c83dba2b465e8"),
5: decodeHex("163ee251955b844012f1493aa962b2a18acbec194ea4856cdc45cd54c8540058"),
6: decodeHex("3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"),
7: decodeHex("2c9252609eda09899d96abe16b947d0e736c43271997c1fa5189e9bcd37ba516"),
8: decodeHex("8edecd4daa5194ea70a2a9f2c71c7c816bd3b1e0a1ca6f4abea7306250191eba"),
9: decodeHex("731d336f9f3255e80b429de54fb77b2ad5e485829eb386d661c668245f30f44b"),
14: decodeHex("306f9d8b94f17d93dc6e7cf8f5c79d652eb4c6c4d13de2dddc24af416e13ecaf"),
}),
},
ExpectedEFIAppDigests: map[pb.HashAlgo][]string{
pb.HashAlgo_SHA256: {
"c7ac5d44444affd8d4a7c5d3dea0ce20a71e05812fc18777a428d092f78ae3ff",
"c5d3b47de11a9a2a4a15ef5cb7202d7800a10609c0dcecc46e3e963d476b76ce",
"af4161084115c9d5c1872f4473fe974b535e3a9a767688293720ac2cc6f7f9a3",
"af4161084115c9d5c1872f4473fe974b535e3a9a767688293720ac2cc6f7f9a3",
},
},
}

func TestParseEventLogs(t *testing.T) {
sbatErrorStr := "asn1: structure error: tags don't match (16 vs {class:0 tag:24 length:10 isCompound:true})"
logs := []struct {
eventLog
name string
extract.Bootloader
extract.Opts
// This field handles known issues with event log parsing or bad event
// logs.
// Set to nil when the event log has no known issues.
knownErrs []string
}{
{Debian10GCE, "Debian10GCE", extract.UnsupportedLoader, nil},
{Rhel8GCE, "Rhel8GCE", extract.GRUB, nil},
{UbuntuAmdSevGCE, "UbuntuAmdSevGCE", extract.GRUB, nil},
{Debian10GCE, "Debian10GCE", extract.Opts{Loader: extract.UnsupportedLoader}, nil},
{Rhel8GCE, "Rhel8GCE", extract.Opts{Loader: extract.GRUB}, nil},
{UbuntuAmdSevGCE, "UbuntuAmdSevGCE", extract.Opts{Loader: extract.GRUB}, nil},
// TODO: remove once the fix is pulled in
// https://github.com/google/go-attestation/pull/222
{Ubuntu2104NoDbxGCE, "Ubuntu2104NoDbxGCE", extract.GRUB, []string{sbatErrorStr}},
{Ubuntu2104NoSecureBootGCE, "Ubuntu2104NoSecureBootGCE", extract.GRUB, []string{sbatErrorStr}},
{Ubuntu2404AmdSevSnp, "Ubuntu2404AmdSevSnp", extract.GRUB, nil},
{Ubuntu2104NoDbxGCE, "Ubuntu2104NoDbxGCE", extract.Opts{Loader: extract.GRUB}, []string{sbatErrorStr}},
{Ubuntu2104NoSecureBootGCE, "Ubuntu2104NoSecureBootGCE", extract.Opts{Loader: extract.GRUB}, []string{sbatErrorStr}},
{Ubuntu2404AmdSevSnp, "Ubuntu2404AmdSevSnp", extract.Opts{Loader: extract.GRUB}, nil},
// This event log has a SecureBoot variable length of 0.
{ArchLinuxWorkstation, "ArchLinuxWorkstation", extract.UnsupportedLoader, archLinuxKnownParsingFailures},
{COS85AmdSev, "COS85AmdSev", extract.GRUB, nil},
{COS93AmdSev, "COS93AmdSev", extract.GRUB, nil},
{COS101AmdSev, "COS101AmdSev", extract.GRUB, nil},
{COS121AmdSev, "COS121AmdSev", extract.GRUB, nil},
{ArchLinuxWorkstation, "ArchLinuxWorkstation", extract.Opts{Loader: extract.UnsupportedLoader, AllowEFIAppBeforeCallingEvent: true, AllowEmptySBVar: true}, nil},
{COS85AmdSev, "COS85AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
{COS93AmdSev, "COS93AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
{COS101AmdSev, "COS101AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
{COS121AmdSev, "COS121AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
{GdcHost, "GdcHost", extract.Opts{Loader: extract.GRUB, AllowEFIAppBeforeCallingEvent: true}, []string{"invalid SCRTM version event for PCR0"}},
}

for _, log := range logs {
Expand All @@ -588,7 +610,7 @@ func TestParseEventLogs(t *testing.T) {
hashName := pb.HashAlgo_name[int32(bank.TCGHashAlgo)]
subtestName := fmt.Sprintf("%s-%s", log.name, hashName)
t.Run(subtestName, func(t *testing.T) {
if _, err := ReplayAndExtract(rawLog, bank, extract.Opts{Loader: log.Bootloader}); err != nil {
if _, err := ReplayAndExtract(rawLog, bank, log.Opts); err != nil {
matched := false
for _, knownErr := range log.knownErrs {
if strings.Contains(err.Error(), knownErr) {
Expand All @@ -605,6 +627,25 @@ func TestParseEventLogs(t *testing.T) {
}
}

func TestParseEventLogCallingEFIAppError(t *testing.T) {
tests := []struct {
eventLog
name string
}{
{ArchLinuxWorkstation, "ArchLinuxWorkstation"},
{GdcHost, "GdcHost"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
for _, bank := range test.Banks {
if _, err := ReplayAndExtract(test.RawLog, bank, extract.Opts{AllowEFIAppBeforeCallingEvent: false}); err == nil || !strings.Contains(err.Error(), "before CallingEFIApp event") {
t.Errorf("ReplayAndExtract(%s): expected Calling EFI App error, received %v", test.name, err)
}
}
})
}
}

func TestParseMachineStateReplayFail(t *testing.T) {
pcrMap := make(map[uint32][]byte)
pcrMap[0] = []byte{0, 0, 0, 0}
Expand Down
Loading