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