File: signature_list.go

package info (click to toggle)
golang-github-foxboron-go-uefi 0.0~git20250207.69fb7db-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,140 kB
  • sloc: makefile: 29; sh: 14
file content (328 lines) | stat: -rw-r--r-- 10,685 bytes parent folder | download
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
package signature

import (
	"bytes"
	"encoding/binary"
	"encoding/pem"
	"fmt"
	"io"
	"log"
	"reflect"

	"github.com/foxboron/go-uefi/efi/util"
	"github.com/pkg/errors"
)

// Section 32.4.1 Signature Database
// Page 1714 -> Page 1717
var (
	CERT_SHA256_GUID         = util.EFIGUID{0xc1c41626, 0x504c, 0x4092, [8]uint8{0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28}}
	CERT_RSA2048_GUID        = util.EFIGUID{0x3c5766e8, 0x269c, 0x4e34, [8]uint8{0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6}}
	CERT_RSA2048_SHA256_GUID = util.EFIGUID{0xe2b36190, 0x879b, 0x4a3d, [8]uint8{0xad, 0x8d, 0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84}}

	CERT_SHA1_GUID         = util.EFIGUID{0x826ca512, 0xcf10, 0x4ac9, [8]uint8{0xb1, 0x87, 0xbe, 0x01, 0x49, 0x66, 0x31, 0xbd}}
	CERT_RSA2048_SHA1_GUID = util.EFIGUID{0x67f8444f, 0x8743, 0x48f1, [8]uint8{0xa3, 0x28, 0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80}}

	CERT_X509_GUID = util.EFIGUID{0xa5c059a1, 0x94e4, 0x4aa7, [8]uint8{0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72}}

	CERT_SHA224_GUID = util.EFIGUID{0xb6e5233, 0xa65c, 0x44c9, [8]uint8{0x94, 0x07, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd}}

	CERT_SHA384_GUID = util.EFIGUID{0xff3e5307, 0x9fd0, 0x48c9, [8]uint8{0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x01}}

	CERT_SHA512_GUID = util.EFIGUID{0x93e0fae, 0xa6c4, 0x4f50, [8]uint8{0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a}}

	CERT_X509_SHA256_GUID = util.EFIGUID{0x3bd2a492, 0x96c0, 0x4079, [8]uint8{0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed}}

	CERT_EXTERNAL_MANAGEMENT_GUID = util.EFIGUID{0x452e8ced, 0xdfff, 0x4b8c, [8]uint8{0xae, 0x01, 0x51, 0x18, 0x86, 0x2e, 0x68, 0x2c}}
)

type CertType string

var ErrNoSuchSignatureScheme = errors.New("no such signature scheme")

// Quick access list
// Maybe a map[string]EFIGUID?
var ValidEFISignatureSchemes = map[util.EFIGUID]CertType{
	CERT_SHA256_GUID:              "SHA256",
	CERT_RSA2048_GUID:             "RSA2048",
	CERT_RSA2048_SHA256_GUID:      "RSA2048 SHA256",
	CERT_SHA1_GUID:                "SHA1",
	CERT_RSA2048_SHA1_GUID:        "RSA2048 SHA1",
	CERT_X509_GUID:                "X509",
	CERT_SHA224_GUID:              "SHA224",
	CERT_SHA384_GUID:              "SHA238",
	CERT_SHA512_GUID:              "SHA512",
	CERT_X509_SHA256_GUID:         "X509 SHA256",
	CERT_EXTERNAL_MANAGEMENT_GUID: "EXTERNAL MANAGEMENT",
}

const (
	CERT_SHA256         CertType = "SHA256"
	CERT_RSA2048                 = "RSA2048"
	CERT_RSA2048_SHA256          = "RSA2048 SHA256"
	CERT_SHA1                    = "SHA1"
	CERT_RSA2048_SHA1            = "RSA2048 SHA1"
	CERT_X509                    = "X509"
	CERT_SHA224                  = "SHA224"
	CERT_SHA384                  = "SHA238"
	CERT_SHA512                  = "SHA512"
	CERT_X509_SHA256             = "X509 SHA256"
)

// Section 3.3 - Globally Defined Variables
// Array of GUIDs representing the type of signatures supported by
// the platform firmware. Should be treated as read-only
func GetSupportedSignatures(f io.Reader) ([]util.EFIGUID, error) {
	// This is a bit bad. But io.Reader is *probably nicer* but we need to know
	// the length in a better way.
	buf := new(bytes.Buffer)
	buf.ReadFrom(f)
	supportedSigs := make([]util.EFIGUID, buf.Len()/16)
	if err := binary.Read(buf, binary.LittleEndian, &supportedSigs); err != nil {
		return nil, errors.Wrapf(err, "could not parse EFIGUIDs from this reader")
	}
	return supportedSigs, nil
}

