-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathtcgeventdata.go
478 lines (420 loc) · 17 KB
/
tcgeventdata.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
// Copyright 2019 Canonical Ltd.
// Licensed under the LGPLv3 with static-linking exception.
// See LICENCE file for details.
package tcglog
import (
"bytes"
"crypto"
"encoding/binary"
"errors"
"fmt"
"io"
"strings"
efi "github.com/canonical/go-efilib"
"github.com/canonical/go-tpm2"
"golang.org/x/xerrors"
"github.com/canonical/tcglog-parser/internal/ioerr"
)
// separatorErrorDigests are the digests of uint32(1) for various algorithms, used
// to identify EV_SEPARATOR events that signal an error, where the digest is the tagged
// hash of uint32(1) but the event data contains information about the error.
var separatorErrorDigests = map[tpm2.HashAlgorithmId]tpm2.Digest{
tpm2.HashAlgorithmSHA1: tpm2.Digest{0x3c, 0x58, 0x56, 0x4, 0xe8, 0x7f, 0x85, 0x59, 0x73, 0x73, 0x1f, 0xea, 0x83, 0xe2, 0x1f, 0xab, 0x93, 0x92, 0xd2, 0xfc},
tpm2.HashAlgorithmSHA256: tpm2.Digest{0x67, 0xab, 0xdd, 0x72, 0x10, 0x24, 0xf0, 0xff, 0x4e, 0xb, 0x3f, 0x4c, 0x2f, 0xc1, 0x3b, 0xc5, 0xba, 0xd4, 0x2d, 0xb,
0x78, 0x51, 0xd4, 0x56, 0xd8, 0x8d, 0x20, 0x3d, 0x15, 0xaa, 0xa4, 0x50},
tpm2.HashAlgorithmSHA384: tpm2.Digest{0x72, 0x10, 0xaf, 0x19, 0x14, 0x5e, 0xc2, 0xa8, 0xe2, 0x50, 0xa7, 0xfe, 0x8e, 0x9e, 0xee, 0xac, 0x13, 0x1, 0xe5, 0x24,
0xda, 0xab, 0x82, 0x36, 0x6c, 0x36, 0xbe, 0x61, 0x4d, 0xc3, 0x54, 0x2, 0xa2, 0x89, 0x10, 0x1e, 0x48, 0xca, 0xd6, 0x1c, 0x45, 0x33, 0x7f, 0x2f, 0x32, 0xc1,
0x4f, 0xdc},
tpm2.HashAlgorithmSHA512: tpm2.Digest{0xed, 0xf9, 0x2e, 0x3d, 0x4f, 0x80, 0xfc, 0x47, 0xd9, 0x48, 0xea, 0x2f, 0x17, 0xb9, 0xbf, 0xc7, 0x42, 0xd3, 0x4e, 0x2e,
0x78, 0x5a, 0x7a, 0x49, 0x27, 0xf3, 0xe2, 0x61, 0xe8, 0xbd, 0x9d, 0x40, 0xb, 0x64, 0x8b, 0xff, 0x21, 0x23, 0xb8, 0x39, 0x6d, 0x24, 0xfb, 0x28, 0xf5, 0x86,
0x99, 0x79, 0xe0, 0x8d, 0x58, 0xb4, 0xb5, 0xd1, 0x56, 0xe6, 0x40, 0x34, 0x4a, 0x2c, 0xa, 0x54, 0x67, 0x5d},
tpm2.HashAlgorithmSHA3_256: tpm2.Digest{0x29, 0x5c, 0xd1, 0x69, 0x8c, 0x6a, 0xc5, 0xbd, 0x80, 0x4a, 0x9, 0xe5, 0xf, 0x19, 0xf8, 0x54, 0x94, 0x75, 0xe5, 0x2d,
0xb1, 0xc6, 0xeb, 0xd4, 0x41, 0xed, 0xc, 0x7b, 0x25, 0x6e, 0x1d, 0xdf},
tpm2.HashAlgorithmSHA3_384: tpm2.Digest{0xe7, 0xb9, 0x1f, 0x51, 0xfb, 0x66, 0x1b, 0x6e, 0xc8, 0xea, 0x28, 0x55, 0xc8, 0x8b, 0x38, 0x80, 0x71, 0xca, 0xf1,
0xf6, 0xa3, 0xc, 0x95, 0x24, 0x50, 0x83, 0x8d, 0x61, 0xb3, 0x55, 0x61, 0x63, 0x8b, 0x93, 0xeb, 0xdc, 0x22, 0x1b, 0x86, 0xa6, 0x81, 0x33, 0xe, 0xef, 0xa1,
0x2e, 0x10, 0xcd},
tpm2.HashAlgorithmSHA3_512: tpm2.Digest{0xda, 0x5e, 0xa4, 0xad, 0x55, 0x26, 0xac, 0xe3, 0x3e, 0x3, 0x75, 0xf3, 0x68, 0xd4, 0x16, 0xd9, 0x5f, 0xbf, 0x6a, 0x68,
0x0, 0xb6, 0x60, 0x85, 0x26, 0xdc, 0x7e, 0x39, 0xce, 0x9a, 0x43, 0xf3, 0xb0, 0xa7, 0xbe, 0xd2, 0x20, 0x60, 0xdc, 0xd7, 0x1a, 0x13, 0xc, 0x95, 0x7b, 0xcd, 0x2,
0xe1, 0x95, 0x7e, 0x6d, 0x76, 0xee, 0x61, 0x79, 0xd7, 0x2, 0x36, 0x94, 0x1, 0x6a, 0x49, 0x25, 0x5b},
}
// StringEventData corresponds to event data that is an non-NULL terminated ASCII string.
// It may or may not be informative.
type StringEventData string
func (d StringEventData) String() string {
return string(d)
}
func (d StringEventData) Write(w io.Writer) error {
_, err := io.WriteString(w, string(d))
return err
}
func (d StringEventData) Bytes() []byte {
return []byte(d)
}
// ComputeStringEventDigest computes the digest associated with the supplied string, for
// events where the data is not informative. The function assumes that the string is
// ASCII encoded and measured without a terminating NULL byte.
func ComputeStringEventDigest(alg crypto.Hash, str string) []byte {
h := alg.New()
io.WriteString(h, str)
return h.Sum(nil)
}
// NullTerminatedStringEventData corresponds to event data that is a NULL terminated
// ASCII string. As with other strings, the go representation is not NULL terminated.
// It's only use in this package is for EV_S_CRTM_CONTENTS, which is informative.
type NullTerminatedStringEventData string
func decodeNullTerminatedStringEventData(data []byte) (NullTerminatedStringEventData, error) {
if !isPrintableASCII(data, true) {
return "", errors.New("data does not contain printable ASCII that is NULL terminated")
}
return NullTerminatedStringEventData(data[:len(data)-1]), nil
}
func (d NullTerminatedStringEventData) String() string {
return string(d)
}
func (d NullTerminatedStringEventData) Write(w io.Writer) error {
if _, err := io.WriteString(w, string(d)); err != nil {
return err
}
_, err := w.Write([]byte{0})
return err
}
func (d NullTerminatedStringEventData) Bytes() []byte {
return append([]byte(d), 0x00)
}
// NullTerminatedUCS2StringEventData corresponds to event data that is a NULL
// terminated UCS2 string. As with other strings, the go representation is not
// NULL terminated and is represented in UTF8. It's only use in this package is
// for EV_S_CRTM_VERSION, which is not informative (the event digest is the tagged
// hash of this event data).
type NullTerminatedUCS2StringEventData string
func decodeNullTerminatedUCS2StringEventData(data []byte) (NullTerminatedUCS2StringEventData, error) {
if !isPrintableUCS2(data, true) {
return "", errors.New("data does not contain printable UCS2 that is NULL terminated")
}
r := bytes.NewReader(data)
ucs2Str := make([]uint16, len(data)/2)
if err := binary.Read(r, binary.LittleEndian, &ucs2Str); err != nil {
return "", err
}
str := efi.ConvertUTF16ToUTF8(ucs2Str[:len(ucs2Str)-1])
return NullTerminatedUCS2StringEventData(str), nil
}
func (d NullTerminatedUCS2StringEventData) String() string {
return string(d)
}
func (d NullTerminatedUCS2StringEventData) Write(w io.Writer) error {
ucs2 := efi.ConvertUTF8ToUCS2(string(d))
if err := binary.Write(w, binary.LittleEndian, ucs2); err != nil {
return err
}
_, err := w.Write([]byte{0x00, 0x00})
return err
}
func (d NullTerminatedUCS2StringEventData) Bytes() []byte {
w := new(bytes.Buffer)
d.Write(w)
return w.Bytes()
}
// ComputeNullTerminatedUCS2StringEventDigest computes the digest for the supplied string,
// for events where the event data is not informative and is represented by a NULL terminated
// UCS2 string. The supplied string must be UTF8 without a NULL termination.
func ComputeNullTerminatedUCS2StringEventDigest(alg crypto.Hash, str string) []byte {
ucs2str := efi.ConvertUTF8ToUCS2(str)
ucs2str = append(ucs2str, 0)
h := alg.New()
binary.Write(h, binary.LittleEndian, ucs2str)
return h.Sum(nil)
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf
//
// (section 11.3.4 "EV_NO_ACTION Event Types")
//
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf
//
// (section 9.4.5 "EV_NO_ACTION Event Types")
func decodeEventDataNoAction(data []byte) (EventData, error) {
r := bytes.NewReader(data)
// Signature field
var sig [16]byte
if _, err := io.ReadFull(r, sig[:]); err != nil {
return nil, ioerr.EOFIsUnexpected(err)
}
signature := strings.TrimRight(string(sig[:]), "\x00")
switch signature {
case "Spec ID Event00":
out, err := decodeSpecIdEvent00(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode Spec ID Event00 data: %w", err)
}
return out, nil
case "Spec ID Event02":
out, err := decodeSpecIdEvent02(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode Spec ID Event02 data: %w", err)
}
return out, nil
case "Spec ID Event03":
out, err := decodeSpecIdEvent03(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode Spec ID Event03 data: %w", err)
}
return out, nil
case "SP800-155 Event":
out, err := decodeBIMReferenceManifestEvent(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode SP800-155 Event data: %w", err)
}
return out, nil
case "SP800-155 Event2":
out, err := decodeBIMReferenceManifestEvent2(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode SP800-155 Event2 data: %w", err)
}
return out, nil
case "SP800-155 Event3":
out, err := decodeBIMReferenceManifestEvent3(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode SP800-155 Event3 data: %w", err)
}
return out, nil
case "StartupLocality":
out, err := decodeStartupLocalityEvent(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decode StartupLocality data: %w", err)
}
return out, nil
case "H-CRTM CompMeas":
out, err := decodeHCRTMComponentEvent(data, r)
if err != nil {
return nil, xerrors.Errorf("cannot decocde H-CRTM CompMeas data: %w", err)
}
return out, nil
default:
return nil, nil
}
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf (section 11.3.3 "EV_ACTION event types")
// https://trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v51.pdf (section 9.4.3 "EV_ACTION Event Types")
func decodeEventDataAction(data []byte) (StringEventData, error) {
if !isPrintableASCII(data, false) {
return "", errors.New("data does not contain printable ASCII that is not NULL terminated")
}
return StringEventData(data), nil
}
// SeparatorEventData is the event data associated with a EV_SEPARATOR event.
type SeparatorEventData struct {
Value uint32 // The separator value measured to the TPM
ErrorInfo []byte // The error information recorded in the log when Value == SeparatorEventErrorValue
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf
//
// (section 3.3.2.2 2 Error Conditions" , section 8.2.3 "Measuring Boot Events")
//
// https://trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v51.pdf:
//
// (section 2.3.2 "Error Conditions", section 2.3.4 "PCR Usage", section 7.2
// "Procedure for Pre-OS to OS-Present Transition")
func decodeEventDataSeparator(data []byte, digests DigestMap) (*SeparatorEventData, error) {
for alg, digest := range digests {
errorDigest, ok := separatorErrorDigests[alg]
if !ok {
continue
}
if bytes.Equal(digest, errorDigest) {
// This separator event indicates an error. The digest is the tagged hash of the
// error value (uint32(1)) and the event data is information about the error.
return &SeparatorEventData{Value: SeparatorEventErrorValue, ErrorInfo: data}, nil
}
}
// Not an error separator. The digest is the tagged hash of the event data, which contains one of
// uint32(0) or uint32(0xffffffff).
if len(data) != binary.Size(uint32(0)) {
return nil, errors.New("data is the wrong size")
}
value := binary.LittleEndian.Uint32(data)
switch value {
case SeparatorEventNormalValue, SeparatorEventAltNormalValue:
default:
return nil, fmt.Errorf("invalid separator value: %d", value)
}
return &SeparatorEventData{Value: value}, nil
}
// IsError indicates that this event was associated with an error condition.
// The value returned from Bytes() contains an implementation defined indication
// of the actual error.
func (e *SeparatorEventData) IsError() bool {
return e.Value == SeparatorEventErrorValue
}
func (e *SeparatorEventData) String() string {
if !e.IsError() {
return ""
}
return fmt.Sprintf("ERROR: 0x%x", e.ErrorInfo)
}
func (e *SeparatorEventData) Bytes() []byte {
w := new(bytes.Buffer)
if err := e.Write(w); err != nil {
panic(err)
}
return w.Bytes()
}
func (e *SeparatorEventData) Write(w io.Writer) error {
switch e.Value {
case SeparatorEventNormalValue, SeparatorEventAltNormalValue:
return binary.Write(w, binary.LittleEndian, e.Value)
case SeparatorEventErrorValue:
_, err := w.Write(e.ErrorInfo)
return err
default:
return errors.New("invalid value")
}
}
// ComputeSeparatorEventDigest computes the digest associated with the separator event. The value
// argument should be one of SeparatorEventNormalValue, SeparatorEventAltNormalValue or
// SeparatorEventErrorValue.
func ComputeSeparatorEventDigest(alg crypto.Hash, value uint32) []byte {
h := alg.New()
binary.Write(h, binary.LittleEndian, value)
return h.Sum(nil)
}
func decodeEventDataCompactHash(data []byte) (StringEventData, error) {
if !isPrintableASCII(data, false) {
return "", errors.New("data does not contain printable ASCII that is not NULL terminated")
}
return StringEventData(data), nil
}
func decodeEventDataPostCode(data []byte) (EventData, error) {
if isPrintableASCII(data, false) {
return StringEventData(data), nil
}
return decodeEventDataEFIPlatformFirmwareBlob(data)
}
func decodeEventDataPostCode2(data []byte) (EventData, error) {
if isPrintableASCII(data, false) {
return StringEventData(data), nil
}
return decodeEventDataEFIPlatformFirmwareBlob2(data)
}
func decodeEventDataOmitBootDeviceEvents(data []byte) (StringEventData, error) {
if !bytes.Equal(data, BootAttemptsOmitted.Bytes()) {
return "", errors.New("data contains unexpected contents")
}
return StringEventData(data), nil
}
// TaggedEvent corresponds to TCG_PCClientTaggedEvent. It is not informative - ie, the
// event digest should be the tagged hash of this field.
type TaggedEvent struct {
EventID uint32
Data []byte
}
func decodeEventDataTaggedEvent(data []byte) (*TaggedEvent, error) {
r := bytes.NewReader(data)
d := new(TaggedEvent)
if err := binary.Read(r, binary.LittleEndian, &d.EventID); err != nil {
return nil, fmt.Errorf("cannot decode taggedEventID: %w", err)
}
data, err := readLengthPrefixed[uint32, byte](r)
if err != nil {
return nil, fmt.Errorf("cannot read taggedEventData: %w", err)
}
d.Data = data
return d, nil
}
func (e *TaggedEvent) String() string {
return fmt.Sprintf("TCG_PCClientTaggedEvent{taggedEventID: %d, taggedEventData: %#x}", e.EventID, e.Data)
}
func (e *TaggedEvent) Bytes() []byte {
w := new(bytes.Buffer)
if err := e.Write(w); err != nil {
panic(err)
}
return w.Bytes()
}
func (e *TaggedEvent) Write(w io.Writer) error {
if err := binary.Write(w, binary.LittleEndian, e.EventID); err != nil {
return fmt.Errorf("cannot write taggedEventID: %w", err)
}
if err := writeLengthPrefixed[uint32, byte](w, e.Data); err != nil {
return fmt.Errorf("cannot write taggedEventData: %w", err)
}
return nil
}
// ComputeTaggedEventDigest computes the digest for the specified TaggedEvent.
func ComputeTaggedEventDigest(alg crypto.Hash, ev *TaggedEvent) []byte {
h := alg.New()
ev.Write(h)
return h.Sum(nil)
}
func decodeEventDataSCRTMContents(data []byte) (EventData, error) {
// If measured by a H-CRTM event, this may be a NULL terminated string
if isPrintableASCII(data, true) {
return decodeNullTerminatedStringEventData(data)
}
// Try UEFI_PLATFORM_FIRMWARE_BLOB
if len(data) == 16 { // The size of a UEFI_PLATFORM_FIRMWARE_BLOB
return decodeEventDataEFIPlatformFirmwareBlob(data)
}
// Try UEFI_PLATFORM_FIRMWARE_BLOB2
return decodeEventDataEFIPlatformFirmwareBlob2(data)
}
func decodeEventDataSCRTMVersion(data []byte) (EventData, error) {
if isPrintableUCS2(data, true) {
return decodeNullTerminatedUCS2StringEventData(data)
}
if len(data) == 16 {
guid, err := efi.ReadGUID(bytes.NewReader(data))
if err != nil {
return nil, err
}
return GUIDEventData(guid), nil
}
return nil, errors.New("event data is not a NULL-terminated UCS2 string or a EFI_GUID")
}
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf (section 11.3.1 "Event Types")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_EFI_Platform_1_22_Final_-v15.pdf (section 7.2 "Event Types")
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf (section 9.4.1 "Event Types")
func decodeEventDataTCG(data []byte, eventType EventType, digests DigestMap) (out EventData, err error) {
switch eventType {
case EventTypeNoAction:
return decodeEventDataNoAction(data)
case EventTypeSeparator:
return decodeEventDataSeparator(data, digests)
case EventTypeAction, EventTypeEFIAction:
return decodeEventDataAction(data)
case EventTypePostCode:
return decodeEventDataPostCode(data)
case EventTypePostCode2:
return decodeEventDataPostCode2(data)
case EventTypeEFIPlatformFirmwareBlob:
return decodeEventDataEFIPlatformFirmwareBlob(data)
case EventTypeEFIPlatformFirmwareBlob2:
return decodeEventDataEFIPlatformFirmwareBlob2(data)
case EventTypeCompactHash:
return decodeEventDataCompactHash(data)
case EventTypeEFIVariableDriverConfig, EventTypeEFIVariableBoot, EventTypeEFIVariableAuthority, EventTypeEFIVariableBoot2:
return decodeEventDataEFIVariable(data)
case EventTypeEFIBootServicesApplication, EventTypeEFIBootServicesDriver, EventTypeEFIRuntimeServicesDriver:
return decodeEventDataEFIImageLoad(data)
case EventTypeEFIGPTEvent, EventTypeEFIGPTEvent2:
return decodeEventDataEFIGPT(data)
case EventTypeEFIHCRTMEvent:
return decodeEventDataEFIHCRTMEvent(data)
case EventTypeEFIHandoffTables:
return decodeEventDataEFIHandoffTablePointers(data)
case EventTypeEFIHandoffTables2:
return decodeEventDataEFIHandoffTablePointers2(data)
case EventTypeEventTag:
return decodeEventDataTaggedEvent(data)
case EventTypeSCRTMContents:
return decodeEventDataSCRTMContents(data)
case EventTypeSCRTMVersion:
return decodeEventDataSCRTMVersion(data)
case EventTypeOmitBootDeviceEvents:
return decodeEventDataOmitBootDeviceEvents(data)
default:
}
if err != nil {
err = xerrors.Errorf("cannot decode %v event data: %w", eventType, err)
}
return
}