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
112 changes: 112 additions & 0 deletions ccel/cceventlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,40 @@ import (
type eventLog struct {
fname string
mrs []register.MR
// TODO: migrate off of the slice based bank type and move to a map-based representation.
rtmrs []register.RTMR
}

var COS113TDX = eventLog{
fname: "../testdata/eventlogs/ccel/cos-113-intel-tdx.bin",
mrs: []register.MR{
register.RTMR{
Index: 0,
Digest: []byte("?\xa2\xf6\x1f9[\x7f_\xee\xfbN\xc2\xdfa)\x7f\x10\x9aث\xcdd\x10\xc1\xb7\xdf`\xf2\x1f7\xb1\x92\x97\xfc5\xe5D\x03\x9c~\x1e\xde\xceu*\xfd\x17\xf6"),
},
register.RTMR{
Index: 1,
Digest: []byte("\xf6-\xbc\a+\xd5\xd3\xf3C\x8b{5Úr\x7fZ\xea/\xfc$s\xf47#\x95?S\r\xafbPO\nyD\xaab\xc4\x1a\x86\xe8\xa8x±\"\xc1"),
},
register.RTMR{
Index: 2,
Digest: []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1"),
},
},
rtmrs: []register.RTMR{
{
Index: 0,
Digest: []byte("?\xa2\xf6\x1f9[\x7f_\xee\xfbN\xc2\xdfa)\x7f\x10\x9aث\xcdd\x10\xc1\xb7\xdf`\xf2\x1f7\xb1\x92\x97\xfc5\xe5D\x03\x9c~\x1e\xde\xceu*\xfd\x17\xf6"),
},
{
Index: 1,
Digest: []byte("\xf6-\xbc\a+\xd5\xd3\xf3C\x8b{5Úr\x7fZ\xea/\xfc$s\xf47#\x95?S\r\xafbPO\nyD\xaab\xc4\x1a\x86\xe8\xa8x±\"\xc1"),
},
{
Index: 2,
Digest: []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1"),
},
},
}

var COS113TDXUnpadded = eventLog{
Expand All @@ -45,6 +79,20 @@ var COS113TDXUnpadded = eventLog{
Digest: []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1"),
},
},
rtmrs: []register.RTMR{
{
Index: 0,
Digest: []byte("\xa4\xde-\xf2>\x96\x11)\x91#\xbaCY\xc4*^W\x8b\x0f\x84\x88\xbf\x1b\xba\x8e\xf5`m\x9e\xa5\xd8\x1c\x97\xc0d\xb4\x82\xa5\xea\xc57\xd1f\xbd\x0f\x0fu-"),
},
{
Index: 1,
Digest: []byte("\x0e\xe96l\x92\x8aw\t/U\xe9\xe1\x14\xc79A\x81\xfd&F\x99\x15_\r\xf7}#Wv\x18\xd5\xf6PV\x8a\x17\xd3y5Z\a\xbd\x84nU/N "),
},
{
Index: 2,
Digest: []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1"),
},
},
}

var COS113TDXPadded = eventLog{
Expand All @@ -63,6 +111,20 @@ var COS113TDXPadded = eventLog{
Digest: []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1"),
},
},
rtmrs: []register.RTMR{
{
Index: 0,
Digest: []byte("\xa4\xde-\xf2>\x96\x11)\x91#\xbaCY\xc4*^W\x8b\x0f\x84\x88\xbf\x1b\xba\x8e\xf5`m\x9e\xa5\xd8\x1c\x97\xc0d\xb4\x82\xa5\xea\xc57\xd1f\xbd\x0f\x0fu-"),
},
{
Index: 1,
Digest: []byte("\x0e\xe96l\x92\x8aw\t/U\xe9\xe1\x14\xc79A\x81\xfd&F\x99\x15_\r\xf7}#Wv\x18\xd5\xf6PV\x8a\x17\xd3y5Z\a\xbd\x84nU/N "),
},
{
Index: 2,
Digest: []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1"),
},
},
}

var IntelTestCCEL = eventLog{
Expand All @@ -81,6 +143,46 @@ var IntelTestCCEL = eventLog{
Digest: []byte("\x80\x83\xcdh\x98\xccR\xa9\x021\xcd\xf9\xc0S+\xf9Q<@F\\oq\xe5l\xbe2\xee,\x11\xa9\xdf\xc00)|\xa3\xca\x0fbG}m\x1fa\r?\xdb"),
},
},
rtmrs: []register.RTMR{
{
Index: 0,
Digest: []byte("\x80\x83\xcdh\x98\xccR\xa9\x021\xcd\xf9\xc0S+\xf9Q<@F\\oq\xe5l\xbe2\xee,\x11\xa9\xdf\xc00)|\xa3\xca\x0fbG}m\x1fa\r?\xdb"),
},
{
Index: 1,
Digest: []byte("\x80\x83\xcdh\x98\xccR\xa9\x021\xcd\xf9\xc0S+\xf9Q<@F\\oq\xe5l\xbe2\xee,\x11\xa9\xdf\xc00)|\xa3\xca\x0fbG}m\x1fa\r?\xdb"),
},
{
Index: 2,
Digest: []byte("\x80\x83\xcdh\x98\xccR\xa9\x021\xcd\xf9\xc0S+\xf9Q<@F\\oq\xe5l\xbe2\xee,\x11\xa9\xdf\xc00)|\xa3\xca\x0fbG}m\x1fa\r?\xdb"),
},
},
}

