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
}
|