| // 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 from go-attestation under Apache 2.0 | 
 | package internal | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"encoding/binary" | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"unicode/utf16" | 
 |  | 
 | 	"github.com/google/certificate-transparency-go/asn1" | 
 | 	"github.com/google/certificate-transparency-go/x509" | 
 | ) | 
 |  | 
 | const ( | 
 | 	// maxNameLen is the maximum accepted byte length for a name field. | 
 | 	// This value should be larger than any reasonable value. | 
 | 	maxNameLen = 2048 | 
 | 	// maxDataLen is the maximum size in bytes of a variable data field. | 
 | 	// This value should be larger than any reasonable value. | 
 | 	maxDataLen = 1024 * 1024 // 1 Megabyte. | 
 | ) | 
 |  | 
 | // GUIDs representing the contents of an UEFI_SIGNATURE_LIST. | 
 | var ( | 
 | 	hashSHA256SigGUID        = efiGUID{0xc1c41626, 0x504c, 0x4092, [8]byte{0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28}} | 
 | 	hashSHA1SigGUID          = efiGUID{0x826ca512, 0xcf10, 0x4ac9, [8]byte{0xb1, 0x87, 0xbe, 0x01, 0x49, 0x66, 0x31, 0xbd}} | 
 | 	hashSHA224SigGUID        = efiGUID{0x0b6e5233, 0xa65c, 0x44c9, [8]byte{0x94, 0x07, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd}} | 
 | 	hashSHA384SigGUID        = efiGUID{0xff3e5307, 0x9fd0, 0x48c9, [8]byte{0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x01}} | 
 | 	hashSHA512SigGUID        = efiGUID{0x093e0fae, 0xa6c4, 0x4f50, [8]byte{0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a}} | 
 | 	keyRSA2048SigGUID        = efiGUID{0x3c5766e8, 0x269c, 0x4e34, [8]byte{0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6}} | 
 | 	certRSA2048SHA256SigGUID = efiGUID{0xe2b36190, 0x879b, 0x4a3d, [8]byte{0xad, 0x8d, 0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84}} | 
 | 	certRSA2048SHA1SigGUID   = efiGUID{0x67f8444f, 0x8743, 0x48f1, [8]byte{0xa3, 0x28, 0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80}} | 
 | 	certX509SigGUID          = efiGUID{0xa5c059a1, 0x94e4, 0x4aa7, [8]byte{0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72}} | 
 | 	certHashSHA256SigGUID    = efiGUID{0x3bd2a492, 0x96c0, 0x4079, [8]byte{0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed}} | 
 | 	certHashSHA384SigGUID    = efiGUID{0x7076876e, 0x80c2, 0x4ee6, [8]byte{0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b}} | 
 | 	certHashSHA512SigGUID    = efiGUID{0x446dbf63, 0x2502, 0x4cda, [8]byte{0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d}} | 
 | ) | 
 |  | 
 | // EventType describes the type of event signalled in the event log. | 
 | type EventType uint32 | 
 |  | 
 | // 	BIOS Events (TCG PC Client Specific Implementation Specification for Conventional BIOS 1.21) | 
 | const ( | 
 | 	PrebootCert          EventType = 0x00000000 | 
 | 	PostCode             EventType = 0x00000001 | 
 | 	unused               EventType = 0x00000002 | 
 | 	NoAction             EventType = 0x00000003 | 
 | 	Separator            EventType = 0x00000004 | 
 | 	Action               EventType = 0x00000005 | 
 | 	EventTag             EventType = 0x00000006 | 
 | 	SCRTMContents        EventType = 0x00000007 | 
 | 	SCRTMVersion         EventType = 0x00000008 | 
 | 	CpuMicrocode         EventType = 0x00000009 | 
 | 	PlatformConfigFlags  EventType = 0x0000000A | 
 | 	TableOfDevices       EventType = 0x0000000B | 
 | 	CompactHash          EventType = 0x0000000C | 
 | 	Ipl                  EventType = 0x0000000D | 
 | 	IplPartitionData     EventType = 0x0000000E | 
 | 	NonhostCode          EventType = 0x0000000F | 
 | 	NonhostConfig        EventType = 0x00000010 | 
 | 	NonhostInfo          EventType = 0x00000011 | 
 | 	OmitBootDeviceEvents EventType = 0x00000012 | 
 | ) | 
 |  | 
 | // EFI Events (TCG EFI Platform Specification Version 1.22) | 
 | const ( | 
 | 	EFIEventBase               EventType = 0x80000000 | 
 | 	EFIVariableDriverConfig    EventType = 0x80000001 | 
 | 	EFIVariableBoot            EventType = 0x80000002 | 
 | 	EFIBootServicesApplication EventType = 0x80000003 | 
 | 	EFIBootServicesDriver      EventType = 0x80000004 | 
 | 	EFIRuntimeServicesDriver   EventType = 0x80000005 | 
 | 	EFIGPTEvent                EventType = 0x80000006 | 
 | 	EFIAction                  EventType = 0x80000007 | 
 | 	EFIPlatformFirmwareBlob    EventType = 0x80000008 | 
 | 	EFIHandoffTables           EventType = 0x80000009 | 
 | 	EFIHCRTMEvent              EventType = 0x80000010 | 
 | 	EFIVariableAuthority       EventType = 0x800000e0 | 
 | ) | 
 |  | 
 | // ErrSigMissingGUID is returned if an EFI_SIGNATURE_DATA structure was parsed | 
 | // successfully, however was missing the SignatureOwner GUID. This case is | 
 | // handled specially as a workaround for a bug relating to authority events. | 
 | var ErrSigMissingGUID = errors.New("signature data was missing owner GUID") | 
 |  | 
 | var eventTypeNames = map[EventType]string{ | 
 | 	PrebootCert:          "Preboot Cert", | 
 | 	PostCode:             "POST Code", | 
 | 	unused:               "Unused", | 
 | 	NoAction:             "No Action", | 
 | 	Separator:            "Separator", | 
 | 	Action:               "Action", | 
 | 	EventTag:             "Event Tag", | 
 | 	SCRTMContents:        "S-CRTM Contents", | 
 | 	SCRTMVersion:         "S-CRTM Version", | 
 | 	CpuMicrocode:         "CPU Microcode", | 
 | 	PlatformConfigFlags:  "Platform Config Flags", | 
 | 	TableOfDevices:       "Table of Devices", | 
 | 	CompactHash:          "Compact Hash", | 
 | 	Ipl:                  "IPL", | 
 | 	IplPartitionData:     "IPL Partition Data", | 
 | 	NonhostCode:          "Non-Host Code", | 
 | 	NonhostConfig:        "Non-HostConfig", | 
 | 	NonhostInfo:          "Non-Host Info", | 
 | 	OmitBootDeviceEvents: "Omit Boot Device Events", | 
 |  | 
 | 	EFIEventBase:               "EFI Event Base", | 
 | 	EFIVariableDriverConfig:    "EFI Variable Driver Config", | 
 | 	EFIVariableBoot:            "EFI Variable Boot", | 
 | 	EFIBootServicesApplication: "EFI Boot Services Application", | 
 | 	EFIBootServicesDriver:      "EFI Boot Services Driver", | 
 | 	EFIRuntimeServicesDriver:   "EFI Runtime Services Driver", | 
 | 	EFIGPTEvent:                "EFI GPT Event", | 
 | 	EFIAction:                  "EFI Action", | 
 | 	EFIPlatformFirmwareBlob:    "EFI Platform Firmware Blob", | 
 | 	EFIVariableAuthority:       "EFI Variable Authority", | 
 | 	EFIHandoffTables:           "EFI Handoff Tables", | 
 | 	EFIHCRTMEvent:              "EFI H-CRTM Event", | 
 | } | 
 |  | 
 | func (e EventType) String() string { | 
 | 	if s, ok := eventTypeNames[e]; ok { | 
 | 		return s | 
 | 	} | 
 | 	return fmt.Sprintf("EventType(0x%x)", uint32(e)) | 
 | } | 
 |  | 
 | // UntrustedParseEventType returns the event type indicated by | 
 | // the provided value. | 
 | func UntrustedParseEventType(et uint32) (EventType, error) { | 
 | 	// "The value associated with a UEFI specific platform event type MUST be in | 
 | 	// the range between 0x80000000 and 0x800000FF, inclusive." | 
 | 	if (et < 0x80000000 && et > 0x800000FF) || (et < 0x0 && et > 0x12) { | 
 | 		return EventType(0), fmt.Errorf("event type not between [0x0, 0x12] or [0x80000000, 0x800000FF]: got %#x", et) | 
 | 	} | 
 | 	if _, ok := eventTypeNames[EventType(et)]; !ok { | 
 | 		return EventType(0), fmt.Errorf("unknown event type %#x", et) | 
 | 	} | 
 | 	return EventType(et), nil | 
 | } | 
 |  | 
 | // efiGUID represents the EFI_GUID type. | 
 | // See section "2.3.1 Data Types" in the specification for more information. | 
 | // type efiGUID [16]byte | 
 | type efiGUID struct { | 
 | 	Data1 uint32 | 
 | 	Data2 uint16 | 
 | 	Data3 uint16 | 
 | 	Data4 [8]byte | 
 | } | 
 |  | 
 | func (d efiGUID) String() string { | 
 | 	var u [8]byte | 
 | 	binary.BigEndian.PutUint32(u[:4], d.Data1) | 
 | 	binary.BigEndian.PutUint16(u[4:6], d.Data2) | 
 | 	binary.BigEndian.PutUint16(u[6:8], d.Data3) | 
 | 	return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], d.Data4[:2], d.Data4[2:]) | 
 | } | 
 |  | 
 | // UEFIVariableDataHeader represents the leading fixed-size fields | 
 | // within UEFI_VARIABLE_DATA. | 
 | type UEFIVariableDataHeader struct { | 
 | 	VariableName       efiGUID | 
 | 	UnicodeNameLength  uint64 // uintN | 
 | 	VariableDataLength uint64 // uintN | 
 | } | 
 |  | 
 | // UEFIVariableData represents the UEFI_VARIABLE_DATA structure. | 
 | type UEFIVariableData struct { | 
 | 	Header       UEFIVariableDataHeader | 
 | 	UnicodeName  []uint16 | 
 | 	VariableData []byte // []int8 | 
 | } | 
 |  | 
 | // ParseUEFIVariableData parses the data section of an event structured as a | 
 | // UEFI variable. | 
 | // | 
 | //   https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_Specific_Platform_Profile_for_TPM_2p0_1p04_PUBLIC.pdf#page=100 | 
 | func ParseUEFIVariableData(r io.Reader) (ret UEFIVariableData, err error) { | 
 | 	err = binary.Read(r, binary.LittleEndian, &ret.Header) | 
 | 	if err != nil { | 
 | 		return | 
 | 	} | 
 | 	if ret.Header.UnicodeNameLength > maxNameLen { | 
 | 		return UEFIVariableData{}, fmt.Errorf("unicode name too long: %d > %d", ret.Header.UnicodeNameLength, maxNameLen) | 
 | 	} | 
 | 	ret.UnicodeName = make([]uint16, ret.Header.UnicodeNameLength) | 
 | 	for i := 0; uint64(i) < ret.Header.UnicodeNameLength; i++ { | 
 | 		err = binary.Read(r, binary.LittleEndian, &ret.UnicodeName[i]) | 
 | 		if err != nil { | 
 | 			return | 
 | 		} | 
 | 	} | 
 | 	if ret.Header.VariableDataLength > maxDataLen { | 
 | 		return UEFIVariableData{}, fmt.Errorf("variable data too long: %d > %d", ret.Header.VariableDataLength, maxDataLen) | 
 | 	} | 
 | 	ret.VariableData = make([]byte, ret.Header.VariableDataLength) | 
 | 	_, err = io.ReadFull(r, ret.VariableData) | 
 | 	return | 
 | } | 
 |  | 
 | func (v *UEFIVariableData) VarName() string { | 
 | 	return string(utf16.Decode(v.UnicodeName)) | 
 | } | 
 |  | 
 | func (v *UEFIVariableData) SignatureData() (certs []x509.Certificate, hashes [][]byte, err error) { | 
 | 	return parseEfiSignatureList(v.VariableData) | 
 | } | 
 |  | 
 | // UEFIVariableAuthority describes the contents of a UEFI variable authority | 
 | // event. | 
 | type UEFIVariableAuthority struct { | 
 | 	Certs []x509.Certificate | 
 | } | 
 |  | 
 | // ParseUEFIVariableAuthority parses the data section of an event structured as | 
 | // a UEFI variable authority. | 
 | // | 
 | // https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf#page=1789 | 
 | func ParseUEFIVariableAuthority(r io.Reader) (UEFIVariableAuthority, error) { | 
 | 	v, err := ParseUEFIVariableData(r) | 
 | 	if err != nil { | 
 | 		return UEFIVariableAuthority{}, err | 
 | 	} | 
 | 	certs, err := parseEfiSignature(v.VariableData) | 
 | 	return UEFIVariableAuthority{Certs: certs}, err | 
 | } | 
 |  | 
 | // efiSignatureData represents the EFI_SIGNATURE_DATA type.  See section | 
 | // "31.4.1 Signature Database" in the specification for more information. | 
 | type efiSignatureData struct { | 
 | 	SignatureOwner efiGUID | 
 | 	SignatureData  []byte // []int8 | 
 | } | 
 |  | 
 | // efiSignatureList represents the EFI_SIGNATURE_LIST type. | 
 | // See section "31.4.1 Signature Database" in the specification for more | 
 | // information. | 
 | type efiSignatureListHeader struct { | 
 | 	SignatureType       efiGUID | 
 | 	SignatureListSize   uint32 | 
 | 	SignatureHeaderSize uint32 | 
 | 	SignatureSize       uint32 | 
 | } | 
 |  | 
 | type efiSignatureList struct { | 
 | 	Header        efiSignatureListHeader | 
 | 	SignatureData []byte | 
 | 	Signatures    []byte | 
 | } | 
 |  | 
 | // parseEfiSignatureList parses a EFI_SIGNATURE_LIST structure. | 
 | // The structure and related GUIDs are defined at: | 
 | // https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf#page=1790 | 
 | func parseEfiSignatureList(b []byte) ([]x509.Certificate, [][]byte, error) { | 
 | 	if len(b) < 28 { | 
 | 		// Being passed an empty signature list here appears to be valid | 
 | 		return nil, nil, nil | 
 | 	} | 
 | 	signatures := efiSignatureList{} | 
 | 	buf := bytes.NewReader(b) | 
 | 	certificates := []x509.Certificate{} | 
 | 	hashes := [][]byte{} | 
 |  | 
 | 	for buf.Len() > 0 { | 
 | 		err := binary.Read(buf, binary.LittleEndian, &signatures.Header) | 
 | 		if err != nil { | 
 | 			return nil, nil, err | 
 | 		} | 
 |  | 
 | 		if signatures.Header.SignatureHeaderSize > maxDataLen { | 
 | 			return nil, nil, fmt.Errorf("signature header too large: %d > %d", signatures.Header.SignatureHeaderSize, maxDataLen) | 
 | 		} | 
 | 		if signatures.Header.SignatureListSize > maxDataLen { | 
 | 			return nil, nil, fmt.Errorf("signature list too large: %d > %d", signatures.Header.SignatureListSize, maxDataLen) | 
 | 		} | 
 |  | 
 | 		signatureType := signatures.Header.SignatureType | 
 | 		switch signatureType { | 
 | 		case certX509SigGUID: // X509 certificate | 
 | 			for sigOffset := 0; uint32(sigOffset) < signatures.Header.SignatureListSize-28; { | 
 | 				signature := efiSignatureData{} | 
 | 				signature.SignatureData = make([]byte, signatures.Header.SignatureSize-16) | 
 | 				err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner) | 
 | 				if err != nil { | 
 | 					return nil, nil, err | 
 | 				} | 
 | 				err = binary.Read(buf, binary.LittleEndian, &signature.SignatureData) | 
 | 				if err != nil { | 
 | 					return nil, nil, err | 
 | 				} | 
 | 				cert, err := x509.ParseCertificate(signature.SignatureData) | 
 | 				if err != nil { | 
 | 					return nil, nil, err | 
 | 				} | 
 | 				sigOffset += int(signatures.Header.SignatureSize) | 
 | 				certificates = append(certificates, *cert) | 
 | 			} | 
 | 		case hashSHA256SigGUID: // SHA256 | 
 | 			for sigOffset := 0; uint32(sigOffset) < signatures.Header.SignatureListSize-28; { | 
 | 				signature := efiSignatureData{} | 
 | 				signature.SignatureData = make([]byte, signatures.Header.SignatureSize-16) | 
 | 				err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner) | 
 | 				if err != nil { | 
 | 					return nil, nil, err | 
 | 				} | 
 | 				err = binary.Read(buf, binary.LittleEndian, &signature.SignatureData) | 
 | 				if err != nil { | 
 | 					return nil, nil, err | 
 | 				} | 
 | 				hashes = append(hashes, signature.SignatureData) | 
 | 				sigOffset += int(signatures.Header.SignatureSize) | 
 | 			} | 
 | 		case keyRSA2048SigGUID: | 
 | 			err = errors.New("unhandled RSA2048 key") | 
 | 		case certRSA2048SHA256SigGUID: | 
 | 			err = errors.New("unhandled RSA2048-SHA256 key") | 
 | 		case hashSHA1SigGUID: | 
 | 			err = errors.New("unhandled SHA1 hash") | 
 | 		case certRSA2048SHA1SigGUID: | 
 | 			err = errors.New("unhandled RSA2048-SHA1 key") | 
 | 		case hashSHA224SigGUID: | 
 | 			err = errors.New("unhandled SHA224 hash") | 
 | 		case hashSHA384SigGUID: | 
 | 			err = errors.New("unhandled SHA384 hash") | 
 | 		case hashSHA512SigGUID: | 
 | 			err = errors.New("unhandled SHA512 hash") | 
 | 		case certHashSHA256SigGUID: | 
 | 			err = errors.New("unhandled X509-SHA256 hash metadata") | 
 | 		case certHashSHA384SigGUID: | 
 | 			err = errors.New("unhandled X509-SHA384 hash metadata") | 
 | 		case certHashSHA512SigGUID: | 
 | 			err = errors.New("unhandled X509-SHA512 hash metadata") | 
 | 		default: | 
 | 			err = fmt.Errorf("unhandled signature type %s", signatureType) | 
 | 		} | 
 | 		if err != nil { | 
 | 			return nil, nil, err | 
 | 		} | 
 | 	} | 
 | 	return certificates, hashes, nil | 
 | } | 
 |  | 
 | // EFISignatureData represents the EFI_SIGNATURE_DATA type. | 
 | // See section "31.4.1 Signature Database" in the specification | 
 | // for more information. | 
 | type EFISignatureData struct { | 
 | 	SignatureOwner efiGUID | 
 | 	SignatureData  []byte // []int8 | 
 | } | 
 |  | 
 | func parseEfiSignature(b []byte) ([]x509.Certificate, error) { | 
 | 	certificates := []x509.Certificate{} | 
 |  | 
 | 	if len(b) < 16 { | 
 | 		return nil, fmt.Errorf("invalid signature: buffer smaller than header (%d < %d)", len(b), 16) | 
 | 	} | 
 |  | 
 | 	buf := bytes.NewReader(b) | 
 | 	signature := EFISignatureData{} | 
 | 	signature.SignatureData = make([]byte, len(b)-16) | 
 |  | 
 | 	if err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner); err != nil { | 
 | 		return certificates, err | 
 | 	} | 
 | 	if err := binary.Read(buf, binary.LittleEndian, &signature.SignatureData); err != nil { | 
 | 		return certificates, err | 
 | 	} | 
 |  | 
 | 	cert, err := x509.ParseCertificate(signature.SignatureData) | 
 | 	if err == nil { | 
 | 		certificates = append(certificates, *cert) | 
 | 	} else { | 
 | 		// A bug in shim may cause an event to be missing the SignatureOwner GUID. | 
 | 		// We handle this, but signal back to the caller using ErrSigMissingGUID. | 
 | 		if _, isStructuralErr := err.(asn1.StructuralError); isStructuralErr { | 
 | 			var err2 error | 
 | 			cert, err2 = x509.ParseCertificate(b) | 
 | 			if err2 == nil { | 
 | 				certificates = append(certificates, *cert) | 
 | 				err = ErrSigMissingGUID | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return certificates, err | 
 | } |