Skip to content
Open
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
29 changes: 25 additions & 4 deletions extract/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig,
hasher.Write([]byte(tcg.CallingEFIApplication))
callingEFIAppDigest := hasher.Sum(nil)

hasher.Reset()
hasher.Write([]byte(tcg.ReturningFromEFIApplication))
returningFromEFIAppDigest := hasher.Sum(nil)

hasher.Reset()
hasher.Write([]byte(tcg.ExitBootServicesInvocation))
exitBootSvcDigest := hasher.Sum(nil)
Expand Down Expand Up @@ -387,14 +391,28 @@ func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig,
if !event.DigestVerified() {
return nil, fmt.Errorf("unverified CallingEFIApp digest for %s%d", registerCfg.Name, index)
}
// We don't support calling more than one boot device.
// Multiple boot attempts are supported now since hardware (like CA4) can consider local
// SSDs as boot options. Thus, firmware events may replay multiple times and there's a need
// to reset the seenCallingEfiApp flag when returning from EFI application. But, there
// shouldn't be multiple successful boot attempts, so we return an error if we see a second
// CallingEFIApp event without a ReturningFromEFIApp event in between.
// Referenced from Section 10.4.4 EV_EFI_ACTION Event Types from TCG doc -
// https://trustedcomputinggroup.org/wp-content/uploads/TCG-PC-Client-PFP-Version-1.06-Revision-49_31July2023.pdf
if seenCallingEfiApp {
return nil, fmt.Errorf("found duplicate CallingEFIApp event in %s%d", registerCfg.Name, index)
}
if seenSeparator4 {
return nil, fmt.Errorf("found CallingEFIApp event in %s%d after separator event", registerCfg.Name, index)
}
seenCallingEfiApp = true
} else if bytes.Equal(returningFromEFIAppDigest, event.ReplayedDigest()) {
if evtType != tcg.EFIAction {
return nil, fmt.Errorf("%s%d contains ReturningFromEFIApp event but non EFIAction type: %d",
registerCfg.Name, index, evtType)
}
if !event.DigestVerified() {
return nil, fmt.Errorf("unverified ReturningFromEFIApp digest for %s%d", registerCfg.Name, index)
}
// If a boot fails, a "Returning from EFI Application" event is expected for a corresponding
// "Calling the EFI Application" event. So reset the seenCallingEfiApp flag on returning.
seenCallingEfiApp = false
}

