blob: 49a8a2643e42053d4109f85738505e4663d6c207 [file] [log] [blame]
Lorenz Bruna50e8452020-09-09 17:09:27 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17// Taken and pruned from go-attestation revision 2453c8f39a4ff46009f6a9db6fb7c6cca789d9a1 under Apache 2.0
18
19package eventlog
20
21import (
22 "bytes"
23 "crypto"
24 "crypto/sha1"
25 "crypto/sha256"
26 "encoding/binary"
27 "errors"
28 "fmt"
29 "io"
30 "sort"
31
32 // Ensure hashes are available.
33 _ "crypto/sha256"
34
35 "github.com/google/go-tpm/tpm2"
36)
37
38// HashAlg identifies a hashing Algorithm.
39type HashAlg uint8
40
41// Valid hash algorithms.
42var (
43 HashSHA1 = HashAlg(tpm2.AlgSHA1)
44 HashSHA256 = HashAlg(tpm2.AlgSHA256)
45)
46
47func (a HashAlg) cryptoHash() crypto.Hash {
48 switch a {
49 case HashSHA1:
50 return crypto.SHA1
51 case HashSHA256:
52 return crypto.SHA256
53 }
54 return 0
55}
56
57func (a HashAlg) goTPMAlg() tpm2.Algorithm {
58 switch a {
59 case HashSHA1:
60 return tpm2.AlgSHA1
61 case HashSHA256:
62 return tpm2.AlgSHA256
63 }
64 return 0
65}
66
67// String returns a human-friendly representation of the hash algorithm.
68func (a HashAlg) String() string {
69 switch a {
70 case HashSHA1:
71 return "SHA1"
72 case HashSHA256:
73 return "SHA256"
74 }
75 return fmt.Sprintf("HashAlg<%d>", int(a))
76}
77
78// ReplayError describes the parsed events that failed to verify against
79// a particular PCR.
80type ReplayError struct {
81 Events []Event
82 invalidPCRs []int
83}
84
85func (e ReplayError) affected(pcr int) bool {
86 for _, p := range e.invalidPCRs {
87 if p == pcr {
88 return true
89 }
90 }
91 return false
92}
93
94// Error returns a human-friendly description of replay failures.
95func (e ReplayError) Error() string {
96 return fmt.Sprintf("event log failed to verify: the following registers failed to replay: %v", e.invalidPCRs)
97}
98
99// TPM algorithms. See the TPM 2.0 specification section 6.3.
100//
101// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=42
102const (
103 algSHA1 uint16 = 0x0004
104 algSHA256 uint16 = 0x000B
105)
106
107// EventType indicates what kind of data an event is reporting.
108type EventType uint32
109
110// Event is a single event from a TCG event log. This reports descrete items such
111// as BIOs measurements or EFI states.
112type Event struct {
113 // order of the event in the event log.
114 sequence int
115
116 // PCR index of the event.
117 Index int
118 // Type of the event.
119 Type EventType
120
121 // Data of the event. For certain kinds of events, this must match the event
122 // digest to be valid.
123 Data []byte
124 // Digest is the verified digest of the event data. While an event can have
125 // multiple for different hash values, this is the one that was matched to the
126 // PCR value.
127 Digest []byte
128
129 // TODO(ericchiang): Provide examples or links for which event types must
130 // match their data to their digest.
131}
132
133func (e *Event) digestEquals(b []byte) error {
134 if len(e.Digest) == 0 {
135 return errors.New("no digests present")
136 }
137
138 switch len(e.Digest) {
139 case crypto.SHA256.Size():
140 s := sha256.Sum256(b)
141 if bytes.Equal(s[:], e.Digest) {
142 return nil
143 }
144 case crypto.SHA1.Size():
145 s := sha1.Sum(b)
146 if bytes.Equal(s[:], e.Digest) {
147 return nil
148 }
149 default:
150 return fmt.Errorf("cannot compare hash of length %d", len(e.Digest))
151 }
152
153 return fmt.Errorf("digest (len %d) does not match", len(e.Digest))
154}
155
156// EventLog is a parsed measurement log. This contains unverified data representing
157// boot events that must be replayed against PCR values to determine authenticity.
158type EventLog struct {
159 // Algs holds the set of algorithms that the event log uses.
160 Algs []HashAlg
161
162 rawEvents []rawEvent
163}
164
165func (e *EventLog) clone() *EventLog {
166 out := EventLog{
167 Algs: make([]HashAlg, len(e.Algs)),
168 rawEvents: make([]rawEvent, len(e.rawEvents)),
169 }
170 copy(out.Algs, e.Algs)
171 copy(out.rawEvents, e.rawEvents)
172 return &out
173}
174
175type elWorkaround struct {
176 id string
177 affectedPCR int
178 apply func(e *EventLog) error
179}
180
181// inject3 appends two new events into the event log.
182func inject3(e *EventLog, pcr int, data1, data2, data3 string) error {
183 if err := inject(e, pcr, data1); err != nil {
184 return err
185 }
186 if err := inject(e, pcr, data2); err != nil {
187 return err
188 }
189 return inject(e, pcr, data3)
190}
191
192// inject2 appends two new events into the event log.
193func inject2(e *EventLog, pcr int, data1, data2 string) error {
194 if err := inject(e, pcr, data1); err != nil {
195 return err
196 }
197 return inject(e, pcr, data2)
198}
199
200// inject appends a new event into the event log.
201func inject(e *EventLog, pcr int, data string) error {
202 evt := rawEvent{
203 data: []byte(data),
204 index: pcr,
205 sequence: e.rawEvents[len(e.rawEvents)-1].sequence + 1,
206 }
207 for _, alg := range e.Algs {
208 h := alg.cryptoHash().New()
209 h.Write([]byte(data))
210 evt.digests = append(evt.digests, digest{hash: alg.cryptoHash(), data: h.Sum(nil)})
211 }
212 e.rawEvents = append(e.rawEvents, evt)
213 return nil
214}
215
216const (
217 ebsInvocation = "Exit Boot Services Invocation"
218 ebsSuccess = "Exit Boot Services Returned with Success"
219 ebsFailure = "Exit Boot Services Returned with Failure"
220)
221
222var eventlogWorkarounds = []elWorkaround{
223 {
224 id: "EBS Invocation + Success",
225 affectedPCR: 5,
226 apply: func(e *EventLog) error {
227 return inject2(e, 5, ebsInvocation, ebsSuccess)
228 },
229 },
230 {
231 id: "EBS Invocation + Failure",
232 affectedPCR: 5,
233 apply: func(e *EventLog) error {
234 return inject2(e, 5, ebsInvocation, ebsFailure)
235 },
236 },
237 {
238 id: "EBS Invocation + Failure + Success",
239 affectedPCR: 5,
240 apply: func(e *EventLog) error {
241 return inject3(e, 5, ebsInvocation, ebsFailure, ebsSuccess)
242 },
243 },
244}
245
246// Verify replays the event log against a TPM's PCR values, returning the
247// events which could be matched to a provided PCR value.
248// An error is returned if the replayed digest for events with a given PCR
249// index do not match any provided value for that PCR index.
250func (e *EventLog) Verify(pcrs []PCR) ([]Event, error) {
251 events, err := e.verify(pcrs)
252 // If there were any issues replaying the PCRs, try each of the workarounds
253 // in turn.
254 // TODO(jsonp): Allow workarounds to be combined.
255 if rErr, isReplayErr := err.(ReplayError); isReplayErr {
256 for _, wkrd := range eventlogWorkarounds {
257 if !rErr.affected(wkrd.affectedPCR) {
258 continue
259 }
260 el := e.clone()
261 if err := wkrd.apply(el); err != nil {
262 return nil, fmt.Errorf("failed applying workaround %q: %v", wkrd.id, err)
263 }
264 if events, err := el.verify(pcrs); err == nil {
265 return events, nil
266 }
267 }
268 }
269
270 return events, err
271}
272
273// PCR encapsulates the value of a PCR at a point in time.
274type PCR struct {
275 Index int
276 Digest []byte
277 DigestAlg crypto.Hash
278}
279
280func (e *EventLog) verify(pcrs []PCR) ([]Event, error) {
281 events, err := replayEvents(e.rawEvents, pcrs)
282 if err != nil {
283 if _, isReplayErr := err.(ReplayError); isReplayErr {
284 return nil, err
285 }
286 return nil, fmt.Errorf("pcrs failed to replay: %v", err)
287 }
288 return events, nil
289}
290
291func extend(pcr PCR, replay []byte, e rawEvent) (pcrDigest []byte, eventDigest []byte, err error) {
292 h := pcr.DigestAlg
293
294 for _, digest := range e.digests {
295 if digest.hash != pcr.DigestAlg {
296 continue
297 }
298 if len(digest.data) != len(pcr.Digest) {
299 return nil, nil, fmt.Errorf("digest data length (%d) doesn't match PCR digest length (%d)", len(digest.data), len(pcr.Digest))
300 }
301 hash := h.New()
302 if len(replay) != 0 {
303 hash.Write(replay)
304 } else {
305 b := make([]byte, h.Size())
306 hash.Write(b)
307 }
308 hash.Write(digest.data)
309 return hash.Sum(nil), digest.data, nil
310 }
311 return nil, nil, fmt.Errorf("no event digest matches pcr algorithm: %v", pcr.DigestAlg)
312}
313
314// replayPCR replays the event log for a specific PCR, using pcr and
315// event digests with the algorithm in pcr. An error is returned if the
316// replayed values do not match the final PCR digest, or any event tagged
317// with that PCR does not posess an event digest with the specified algorithm.
318func replayPCR(rawEvents []rawEvent, pcr PCR) ([]Event, bool) {
319 var (
320 replay []byte
321 outEvents []Event
322 )
323
324 for _, e := range rawEvents {
325 if e.index != pcr.Index {
326 continue
327 }
328
329 replayValue, digest, err := extend(pcr, replay, e)
330 if err != nil {
331 return nil, false
332 }
333 replay = replayValue
334 outEvents = append(outEvents, Event{sequence: e.sequence, Data: e.data, Digest: digest, Index: pcr.Index, Type: e.typ})
335 }
336
337 if len(outEvents) > 0 && !bytes.Equal(replay, pcr.Digest) {
338 return nil, false
339 }
340 return outEvents, true
341}
342
343type pcrReplayResult struct {
344 events []Event
345 successful bool
346}
347
348func replayEvents(rawEvents []rawEvent, pcrs []PCR) ([]Event, error) {
349 var (
350 invalidReplays []int
351 verifiedEvents []Event
352 allPCRReplays = map[int][]pcrReplayResult{}
353 )
354
355 // Replay the event log for every PCR and digest algorithm combination.
356 for _, pcr := range pcrs {
357 events, ok := replayPCR(rawEvents, pcr)
358 allPCRReplays[pcr.Index] = append(allPCRReplays[pcr.Index], pcrReplayResult{events, ok})
359 }
360
361 // Record PCR indices which do not have any successful replay. Record the
362 // events for a successful replay.
363pcrLoop:
364 for i, replaysForPCR := range allPCRReplays {
365 for _, replay := range replaysForPCR {
366 if replay.successful {
367 // We consider the PCR verified at this stage: The replay of values with
368 // one digest algorithm matched a provided value.
369 // As such, we save the PCR's events, and proceed to the next PCR.
370 verifiedEvents = append(verifiedEvents, replay.events...)
371 continue pcrLoop
372 }
373 }
374 invalidReplays = append(invalidReplays, i)
375 }
376
377 if len(invalidReplays) > 0 {
378 events := make([]Event, 0, len(rawEvents))
379 for _, e := range rawEvents {
380 events = append(events, Event{e.sequence, e.index, e.typ, e.data, nil})
381 }
382 return nil, ReplayError{
383 Events: events,
384 invalidPCRs: invalidReplays,
385 }
386 }
387
388 sort.Slice(verifiedEvents, func(i int, j int) bool {
389 return verifiedEvents[i].sequence < verifiedEvents[j].sequence
390 })
391 return verifiedEvents, nil
392}
393
394// EV_NO_ACTION is a special event type that indicates information to the parser
395// instead of holding a measurement. For TPM 2.0, this event type is used to signal
396// switching from SHA1 format to a variable length digest.
397//
398// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=110
399const eventTypeNoAction = 0x03
400
401// ParseEventLog parses an unverified measurement log.
402func ParseEventLog(measurementLog []byte) (*EventLog, error) {
403 var specID *specIDEvent
404 r := bytes.NewBuffer(measurementLog)
405 parseFn := parseRawEvent
406 var el EventLog
407 e, err := parseFn(r, specID)
408 if err != nil {
409 return nil, fmt.Errorf("parse first event: %v", err)
410 }
411 if e.typ == eventTypeNoAction {
412 specID, err = parseSpecIDEvent(e.data)
413 if err != nil {
414 return nil, fmt.Errorf("failed to parse spec ID event: %v", err)
415 }
416 for _, alg := range specID.algs {
417 switch tpm2.Algorithm(alg.ID) {
418 case tpm2.AlgSHA1:
419 el.Algs = append(el.Algs, HashSHA1)
420 case tpm2.AlgSHA256:
421 el.Algs = append(el.Algs, HashSHA256)
422 }
423 }
424 if len(el.Algs) == 0 {
425 return nil, fmt.Errorf("measurement log didn't use sha1 or sha256 digests")
426 }
427 // Switch to parsing crypto agile events. Don't include this in the
428 // replayed events since it intentionally doesn't extend the PCRs.
429 //
430 // Note that this doesn't actually guarentee that events have SHA256
431 // digests.
432 parseFn = parseRawEvent2
433 } else {
434 el.Algs = []HashAlg{HashSHA1}
435 el.rawEvents = append(el.rawEvents, e)
436 }
437 sequence := 1
438 for r.Len() != 0 {
439 e, err := parseFn(r, specID)
440 if err != nil {
441 return nil, err
442 }
443 e.sequence = sequence
444 sequence++
445 el.rawEvents = append(el.rawEvents, e)
446 }
447 return &el, nil
448}
449
450type specIDEvent struct {
451 algs []specAlgSize
452}
453
454type specAlgSize struct {
455 ID uint16
456 Size uint16
457}
458
459// Expected values for various Spec ID Event fields.
460// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=19
461var wantSignature = [16]byte{0x53, 0x70,
462 0x65, 0x63, 0x20, 0x49,
463 0x44, 0x20, 0x45, 0x76,
464 0x65, 0x6e, 0x74, 0x30,
465 0x33, 0x00} // "Spec ID Event03\0"
466
467const (
468 wantMajor = 2
469 wantMinor = 0
470 wantErrata = 0
471)
472
473// parseSpecIDEvent parses a TCG_EfiSpecIDEventStruct structure from the reader.
474//
475// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=18
476func parseSpecIDEvent(b []byte) (*specIDEvent, error) {
477 r := bytes.NewReader(b)
478 var header struct {
479 Signature [16]byte
480 PlatformClass uint32
481 VersionMinor uint8
482 VersionMajor uint8
483 Errata uint8
484 UintnSize uint8
485 NumAlgs uint32
486 }
487 if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
488 return nil, fmt.Errorf("reading event header: %v", err)
489 }
490 if header.Signature != wantSignature {
491 return nil, fmt.Errorf("invalid spec id signature: %x", header.Signature)
492 }
493 if header.VersionMajor != wantMajor {
494 return nil, fmt.Errorf("invalid spec major version, got %02x, wanted %02x",
495 header.VersionMajor, wantMajor)
496 }
497 if header.VersionMinor != wantMinor {
498 return nil, fmt.Errorf("invalid spec minor version, got %02x, wanted %02x",
499 header.VersionMajor, wantMinor)
500 }
501
502 // TODO(ericchiang): Check errata? Or do we expect that to change in ways
503 // we're okay with?
504
505 specAlg := specAlgSize{}
506 e := specIDEvent{}
507 for i := 0; i < int(header.NumAlgs); i++ {
508 if err := binary.Read(r, binary.LittleEndian, &specAlg); err != nil {
509 return nil, fmt.Errorf("reading algorithm: %v", err)
510 }
511 e.algs = append(e.algs, specAlg)
512 }
513
514 var vendorInfoSize uint8
515 if err := binary.Read(r, binary.LittleEndian, &vendorInfoSize); err != nil {
516 return nil, fmt.Errorf("reading vender info size: %v", err)
517 }
518 if r.Len() != int(vendorInfoSize) {
519 return nil, fmt.Errorf("reading vendor info, expected %d remaining bytes, got %d", vendorInfoSize, r.Len())
520 }
521 return &e, nil
522}
523
524type digest struct {
525 hash crypto.Hash
526 data []byte
527}
528
529type rawEvent struct {
530 sequence int
531 index int
532 typ EventType
533 data []byte
534 digests []digest
535}
536
537// TPM 1.2 event log format. See "5.1 SHA1 Event Log Entry Format"
538// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=15
539type rawEventHeader struct {
540 PCRIndex uint32
541 Type uint32
542 Digest [20]byte
543 EventSize uint32
544}
545
546type eventSizeErr struct {
547 eventSize uint32
548 logSize int
549}
550
551func (e *eventSizeErr) Error() string {
552 return fmt.Sprintf("event data size (%d bytes) is greater than remaining measurement log (%d bytes)", e.eventSize, e.logSize)
553}
554
555func parseRawEvent(r *bytes.Buffer, specID *specIDEvent) (event rawEvent, err error) {
556 var h rawEventHeader
557 if err = binary.Read(r, binary.LittleEndian, &h); err != nil {
558 return event, err
559 }
560 if h.EventSize == 0 {
561 return event, errors.New("event data size is 0")
562 }
563 if h.EventSize > uint32(r.Len()) {
564 return event, &eventSizeErr{h.EventSize, r.Len()}
565 }
566
567 data := make([]byte, int(h.EventSize))
568 if _, err := io.ReadFull(r, data); err != nil {
569 return event, err
570 }
571
572 digests := []digest{{hash: crypto.SHA1, data: h.Digest[:]}}
573
574 return rawEvent{
575 typ: EventType(h.Type),
576 data: data,
577 index: int(h.PCRIndex),
578 digests: digests,
579 }, nil
580}
581
582// TPM 2.0 event log format. See "5.2 Crypto Agile Log Entry Format"
583// https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=15
584type rawEvent2Header struct {
585 PCRIndex uint32
586 Type uint32
587}
588
589func parseRawEvent2(r *bytes.Buffer, specID *specIDEvent) (event rawEvent, err error) {
590 var h rawEvent2Header
591
592 if err = binary.Read(r, binary.LittleEndian, &h); err != nil {
593 return event, err
594 }
595 event.typ = EventType(h.Type)
596 event.index = int(h.PCRIndex)
597
598 // parse the event digests
599 var numDigests uint32
600 if err := binary.Read(r, binary.LittleEndian, &numDigests); err != nil {
601 return event, err
602 }
603
604 for i := 0; i < int(numDigests); i++ {
605 var algID uint16
606 if err := binary.Read(r, binary.LittleEndian, &algID); err != nil {
607 return event, err
608 }
609 var digest digest
610
611 for _, alg := range specID.algs {
612 if alg.ID != algID {
613 continue
614 }
615 if uint16(r.Len()) < alg.Size {
616 return event, fmt.Errorf("reading digest: %v", io.ErrUnexpectedEOF)
617 }
618 digest.data = make([]byte, alg.Size)
619 digest.hash = HashAlg(alg.ID).cryptoHash()
620 }
621 if len(digest.data) == 0 {
622 return event, fmt.Errorf("unknown algorithm ID %x", algID)
623 }
624 if _, err := io.ReadFull(r, digest.data); err != nil {
625 return event, err
626 }
627 event.digests = append(event.digests, digest)
628 }
629
630 // parse event data
631 var eventSize uint32
632 if err = binary.Read(r, binary.LittleEndian, &eventSize); err != nil {
633 return event, err
634 }
635 if eventSize == 0 {
636 return event, errors.New("event data size is 0")
637 }
638 if eventSize > uint32(r.Len()) {
639 return event, &eventSizeErr{eventSize, r.Len()}
640 }
641 event.data = make([]byte, int(eventSize))
642 if _, err := io.ReadFull(r, event.data); err != nil {
643 return event, err
644 }
645 return event, err
646}