github.com/linuxboot/fiano@v1.2.0/pkg/amd/psb/psbbinary.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  )
    12  
    13  // pspHeaderSize represents the size of the header pre-pended to PSP binaries
    14  const pspHeaderSize = 0x100
    15  
    16  // signedDataStart indicates the start address of signed data content within a PSP binary
    17  const signedDataStart = 0x0
    18  
    19  // PSPHeaderData embeds the data of PspHeader
    20  type PSPHeaderData struct {
    21  	Nonce                 Buf16B
    22  	HeaderVersion         uint32
    23  	SizeSigned            uint32
    24  	EncryptionOptions     uint32
    25  	IKEKType              uint8
    26  	Reserved0             Buf3B
    27  	EncryptionParameters  Buf16B
    28  	SignatureOption       uint32
    29  	SignatureAlgorithmID  uint32
    30  	SignatureParameters   Buf16B
    31  	CompressionOptions    uint32
    32  	SecurityPatchLevel    uint32
    33  	UncompressedImageSize uint32
    34  	CompressedImageSize   uint32
    35  	CompressionParameters Buf8B
    36  	ImageVersion          uint32
    37  	ApuFamilyID           uint32
    38  	FirmwareLoadAddress   uint32
    39  	SizeImage             uint32
    40  	SizeFwUnsigned        uint32
    41  	FirmwareSplitAddress  uint32
    42  	Reserved              Buf4B
    43  	FwType                uint8
    44  	FwSubType             uint8
    45  	Reserved1             uint16
    46  	EncryptionKey         Buf16B
    47  	SigningInfo           Buf16B
    48  	FwSpecificData        Buf32B
    49  	DebugEncKey           Buf16B
    50  }
    51  
    52  // PspHeader models the header pre-pended to PSP binaries
    53  type PspHeader struct {
    54  	data PSPHeaderData
    55  
    56  	// There should be 48 bytes of padding after the last field of the header,
    57  	// which can be ignored. What we care about is the signature of the binary,
    58  	// stored at the bottom of the image described by the header. This can be
    59  	// looked up via sizeSigned and sizeImage
    60  
    61  }
    62  
    63  // newPspHeader returns a PspHeader object deserialized from binary format
    64  func newPspHeader(data []byte) (*PspHeader, error) {
    65  	hdr := PspHeader{}
    66  	if err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &hdr.data); err != nil {
    67  		return nil, err
    68  	}
    69  	return &hdr, nil
    70  }
    71  
    72  // Version returns the headerVersion field of the pspHeader structure
    73  func (h *PspHeader) Version() uint32 {
    74  	return h.data.HeaderVersion
    75  }
    76  
    77  // PSPBinary represents a generic PSPBinary with pre-pended header structure
    78  type PSPBinary struct {
    79  
    80  	// header of the binary
    81  	header PspHeader
    82  
    83  	// raw data of the whole PSP binary, including header. Signature includes
    84  	// the header, keeping track of the whole raw content of the image allows
    85  	// to easily build structures necessary for signature validation.
    86  	raw []byte
    87  }
    88  
    89  // Header returns a pointer to the PspHeader structure of the binary. Fields of the PspHeader structure are not exported
    90  func (b *PSPBinary) Header() *PspHeader {
    91  	return &b.header
    92  }
    93  
    94  // getSignedBlob returns the PSP binary object as a signature-validated SignedBlob structure
    95  func (b *PSPBinary) getSignedBlob(keyDB KeySet) (*SignedBlob, error) {
    96  	if b.header.data.SizeSigned == 0 {
    97  		return nil, newErrInvalidFormat(fmt.Errorf("size of signed data cannot be 0 for PSPBinary"))
    98  	}
    99  	if b.header.data.SizeImage == 0 {
   100  		return nil, newErrInvalidFormat(fmt.Errorf("size of image cannot be 0 for PSPBinary"))
   101  	}
   102  
   103  	// Try use signatureParameters as KeyID field for the signing key which signed the PSP binary.
   104  	// We need to look-up the signing key to infer the expected size of the signature.
   105  	signingKeyID := KeyID(b.header.data.SignatureParameters)
   106  	signingKey := keyDB.GetKey(signingKeyID)
   107  	if signingKey == nil {
   108  		return nil, &UnknownSigningKeyError{keyID: signingKeyID}
   109  	}
   110  
   111  	// The recommended value for RSA exponent is 0x10001. The specification does not enforce
   112  	// that modulus and exponent buffer size should be the same, but so far this has been the
   113  	// case. This should probably be clarified with AMD and possibly be removed in the future.
   114  	if signingKey.data.ModulusSize != signingKey.data.ExponentSize {
   115  		return nil, fmt.Errorf("exponent size (%d) and modulus size (%d) do not match", signingKey.data.ModulusSize, signingKey.data.ExponentSize)
   116  	}
   117  
   118  	sizeSignature := signingKey.data.ModulusSize / 8
   119  
   120  	sizeImage := uint32(0)
   121  	sizeSignedImage := uint32(0)
   122  	if b.header.data.CompressionOptions == 0x0 {
   123  		// the image is not compressed, sizeSigned and sizeImage constitute the source of truth
   124  		if b.header.data.SizeSigned > b.header.data.SizeImage {
   125  			return nil, newErrInvalidFormat(fmt.Errorf("size of signed image cannot be > size of image (%d > %d)", b.header.data.SizeSigned, b.header.data.SizeImage))
   126  		}
   127  		// sizeSigned does not include the size of the header
   128  		sizeSignedImage = b.header.data.SizeSigned + pspHeaderSize
   129  		sizeImage = b.header.data.SizeImage
   130  	} else {
   131  		// the image is compressed, SizeFWSigned is to be ignored and instead compressedImageSize should be
   132  		// taken into consideration and aligned to 16 bits. PSP header size is not included in compressedImageSize.
   133  		alignment := uint32(0x10)
   134  		sizeSignedImage = (b.header.data.CompressedImageSize+alignment-1) & ^(alignment-1) + pspHeaderSize
   135  		sizeImage = sizeSignedImage + sizeSignature
   136  	}
   137  
   138  	if sizeImage <= sizeSignature {
   139  		return nil, newErrInvalidFormat(fmt.Errorf("sizeImage (%d) cannot be <= of sizeSignature (%d)", sizeImage, sizeSignature))
   140  	}
   141  	signatureStart := sizeImage - sizeSignature
   142  	signatureEnd := signatureStart + sizeSignature
   143  
   144  	if err := checkBoundaries(uint64(signatureStart), uint64(signatureEnd), b.raw); err != nil {
   145  		return nil, newErrInvalidFormat(fmt.Errorf("could not extract signature from raw PSPBinary: %w", err))
   146  	}
   147  
   148  	signedDataEnd := signedDataStart + sizeSignedImage
   149  	if err := checkBoundaries(uint64(signedDataStart), uint64(signedDataEnd), b.raw); err != nil {
   150  		return nil, newErrInvalidFormat(fmt.Errorf("could not extract signed data from raw PSPBinary: %w", err))
   151  	}
   152  
   153  	signature := b.raw[signatureStart:signatureEnd]
   154  
   155  	signedData := b.raw[signedDataStart:signedDataEnd]
   156  	if len(signedData) <= pspHeaderSize {
   157  		return nil, newErrInvalidFormat(fmt.Errorf("PSP binary cannot be smaller than or equal to header size"))
   158  	}
   159  	return NewSignedBlob(signature, signedData, signingKey)
   160  }
   161  
   162  // newPSPBinary creates a PSPBinary object, with associated header
   163  func newPSPBinary(data []byte) (*PSPBinary, error) {
   164  
   165  	pspBinary := PSPBinary{}
   166  	pspBinary.raw = make([]byte, len(data))
   167  	copied := copy(pspBinary.raw, data)
   168  	if copied != len(data) {
   169  		return nil, fmt.Errorf("expected %d copied data for raw PSP binary, got %d", len(data), copied)
   170  	}
   171  
   172  	header, err := newPspHeader(pspBinary.raw)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	pspBinary.header = *header
   177  
   178  	return &pspBinary, nil
   179  }