if evtType == tcg.EFIBootServicesApplication {
Expand All @@ -409,6 +427,9 @@ func EfiState(hash crypto.Hash, events []tcg.Event, registerCfg registerConfig,
return nil, err
}
if isSeparator {
if !seenCallingEfiApp {
return nil, fmt.Errorf("found separator event in %s%d before CallingEFIApp event", registerCfg.Name, index)
}
if seenSeparator4 {
return nil, fmt.Errorf("found duplicate Separator event in %s%d", registerCfg.Name, registerCfg.EFIAppIdx)
}
Expand Down
126 changes: 112 additions & 14 deletions extract/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,22 @@ func TestExtractFirmwareLogStateRTMR(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
evts := getCCELEvents(t)
tc.mutate(evts)
fs, err := FirmwareLogState(evts, crypto.SHA384, RTMRRegisterConfig, Opts{Loader: GRUB})
if (err != nil) != tc.expectErr {
t.Errorf("FirmwareLogState(%v) = got %v, wantErr: %v", tc.name, err, tc.expectErr)
eventGetters := map[string]func(*testing.T) []tcg.Event{
"singleBoot": getCCELEvents,
"multipleBoot": getCCELEventsWithMultipleBootAttempts,
}
if fs.LogType != pb.LogType_LOG_TYPE_CC {
t.Errorf("FirmwareLogState(%v) = got LogType %v, want LogType: %v", tc.name, fs.LogType, pb.LogType_LOG_TYPE_CC)
for name, getEvents := range eventGetters {
t.Run(name, func(t *testing.T) {
evts := getEvents(t)
tc.mutate(evts)
fs, err := FirmwareLogState(evts, crypto.SHA384, RTMRRegisterConfig, Opts{Loader: GRUB})
if (err != nil) != tc.expectErr {
t.Errorf("FirmwareLogState() = got %v, wantErr: %v", err, tc.expectErr)
}
if err == nil && fs.LogType != pb.LogType_LOG_TYPE_CC {
t.Errorf("FirmwareLogState() = got LogType %v, want LogType: %v", fs.LogType, pb.LogType_LOG_TYPE_CC)
}
})
}
})
}
Expand Down Expand Up @@ -188,6 +196,28 @@ func getCCELEvents(t *testing.T) []tcg.Event {
return events
}

func getCCELEventsWithMultipleBootAttempts(t *testing.T) []tcg.Event {
elBytes, err := os.ReadFile("../testdata/eventlogs/ccel/ubuntu-2404-intel-tdx.bin")
if err != nil {
t.Fatal(err)
}
rtmr0 := []byte(">/\xb8\xad]\xe9\xb9\xe6m\x0f\xe7:T\xc0)\x13\x0e\xb9\xc0\xae\xf0\x97\x10\xe3\x18\xc9w\xcc\x13\xc7\x186\x8cJ\xdc\x02\xb7K\xc9\xcfL\xf8\x11\x8e\xfe\x1ao\x93")
rtmr1 := []byte("\x952\x8d\xff\x96\xc9\xd6\xc5T\xa4\x01\x98eX|\xf1~\xccw\xffH\xa9}\xec^R\xe0a\xe58\xbd\x13\xc0\xb7\xf2 ~\xc4\x06|\xb6m\xbe:\x9c\x99\xda'")
rtmr2 := []byte("\x81\x93\xfdM\xa6-`\xabe\x97\xfc*S˨z\x85\xa9\xa5\xf0\x97\x9f\xd5\xcag\r\x15\xa0x \xe3/\xf6M\xa4i\x9a\xe8+O*`\x05\xaau\xc4x\xd5")
rtmr3 := []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
mrs := []register.MR{
register.RTMR{Index: 0, Digest: rtmr0},
register.RTMR{Index: 1, Digest: rtmr1},
register.RTMR{Index: 2, Digest: rtmr2},
register.RTMR{Index: 3, Digest: rtmr3},
}
events, err := tcg.ParseAndReplay(elBytes, mrs, tcg.ParseOpts{AllowPadding: true})
if err != nil {
t.Fatal(err)
}
return events
}

func TestExtractFirmwareLogStateTPM(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -303,14 +333,22 @@ func TestExtractFirmwareLogStateTPM(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
hash, evts := getTPMELEvents(t)
tc.mutate(evts)
fs, err := FirmwareLogState(evts, hash, TPMRegisterConfig, Opts{Loader: GRUB})
if (err != nil) != tc.expectErr {
t.Errorf("ExtractFirmwareLogState(%v) = got %v, wantErr: %v", tc.name, err, tc.expectErr)
eventGetters := map[string]func(*testing.T) (crypto.Hash, []tcg.Event){
"singleBoot": getTPMELEvents,
"multipleBoot": getTPMELEventsWithMultipleBootAttempts,
}
if fs.LogType != pb.LogType_LOG_TYPE_TCG2 {
t.Errorf("FirmwareLogState(%v) = got LogType %v, want LogType: %v", tc.name, fs.LogType, pb.LogType_LOG_TYPE_TCG2)
for name, getEvents := range eventGetters {
t.Run(name, func(t *testing.T) {
hash, evts := getEvents(t)
tc.mutate(evts)
fs, err := FirmwareLogState(evts, hash, TPMRegisterConfig, Opts{Loader: GRUB})
if (err != nil) != tc.expectErr {
t.Errorf("FirmwareLogState() = got %v, wantErr: %v", err, tc.expectErr)
}
if err == nil && fs.LogType != pb.LogType_LOG_TYPE_TCG2 {
t.Errorf("FirmwareLogState() = got LogType %v, want LogType: %v", fs.LogType, pb.LogType_LOG_TYPE_TCG2)
}
})
}
})
}
Expand Down Expand Up @@ -503,6 +541,26 @@ func TestEfiState(t *testing.T) {
AllowEFIAppBeforeCallingEvent: false,
},
},
{
name: "failed with valid boot attempt before Separator event in CCEL logs",
events: func() (crypto.Hash, []tcg.Event) {
hash, evts := crypto.SHA384, getCCELEvents(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: RTMRRegisterConfig,
wantPass: false,
wantEfiState: nil,
opts: Opts{
AllowEFIAppBeforeCallingEvent: true,
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
Expand Down Expand Up @@ -545,6 +603,46 @@ func getTPMELEvents(t *testing.T) (crypto.Hash, []tcg.Event) {
return cryptoHash, events
}

func getTPMELEventsWithMultipleBootAttempts(t *testing.T) (crypto.Hash, []tcg.Event) {
log := testdata.Ubuntu2404IntelTdxEventLog
bank := testutil.MakePCRBank(pb.HashAlgo_SHA384, map[uint32][]byte{
0: decodeHex("592b3f42ec556a9c093f201124cc7313fdaa4ce40ae1602e14d51f18fbfc480d6a1e196d1c52ad919328410272dc7222"),
1: decodeHex("ba1ac69c213175dc72db1493bd5bdfa4799028fe5d5c2bb41ddccc6affa50ba01f189d4639a77afbedd6dd6aff1af3b4"),
2: decodeHex("3d29b768ef16e5d7b775ff0397d9d1d22ec83078d1a26ae103de671b6906f0688d713844db3b84783235246e1b564257"),
3: decodeHex("518923b0f955d08da077c96aaba522b9decede61c599cea6c41889cfbea4ae4d50529d96fe4d1afdafb65e7f95bf23c4"),
4: decodeHex("1213ef15e54d13181724275c16f22f89f866ba2c5b3d24a99a79e4962af4126a8b220c22429fde6e747a4bc4378b556d"),
5: decodeHex("c50b529497c7f441ea47305587d6ce83e2e31f7b4fab6c13dc0b0c3c900e1d0caf0768321100927862df142bf0465ee4"),
6: decodeHex("518923b0f955d08da077c96aaba522b9decede61c599cea6c41889cfbea4ae4d50529d96fe4d1afdafb65e7f95bf23c4"),
7: decodeHex("3ee5663e4119df40192276ff9749a3cd339c489ebc2ab6fd65b11b12a4845d82f4a93bca684126f382feed3324fca561"),
8: decodeHex("58b0d4e1a9d3cb21342f0574312c49748f30d30ede290465e79d5238cf76f60d0c89054c5524e7cb1504555913f31efb"),
9: decodeHex("8d799e8eb5bdf56009f435adb4238158951e9cf95fd05a9c1bfd3c60eecab5ea0c9d63a2c90ec20b30435f894e8d33db"),
10: decodeHex("fce98e2810c72187e60a9f83f4e05309a6395a72fb50a366602551227973df5df0c6ef42d9158d94719f4d3f6fdc5be3"),
11: decodeHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
12: decodeHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
13: decodeHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
14: decodeHex("937437d07298010015f4598395c9f8dc202ef36e0be3897bba89874bf612b5da092beadfe37f79714a60193819e384ad"),
15: decodeHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
16: decodeHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
17: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
18: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
19: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
20: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
21: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
22: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
23: decodeHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
})
cryptoHash, err := bank.CryptoHash()
if err != nil {
t.Fatal(err)
}
events, err := tcg.ParseAndReplay(log, bank.MRs(), tcg.ParseOpts{})
if err != nil {
t.Fatal(err)

}
return cryptoHash, events
}

func decodeHex(hexStr string) []byte {
bytes, err := hex.DecodeString(hexStr)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions tcg/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,9 @@ func UntrustedParseEventType(et uint32) (EventType, error) {
// Table 17 EV_EFI_ACTION Strings.
const (
// Measured when Boot Manager attempts to execute code from a Boot Option.
CallingEFIApplication string = "Calling EFI Application from Boot Option"
ExitBootServicesInvocation string = "Exit Boot Services Invocation"
CallingEFIApplication string = "Calling EFI Application from Boot Option"
ExitBootServicesInvocation string = "Exit Boot Services Invocation"
ReturningFromEFIApplication string = "Returning from EFI Application from Boot Option"
)

// EFIDeviceType describes the type of a device specified by a device path.
Expand Down
2 changes: 2 additions & 0 deletions testdata/eventlog_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var (
Cos121AmdSevEventLog []byte
//go:embed eventlogs/tpm/gdc-host.bin
GdcHost []byte
//go:embed eventlogs/tpm/ubuntu-2404-intel-tdx.bin
Ubuntu2404IntelTdxEventLog []byte
)

// Kernel command lines from event logs.
Expand Down
Binary file not shown.
Binary file added testdata/eventlogs/tpm/ubuntu-2404-intel-tdx.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion tpmeventlog/replay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ func TestParseEventLogs(t *testing.T) {
{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.Opts{Loader: extract.UnsupportedLoader, AllowEFIAppBeforeCallingEvent: true, AllowEmptySBVar: true}, nil},
{ArchLinuxWorkstation, "ArchLinuxWorkstation", extract.Opts{Loader: extract.UnsupportedLoader, AllowEFIAppBeforeCallingEvent: true, AllowEmptySBVar: true}, []string{"found separator event in PCR4 before CallingEFIApp event"}},
{COS85AmdSev, "COS85AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
{COS93AmdSev, "COS93AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
{COS101AmdSev, "COS101AmdSev", extract.Opts{Loader: extract.GRUB}, nil},
Expand Down