+/** @file\r
+\r
+ Blob verifier library that uses SEV hashes table. The hashes table holds the\r
+ allowed hashes of the kernel, initrd, and cmdline blobs.\r
+\r
+ Copyright (C) 2021, IBM Corporation\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Library/BaseCryptLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BlobVerifierLib.h>\r
+\r
+/**\r
+ The SEV Hashes table must be in encrypted memory and has the table\r
+ and its entries described by\r
+\r
+ <GUID>|UINT16 <len>|<data>\r
+\r
+ With the whole table GUID being 9438d606-4f22-4cc9-b479-a793d411fd21\r
+\r
+ The current possible table entries are for the kernel, the initrd\r
+ and the cmdline:\r
+\r
+ 4de79437-abd2-427f-b835-d5b172d2045b kernel\r
+ 44baf731-3a2f-4bd7-9af1-41e29169781d initrd\r
+ 97d02dd8-bd20-4c94-aa78-e7714d36ab2a cmdline\r
+\r
+ The size of the entry is used to identify the hash, but the\r
+ expectation is that it will be 32 bytes of SHA-256.\r
+**/\r
+\r
+#define SEV_HASH_TABLE_GUID \\r
+ (GUID) { 0x9438d606, 0x4f22, 0x4cc9, { 0xb4, 0x79, 0xa7, 0x93, 0xd4, 0x11, 0xfd, 0x21 } }\r
+#define SEV_KERNEL_HASH_GUID \\r
+ (GUID) { 0x4de79437, 0xabd2, 0x427f, { 0xb8, 0x35, 0xd5, 0xb1, 0x72, 0xd2, 0x04, 0x5b } }\r
+#define SEV_INITRD_HASH_GUID \\r
+ (GUID) { 0x44baf731, 0x3a2f, 0x4bd7, { 0x9a, 0xf1, 0x41, 0xe2, 0x91, 0x69, 0x78, 0x1d } }\r
+#define SEV_CMDLINE_HASH_GUID \\r
+ (GUID) { 0x97d02dd8, 0xbd20, 0x4c94, { 0xaa, 0x78, 0xe7, 0x71, 0x4d, 0x36, 0xab, 0x2a } }\r
+\r
+STATIC CONST EFI_GUID mSevKernelHashGuid = SEV_KERNEL_HASH_GUID;\r
+STATIC CONST EFI_GUID mSevInitrdHashGuid = SEV_INITRD_HASH_GUID;\r
+STATIC CONST EFI_GUID mSevCmdlineHashGuid = SEV_CMDLINE_HASH_GUID;\r
+\r
+#pragma pack (1)\r
+typedef struct {\r
+ GUID Guid;\r
+ UINT16 Len;\r
+ UINT8 Data[];\r
+} HASH_TABLE;\r
+#pragma pack ()\r
+\r
+STATIC HASH_TABLE *mHashesTable;\r
+STATIC UINT16 mHashesTableSize;\r
+\r
+STATIC\r
+CONST GUID*\r
+FindBlobEntryGuid (\r
+ IN CONST CHAR16 *BlobName\r
+ )\r
+{\r
+ if (StrCmp (BlobName, L"kernel") == 0) {\r
+ return &mSevKernelHashGuid;\r
+ } else if (StrCmp (BlobName, L"initrd") == 0) {\r
+ return &mSevInitrdHashGuid;\r
+ } else if (StrCmp (BlobName, L"cmdline") == 0) {\r
+ return &mSevCmdlineHashGuid;\r
+ } else {\r
+ return NULL;\r
+ }\r
+}\r
+\r
+/**\r
+ Verify blob from an external source.\r
+\r
+ @param[in] BlobName The name of the blob\r
+ @param[in] Buf The data of the blob\r
+ @param[in] BufSize The size of the blob in bytes\r
+\r
+ @retval EFI_SUCCESS The blob was verified successfully.\r
+ @retval EFI_ACCESS_DENIED The blob could not be verified, and therefore\r
+ should be considered non-secure.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VerifyBlob (\r
+ IN CONST CHAR16 *BlobName,\r
+ IN CONST VOID *Buf,\r
+ IN UINT32 BufSize\r
+ )\r
+{\r
+ CONST GUID *Guid;\r
+ INT32 Remaining;\r
+ HASH_TABLE *Entry;\r
+\r
+ if (mHashesTable == NULL || mHashesTableSize == 0) {\r
+ DEBUG ((DEBUG_ERROR,\r
+ "%a: Verifier called but no hashes table discoverd in MEMFD\n",\r
+ __FUNCTION__));\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ Guid = FindBlobEntryGuid (BlobName);\r
+ if (Guid == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Unknown blob name \"%s\"\n", __FUNCTION__,\r
+ BlobName));\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Remaining is INT32 to catch underflow in case Entry->Len has a\r
+ // very high UINT16 value\r
+ //\r
+ for (Entry = mHashesTable, Remaining = mHashesTableSize;\r
+ Remaining >= sizeof *Entry && Remaining >= Entry->Len;\r
+ Remaining -= Entry->Len,\r
+ Entry = (HASH_TABLE *)((UINT8 *)Entry + Entry->Len)) {\r
+ UINTN EntrySize;\r
+ EFI_STATUS Status;\r
+ UINT8 Hash[SHA256_DIGEST_SIZE];\r
+\r
+ if (!CompareGuid (&Entry->Guid, Guid)) {\r
+ continue;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%a: Found GUID %g in table\n", __FUNCTION__, Guid));\r
+\r
+ EntrySize = Entry->Len - sizeof Entry->Guid - sizeof Entry->Len;\r
+ if (EntrySize != SHA256_DIGEST_SIZE) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Hash has the wrong size %d != %d\n",\r
+ __FUNCTION__, EntrySize, SHA256_DIGEST_SIZE));\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Calculate the buffer's hash and verify that it is identical to the\r
+ // expected hash table entry\r
+ //\r
+ Sha256HashAll (Buf, BufSize, Hash);\r
+\r
+ if (CompareMem (Entry->Data, Hash, EntrySize) == 0) {\r
+ Status = EFI_SUCCESS;\r
+ DEBUG ((DEBUG_INFO, "%a: Hash comparison succeeded for \"%s\"\n",\r
+ __FUNCTION__, BlobName));\r
+ } else {\r
+ Status = EFI_ACCESS_DENIED;\r
+ DEBUG ((DEBUG_ERROR, "%a: Hash comparison failed for \"%s\"\n",\r
+ __FUNCTION__, BlobName));\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((DEBUG_ERROR, "%a: Hash GUID %g not found in table\n", __FUNCTION__,\r
+ Guid));\r
+ return EFI_ACCESS_DENIED;\r
+}\r
+\r
+/**\r
+ Locate the SEV hashes table.\r
+\r
+ This function always returns success, even if the table can't be found. The\r
+ subsequent VerifyBlob calls will fail if no table was found.\r
+\r
+ @retval RETURN_SUCCESS The hashes table is set up correctly, or there is no\r
+ hashes table\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+BlobVerifierLibSevHashesConstructor (\r
+ VOID\r
+ )\r
+{\r
+ HASH_TABLE *Ptr;\r
+ UINT32 Size;\r
+\r
+ mHashesTable = NULL;\r
+ mHashesTableSize = 0;\r
+\r
+ Ptr = (void *)(UINTN)FixedPcdGet64 (PcdQemuHashTableBase);\r
+ Size = FixedPcdGet32 (PcdQemuHashTableSize);\r
+\r
+ if (Ptr == NULL || Size < sizeof *Ptr ||\r
+ !CompareGuid (&Ptr->Guid, &SEV_HASH_TABLE_GUID) ||\r
+ Ptr->Len < sizeof *Ptr || Ptr->Len > Size) {\r
+ return RETURN_SUCCESS;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%a: Found injected hashes table in secure location\n",\r
+ __FUNCTION__));\r
+\r
+ mHashesTable = (HASH_TABLE *)Ptr->Data;\r
+ mHashesTableSize = Ptr->Len - sizeof Ptr->Guid - sizeof Ptr->Len;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: mHashesTable=0x%p, Size=%u\n", __FUNCTION__,\r
+ mHashesTable, mHashesTableSize));\r
+\r
+ return RETURN_SUCCESS;\r
+}\r