From 00cf15777885f8a873095c74289268866cbc264d Mon Sep 17 00:00:00 2001 From: Jiankun Lu Date: Tue, 19 Aug 2025 17:33:33 -0700 Subject: [PATCH] Add options to skip Calling EFI Application check https://github.com/google/go-tpm-tools/pull/573 --- extract/extract.go | 11 ++-- extract/extract_test.go | 21 +++++++- testdata/eventlog_data.go | 2 + testdata/eventlogs/tpm/gdc-host.bin | Bin 0 -> 50430 bytes tpmeventlog/replay_test.go | 79 +++++++++++++++++++++------- 5 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 testdata/eventlogs/tpm/gdc-host.bin diff --git a/extract/extract.go b/extract/extract.go index 7246d86..5e48555 100644 --- a/extract/extract.go +++ b/extract/extract.go @@ -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 @@ -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) @@ -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. @@ -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()}) diff --git a/extract/extract_test.go b/extract/extract_test.go index 365077e..8f3fcaf 100644 --- a/extract/extract_test.go +++ b/extract/extract_test.go @@ -383,6 +383,7 @@ func TestEfiState(t *testing.T) { registserConfig registerConfig wantPass bool wantEfiState *pb.EfiState + opts Opts }{ { name: "success with TPM logs", @@ -401,6 +402,9 @@ func TestEfiState(t *testing.T) { }, }, }, + opts: Opts{ + AllowEFIAppBeforeCallingEvent: false, + }, }, { name: "success with CCEL logs", @@ -419,6 +423,9 @@ func TestEfiState(t *testing.T) { }, }, }, + opts: Opts{ + AllowEFIAppBeforeCallingEvent: false, + }, }, { name: "nil EFI state with missing ExitBootServicesInvocation event in TPM logs", @@ -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", @@ -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", @@ -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", @@ -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) } diff --git a/testdata/eventlog_data.go b/testdata/eventlog_data.go index d79a9c1..f1f0b6b 100644 --- a/testdata/eventlog_data.go +++ b/testdata/eventlog_data.go @@ -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. diff --git a/testdata/eventlogs/tpm/gdc-host.bin b/testdata/eventlogs/tpm/gdc-host.bin new file mode 100644 index 0000000000000000000000000000000000000000..4c607a9cc8787fa24545f9447cd230cc4820103a GIT binary patch literal 50430 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 Lw!0O;;rf37k?@ND literal 0 HcmV?d00001 diff --git a/tpmeventlog/replay_test.go b/tpmeventlog/replay_test.go index 719190a..b241aad 100644 --- a/tpmeventlog/replay_test.go +++ b/tpmeventlog/replay_test.go @@ -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, @@ -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 { @@ -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) { @@ -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}