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