var GDCCCEL = eventLog{
fname: "../testdata/eventlogs/ccel/gdc-tdx.bin",
mrs: []register.MR{
register.RTMR{
Index: 0,
Digest: []byte("FU\xef\x03\xc8w\xb3\xd7Jf >F\x85\x8f\xb9\x90۩t\xa4\\\xa6P\x85\xbcFE\x943n\x04\xebI\xca\x10\x0ej\x1c\xeb\xe7\xae2/2\x88\xb0\x8f")},
register.RTMR{
Index: 1,
Digest: []byte("\xbf\x86\xaa\xc1@\xc1\x05\a\xb7<#\xd2\xf3\xa6v\xb6\xa3iZ\x9a\xad\xe3c5s1\x80\xb0K\x0e\xec\xd2\r\x05\xab\xe2\xe3\xaa^\x8b\v\xads\xfa\xe3\x0f4\xf4")},
register.RTMR{
Index: 2,
Digest: []byte("\xb6_\x82\x02\xd0\xd3\xc9g\x9f\xe0\xb1\xf3\xf3A\xa5\xc8\ue91e\xa4\x93\x14d\x16\xde\xed\x8a\xe3c\xd7c%D\xd4)BN* \x824\xc7n\xd5\xc1\xba\t\xce")},
},
rtmrs: []register.RTMR{
{
Index: 0,
Digest: []byte("FU\xef\x03\xc8w\xb3\xd7Jf >F\x85\x8f\xb9\x90۩t\xa4\\\xa6P\x85\xbcFE\x943n\x04\xebI\xca\x10\x0ej\x1c\xeb\xe7\xae2/2\x88\xb0\x8f")},
{
Index: 1,
Digest: []byte("\xbf\x86\xaa\xc1@\xc1\x05\a\xb7<#\xd2\xf3\xa6v\xb6\xa3iZ\x9a\xad\xe3c5s1\x80\xb0K\x0e\xec\xd2\r\x05\xab\xe2\xe3\xaa^\x8b\v\xads\xfa\xe3\x0f4\xf4")},
{
Index: 2,
Digest: []byte("\xb6_\x82\x02\xd0\xd3\xc9g\x9f\xe0\xb1\xf3\xf3A\xa5\xc8\ue91e\xa4\x93\x14d\x16\xde\xed\x8a\xe3c\xd7c%D\xd4)BN* \x824\xc7n\xd5\xc1\xba\t\xce")},
},
}

