Add workaround in validation for missing exit boot services event log messages (#153)

This commit is contained in:
Tom D 2020-04-16 10:20:55 -07:00 committed by GitHub
parent 0815f5e221
commit 67c0b4ad07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 162 additions and 0 deletions

View File

@ -40,6 +40,15 @@ type ReplayError struct {
invalidPCRs []int
}
func (e ReplayError) affected(pcr int) bool {
for _, p := range e.invalidPCRs {
if p == pcr {
return true
}
}
return false
}
// Error returns a human-friendly description of replay failures.
func (e ReplayError) Error() string {
return fmt.Sprintf("event log failed to verify: the following registers failed to replay: %v", e.invalidPCRs)
@ -111,11 +120,44 @@ type EventLog struct {
rawEvents []rawEvent
}
func (e *EventLog) clone() *EventLog {
out := EventLog{
Algs: make([]HashAlg, len(e.Algs)),
rawEvents: make([]rawEvent, len(e.rawEvents)),
}
copy(out.Algs, e.Algs)
copy(out.rawEvents, e.rawEvents)
return &out
}
// Verify replays the event log against a TPM's PCR values, returning the
// events which could be matched to a provided PCR value.
// An error is returned if the replayed digest for events with a given PCR
// index do not match any provided value for that PCR index.
func (e *EventLog) Verify(pcrs []PCR) ([]Event, error) {
events, err := e.verify(pcrs)
// If there were any issues replaying the PCRs, try each of the workarounds
// in turn.
// TODO(jsonp): Allow workarounds to be combined.
if rErr, isReplayErr := err.(ReplayError); isReplayErr {
for _, wkrd := range eventlogWorkarounds {
if !rErr.affected(wkrd.affectedPCR) {
continue
}
el := e.clone()
if err := wkrd.apply(el); err != nil {
return nil, fmt.Errorf("failed applying workaround %q: %v", wkrd.id, err)
}
if events, err := el.verify(pcrs); err == nil {
return events, nil
}
}
}
return events, err
}
func (e *EventLog) verify(pcrs []PCR) ([]Event, error) {
events, err := replayEvents(e.rawEvents, pcrs)
if err != nil {
if _, isReplayErr := err.(ReplayError); isReplayErr {

View File

@ -261,3 +261,37 @@ func TestParseSpecIDEvent(t *testing.T) {
})
}
}
func TestEBSVerifyWorkaround(t *testing.T) {
pcr5 := []PCR{
{
Index: 5,
Digest: []byte{
0x31, 0x24, 0x58, 0x08, 0xd6, 0xd3, 0x58, 0x49, 0xbc, 0x39,
0x4f, 0x63, 0x43, 0xf2, 0xb3, 0xff, 0x90, 0x8e, 0xd5, 0xe3,
},
DigestAlg: HashSHA1.cryptoHash(),
},
{
Index: 5,
Digest: []byte{
0x6c, 0xae, 0xa1, 0x23, 0xfa, 0x61, 0x11, 0x30, 0x5e, 0xe6, 0x24,
0xe4, 0x52, 0xe2, 0x69, 0xad, 0x14, 0xac, 0x52, 0x2a, 0xb8, 0xbf,
0x0c, 0x88, 0xe1, 0x16, 0x16, 0xde, 0x4c, 0x22, 0x2f, 0x7d,
},
DigestAlg: HashSHA256.cryptoHash(),
},
}
elr, err := ioutil.ReadFile("testdata/ebs_event_missing_eventlog")
if err != nil {
t.Fatal(err)
}
el, err := ParseEventLog(elr)
if err != nil {
t.Fatalf("ParseEventLog() failed: %v", err)
}
if _, err := el.Verify(pcr5); err != nil {
t.Errorf("Verify() failed: %v", err)
}
}

View File

@ -0,0 +1,86 @@
// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
package attest
type elWorkaround struct {
id string
affectedPCR int
apply func(e *EventLog) error
}
// inject3 appends two new events into the event log.
func inject3(e *EventLog, pcr int, data1, data2, data3 string) error {
if err := inject(e, pcr, data1); err != nil {
return err
}
if err := inject(e, pcr, data2); err != nil {
return err
}
return inject(e, pcr, data3)
}
// inject2 appends two new events into the event log.
func inject2(e *EventLog, pcr int, data1, data2 string) error {
if err := inject(e, pcr, data1); err != nil {
return err
}
return inject(e, pcr, data2)
}
// inject appends a new event into the event log.
func inject(e *EventLog, pcr int, data string) error {
evt := rawEvent{
data: []byte(data),
index: pcr,
sequence: e.rawEvents[len(e.rawEvents)-1].sequence + 1,
}
for _, alg := range e.Algs {
h := alg.cryptoHash().New()
h.Write([]byte(data))
evt.digests = append(evt.digests, h.Sum(nil))
}
e.rawEvents = append(e.rawEvents, evt)
return nil
}
const (
ebsInvocation = "Exit Boot Services Invocation"
ebsSuccess = "Exit Boot Services Returned with Success"
ebsFailure = "Exit Boot Services Returned with Failure"
)
var eventlogWorkarounds = []elWorkaround{
{
id: "EBS Invocation + Success",
affectedPCR: 5,
apply: func(e *EventLog) error {
return inject2(e, 5, ebsInvocation, ebsSuccess)
},
},
{
id: "EBS Invocation + Failure",
affectedPCR: 5,
apply: func(e *EventLog) error {
return inject2(e, 5, ebsInvocation, ebsFailure)
},
},
{
id: "EBS Invocation + Failure + Success",
affectedPCR: 5,
apply: func(e *EventLog) error {
return inject3(e, 5, ebsInvocation, ebsFailure, ebsSuccess)
},
},
}

Binary file not shown.