github.com/linuxboot/fiano@v1.2.0/pkg/amd/psb/keyset.go (about)

     1  // Copyright 2023 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package psb
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"strings"
    13  
    14  	amd_manifest "github.com/linuxboot/fiano/pkg/amd/manifest"
    15  )
    16  
    17  // KeyType represents the type of the key stored in KeySet
    18  type KeyType string
    19  
    20  const (
    21  	// OEMKey represents the OEM signing key
    22  	OEMKey KeyType = "OEMKey"
    23  	// AMDRootKey represents the AMD signing key
    24  	AMDRootKey KeyType = "AMDRootKey"
    25  	// KeyDatabaseKey represents a key extracted from KeyDatabase
    26  	KeyDatabaseKey KeyType = "KeyDatabaseKey"
    27  	// ABLKey represents the ABL signing key
    28  	ABLKey KeyType = "ALBKey"
    29  )
    30  
    31  // KeySet is a container for all keys known to the system
    32  type KeySet struct {
    33  	// db holds a mapping between keyID and key
    34  	db map[KeyID]*Key
    35  	// keyType holds a mapping betweek KeyType and KeyID
    36  	keyType map[KeyType][]KeyID
    37  }
    38  
    39  // String returns a string representation of the key in the set
    40  func (kdb *KeySet) String() string {
    41  	var s strings.Builder
    42  	fmt.Fprintf(&s, "Number of keys in key set: %d\n\n", len(kdb.db))
    43  
    44  	for _, key := range kdb.db {
    45  		fmt.Fprintf(&s, "%s\n\n", key.String())
    46  	}
    47  	return s.String()
    48  }
    49  
    50  // AddKey adds a key to the key set
    51  func (kdb KeySet) AddKey(k *Key, keyType KeyType) error {
    52  	if _, ok := kdb.db[k.data.KeyID]; ok {
    53  		return fmt.Errorf("cannot add key id %s to set, key with same id already exists", k.data.KeyID.Hex())
    54  	}
    55  
    56  	kdb.db[k.data.KeyID] = k
    57  	// assume the key cannot be already present in the keyType mapping
    58  	kdb.keyType[keyType] = append(kdb.keyType[keyType], k.data.KeyID)
    59  	return nil
    60  }
    61  
    62  // NewKeySet builds an empty key set object
    63  func NewKeySet() KeySet {
    64  	keySet := KeySet{}
    65  	keySet.db = make(map[KeyID]*Key)
    66  	keySet.keyType = make(map[KeyType][]KeyID)
    67  	return keySet
    68  }
    69  
    70  // GetKey returns a key if known to the KeySet. If the key is not known, null is returned
    71  func (kdb KeySet) GetKey(id KeyID) *Key {
    72  	if kdb.db == nil {
    73  		return nil
    74  	}
    75  	return kdb.db[id]
    76  }
    77  
    78  // AllKeyIDs returns a list of all KeyIDs stored in the KeySet
    79  func (kdb KeySet) AllKeyIDs() KeyIDs {
    80  	keyIDs := make(KeyIDs, 0, len(kdb.db))
    81  	for keyID := range kdb.db {
    82  		keyIDs = append(keyIDs, keyID)
    83  	}
    84  	return keyIDs
    85  }
    86  
    87  // KeysetFromType returns a KeySet containing all KeyIDs of a specific type
    88  func (kdb KeySet) KeysetFromType(keyType KeyType) (KeySet, error) {
    89  	if _, ok := kdb.keyType[keyType]; !ok {
    90  		return NewKeySet(), newErrNotFound(nil)
    91  	}
    92  	keySet := NewKeySet()
    93  	for _, keyID := range kdb.keyType[keyType] {
    94  		key := kdb.GetKey(keyID)
    95  		if key == nil {
    96  			return NewKeySet(), newErrInvalidFormat(fmt.Errorf("KeySet in inconsistent state, no key is present with keyID %s", keyID.Hex()))
    97  		}
    98  
    99  		err := keySet.AddKey(key, keyType)
   100  		if err != nil {
   101  			return NewKeySet(), fmt.Errorf("unable to add key %s: %w", keyID, err)
   102  		}
   103  	}
   104  	return keySet, nil
   105  }
   106  
   107  // keyDBHeader represents the header pre-pended to keydb structure
   108  type keyDBHeader struct {
   109  	DataSize        uint32
   110  	Version         uint32
   111  	Cookie          uint32
   112  	Reserved        Buf36B
   113  	CustomerDefined Buf32B
   114  }
   115  
   116  func readAndCountSize(r io.Reader, order binary.ByteOrder, data interface{}, counter *uint64) error {
   117  	if err := binary.Read(r, order, data); err != nil {
   118  		return err
   119  	}
   120  	if counter != nil {
   121  		*counter += uint64(binary.Size(data))
   122  	}
   123  	return nil
   124  }
   125  
   126  // extractKeydbHeader parses keydbHeader from binary buffer. KeyDB header is supposed to be 80 bytes long
   127  func extractKeydbHeader(buff io.Reader) (*keyDBHeader, error) {
   128  	header := keyDBHeader{}
   129  
   130  	if err := binary.Read(buff, binary.LittleEndian, &header); err != nil {
   131  		return nil, fmt.Errorf("could not read key database header: %w", err)
   132  	}
   133  
   134  	return &header, nil
   135  }
   136  
   137  // parseKeyDatabase parses a raw buffer representing a key database and adds all extracted key
   138  // to the associated keySet. The raw buffer must be stripped off of the PSP header and the signature
   139  // appended at the end.
   140  func parseKeyDatabase(rawDB []byte, keySet KeySet) error {
   141  
   142  	buff := bytes.NewBuffer(rawDB)
   143  	_, err := extractKeydbHeader(buff)
   144  	if err != nil {
   145  		return fmt.Errorf("could not extract keydb header: %w", err)
   146  	}
   147  
   148  	for {
   149  		if buff.Len() == 0 {
   150  			break
   151  		}
   152  		key, err := NewKeyFromDatabase(buff)
   153  		if err != nil {
   154  			return fmt.Errorf("could not extract key entry from key database: %w", err)
   155  		}
   156  		if err := keySet.AddKey(key, KeyDatabaseKey); err != nil {
   157  			return fmt.Errorf("cannot add key to key database: %w", err)
   158  		}
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  // getKeysFromDatabase extracts the keys in the firmware key database and adds them to the KeySet passed
   165  // as argument after validating the signature of the database itself
   166  func getKeysFromDatabase(amdFw *amd_manifest.AMDFirmware, pspLevel uint, keySet KeySet) error {
   167  
   168  	/**
   169  	 * PSP Directory Level 2 does not contain the AMD
   170  	 * Public Root Keys, so we are forced to use PSP Directory
   171  	 * Level 1 to get them, and not have it configurable
   172  	 */
   173  	pubKeyBytes, err := ExtractPSPEntry(amdFw, 1, AMDPublicKeyEntry)
   174  	if err != nil {
   175  		return fmt.Errorf("could not extract raw PSP entry for AMD Public Key: %w", err)
   176  	}
   177  	amdPk, err := NewRootKey(bytes.NewBuffer(pubKeyBytes))
   178  	if err != nil {
   179  		return addFirmwareItemToError(err, newPSPDirectoryEntryItem(uint8(pspLevel), AMDPublicKeyEntry))
   180  	}
   181  
   182  	// All keys which get added the KeySet are supposed to be trusted. AMD root key is trusted as a result of being matched against a
   183  	// "source of truth" (in hardware, this is a hash burnt into the CPU. Software tooling should check it against some external
   184  	// reference).
   185  	if err := keySet.AddKey(amdPk, AMDRootKey); err != nil {
   186  		return fmt.Errorf("could not add AMD key to the key database: %w", err)
   187  	}
   188  
   189  	data, err := ExtractPSPEntry(amdFw, pspLevel, KeyDatabaseEntry)
   190  	if err != nil {
   191  		return fmt.Errorf("could not extract entry 0x%x (KeyDatabaseEntry) from PSP table: %w", KeyDatabaseEntry, err)
   192  	}
   193  
   194  	binary, err := newPSPBinary(data)
   195  	if err != nil {
   196  		return newErrInvalidFormatWithItem(newPSPDirectoryEntryItem(uint8(pspLevel), KeyDatabaseEntry),
   197  			fmt.Errorf("could not create PSB binary from raw data for entry 0x%x (KeyDatabaseEntry): %w", KeyDatabaseEntry, err))
   198  	}
   199  
   200  	// getSignedBlob returns the whole PSP blob as a signature-validated structure.
   201  	signedBlob, err := binary.getSignedBlob(keySet)
   202  	if err != nil {
   203  		return addFirmwareItemToError(err, newPSPDirectoryEntryItem(uint8(pspLevel), KeyDatabaseEntry))
   204  	}
   205  
   206  	// We need to strip off pspHeader to get the content which actually represents the keys database
   207  	signedData := signedBlob.SignedData()
   208  	if len(signedData) <= pspHeaderSize {
   209  		return newErrInvalidFormatWithItem(newPSPDirectoryEntryItem(uint8(pspLevel), KeyDatabaseEntry),
   210  			fmt.Errorf("length of key database entry (%d) is less than pspHeader length (%d)", len(signedData), pspHeaderSize))
   211  	}
   212  
   213  	return parseKeyDatabase(signedData[pspHeaderSize:], keySet)
   214  }