func TestParseAndReplay(t *testing.T) {
Expand Down Expand Up @@ -109,6 +211,16 @@ func TestParseAndReplay(t *testing.T) {
allowPadding: false,
wantErr: true,
},
{
el: GDCCCEL,
allowPadding: true,
wantErr: false,
},
{
el: GDCCCEL,
allowPadding: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.el.fname+"_allowPadding_"+strconv.FormatBool(tt.allowPadding), func(t *testing.T) {
Expand Down
46 changes: 31 additions & 15 deletions ccel/replay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package ccel

import (
"os"
"strconv"
"strings"
"testing"

Expand All @@ -24,26 +25,41 @@ import (
)

func TestReplayAndExtract(t *testing.T) {
elBytes, err := os.ReadFile("../testdata/eventlogs/ccel/cos-113-intel-tdx.bin")
if err != nil {
t.Fatal(err)
}
tableBytes, err := os.ReadFile("../testdata/eventlogs/ccel/cos-113-intel-tdx.table.bin")
if err != nil {
t.Fatal(err)
}
tests := []struct {
el eventLog
opts extract.Opts
wantErr bool
}{
{
el: COS113TDX,
opts: extract.Opts{Loader: extract.GRUB},
},
{
el: GDCCCEL,
opts: extract.Opts{Loader: extract.GRUB, AllowEmptySBVar: true},
},
{
el: GDCCCEL,
opts: extract.Opts{Loader: extract.GRUB},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.el.fname+strconv.FormatBool(tt.wantErr), func(t *testing.T) {
elBytes, err := os.ReadFile(tt.el.fname)
if err != nil {
t.Fatal(err)
}

rtmr0 := []byte("?\xa2\xf6\x1f9[\x7f_\xee\xfbN\xc2\xdfa)\x7f\x10\x9aث\xcdd\x10\xc1\xb7\xdf`\xf2\x1f7\xb1\x92\x97\xfc5\xe5D\x03\x9c~\x1e\xde\xceu*\xfd\x17\xf6")
rtmr1 := []byte("\xf6-\xbc\a+\xd5\xd3\xf3C\x8b{5Úr\x7fZ\xea/\xfc$s\xf47#\x95?S\r\xafbPO\nyD\xaab\xc4\x1a\x86\xe8\xa8x±\"\xc1")
rtmr2 := []byte("IihM\xc8s\x81\xfc;14\x17l\x8d\x88\x06\xea\xf0\xa9\x01\x85\x9f_pϮ\x8d\x17qKF\xc1\n\x8d\xe2\x19\x04\x8c\x9f\xc0\x9f\x11\xf3\x81\xa6\xfb\xe7\xc1")
bank := register.RTMRBank{RTMRs: []register.RTMR{
{Index: 0, Digest: rtmr0},
{Index: 1, Digest: rtmr1},
{Index: 2, Digest: rtmr2},
}}
_, err = ReplayAndExtract(tableBytes, elBytes, bank, extract.Opts{Loader: extract.GRUB})
if err != nil {
t.Errorf("failed to ReplayAndExtract from CCEL: %v", err)
_, err = ReplayAndExtract(tableBytes, elBytes, register.RTMRBank{RTMRs: tt.el.rtmrs}, tt.opts)
if (err != nil) != tt.wantErr {
t.Errorf("ReplayAndExtract: got %v, wantErr %v", err, tt.wantErr)
}
})
}
}

Expand Down
9 changes: 6 additions & 3 deletions extract/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ const (
// Opts gives options for extracting information from an event log.
type Opts struct {
Loader Bootloader
// 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
}

// FirmwareLogState extracts event info from a verified TCG PC Client event
Expand All @@ -81,7 +84,7 @@ func FirmwareLogState(events []tcg.Event, hash crypto.Hash, registerCfg register
if err != nil {
joined = errors.Join(joined, err)
}
sbState, err := SecureBootState(events, registerCfg)
sbState, err := SecureBootState(events, registerCfg, opts)
if err != nil {
joined = errors.Join(joined, err)
}
Expand Down Expand Up @@ -209,8 +212,8 @@ func matchWellKnown(cert x509.Certificate) (pb.WellKnownCertificate, error) {

// SecureBootState extracts Secure Boot information from a UEFI TCG2
// firmware event log.
func SecureBootState(replayEvents []tcg.Event, registerCfg registerConfig) (*pb.SecureBootState, error) {
attestSbState, err := ParseSecurebootState(replayEvents, registerCfg)
func SecureBootState(replayEvents []tcg.Event, registerCfg registerConfig, opts Opts) (*pb.SecureBootState, error) {
attestSbState, err := ParseSecurebootState(replayEvents, registerCfg, opts)
if err != nil {
return nil, fmt.Errorf("failed to parse SecureBootState: %v", err)
}
Expand Down
12 changes: 8 additions & 4 deletions extract/secureboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ func ParseSecurebootStateLegacy(events []tcg.Event) (*SecurebootState, error) {
// - If SecureBoot was 1 (enabled), platform + exchange + database keys
// were specified.
// - No UEFI debugger was attached.
return ParseSecurebootState(events, TPMRegisterConfig)
return ParseSecurebootState(events, TPMRegisterConfig, Opts{})
}

// ParseSecurebootState parses a series of events to determine the
// configuration of secure boot on a device. An error is returned if
// the state cannot be determined, or if the event log is structured
// in such a way that it may have been tampered post-execution of
// platform firmware.
func ParseSecurebootState(events []tcg.Event, registerCfg registerConfig) (*SecurebootState, error) {
func ParseSecurebootState(events []tcg.Event, registerCfg registerConfig, opts Opts) (*SecurebootState, error) {
var (
out SecurebootState
seenSeparator7 bool
Expand Down Expand Up @@ -173,10 +173,14 @@ func ParseSecurebootState(events []tcg.Event, registerCfg registerConfig) (*Secu

switch v.VarName() {
case "SecureBoot":
if len(v.VariableData) != 1 {
if len(v.VariableData) == 1 {
out.Enabled = v.VariableData[0] == 1
} else if len(v.VariableData) == 0 && opts.AllowEmptySBVar {
out.Enabled = false
} else {
return nil, fmt.Errorf("event %d: SecureBoot data len is %d, expected 1", e.Num(), len(v.VariableData))
}
out.Enabled = v.VariableData[0] == 1

case "PK":
if out.PlatformKeys, out.PlatformKeyHashes, err = v.SignatureData(); err != nil {
return nil, fmt.Errorf("event %d: failed parsing platform keys: %v", e.Num(), err)
Expand Down
Loading
Loading