| // Copyright 2020 The Monogon Project Authors. | 
 | // | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // 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. | 
 |  | 
 | // Taken and pruned from go-attestation revision | 
 | // 2453c8f39a4ff46009f6a9db6fb7c6cca789d9a1 under Apache 2.0 | 
 |  | 
 | package eventlog | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"crypto" | 
 | 	"crypto/sha1" | 
 | 	"crypto/sha256" | 
 | 	"encoding/binary" | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"sort" | 
 |  | 
 | 	// Ensure hashes are available. | 
 | 	_ "crypto/sha256" | 
 |  | 
 | 	"github.com/google/go-tpm/tpm2" | 
 | ) | 
 |  | 
 | // HashAlg identifies a hashing Algorithm. | 
 | type HashAlg uint8 | 
 |  | 
 | // Valid hash algorithms. | 
 | var ( | 
 | 	HashSHA1   = HashAlg(tpm2.AlgSHA1) | 
 | 	HashSHA256 = HashAlg(tpm2.AlgSHA256) | 
 | ) | 
 |  | 
 | func (a HashAlg) cryptoHash() crypto.Hash { | 
 | 	switch a { | 
 | 	case HashSHA1: | 
 | 		return crypto.SHA1 | 
 | 	case HashSHA256: | 
 | 		return crypto.SHA256 | 
 | 	} | 
 | 	return 0 | 
 | } | 
 |  | 
 | func (a HashAlg) goTPMAlg() tpm2.Algorithm { | 
 | 	switch a { | 
 | 	case HashSHA1: | 
 | 		return tpm2.AlgSHA1 | 
 | 	case HashSHA256: | 
 | 		return tpm2.AlgSHA256 | 
 | 	} | 
 | 	return 0 | 
 | } | 
 |  | 
 | // String returns a human-friendly representation of the hash algorithm. | 
 | func (a HashAlg) String() string { | 
 | 	switch a { | 
 | 	case HashSHA1: | 
 | 		return "SHA1" | 
 | 	case HashSHA256: | 
 | 		return "SHA256" | 
 | 	} | 
 | 	return fmt.Sprintf("HashAlg<%d>", int(a)) | 
 | } | 
 |  | 
 | // ReplayError describes the parsed events that failed to verify against | 
 | // a particular PCR. | 
 | type ReplayError struct { | 
 | 	Events      []Event | 
 | 	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) | 
 | } | 
 |  | 
 | // TPM algorithms. See the TPM 2.0 specification section 6.3. | 
 | // | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=42 | 
 | const ( | 
 | 	algSHA1   uint16 = 0x0004 | 
 | 	algSHA256 uint16 = 0x000B | 
 | ) | 
 |  | 
 | // EventType indicates what kind of data an event is reporting. | 
 | type EventType uint32 | 
 |  | 
 | // Event is a single event from a TCG event log. This reports descrete items such | 
 | // as BIOs measurements or EFI states. | 
 | type Event struct { | 
 | 	// order of the event in the event log. | 
 | 	sequence int | 
 |  | 
 | 	// PCR index of the event. | 
 | 	Index int | 
 | 	// Type of the event. | 
 | 	Type EventType | 
 |  | 
 | 	// Data of the event. For certain kinds of events, this must match the event | 
 | 	// digest to be valid. | 
 | 	Data []byte | 
 | 	// Digest is the verified digest of the event data. While an event can have | 
 | 	// multiple for different hash values, this is the one that was matched to the | 
 | 	// PCR value. | 
 | 	Digest []byte | 
 |  | 
 | 	// TODO(ericchiang): Provide examples or links for which event types must | 
 | 	// match their data to their digest. | 
 | } | 
 |  | 
 | func (e *Event) digestEquals(b []byte) error { | 
 | 	if len(e.Digest) == 0 { | 
 | 		return errors.New("no digests present") | 
 | 	} | 
 |  | 
 | 	switch len(e.Digest) { | 
 | 	case crypto.SHA256.Size(): | 
 | 		s := sha256.Sum256(b) | 
 | 		if bytes.Equal(s[:], e.Digest) { | 
 | 			return nil | 
 | 		} | 
 | 	case crypto.SHA1.Size(): | 
 | 		s := sha1.Sum(b) | 
 | 		if bytes.Equal(s[:], e.Digest) { | 
 | 			return nil | 
 | 		} | 
 | 	default: | 
 | 		return fmt.Errorf("cannot compare hash of length %d", len(e.Digest)) | 
 | 	} | 
 |  | 
 | 	return fmt.Errorf("digest (len %d) does not match", len(e.Digest)) | 
 | } | 
 |  | 
 | // EventLog is a parsed measurement log. This contains unverified data representing | 
 | // boot events that must be replayed against PCR values to determine authenticity. | 
 | type EventLog struct { | 
 | 	// Algs holds the set of algorithms that the event log uses. | 
 | 	Algs []HashAlg | 
 |  | 
 | 	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 | 
 | } | 
 |  | 
 | 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, digest{hash: alg.cryptoHash(), data: 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) | 
 | 		}, | 
 | 	}, | 
 | } | 
 |  | 
 | // 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 | 
 | } | 
 |  | 
 | // PCR encapsulates the value of a PCR at a point in time. | 
 | type PCR struct { | 
 | 	Index     int | 
 | 	Digest    []byte | 
 | 	DigestAlg crypto.Hash | 
 | } | 
 |  | 
 | func (e *EventLog) verify(pcrs []PCR) ([]Event, error) { | 
 | 	events, err := replayEvents(e.rawEvents, pcrs) | 
 | 	if err != nil { | 
 | 		if _, isReplayErr := err.(ReplayError); isReplayErr { | 
 | 			return nil, err | 
 | 		} | 
 | 		return nil, fmt.Errorf("pcrs failed to replay: %v", err) | 
 | 	} | 
 | 	return events, nil | 
 | } | 
 |  | 
 | func extend(pcr PCR, replay []byte, e rawEvent) (pcrDigest []byte, eventDigest []byte, err error) { | 
 | 	h := pcr.DigestAlg | 
 |  | 
 | 	for _, digest := range e.digests { | 
 | 		if digest.hash != pcr.DigestAlg { | 
 | 			continue | 
 | 		} | 
 | 		if len(digest.data) != len(pcr.Digest) { | 
 | 			return nil, nil, fmt.Errorf("digest data length (%d) doesn't match PCR digest length (%d)", len(digest.data), len(pcr.Digest)) | 
 | 		} | 
 | 		hash := h.New() | 
 | 		if len(replay) != 0 { | 
 | 			hash.Write(replay) | 
 | 		} else { | 
 | 			b := make([]byte, h.Size()) | 
 | 			hash.Write(b) | 
 | 		} | 
 | 		hash.Write(digest.data) | 
 | 		return hash.Sum(nil), digest.data, nil | 
 | 	} | 
 | 	return nil, nil, fmt.Errorf("no event digest matches pcr algorithm: %v", pcr.DigestAlg) | 
 | } | 
 |  | 
 | // replayPCR replays the event log for a specific PCR, using pcr and | 
 | // event digests with the algorithm in pcr. An error is returned if the | 
 | // replayed values do not match the final PCR digest, or any event tagged | 
 | // with that PCR does not posess an event digest with the specified algorithm. | 
 | func replayPCR(rawEvents []rawEvent, pcr PCR) ([]Event, bool) { | 
 | 	var ( | 
 | 		replay    []byte | 
 | 		outEvents []Event | 
 | 	) | 
 |  | 
 | 	for _, e := range rawEvents { | 
 | 		if e.index != pcr.Index { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		replayValue, digest, err := extend(pcr, replay, e) | 
 | 		if err != nil { | 
 | 			return nil, false | 
 | 		} | 
 | 		replay = replayValue | 
 | 		outEvents = append(outEvents, Event{sequence: e.sequence, Data: e.data, Digest: digest, Index: pcr.Index, Type: e.typ}) | 
 | 	} | 
 |  | 
 | 	if len(outEvents) > 0 && !bytes.Equal(replay, pcr.Digest) { | 
 | 		return nil, false | 
 | 	} | 
 | 	return outEvents, true | 
 | } | 
 |  | 
 | type pcrReplayResult struct { | 
 | 	events     []Event | 
 | 	successful bool | 
 | } | 
 |  | 
 | func replayEvents(rawEvents []rawEvent, pcrs []PCR) ([]Event, error) { | 
 | 	var ( | 
 | 		invalidReplays []int | 
 | 		verifiedEvents []Event | 
 | 		allPCRReplays  = map[int][]pcrReplayResult{} | 
 | 	) | 
 |  | 
 | 	// Replay the event log for every PCR and digest algorithm combination. | 
 | 	for _, pcr := range pcrs { | 
 | 		events, ok := replayPCR(rawEvents, pcr) | 
 | 		allPCRReplays[pcr.Index] = append(allPCRReplays[pcr.Index], pcrReplayResult{events, ok}) | 
 | 	} | 
 |  | 
 | 	// Record PCR indices which do not have any successful replay. Record the | 
 | 	// events for a successful replay. | 
 | pcrLoop: | 
 | 	for i, replaysForPCR := range allPCRReplays { | 
 | 		for _, replay := range replaysForPCR { | 
 | 			if replay.successful { | 
 | 				// We consider the PCR verified at this stage: The replay of values with | 
 | 				// one digest algorithm matched a provided value. | 
 | 				// As such, we save the PCR's events, and proceed to the next PCR. | 
 | 				verifiedEvents = append(verifiedEvents, replay.events...) | 
 | 				continue pcrLoop | 
 | 			} | 
 | 		} | 
 | 		invalidReplays = append(invalidReplays, i) | 
 | 	} | 
 |  | 
 | 	if len(invalidReplays) > 0 { | 
 | 		events := make([]Event, 0, len(rawEvents)) | 
 | 		for _, e := range rawEvents { | 
 | 			events = append(events, Event{e.sequence, e.index, e.typ, e.data, nil}) | 
 | 		} | 
 | 		return nil, ReplayError{ | 
 | 			Events:      events, | 
 | 			invalidPCRs: invalidReplays, | 
 | 		} | 
 | 	} | 
 |  | 
 | 	sort.Slice(verifiedEvents, func(i int, j int) bool { | 
 | 		return verifiedEvents[i].sequence < verifiedEvents[j].sequence | 
 | 	}) | 
 | 	return verifiedEvents, nil | 
 | } | 
 |  | 
 | // EV_NO_ACTION is a special event type that indicates information to the | 
 | // parser instead of holding a measurement. For TPM 2.0, this event type is | 
 | // used to signal switching from SHA1 format to a variable length digest. | 
 | // | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=110 | 
 | const eventTypeNoAction = 0x03 | 
 |  | 
 | // ParseEventLog parses an unverified measurement log. | 
 | func ParseEventLog(measurementLog []byte) (*EventLog, error) { | 
 | 	var specID *specIDEvent | 
 | 	r := bytes.NewBuffer(measurementLog) | 
 | 	parseFn := parseRawEvent | 
 | 	var el EventLog | 
 | 	e, err := parseFn(r, specID) | 
 | 	if err != nil { | 
 | 		return nil, fmt.Errorf("parse first event: %v", err) | 
 | 	} | 
 | 	if e.typ == eventTypeNoAction { | 
 | 		specID, err = parseSpecIDEvent(e.data) | 
 | 		if err != nil { | 
 | 			return nil, fmt.Errorf("failed to parse spec ID event: %v", err) | 
 | 		} | 
 | 		for _, alg := range specID.algs { | 
 | 			switch tpm2.Algorithm(alg.ID) { | 
 | 			case tpm2.AlgSHA1: | 
 | 				el.Algs = append(el.Algs, HashSHA1) | 
 | 			case tpm2.AlgSHA256: | 
 | 				el.Algs = append(el.Algs, HashSHA256) | 
 | 			} | 
 | 		} | 
 | 		if len(el.Algs) == 0 { | 
 | 			return nil, fmt.Errorf("measurement log didn't use sha1 or sha256 digests") | 
 | 		} | 
 | 		// Switch to parsing crypto agile events. Don't include this in the | 
 | 		// replayed events since it intentionally doesn't extend the PCRs. | 
 | 		// | 
 | 		// Note that this doesn't actually guarentee that events have SHA256 | 
 | 		// digests. | 
 | 		parseFn = parseRawEvent2 | 
 | 	} else { | 
 | 		el.Algs = []HashAlg{HashSHA1} | 
 | 		el.rawEvents = append(el.rawEvents, e) | 
 | 	} | 
 | 	sequence := 1 | 
 | 	for r.Len() != 0 { | 
 | 		e, err := parseFn(r, specID) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		e.sequence = sequence | 
 | 		sequence++ | 
 | 		el.rawEvents = append(el.rawEvents, e) | 
 | 	} | 
 | 	return &el, nil | 
 | } | 
 |  | 
 | type specIDEvent struct { | 
 | 	algs []specAlgSize | 
 | } | 
 |  | 
 | type specAlgSize struct { | 
 | 	ID   uint16 | 
 | 	Size uint16 | 
 | } | 
 |  | 
 | // Expected values for various Spec ID Event fields. | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=19 | 
 | var wantSignature = [16]byte{0x53, 0x70, | 
 | 	0x65, 0x63, 0x20, 0x49, | 
 | 	0x44, 0x20, 0x45, 0x76, | 
 | 	0x65, 0x6e, 0x74, 0x30, | 
 | 	0x33, 0x00} // "Spec ID Event03\0" | 
 |  | 
 | const ( | 
 | 	wantMajor  = 2 | 
 | 	wantMinor  = 0 | 
 | 	wantErrata = 0 | 
 | ) | 
 |  | 
 | // parseSpecIDEvent parses a TCG_EfiSpecIDEventStruct structure from the reader. | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=18 | 
 | func parseSpecIDEvent(b []byte) (*specIDEvent, error) { | 
 | 	r := bytes.NewReader(b) | 
 | 	var header struct { | 
 | 		Signature     [16]byte | 
 | 		PlatformClass uint32 | 
 | 		VersionMinor  uint8 | 
 | 		VersionMajor  uint8 | 
 | 		Errata        uint8 | 
 | 		UintnSize     uint8 | 
 | 		NumAlgs       uint32 | 
 | 	} | 
 | 	if err := binary.Read(r, binary.LittleEndian, &header); err != nil { | 
 | 		return nil, fmt.Errorf("reading event header: %v", err) | 
 | 	} | 
 | 	if header.Signature != wantSignature { | 
 | 		return nil, fmt.Errorf("invalid spec id signature: %x", header.Signature) | 
 | 	} | 
 | 	if header.VersionMajor != wantMajor { | 
 | 		return nil, fmt.Errorf("invalid spec major version, got %02x, wanted %02x", | 
 | 			header.VersionMajor, wantMajor) | 
 | 	} | 
 | 	if header.VersionMinor != wantMinor { | 
 | 		return nil, fmt.Errorf("invalid spec minor version, got %02x, wanted %02x", | 
 | 			header.VersionMajor, wantMinor) | 
 | 	} | 
 |  | 
 | 	// TODO(ericchiang): Check errata? Or do we expect that to change in ways | 
 | 	// we're okay with? | 
 |  | 
 | 	specAlg := specAlgSize{} | 
 | 	e := specIDEvent{} | 
 | 	for i := 0; i < int(header.NumAlgs); i++ { | 
 | 		if err := binary.Read(r, binary.LittleEndian, &specAlg); err != nil { | 
 | 			return nil, fmt.Errorf("reading algorithm: %v", err) | 
 | 		} | 
 | 		e.algs = append(e.algs, specAlg) | 
 | 	} | 
 |  | 
 | 	var vendorInfoSize uint8 | 
 | 	if err := binary.Read(r, binary.LittleEndian, &vendorInfoSize); err != nil { | 
 | 		return nil, fmt.Errorf("reading vender info size: %v", err) | 
 | 	} | 
 | 	if r.Len() != int(vendorInfoSize) { | 
 | 		return nil, fmt.Errorf("reading vendor info, expected %d remaining bytes, got %d", vendorInfoSize, r.Len()) | 
 | 	} | 
 | 	return &e, nil | 
 | } | 
 |  | 
 | type digest struct { | 
 | 	hash crypto.Hash | 
 | 	data []byte | 
 | } | 
 |  | 
 | type rawEvent struct { | 
 | 	sequence int | 
 | 	index    int | 
 | 	typ      EventType | 
 | 	data     []byte | 
 | 	digests  []digest | 
 | } | 
 |  | 
 | // TPM 1.2 event log format. See "5.1 SHA1 Event Log Entry Format" | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=15 | 
 | type rawEventHeader struct { | 
 | 	PCRIndex  uint32 | 
 | 	Type      uint32 | 
 | 	Digest    [20]byte | 
 | 	EventSize uint32 | 
 | } | 
 |  | 
 | type eventSizeErr struct { | 
 | 	eventSize uint32 | 
 | 	logSize   int | 
 | } | 
 |  | 
 | func (e *eventSizeErr) Error() string { | 
 | 	return fmt.Sprintf("event data size (%d bytes) is greater than remaining measurement log (%d bytes)", e.eventSize, e.logSize) | 
 | } | 
 |  | 
 | func parseRawEvent(r *bytes.Buffer, specID *specIDEvent) (event rawEvent, err error) { | 
 | 	var h rawEventHeader | 
 | 	if err = binary.Read(r, binary.LittleEndian, &h); err != nil { | 
 | 		return event, err | 
 | 	} | 
 | 	if h.EventSize == 0 { | 
 | 		return event, errors.New("event data size is 0") | 
 | 	} | 
 | 	if h.EventSize > uint32(r.Len()) { | 
 | 		return event, &eventSizeErr{h.EventSize, r.Len()} | 
 | 	} | 
 |  | 
 | 	data := make([]byte, int(h.EventSize)) | 
 | 	if _, err := io.ReadFull(r, data); err != nil { | 
 | 		return event, err | 
 | 	} | 
 |  | 
 | 	digests := []digest{{hash: crypto.SHA1, data: h.Digest[:]}} | 
 |  | 
 | 	return rawEvent{ | 
 | 		typ:     EventType(h.Type), | 
 | 		data:    data, | 
 | 		index:   int(h.PCRIndex), | 
 | 		digests: digests, | 
 | 	}, nil | 
 | } | 
 |  | 
 | // TPM 2.0 event log format. See "5.2 Crypto Agile Log Entry Format" | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf#page=15 | 
 | type rawEvent2Header struct { | 
 | 	PCRIndex uint32 | 
 | 	Type     uint32 | 
 | } | 
 |  | 
 | func parseRawEvent2(r *bytes.Buffer, specID *specIDEvent) (event rawEvent, err error) { | 
 | 	var h rawEvent2Header | 
 |  | 
 | 	if err = binary.Read(r, binary.LittleEndian, &h); err != nil { | 
 | 		return event, err | 
 | 	} | 
 | 	event.typ = EventType(h.Type) | 
 | 	event.index = int(h.PCRIndex) | 
 |  | 
 | 	// parse the event digests | 
 | 	var numDigests uint32 | 
 | 	if err := binary.Read(r, binary.LittleEndian, &numDigests); err != nil { | 
 | 		return event, err | 
 | 	} | 
 |  | 
 | 	for i := 0; i < int(numDigests); i++ { | 
 | 		var algID uint16 | 
 | 		if err := binary.Read(r, binary.LittleEndian, &algID); err != nil { | 
 | 			return event, err | 
 | 		} | 
 | 		var digest digest | 
 |  | 
 | 		for _, alg := range specID.algs { | 
 | 			if alg.ID != algID { | 
 | 				continue | 
 | 			} | 
 | 			if uint16(r.Len()) < alg.Size { | 
 | 				return event, fmt.Errorf("reading digest: %v", io.ErrUnexpectedEOF) | 
 | 			} | 
 | 			digest.data = make([]byte, alg.Size) | 
 | 			digest.hash = HashAlg(alg.ID).cryptoHash() | 
 | 		} | 
 | 		if len(digest.data) == 0 { | 
 | 			return event, fmt.Errorf("unknown algorithm ID %x", algID) | 
 | 		} | 
 | 		if _, err := io.ReadFull(r, digest.data); err != nil { | 
 | 			return event, err | 
 | 		} | 
 | 		event.digests = append(event.digests, digest) | 
 | 	} | 
 |  | 
 | 	// parse event data | 
 | 	var eventSize uint32 | 
 | 	if err = binary.Read(r, binary.LittleEndian, &eventSize); err != nil { | 
 | 		return event, err | 
 | 	} | 
 | 	if eventSize == 0 { | 
 | 		return event, errors.New("event data size is 0") | 
 | 	} | 
 | 	if eventSize > uint32(r.Len()) { | 
 | 		return event, &eventSizeErr{eventSize, r.Len()} | 
 | 	} | 
 | 	event.data = make([]byte, int(eventSize)) | 
 | 	if _, err := io.ReadFull(r, event.data); err != nil { | 
 | 		return event, err | 
 | 	} | 
 | 	return event, err | 
 | } |