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 }