--- /dev/null
+/** @file\r
+ FMP Authentication RSA2048SHA256 handler.\r
+ Provide generic FMP authentication functions for DXE/PEI post memory phase.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This module will have external input - capsule image.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+\r
+ FmpAuthenticatedHandlerRsa2048Sha256(), AuthenticateFmpImage() will receive\r
+ untrusted input and do basic validation.\r
+\r
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Guid/SystemResourceTable.h>\r
+#include <Guid/FirmwareContentsSigned.h>\r
+#include <Guid/WinCertificate.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+#include <Library/FmpAuthenticationLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Protocol/FirmwareManagement.h>\r
+#include <Guid/SystemResourceTable.h>\r
+\r
+///\r
+/// Public Exponent of RSA Key.\r
+///\r
+STATIC CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };\r
+\r
+/**\r
+ The handler is used to do the authentication for FMP capsule based upon\r
+ EFI_FIRMWARE_IMAGE_AUTHENTICATION.\r
+\r
+ Caution: This function may receive untrusted input.\r
+\r
+ This function assumes the caller AuthenticateFmpImage()\r
+ already did basic validation for EFI_FIRMWARE_IMAGE_AUTHENTICATION.\r
+\r
+ @param[in] Image Points to an FMP authentication image, started from EFI_FIRMWARE_IMAGE_AUTHENTICATION.\r
+ @param[in] ImageSize Size of the authentication image in bytes.\r
+ @param[in] PublicKeyData The public key data used to validate the signature.\r
+ @param[in] PublicKeyDataLength The length of the public key data.\r
+\r
+ @retval RETURN_SUCCESS Authentication pass.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_SUCCESS.\r
+ @retval RETURN_SECURITY_VIOLATION Authentication fail.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR.\r
+ @retval RETURN_INVALID_PARAMETER The image is in an invalid format.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT.\r
+ @retval RETURN_OUT_OF_RESOURCES No Authentication handler associated with CertType.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES.\r
+**/\r
+RETURN_STATUS\r
+FmpAuthenticatedHandlerRsa2048Sha256 (\r
+ IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,\r
+ IN UINTN ImageSize,\r
+ IN CONST UINT8 *PublicKeyData,\r
+ IN UINTN PublicKeyDataLength\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlockRsa2048Sha256;\r
+ BOOLEAN CryptoStatus;\r
+ UINT8 Digest[SHA256_DIGEST_SIZE];\r
+ UINT8 *PublicKey;\r
+ UINTN PublicKeyBufferSize;\r
+ VOID *HashContext;\r
+ VOID *Rsa;\r
+\r
+ DEBUG ((DEBUG_INFO, "FmpAuthenticatedHandlerRsa2048Sha256 - Image: 0x%08x - 0x%08x\n", (UINTN)Image, (UINTN)ImageSize));\r
+\r
+ if (Image->AuthInfo.Hdr.dwLength != OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData) + sizeof(EFI_CERT_BLOCK_RSA_2048_SHA256)) {\r
+ DEBUG((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256 - dwLength: 0x%04x, dwLength - 0x%04x\n", (UINTN)Image->AuthInfo.Hdr.dwLength, (UINTN)OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData) + sizeof(EFI_CERT_BLOCK_RSA_2048_SHA256)));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ CertBlockRsa2048Sha256 = (EFI_CERT_BLOCK_RSA_2048_SHA256 *)Image->AuthInfo.CertData;\r
+ if (!CompareGuid(&CertBlockRsa2048Sha256->HashType, &gEfiHashAlgorithmSha256Guid)) {\r
+ DEBUG((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256 - HashType: %g, expect - %g\n", &CertBlockRsa2048Sha256->HashType, &gEfiHashAlgorithmSha256Guid));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ HashContext = NULL;\r
+ Rsa = NULL;\r
+\r
+ //\r
+ // Allocate hash context buffer required for SHA 256\r
+ //\r
+ HashContext = AllocatePool (Sha256GetContextSize ());\r
+ if (HashContext == NULL) {\r
+ CryptoStatus = FALSE;\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Can not allocate hash context\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Hash public key from data payload with SHA256.\r
+ //\r
+ ZeroMem (Digest, SHA256_DIGEST_SIZE);\r
+ CryptoStatus = Sha256Init (HashContext);\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Init() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ CryptoStatus = Sha256Update (HashContext, &CertBlockRsa2048Sha256->PublicKey, sizeof(CertBlockRsa2048Sha256->PublicKey));\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Update() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ CryptoStatus = Sha256Final (HashContext, Digest);\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Final() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Fail if the PublicKey is not one of the public keys in PcdRsa2048Sha256PublicKeyBuffer\r
+ //\r
+ PublicKey = (VOID *)PublicKeyData;\r
+ PublicKeyBufferSize = PublicKeyDataLength;\r
+ CryptoStatus = FALSE;\r
+ while (PublicKeyBufferSize != 0) {\r
+ if (CompareMem (Digest, PublicKey, SHA256_DIGEST_SIZE) == 0) {\r
+ CryptoStatus = TRUE;\r
+ break;\r
+ }\r
+ PublicKey = PublicKey + SHA256_DIGEST_SIZE;\r
+ PublicKeyBufferSize = PublicKeyBufferSize - SHA256_DIGEST_SIZE;\r
+ }\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Public key in section is not supported\n"));\r
+ Status = RETURN_SECURITY_VIOLATION;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Generate & Initialize RSA Context.\r
+ //\r
+ Rsa = RsaNew ();\r
+ if (Rsa == NULL) {\r
+ CryptoStatus = FALSE;\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaNew() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Set RSA Key Components.\r
+ // NOTE: Only N and E are needed to be set as RSA public key for signature verification.\r
+ //\r
+ CryptoStatus = RsaSetKey (Rsa, RsaKeyN, CertBlockRsa2048Sha256->PublicKey, sizeof(CertBlockRsa2048Sha256->PublicKey));\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaSetKey(RsaKeyN) failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ CryptoStatus = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaSetKey(RsaKeyE) failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Hash data payload with SHA256.\r
+ //\r
+ ZeroMem (Digest, SHA256_DIGEST_SIZE);\r
+ CryptoStatus = Sha256Init (HashContext);\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Init() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ // It is a signature across the variable data and the Monotonic Count value.\r
+ CryptoStatus = Sha256Update (\r
+ HashContext,\r
+ (UINT8 *)Image + sizeof(Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength,\r
+ ImageSize - sizeof(Image->MonotonicCount) - Image->AuthInfo.Hdr.dwLength\r
+ );\r
+ if (!CryptoStatus) {\r
+ DEBUG((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Update() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ CryptoStatus = Sha256Update (\r
+ HashContext,\r
+ (UINT8 *)&Image->MonotonicCount,\r
+ sizeof(Image->MonotonicCount)\r
+ );\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Update() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ CryptoStatus = Sha256Final (HashContext, Digest);\r
+ if (!CryptoStatus) {\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Final() failed\n"));\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Verify the RSA 2048 SHA 256 signature.\r
+ //\r
+ CryptoStatus = RsaPkcs1Verify (\r
+ Rsa,\r
+ Digest,\r
+ SHA256_DIGEST_SIZE,\r
+ CertBlockRsa2048Sha256->Signature,\r
+ sizeof (CertBlockRsa2048Sha256->Signature)\r
+ );\r
+ if (!CryptoStatus) {\r
+ //\r
+ // If RSA 2048 SHA 256 signature verification fails, AUTH tested failed bit is set.\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaPkcs1Verify() failed\n"));\r
+ Status = RETURN_SECURITY_VIOLATION;\r
+ goto Done;\r
+ }\r
+ DEBUG ((DEBUG_INFO, "FmpAuthenticatedHandlerRsa2048Sha256: PASS verification\n"));\r
+\r
+ Status = RETURN_SUCCESS;\r
+\r
+Done:\r
+ //\r
+ // Free allocated resources used to perform RSA 2048 SHA 256 signature verification\r
+ //\r
+ if (Rsa != NULL) {\r
+ RsaFree (Rsa);\r
+ }\r
+ if (HashContext != NULL) {\r
+ FreePool (HashContext);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The function is used to do the authentication for FMP capsule based upon\r
+ EFI_FIRMWARE_IMAGE_AUTHENTICATION.\r
+\r
+ The FMP capsule image should start with EFI_FIRMWARE_IMAGE_AUTHENTICATION,\r
+ followed by the payload.\r
+\r
+ If the return status is RETURN_SUCCESS, the caller may continue the rest\r
+ FMP update process.\r
+ If the return status is NOT RETURN_SUCCESS, the caller should stop the FMP\r
+ update process and convert the return status to LastAttemptStatus\r
+ to indicate that FMP update fails.\r
+ The LastAttemptStatus can be got from ESRT table or via\r
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo().\r
+\r
+ Caution: This function may receive untrusted input.\r
+\r
+ @param[in] Image Points to an FMP authentication image, started from EFI_FIRMWARE_IMAGE_AUTHENTICATION.\r
+ @param[in] ImageSize Size of the authentication image in bytes.\r
+ @param[in] PublicKeyData The public key data used to validate the signature.\r
+ @param[in] PublicKeyDataLength The length of the public key data.\r
+\r
+ @retval RETURN_SUCCESS Authentication pass.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_SUCCESS.\r
+ @retval RETURN_SECURITY_VIOLATION Authentication fail.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR.\r
+ @retval RETURN_INVALID_PARAMETER The image is in an invalid format.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT.\r
+ @retval RETURN_UNSUPPORTED No Authentication handler associated with CertType.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT.\r
+ @retval RETURN_UNSUPPORTED Image or ImageSize is invalid.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT.\r
+ @retval RETURN_OUT_OF_RESOURCES No Authentication handler associated with CertType.\r
+ The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+AuthenticateFmpImage (\r
+ IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,\r
+ IN UINTN ImageSize,\r
+ IN CONST UINT8 *PublicKeyData,\r
+ IN UINTN PublicKeyDataLength\r
+ )\r
+{\r
+ GUID *CertType;\r
+ EFI_STATUS Status;\r
+\r
+ if ((Image == NULL) || (ImageSize == 0)) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ if (ImageSize < sizeof(EFI_FIRMWARE_IMAGE_AUTHENTICATION)) {\r
+ DEBUG((DEBUG_ERROR, "AuthenticateFmpImage - ImageSize too small\n"));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (Image->AuthInfo.Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {\r
+ DEBUG((DEBUG_ERROR, "AuthenticateFmpImage - dwLength too small\n"));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (Image->AuthInfo.Hdr.dwLength > MAX_UINTN - sizeof(UINT64)) {\r
+ DEBUG((DEBUG_ERROR, "AuthenticateFmpImage - dwLength too big\n"));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (ImageSize <= sizeof(Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) {\r
+ DEBUG((DEBUG_ERROR, "AuthenticateFmpImage - ImageSize too small\n"));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (Image->AuthInfo.Hdr.wRevision != 0x0200) {\r
+ DEBUG((DEBUG_ERROR, "AuthenticateFmpImage - wRevision: 0x%02x, expect - 0x%02x\n", (UINTN)Image->AuthInfo.Hdr.wRevision, (UINTN)0x0200));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (Image->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) {\r
+ DEBUG((DEBUG_ERROR, "AuthenticateFmpImage - wCertificateType: 0x%02x, expect - 0x%02x\n", (UINTN)Image->AuthInfo.Hdr.wCertificateType, (UINTN)WIN_CERT_TYPE_EFI_GUID));\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ CertType = &Image->AuthInfo.CertType;\r
+ DEBUG((DEBUG_INFO, "AuthenticateFmpImage - CertType: %g\n", CertType));\r
+\r
+ if (CompareGuid (&gEfiCertTypeRsa2048Sha256Guid, CertType)) {\r
+ //\r
+ // Call the match handler to extract raw data for the input section data.\r
+ //\r
+ Status = FmpAuthenticatedHandlerRsa2048Sha256 (\r
+ Image,\r
+ ImageSize,\r
+ PublicKeyData,\r
+ PublicKeyDataLength\r
+ );\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Not found, the input guided section is not supported.\r
+ //\r
+ return RETURN_UNSUPPORTED;\r
+}\r
+\r