// Section 32.4.1 - Signature Database
// Page 1712
type SignatureData struct {
	Owner util.EFIGUID
	Data  []uint8
}

func ReadSignatureData(f io.Reader, size uint32) (*SignatureData, error) {
	s := SignatureData{}
	if err := binary.Read(f, binary.LittleEndian, &s.Owner); err != nil {
		return &SignatureData{}, errors.Wrapf(err, "could not read Signature Data")
	}
	data := make([]uint8, size-util.SizeofEFIGUID) // Subtract the size of Owner
	if err := binary.Read(f, binary.LittleEndian, &data); err != nil {
		return &SignatureData{}, errors.Wrapf(err, "Couldn't read Signature Data")
	}
	s.Data = data[:]
	return &s, nil
}

func WriteSignatureData(b io.Writer, s SignatureData) {
	for _, v := range []interface{}{s.Owner, s.Data} {
		err := binary.Write(b, binary.LittleEndian, v)
		if err != nil {
			log.Fatalf("Couldn't write signature data: %s", err)
		}
	}
}

func (sd *SignatureData) Bytes() []byte {
	buf := new(bytes.Buffer)
	WriteSignatureData(buf, *sd)
	return buf.Bytes()
}

// Section 32.4.1 - Signature Database
// Page 1713
type SignatureList struct {
	SignatureType   util.EFIGUID
	ListSize        uint32          // Total size of the signature list, including this header
	HeaderSize      uint32          // Size of SignatureHead
	Size            uint32          // Size of each signature. At least the size of EFI_SIGNATURE_DATA
	SignatureHeader []uint8         // SignatureType defines the content of this header
	Signatures      []SignatureData // SignatureData List
}

// SignatureSize + sizeof(SignatureType) + sizeof(uint32)*3
const SizeofSignatureList uint32 = util.SizeofEFIGUID + 4 + 4 + 4

var ErrNotFoundSigData = errors.New("signature data not found")
var ErrSigDataExists = errors.New("signature data exists already")

func NewSignatureList(certtype util.EFIGUID) *SignatureList {
	return &SignatureList{
		SignatureType:   certtype,
		ListSize:        SizeofSignatureList,
		HeaderSize:      0,
		Size:            0,
		SignatureHeader: []uint8{},
		Signatures:      []SignatureData{},
	}
}

// Compare the signature lists header to see if they are the same type of list
// This is usefull if you wonder if you can merge the lists or not
func (sl *SignatureList) CmpHeader(siglist *SignatureList) bool {
	if !util.CmpEFIGUID(sl.SignatureType, siglist.SignatureType) {
		return false
	}
	if sl.Size != siglist.Size {
		return false
	}
	if !reflect.DeepEqual(sl.SignatureHeader, siglist.SignatureHeader) {
		return false
	}
	return true
}

// Check if signature exists in the signature list
// Return true if it does along with the index
func (sl *SignatureList) Exists(sigdata *SignatureData) (bool, int) {
	for index, sigs := range sl.Signatures {
		if !util.CmpEFIGUID(sigs.Owner, sigdata.Owner) {
			continue
		}
		if !bytes.Equal(sigs.Data, sigdata.Data) {
			continue
		}
		return true, index
	}
	return false, 0
}

func (sl *SignatureList) ExistsInList(siglist *SignatureList) bool {
	for _, item := range siglist.Signatures {
		if ok, _ := sl.Exists(&item); !ok {
			return false
		}
	}
	return true
}

func (sl *SignatureList) AppendBytes(owner util.EFIGUID, data []byte) error {
	if ok, _ := sl.Exists(&SignatureData{owner, data}); ok {
		return ErrSigDataExists
	}
	switch sl.SignatureType {
	case CERT_X509_GUID:
		// Check if the cert is PEM encoded
		// We need the DER encoded cert, but this makes it nicer
		// for us in the API
		if block, _ := pem.Decode(data); block != nil {
			data = block.Bytes
		}
	case CERT_SHA256_GUID:
		if len(data) != 32 {
			return errors.New("not a sha256 hash")
		}
	}
	sl.Signatures = append(sl.Signatures, SignatureData{Owner: owner, Data: data})
	sl.Size = uint32(len(data)) + util.SizeofEFIGUID
	sl.ListSize += sl.Size
	return nil
}

