github.com/linuxboot/fiano@v1.2.0/pkg/amd/psb/psbbinary_test.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 "crypto/rsa" 10 "crypto/sha256" 11 "encoding/hex" 12 "errors" 13 "math/big" 14 "testing" 15 16 "github.com/klauspost/compress/zstd" 17 18 amd_manifest "github.com/linuxboot/fiano/pkg/amd/manifest" 19 "github.com/stretchr/testify/require" 20 "github.com/stretchr/testify/suite" 21 ) 22 23 // SMU off chip firmware signing key information 24 var N = "971472917905694235859527690907502923402301948031921241171273698806712501341143764872164817936205323093673268936716169271014956780829744939605805336378355604317689589643879933296619769853977997537504409513857548495835554760316169706145871580241299859391727532123062316131763291333497897666085493912196453830838835409116482525805019707393297818644968410993413227674593522445365251216146843534545538446317137254095278019564256619548518070446087126473960778214502148894932879929259282158773739433517309889480032232186219446391610435919379779378252560032397509979809202392043110002380553502817271070140095904800150060054898411959116413989750574319580379588063907374731560190664369153626299598002608484164204182073967364520170387618360414494928968064669008506200599321614764379343663974785424808230448954042498800415697272404985468748776993396194504501173460207698442122815473122167399338113883863561341937240713815386295760840924504030444583243381824402019007306020777513381800531855947355754646526659806863711949295870720607069380951550984974668335178048036250641301510976999844724811954772642716092471925323531085871838421575832526959241303117218657087055880924069551198635498158413029865582648473844773084423426422930595846516126856911" 25 var expectedSmuOffChipFirmwareHash = [32]byte{ 26 0xd8, 0xdc, 0x03, 0xff, 0x18, 0x1a, 0xcc, 0x9d, 0x09, 0xac, 0x5a, 0xe7, 0x59, 0x67, 0xdc, 0x96, 27 0x60, 0xe7, 0xbb, 0x08, 0xd0, 0x3f, 0xa3, 0xb1, 0xbf, 0x64, 0x17, 0x0e, 0x43, 0xdc, 0xb2, 0xf2, 28 } 29 var expectedZeroSmuOffChipFirmwareHash = [32]byte{ 30 0x10, 0xe2, 0x10, 0x3e, 0xe7, 0x39, 0x21, 0x93, 0x1a, 0x78, 0x28, 0xeb, 0xdf, 0x32, 0x5d, 0x3a, 31 0x3a, 0x64, 0xc7, 0xa9, 0x0c, 0xc1, 0xda, 0x5c, 0x0c, 0xa6, 0xfe, 0x17, 0xb1, 0xe3, 0xdd, 0x78, 32 } 33 34 const FirmwareLen = 16777216 35 36 type PsbBinarySuite struct { 37 suite.Suite 38 39 firmwareImage []byte 40 } 41 42 func (suite *PsbBinarySuite) SetupTest() { 43 suite.firmwareImage = make([]byte, 0) 44 reader, err := zstd.NewReader(nil) 45 if err != nil { 46 panic("could not create zstd reader") 47 } 48 suite.firmwareImage, err = reader.DecodeAll(firmwareImageCompressed, nil) 49 if err != nil { 50 panic("could not decompress zstd firmware") 51 } 52 } 53 54 func (suite *PsbBinarySuite) TestPSBBinaryIsParsedCorrectly() { 55 psbBinary, err := newPSPBinary(smuOffChipFirmware) 56 require.NoError(suite.T(), err) 57 58 hdr := psbBinary.Header() 59 require.Equal(suite.T(), uint32(0x31535024), hdr.Version()) 60 } 61 62 func (suite *PsbBinarySuite) TestPSBBinarySignedData() { 63 // we are using SMU off-chip firmware for testing PSB binary control 64 // paths and we need the corresponding signing key, which is contained 65 // in the key database. We could just extract the single key from the 66 // database, but it's easier to parse the database as a whole 67 keySet := NewKeySet() 68 err := parseKeyDatabase(keyDB, keySet) 69 require.NoError(suite.T(), err) 70 71 psbBinary, err := newPSPBinary(smuOffChipFirmware) 72 require.NoError(suite.T(), err) 73 74 blob, err := psbBinary.getSignedBlob(keySet) 75 76 require.NoError(suite.T(), err) 77 78 // verify that the signed data matches the content of the blob, excluding the final signature 79 require.Equal(suite.T(), smuOffChipFirmware[:len(smuOffChipFirmware)-512], blob.SignedData()) 80 81 sig := blob.Signature() 82 require.NotNil(suite.T(), sig) 83 key := sig.SigningKey() 84 require.NotNil(suite.T(), key) 85 require.Equal(suite.T(), hex.EncodeToString(smuSigningKeyID[:]), key.data.KeyID.String()) 86 87 // obtain the RSA key from the generic Key object 88 pubKey, err := key.Get() 89 require.NoError(suite.T(), err) 90 rsaKey := pubKey.(*rsa.PublicKey) 91 require.NotNil(suite.T(), rsaKey) 92 93 n := big.Int{} 94 n.SetString(N, 10) 95 require.Equal(suite.T(), n, *rsaKey.N) 96 require.Equal(suite.T(), int(65537), rsaKey.E) 97 } 98 99 func (suite *PsbBinarySuite) TestPSBBinaryPSPDirectoryLevel2Entry() { 100 // Test full extraction from firmware of entry from PSP Directory 101 require.Equal(suite.T(), FirmwareLen, len(suite.firmwareImage)) 102 103 amdFw, err := ParseAMDFirmware(suite.firmwareImage) 104 require.NoError(suite.T(), err) 105 106 smuOffChipFirmwareType := amd_manifest.PSPDirectoryTableEntryType(0x12) 107 108 data, err := ExtractPSPEntry(amdFw, 2, smuOffChipFirmwareType) 109 require.NoError(suite.T(), err) 110 shaSmuOffChipFirmwareHash := sha256.Sum256(data) 111 112 require.Equal(suite.T(), expectedSmuOffChipFirmwareHash, shaSmuOffChipFirmwareHash) 113 114 // if we dump the SMU off-chip firmware level 1 entry, we expect to find a region of all zeros 115 data, err = ExtractPSPEntry(amdFw, 1, smuOffChipFirmwareType) 116 require.NoError(suite.T(), err) 117 shaSmuOffChipFirmwareHash = sha256.Sum256(data) 118 119 require.Equal(suite.T(), expectedZeroSmuOffChipFirmwareHash, shaSmuOffChipFirmwareHash) 120 } 121 122 func (suite *PsbBinarySuite) TestPSBBinaryPSPDirectoryLevel2EntryValidation() { 123 // Test positive validation of PSP Directory entry 124 require.Equal(suite.T(), FirmwareLen, len(suite.firmwareImage)) 125 126 amdFw, err := ParseAMDFirmware(suite.firmwareImage) 127 require.NoError(suite.T(), err) 128 129 keyDB, err := GetKeys(amdFw, 2) 130 require.NoError(suite.T(), err) 131 132 signatureValidation, err := ValidatePSPEntries(amdFw, keyDB, PSPDirectoryLevel2, []uint32{0x12}) 133 134 require.NoError(suite.T(), err) 135 require.Equal(suite.T(), 1, len(signatureValidation)) 136 require.NoError(suite.T(), signatureValidation[0].err) 137 require.NotNil(suite.T(), signatureValidation[0].signingKey) 138 139 signingKey := signatureValidation[0].signingKey 140 141 require.Equal(suite.T(), hex.EncodeToString(smuSigningKeyID[:]), signingKey.data.KeyID.String()) 142 } 143 144 func (suite *PsbBinarySuite) TestPSBBinaryPSPDirectoryLevel2EntryWrongSignature() { 145 // Test negative validation of PSP Directory entry after corruption 146 require.Equal(suite.T(), FirmwareLen, len(suite.firmwareImage)) 147 148 amdFw, err := ParseAMDFirmware(suite.firmwareImage) 149 require.NoError(suite.T(), err) 150 151 smuOffChipFirmwareType := 0x12 152 153 // obtain the ranges of entry 0x12 within PSP Directory Level 2 (SMU off-chip firmware) 154 // and corrupt the very beginning of the blob 155 pspFirmware := amdFw.PSPFirmware() 156 for _, entry := range pspFirmware.PSPDirectoryLevel2.Entries { 157 if entry.Type == amd_manifest.PSPDirectoryTableEntryType(smuOffChipFirmwareType) { 158 amdFw.Firmware().ImageBytes()[entry.LocationOrValue] = 0x0 159 } 160 } 161 162 keyDB, err := GetKeys(amdFw, 2) 163 require.NoError(suite.T(), err) 164 165 // ValidatePSPEntries will succeed, but the signature validation object returned will hold a signature check error 166 signatureValidation, err := ValidatePSPEntries(amdFw, keyDB, PSPDirectoryLevel2, []uint32{uint32(smuOffChipFirmwareType)}) 167 require.NoError(suite.T(), err) 168 169 require.Equal(suite.T(), 1, len(signatureValidation)) 170 require.Error(suite.T(), signatureValidation[0].err) 171 var sigErr *SignatureCheckError 172 require.True(suite.T(), errors.As(signatureValidation[0].err, &sigErr)) 173 174 require.NotNil(suite.T(), signatureValidation[0].signingKey) 175 signingKey := signatureValidation[0].signingKey 176 require.Equal(suite.T(), hex.EncodeToString(smuSigningKeyID[:]), signingKey.data.KeyID.String()) 177 } 178 179 func (suite *PsbBinarySuite) TestPSBBinaryPSPDirectoryLevel2EntryWrongKeys() { 180 // Test negative validation of PSP Directory entry after corruption 181 require.Equal(suite.T(), FirmwareLen, len(suite.firmwareImage)) 182 183 amdFw, err := ParseAMDFirmware(suite.firmwareImage) 184 require.NoError(suite.T(), err) 185 186 smuOffChipFirmwareType := 0x12 187 188 // signatureParameters indicates the id of the signing key and is placed at 56 bytes offset 189 // from the beginning of the blob 190 signatureParametersOffset := uint64(56) 191 192 // obtain the ranges of entry 0x12 within PSP Directory Level 2 (SMU off-chip firmware) 193 // and modify the fingerprint of the signing key for the blob so that the key becomes 194 // effectively unknown 195 pspFirmware := amdFw.PSPFirmware() 196 for _, entry := range pspFirmware.PSPDirectoryLevel2.Entries { 197 if entry.Type == amd_manifest.PSPDirectoryTableEntryType(smuOffChipFirmwareType) { 198 amdFw.Firmware().ImageBytes()[entry.LocationOrValue+signatureParametersOffset] = 0x99 199 } 200 } 201 202 keyDB, err := GetKeys(amdFw, 2) 203 require.NoError(suite.T(), err) 204 205 // ValidatePSPEntries will succeed, but the signature validation object returned will hold a signature check error 206 signatureValidation, err := ValidatePSPEntries(amdFw, keyDB, PSPDirectoryLevel2, []uint32{uint32(smuOffChipFirmwareType)}) 207 require.NoError(suite.T(), err) 208 209 require.Equal(suite.T(), 1, len(signatureValidation)) 210 require.Error(suite.T(), signatureValidation[0].err) 211 212 var unknownSigningKeyErr *UnknownSigningKeyError 213 require.True(suite.T(), errors.As(signatureValidation[0].err, &unknownSigningKeyErr)) 214 } 215 216 func (suite *PsbBinarySuite) TestPSBBinaryDumpEntry() { 217 require.Equal(suite.T(), FirmwareLen, len(suite.firmwareImage)) 218 219 amdFw, err := ParseAMDFirmware(suite.firmwareImage) 220 require.NoError(suite.T(), err) 221 222 var buff bytes.Buffer 223 224 // dump SMU off-chip firmware 225 smuOffChipFirmwareType := amd_manifest.PSPDirectoryTableEntryType(0x12) 226 n, err := DumpPSPEntry(amdFw, 2, smuOffChipFirmwareType, &buff) 227 228 require.NoError(suite.T(), err) 229 require.Equal(suite.T(), n, len(smuOffChipFirmware)) 230 231 shaSmuOffChipFirmwareHash := sha256.Sum256(buff.Bytes()) 232 expectedSmuOffChipFirmwareHash := [32]byte{ 233 0xd8, 0xdc, 0x03, 0xff, 0x18, 0x1a, 0xcc, 0x9d, 0x09, 0xac, 0x5a, 0xe7, 0x59, 0x67, 0xdc, 0x96, 234 0x60, 0xe7, 0xbb, 0x08, 0xd0, 0x3f, 0xa3, 0xb1, 0xbf, 0x64, 0x17, 0x0e, 0x43, 0xdc, 0xb2, 0xf2, 235 } 236 require.Equal(suite.T(), expectedSmuOffChipFirmwareHash, shaSmuOffChipFirmwareHash) 237 238 // dump Key database 239 keyDatabaseLen := 4992 240 241 buff.Reset() 242 n, err = DumpPSPEntry(amdFw, 2, KeyDatabaseEntry, &buff) 243 244 require.NoError(suite.T(), err) 245 require.Equal(suite.T(), n, keyDatabaseLen) 246 247 keyDBHash := sha256.Sum256(buff.Bytes()) 248 249 expectedKeyDBHash := [32]byte{ 250 0xec, 0x16, 0x0f, 0xfa, 0x63, 0xae, 0xcd, 0xc9, 0x23, 0xb0, 0x34, 0x16, 0x70, 0x85, 0x50, 0xe7, 251 0x49, 0x48, 0xba, 0x6c, 0xf7, 0x7f, 0x01, 0x49, 0x53, 0x1b, 0x2a, 0x6a, 0x66, 0x28, 0x2a, 0x2c, 252 } 253 254 require.Equal(suite.T(), expectedKeyDBHash, keyDBHash) 255 } 256 257 func (suite *PsbBinarySuite) TestPSBBinaryPatchEntry() { 258 require.Equal(suite.T(), FirmwareLen, len(suite.firmwareImage)) 259 260 amdFw, err := ParseAMDFirmware(suite.firmwareImage) 261 require.NoError(suite.T(), err) 262 263 smuOffChipFirmwareType := amd_manifest.PSPDirectoryTableEntryType(0x12) 264 patchedEntry := make([]byte, len(smuOffChipFirmware)) 265 buff := bytes.NewBuffer(patchedEntry) 266 267 firmwareImageCopy := make([]byte, 0, len(suite.firmwareImage)) 268 buffImage := bytes.NewBuffer(firmwareImageCopy) 269 270 n, err := PatchPSPEntry(amdFw, 2, smuOffChipFirmwareType, buff, buffImage) 271 272 require.NoError(suite.T(), err) 273 require.Equal(suite.T(), len(suite.firmwareImage), n) 274 275 start := uint64(0) 276 end := uint64(0) 277 pspFirmware := amdFw.PSPFirmware() 278 for _, entry := range pspFirmware.PSPDirectoryLevel2.Entries { 279 if entry.Type == amd_manifest.PSPDirectoryTableEntryType(smuOffChipFirmwareType) { 280 start = entry.LocationOrValue 281 end = entry.LocationOrValue + uint64(entry.Size) 282 } 283 } 284 285 require.NotEqual(suite.T(), 0, start) 286 require.NotEqual(suite.T(), 0, end) 287 288 require.Equal(suite.T(), sha256.Sum256(firmwareImageCopy[:start]), sha256.Sum256(suite.firmwareImage[:start])) 289 290 require.Equal(suite.T(), expectedSmuOffChipFirmwareHash, sha256.Sum256(suite.firmwareImage[start:end])) 291 require.Equal(suite.T(), expectedZeroSmuOffChipFirmwareHash, sha256.Sum256(buffImage.Bytes()[start:end])) 292 293 require.Equal(suite.T(), sha256.Sum256(buffImage.Bytes()[end:]), sha256.Sum256(suite.firmwareImage[end:])) 294 } 295 296 func TestPsbBinarySuite(t *testing.T) { 297 suite.Run(t, new(PsbBinarySuite)) 298 }