blob: 23b00c2a7e4372c1889bc053eb4b248d6e904221 [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 from go-attestation under Apache 2.0
18package internal
19
20import (
21 "bytes"
22 "encoding/binary"
23 "errors"
24 "fmt"
25 "io"
26 "unicode/utf16"
27
28 "github.com/google/certificate-transparency-go/asn1"
29 "github.com/google/certificate-transparency-go/x509"
30)
31
32const (
33 // maxNameLen is the maximum accepted byte length for a name field.
34 // This value should be larger than any reasonable value.
35 maxNameLen = 2048
36 // maxDataLen is the maximum size in bytes of a variable data field.
37 // This value should be larger than any reasonable value.
38 maxDataLen = 1024 * 1024 // 1 Megabyte.
39)
40
41// GUIDs representing the contents of an UEFI_SIGNATURE_LIST.
42var (
43 hashSHA256SigGUID = efiGUID{0xc1c41626, 0x504c, 0x4092, [8]byte{0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28}}
44 hashSHA1SigGUID = efiGUID{0x826ca512, 0xcf10, 0x4ac9, [8]byte{0xb1, 0x87, 0xbe, 0x01, 0x49, 0x66, 0x31, 0xbd}}
45 hashSHA224SigGUID = efiGUID{0x0b6e5233, 0xa65c, 0x44c9, [8]byte{0x94, 0x07, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd}}
46 hashSHA384SigGUID = efiGUID{0xff3e5307, 0x9fd0, 0x48c9, [8]byte{0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x01}}
47 hashSHA512SigGUID = efiGUID{0x093e0fae, 0xa6c4, 0x4f50, [8]byte{0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a}}
48 keyRSA2048SigGUID = efiGUID{0x3c5766e8, 0x269c, 0x4e34, [8]byte{0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6}}
49 certRSA2048SHA256SigGUID = efiGUID{0xe2b36190, 0x879b, 0x4a3d, [8]byte{0xad, 0x8d, 0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84}}
50 certRSA2048SHA1SigGUID = efiGUID{0x67f8444f, 0x8743, 0x48f1, [8]byte{0xa3, 0x28, 0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80}}
51 certX509SigGUID = efiGUID{0xa5c059a1, 0x94e4, 0x4aa7, [8]byte{0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72}}
52 certHashSHA256SigGUID = efiGUID{0x3bd2a492, 0x96c0, 0x4079, [8]byte{0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed}}
53 certHashSHA384SigGUID = efiGUID{0x7076876e, 0x80c2, 0x4ee6, [8]byte{0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b}}
54 certHashSHA512SigGUID = efiGUID{0x446dbf63, 0x2502, 0x4cda, [8]byte{0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d}}
55)
56
57// EventType describes the type of event signalled in the event log.
58type EventType uint32
59
Tim Windelschmidt99e15112025-02-05 17:38:16 +010060// BIOS Events (TCG PC Client Specific Implementation Specification for
61// Conventional BIOS 1.21)
Lorenz Bruna50e8452020-09-09 17:09:27 +020062const (
63 PrebootCert EventType = 0x00000000
64 PostCode EventType = 0x00000001
65 unused EventType = 0x00000002
66 NoAction EventType = 0x00000003
67 Separator EventType = 0x00000004
68 Action EventType = 0x00000005
69 EventTag EventType = 0x00000006
70 SCRTMContents EventType = 0x00000007
71 SCRTMVersion EventType = 0x00000008
72 CpuMicrocode EventType = 0x00000009
73 PlatformConfigFlags EventType = 0x0000000A
74 TableOfDevices EventType = 0x0000000B
75 CompactHash EventType = 0x0000000C
76 Ipl EventType = 0x0000000D
77 IplPartitionData EventType = 0x0000000E
78 NonhostCode EventType = 0x0000000F
79 NonhostConfig EventType = 0x00000010
80 NonhostInfo EventType = 0x00000011
81 OmitBootDeviceEvents EventType = 0x00000012
82)
83
84// EFI Events (TCG EFI Platform Specification Version 1.22)
85const (
86 EFIEventBase EventType = 0x80000000
87 EFIVariableDriverConfig EventType = 0x80000001
88 EFIVariableBoot EventType = 0x80000002
89 EFIBootServicesApplication EventType = 0x80000003
90 EFIBootServicesDriver EventType = 0x80000004
91 EFIRuntimeServicesDriver EventType = 0x80000005
92 EFIGPTEvent EventType = 0x80000006
93 EFIAction EventType = 0x80000007
94 EFIPlatformFirmwareBlob EventType = 0x80000008
95 EFIHandoffTables EventType = 0x80000009
96 EFIHCRTMEvent EventType = 0x80000010
97 EFIVariableAuthority EventType = 0x800000e0
98)
99
100// ErrSigMissingGUID is returned if an EFI_SIGNATURE_DATA structure was parsed
101// successfully, however was missing the SignatureOwner GUID. This case is
102// handled specially as a workaround for a bug relating to authority events.
103var ErrSigMissingGUID = errors.New("signature data was missing owner GUID")
104
105var eventTypeNames = map[EventType]string{
106 PrebootCert: "Preboot Cert",
107 PostCode: "POST Code",
108 unused: "Unused",
109 NoAction: "No Action",
110 Separator: "Separator",
111 Action: "Action",
112 EventTag: "Event Tag",
113 SCRTMContents: "S-CRTM Contents",
114 SCRTMVersion: "S-CRTM Version",
115 CpuMicrocode: "CPU Microcode",
116 PlatformConfigFlags: "Platform Config Flags",
117 TableOfDevices: "Table of Devices",
118 CompactHash: "Compact Hash",
119 Ipl: "IPL",
120 IplPartitionData: "IPL Partition Data",
121 NonhostCode: "Non-Host Code",
122 NonhostConfig: "Non-HostConfig",
123 NonhostInfo: "Non-Host Info",
124 OmitBootDeviceEvents: "Omit Boot Device Events",
125
126 EFIEventBase: "EFI Event Base",
127 EFIVariableDriverConfig: "EFI Variable Driver Config",
128 EFIVariableBoot: "EFI Variable Boot",
129 EFIBootServicesApplication: "EFI Boot Services Application",
130 EFIBootServicesDriver: "EFI Boot Services Driver",
131 EFIRuntimeServicesDriver: "EFI Runtime Services Driver",
132 EFIGPTEvent: "EFI GPT Event",
133 EFIAction: "EFI Action",
134 EFIPlatformFirmwareBlob: "EFI Platform Firmware Blob",
135 EFIVariableAuthority: "EFI Variable Authority",
136 EFIHandoffTables: "EFI Handoff Tables",
137 EFIHCRTMEvent: "EFI H-CRTM Event",
138}
139
140func (e EventType) String() string {
141 if s, ok := eventTypeNames[e]; ok {
142 return s
143 }
144 return fmt.Sprintf("EventType(0x%x)", uint32(e))
145}
146
147// UntrustedParseEventType returns the event type indicated by
148// the provided value.
149func UntrustedParseEventType(et uint32) (EventType, error) {
150 // "The value associated with a UEFI specific platform event type MUST be in
151 // the range between 0x80000000 and 0x800000FF, inclusive."
Jan Schärc8f5e642024-04-30 17:20:32 +0200152 if (et < 0x80000000 || et > 0x800000FF) && et > 0x12 {
Lorenz Bruna50e8452020-09-09 17:09:27 +0200153 return EventType(0), fmt.Errorf("event type not between [0x0, 0x12] or [0x80000000, 0x800000FF]: got %#x", et)
154 }
155 if _, ok := eventTypeNames[EventType(et)]; !ok {
156 return EventType(0), fmt.Errorf("unknown event type %#x", et)
157 }
158 return EventType(et), nil
159}
160
161// efiGUID represents the EFI_GUID type.
162// See section "2.3.1 Data Types" in the specification for more information.
163// type efiGUID [16]byte
164type efiGUID struct {
165 Data1 uint32
166 Data2 uint16
167 Data3 uint16
168 Data4 [8]byte
169}
170
171func (d efiGUID) String() string {
172 var u [8]byte
173 binary.BigEndian.PutUint32(u[:4], d.Data1)
174 binary.BigEndian.PutUint16(u[4:6], d.Data2)
175 binary.BigEndian.PutUint16(u[6:8], d.Data3)
176 return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], d.Data4[:2], d.Data4[2:])
177}
178
179// UEFIVariableDataHeader represents the leading fixed-size fields
180// within UEFI_VARIABLE_DATA.
181type UEFIVariableDataHeader struct {
182 VariableName efiGUID
183 UnicodeNameLength uint64 // uintN
184 VariableDataLength uint64 // uintN
185}
186
187// UEFIVariableData represents the UEFI_VARIABLE_DATA structure.
188type UEFIVariableData struct {
189 Header UEFIVariableDataHeader
190 UnicodeName []uint16
191 VariableData []byte // []int8
192}
193
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200194// ParseUEFIVariableData parses the data section of an event structured as a
195// UEFI variable.
Lorenz Bruna50e8452020-09-09 17:09:27 +0200196//
Tim Windelschmidt99e15112025-02-05 17:38:16 +0100197// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_Specific_Platform_Profile_for_TPM_2p0_1p04_PUBLIC.pdf#page=100
Lorenz Bruna50e8452020-09-09 17:09:27 +0200198func ParseUEFIVariableData(r io.Reader) (ret UEFIVariableData, err error) {
199 err = binary.Read(r, binary.LittleEndian, &ret.Header)
200 if err != nil {
201 return
202 }
203 if ret.Header.UnicodeNameLength > maxNameLen {
204 return UEFIVariableData{}, fmt.Errorf("unicode name too long: %d > %d", ret.Header.UnicodeNameLength, maxNameLen)
205 }
206 ret.UnicodeName = make([]uint16, ret.Header.UnicodeNameLength)
207 for i := 0; uint64(i) < ret.Header.UnicodeNameLength; i++ {
208 err = binary.Read(r, binary.LittleEndian, &ret.UnicodeName[i])
209 if err != nil {
210 return
211 }
212 }
213 if ret.Header.VariableDataLength > maxDataLen {
214 return UEFIVariableData{}, fmt.Errorf("variable data too long: %d > %d", ret.Header.VariableDataLength, maxDataLen)
215 }
216 ret.VariableData = make([]byte, ret.Header.VariableDataLength)
217 _, err = io.ReadFull(r, ret.VariableData)
218 return
219}
220
221func (v *UEFIVariableData) VarName() string {
222 return string(utf16.Decode(v.UnicodeName))
223}
224
225func (v *UEFIVariableData) SignatureData() (certs []x509.Certificate, hashes [][]byte, err error) {
226 return parseEfiSignatureList(v.VariableData)
227}
228
229// UEFIVariableAuthority describes the contents of a UEFI variable authority
230// event.
231type UEFIVariableAuthority struct {
232 Certs []x509.Certificate
233}
234
235// ParseUEFIVariableAuthority parses the data section of an event structured as
236// a UEFI variable authority.
237//
238// https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf#page=1789
239func ParseUEFIVariableAuthority(r io.Reader) (UEFIVariableAuthority, error) {
240 v, err := ParseUEFIVariableData(r)
241 if err != nil {
242 return UEFIVariableAuthority{}, err
243 }
244 certs, err := parseEfiSignature(v.VariableData)
245 return UEFIVariableAuthority{Certs: certs}, err
246}
247
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200248// efiSignatureData represents the EFI_SIGNATURE_DATA type. See section
249// "31.4.1 Signature Database" in the specification for more information.
Lorenz Bruna50e8452020-09-09 17:09:27 +0200250type efiSignatureData struct {
251 SignatureOwner efiGUID
252 SignatureData []byte // []int8
253}
254
255// efiSignatureList represents the EFI_SIGNATURE_LIST type.
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200256// See section "31.4.1 Signature Database" in the specification for more
257// information.
Lorenz Bruna50e8452020-09-09 17:09:27 +0200258type efiSignatureListHeader struct {
259 SignatureType efiGUID
260 SignatureListSize uint32
261 SignatureHeaderSize uint32
262 SignatureSize uint32
263}
264
265type efiSignatureList struct {
266 Header efiSignatureListHeader
267 SignatureData []byte
268 Signatures []byte
269}
270
271// parseEfiSignatureList parses a EFI_SIGNATURE_LIST structure.
272// The structure and related GUIDs are defined at:
273// https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf#page=1790
274func parseEfiSignatureList(b []byte) ([]x509.Certificate, [][]byte, error) {
275 if len(b) < 28 {
276 // Being passed an empty signature list here appears to be valid
277 return nil, nil, nil
278 }
Tim Windelschmidt3b5a9172024-05-23 13:33:52 +0200279 var signatures efiSignatureList
Lorenz Bruna50e8452020-09-09 17:09:27 +0200280 buf := bytes.NewReader(b)
Tim Windelschmidtbda384c2024-04-11 01:41:57 +0200281 var certificates []x509.Certificate
282 var hashes [][]byte
Lorenz Bruna50e8452020-09-09 17:09:27 +0200283
284 for buf.Len() > 0 {
285 err := binary.Read(buf, binary.LittleEndian, &signatures.Header)
286 if err != nil {
287 return nil, nil, err
288 }
289
290 if signatures.Header.SignatureHeaderSize > maxDataLen {
291 return nil, nil, fmt.Errorf("signature header too large: %d > %d", signatures.Header.SignatureHeaderSize, maxDataLen)
292 }
293 if signatures.Header.SignatureListSize > maxDataLen {
294 return nil, nil, fmt.Errorf("signature list too large: %d > %d", signatures.Header.SignatureListSize, maxDataLen)
295 }
296
297 signatureType := signatures.Header.SignatureType
298 switch signatureType {
299 case certX509SigGUID: // X509 certificate
300 for sigOffset := 0; uint32(sigOffset) < signatures.Header.SignatureListSize-28; {
Tim Windelschmidt3b5a9172024-05-23 13:33:52 +0200301 var signature efiSignatureData
Lorenz Bruna50e8452020-09-09 17:09:27 +0200302 signature.SignatureData = make([]byte, signatures.Header.SignatureSize-16)
303 err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner)
304 if err != nil {
305 return nil, nil, err
306 }
307 err = binary.Read(buf, binary.LittleEndian, &signature.SignatureData)
308 if err != nil {
309 return nil, nil, err
310 }
311 cert, err := x509.ParseCertificate(signature.SignatureData)
312 if err != nil {
313 return nil, nil, err
314 }
315 sigOffset += int(signatures.Header.SignatureSize)
316 certificates = append(certificates, *cert)
317 }
318 case hashSHA256SigGUID: // SHA256
319 for sigOffset := 0; uint32(sigOffset) < signatures.Header.SignatureListSize-28; {
Tim Windelschmidt3b5a9172024-05-23 13:33:52 +0200320 var signature efiSignatureData
Lorenz Bruna50e8452020-09-09 17:09:27 +0200321 signature.SignatureData = make([]byte, signatures.Header.SignatureSize-16)
322 err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner)
323 if err != nil {
324 return nil, nil, err
325 }
326 err = binary.Read(buf, binary.LittleEndian, &signature.SignatureData)
327 if err != nil {
328 return nil, nil, err
329 }
330 hashes = append(hashes, signature.SignatureData)
331 sigOffset += int(signatures.Header.SignatureSize)
332 }
333 case keyRSA2048SigGUID:
334 err = errors.New("unhandled RSA2048 key")
335 case certRSA2048SHA256SigGUID:
336 err = errors.New("unhandled RSA2048-SHA256 key")
337 case hashSHA1SigGUID:
338 err = errors.New("unhandled SHA1 hash")
339 case certRSA2048SHA1SigGUID:
340 err = errors.New("unhandled RSA2048-SHA1 key")
341 case hashSHA224SigGUID:
342 err = errors.New("unhandled SHA224 hash")
343 case hashSHA384SigGUID:
344 err = errors.New("unhandled SHA384 hash")
345 case hashSHA512SigGUID:
346 err = errors.New("unhandled SHA512 hash")
347 case certHashSHA256SigGUID:
348 err = errors.New("unhandled X509-SHA256 hash metadata")
349 case certHashSHA384SigGUID:
350 err = errors.New("unhandled X509-SHA384 hash metadata")
351 case certHashSHA512SigGUID:
352 err = errors.New("unhandled X509-SHA512 hash metadata")
353 default:
354 err = fmt.Errorf("unhandled signature type %s", signatureType)
355 }
356 if err != nil {
357 return nil, nil, err
358 }
359 }
360 return certificates, hashes, nil
361}
362
363// EFISignatureData represents the EFI_SIGNATURE_DATA type.
364// See section "31.4.1 Signature Database" in the specification
365// for more information.
366type EFISignatureData struct {
367 SignatureOwner efiGUID
368 SignatureData []byte // []int8
369}
370
371func parseEfiSignature(b []byte) ([]x509.Certificate, error) {
Tim Windelschmidtbda384c2024-04-11 01:41:57 +0200372 var certificates []x509.Certificate
Lorenz Bruna50e8452020-09-09 17:09:27 +0200373
374 if len(b) < 16 {
375 return nil, fmt.Errorf("invalid signature: buffer smaller than header (%d < %d)", len(b), 16)
376 }
377
378 buf := bytes.NewReader(b)
Tim Windelschmidt3b5a9172024-05-23 13:33:52 +0200379 var signature EFISignatureData
Lorenz Bruna50e8452020-09-09 17:09:27 +0200380 signature.SignatureData = make([]byte, len(b)-16)
381
382 if err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner); err != nil {
383 return certificates, err
384 }
385 if err := binary.Read(buf, binary.LittleEndian, &signature.SignatureData); err != nil {
386 return certificates, err
387 }
388
389 cert, err := x509.ParseCertificate(signature.SignatureData)
390 if err == nil {
391 certificates = append(certificates, *cert)
392 } else {
393 // A bug in shim may cause an event to be missing the SignatureOwner GUID.
394 // We handle this, but signal back to the caller using ErrSigMissingGUID.
Tim Windelschmidtaf821c82024-04-23 15:03:52 +0200395 var structuralError asn1.StructuralError
396 if errors.As(err, &structuralError) {
Lorenz Bruna50e8452020-09-09 17:09:27 +0200397 var err2 error
398 cert, err2 = x509.ParseCertificate(b)
399 if err2 == nil {
400 certificates = append(certificates, *cert)
401 err = ErrSigMissingGUID
402 }
403 }
404 }
405 return certificates, err
406}