func (sl *SignatureList) AppendSignature(s SignatureData) error {
	return sl.AppendBytes(s.Owner, s.Data)
}

func (sl *SignatureList) RemoveBytes(owner util.EFIGUID, data []byte) error {
	ok, index := sl.Exists(&SignatureData{owner, data})
	if !ok {
		return ErrNotFoundSigData
	}
	if len(sl.Signatures) == 1 {
		*sl = *NewSignatureList(sl.SignatureType)
		return nil
	}
	sl.Signatures = append(sl.Signatures[:index], sl.Signatures[index+1:]...)
	sl.ListSize -= sl.Size
	return nil
}

func (sl *SignatureList) RemoveSignature(s SignatureData) error {
	return sl.RemoveBytes(s.Owner, s.Data)
}

func (sl *SignatureList) Bytes() []byte {
	buf := new(bytes.Buffer)
	WriteSignatureList(buf, *sl)
	return buf.Bytes()
}

// Writes a signature list
func WriteSignatureList(b io.Writer, s SignatureList) {
	for _, v := range []interface{}{s.SignatureType, s.ListSize, s.HeaderSize, s.Size, s.SignatureHeader} {
		err := binary.Write(b, binary.LittleEndian, v)
		if err != nil {
			log.Fatalf("Couldn't write signature list: %s", err)
		}
	}
	for _, l := range s.Signatures {
		WriteSignatureData(b, l)
	}
}

// Read an EFI_SIGNATURE_LIST from io.Reader. It will read until io.EOF.
// io.EOF should be somewhat expected if we are trying to read multiple
// lists as they should be either at the end of the file, or the entire file.
func ReadSignatureList(f io.Reader) (*SignatureList, error) {
	s := SignatureList{}
	for _, i := range []interface{}{&s.SignatureType, &s.ListSize, &s.HeaderSize, &s.Size} {
		err := binary.Read(f, binary.LittleEndian, i)
		if errors.Is(err, io.EOF) {
			return &SignatureList{}, err
		} else if err != nil {
			return &SignatureList{}, errors.Wrapf(err, "couldn't read signature list")
		}
	}

	var sigData []SignatureData
	var err error

	// The list size minus the size of the SignatureList struct
	// lets us figure out how much signature data we should read.
	totalSize := s.ListSize - SizeofSignatureList

	sig := ValidEFISignatureSchemes[s.SignatureType]
	// Anonymous function because I really can't figure out a better name for it
	parseList := func(data []SignatureData, size uint32) ([]SignatureData, error) {
		for {
			if totalSize == 0 {
				return data, nil
			}
			sigdata, err := ReadSignatureData(f, size)
			if err != nil {
				return nil, err
			}
			data = append(data, *sigdata)
			totalSize -= s.Size
		}
	}
	switch sig {
	case "X509":
		if s.HeaderSize != 0 {
			return nil, fmt.Errorf("unexpected HeaderSize for x509 cert. Should be 0")
		}
		sigData, err = parseList(sigData, s.Size)
	case "SHA256":
		if s.HeaderSize != 0 {
			return nil, fmt.Errorf("unexpected HeaderSize for SHA256. Should be 0")
		}
		if s.Size != 48 {
			return nil, fmt.Errorf("unexpected signature size for SHA256. Should be 16+32")
		}
		sigData, err = parseList(sigData, s.Size)
	case "EXTERNAL MANAGEMENT":
		if s.HeaderSize != 0 {
			return nil, fmt.Errorf("unexpected HeaderSize for EXTERNAL MANAGEMENT. Should be 0")
		}

		if s.Size != 17 {
			return nil, fmt.Errorf("unexpected signature size for EXTERNAL MANAGEMENT. Should be 16+1")
		}
		// Certs under external management should be ignored.
		// we discard the data we read.
		_, err = parseList(sigData, s.Size)
	default:
		// if s.Size != 0 {
		// 	buf := make([]byte, s.Size)
		// 	if err := binary.Read(f, binary.LittleEndian, buf); err != nil {
		// 		return nil, errors.Wrap(err, "could not read default list")
		// 	}
		// }
		return nil, fmt.Errorf("not implemented signature list certificate: %s", sig)
	}
	if err != nil {
		return &SignatureList{}, err
	}
	s.Signatures = sigData
	return &s, nil
}