--- /dev/null
+/** @file\r
+ Pkcs7Verify Driver to produce the UEFI PKCS7 Verification Protocol.\r
+\r
+ The driver will produce the UEFI PKCS7 Verification Protocol which is used to\r
+ verify data signed using PKCS7 structure. The PKCS7 data to be verified must\r
+ be ASN.1 (DER) encoded.\r
+\r
+Copyright (c) 2015, 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 <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+#include <Protocol/Pkcs7Verify.h>\r
+\r
+#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE\r
+\r
+/**\r
+ Calculates the hash of the given data based on the specified hash GUID.\r
+\r
+ @param[in] Data Pointer to the data buffer to be hashed.\r
+ @param[in] DataSize The size of data buffer in bytes.\r
+ @param[in] CertGuid The GUID to identify the hash algorithm to be used.\r
+ @param[out] HashValue Pointer to a buffer that receives the hash result.\r
+\r
+ @retval TRUE Data hash calculation succeeded.\r
+ @retval FALSE Data hash calculation failed.\r
+\r
+**/\r
+BOOLEAN\r
+CalculateDataHash (\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN EFI_GUID *CertGuid,\r
+ OUT UINT8 *HashValue\r
+ )\r
+{\r
+ BOOLEAN Status;\r
+ VOID *HashCtx;\r
+ UINTN CtxSize;\r
+\r
+ Status = FALSE;\r
+ HashCtx = NULL;\r
+\r
+ if (CompareGuid (CertGuid, &gEfiCertSha1Guid)) {\r
+ //\r
+ // SHA-1 Hash\r
+ //\r
+ CtxSize = Sha1GetContextSize ();\r
+ HashCtx = AllocatePool (CtxSize);\r
+ if (HashCtx == NULL) {\r
+ goto _Exit;\r
+ }\r
+ Status = Sha1Init (HashCtx);\r
+ Status = Sha1Update (HashCtx, Data, DataSize);\r
+ Status = Sha1Final (HashCtx, HashValue);\r
+\r
+ } else if (CompareGuid (CertGuid, &gEfiCertSha256Guid)) {\r
+ //\r
+ // SHA256 Hash\r
+ //\r
+ CtxSize = Sha256GetContextSize ();\r
+ HashCtx = AllocatePool (CtxSize);\r
+ if (HashCtx == NULL) {\r
+ goto _Exit;\r
+ }\r
+ Status = Sha256Init (HashCtx);\r
+ Status = Sha256Update (HashCtx, Data, DataSize);\r
+ Status = Sha256Final (HashCtx, HashValue);\r
+\r
+ } else if (CompareGuid (CertGuid, &gEfiCertSha384Guid)) {\r
+ //\r
+ // SHA384 Hash\r
+ //\r
+ CtxSize = Sha384GetContextSize ();\r
+ HashCtx = AllocatePool (CtxSize);\r
+ if (HashCtx == NULL) {\r
+ goto _Exit;\r
+ }\r
+ Status = Sha384Init (HashCtx);\r
+ Status = Sha384Update (HashCtx, Data, DataSize);\r
+ Status = Sha384Final (HashCtx, HashValue);\r
+\r
+ } else if (CompareGuid (CertGuid, &gEfiCertSha512Guid)) {\r
+ //\r
+ // SHA512 Hash\r
+ //\r
+ CtxSize = Sha512GetContextSize ();\r
+ HashCtx = AllocatePool (CtxSize);\r
+ if (HashCtx == NULL) {\r
+ goto _Exit;\r
+ }\r
+ Status = Sha512Init (HashCtx);\r
+ Status = Sha512Update (HashCtx, Data, DataSize);\r
+ Status = Sha512Final (HashCtx, HashValue);\r
+ }\r
+\r
+_Exit:\r
+ if (HashCtx != NULL) {\r
+ FreePool (HashCtx);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check whether the hash of data content is revoked by the revocation database.\r
+\r
+ @param[in] Content Pointer to the content buffer that is searched for.\r
+ @param[in] ContentSize The size of data content in bytes.\r
+ @param[in] RevokedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structure which contains list of X.509 certificates\r
+ of revoked signers and revoked content hashes.\r
+\r
+ @return TRUE The matched content hash is found in the revocation database.\r
+ @return FALSE The matched content hash is not found in the revocation database.\r
+\r
+**/\r
+BOOLEAN\r
+IsContentHashRevoked (\r
+ IN UINT8 *Content,\r
+ IN UINTN ContentSize,\r
+ IN EFI_SIGNATURE_LIST **RevokedDb\r
+ )\r
+{\r
+ EFI_SIGNATURE_LIST *SigList;\r
+ EFI_SIGNATURE_DATA *SigData;\r
+ UINTN Index;\r
+ UINT8 HashVal[MAX_DIGEST_SIZE];\r
+ UINTN EntryIndex;\r
+ UINTN EntryCount;\r
+ BOOLEAN Status;\r
+\r
+ if (RevokedDb == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ Status = FALSE;\r
+ //\r
+ // Check if any hash matching content hash can be found in RevokedDB\r
+ //\r
+ for (Index = 0; ; Index++) {\r
+ SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);\r
+\r
+ //\r
+ // The list is terminated by a NULL pointer.\r
+ //\r
+ if (SigList == NULL) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Calculate the digest of supplied data based on the signature hash type.\r
+ //\r
+ if (!CalculateDataHash (Content, ContentSize, &SigList->SignatureType, HashVal)) {\r
+ //\r
+ // Un-matched Hash GUID or other failure.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Search the signature database to search the revoked content hash\r
+ //\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +\r
+ SigList->SignatureHeaderSize);\r
+ EntryCount = (SigList->SignatureListSize - SigList->SignatureHeaderSize -\r
+ sizeof (EFI_SIGNATURE_LIST)) / SigList->SignatureSize;\r
+ for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex++) {\r
+ //\r
+ // Compare Data Hash with Signature Data\r
+ //\r
+ if (CompareMem (SigData->SignatureData, HashVal, (SigList->SignatureSize - sizeof (EFI_GUID))) == 0) {\r
+ Status = TRUE;\r
+ goto _Exit;\r
+ }\r
+\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigData + SigList->SignatureSize);\r
+ }\r
+ }\r
+\r
+_Exit:\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check whether the hash of an given certificate is revoked by the revocation database.\r
+\r
+ @param[in] Certificate Pointer to the certificate that is searched for.\r
+ @param[in] CertSize Size of certificate in bytes.\r
+ @param[in] RevokedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structures which contains list of X.509 certificate\r
+ of revoked signers and revoked content hashes.\r
+ @param[out] RevocationTime Return the time that the certificate was revoked.\r
+\r
+ @return TRUE The certificate hash is found in the revocation database.\r
+ @return FALSE The certificate hash is not found in the revocation database.\r
+\r
+**/\r
+BOOLEAN\r
+IsCertHashRevoked (\r
+ IN UINT8 *Certificate,\r
+ IN UINTN CertSize,\r
+ IN EFI_SIGNATURE_LIST **RevokedDb,\r
+ OUT EFI_TIME *RevocationTime\r
+ )\r
+{\r
+ BOOLEAN Status;\r
+ EFI_SIGNATURE_LIST *SigList;\r
+ EFI_SIGNATURE_DATA *SigData;\r
+ UINT8 *TBSCert;\r
+ UINTN TBSCertSize;\r
+ UINTN Index;\r
+ UINTN EntryIndex;\r
+ UINTN EntryCount;\r
+ UINT8 CertHashVal[MAX_DIGEST_SIZE];\r
+\r
+ if ((RevocationTime == NULL) || (RevokedDb == NULL)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Retrieve the TBSCertificate from the X.509 Certificate for hash calculation\r
+ //\r
+ if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {\r
+ return FALSE;\r
+ }\r
+\r
+ Status = FALSE;\r
+ for (Index = 0; ; Index++) {\r
+\r
+ SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);\r
+ //\r
+ // The list is terminated by a NULL pointer.\r
+ //\r
+ if (SigList == NULL) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Determine Hash Algorithm based on the entry type in revocation database, and\r
+ // calculate the certificate hash.\r
+ //\r
+ if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Sha256Guid)) {\r
+ Status = CalculateDataHash (TBSCert, TBSCertSize, &gEfiCertSha256Guid, CertHashVal);\r
+\r
+ } else if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Sha384Guid)) {\r
+ Status = CalculateDataHash (TBSCert, TBSCertSize, &gEfiCertSha384Guid, CertHashVal);\r
+\r
+ } else if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Sha512Guid)) {\r
+ Status = CalculateDataHash (TBSCert, TBSCertSize, &gEfiCertSha512Guid, CertHashVal);\r
+\r
+ } else {\r
+ //\r
+ // Un-matched Cert Hash GUID\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (!Status) {\r
+ continue;\r
+ }\r
+\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +\r
+ SigList->SignatureHeaderSize);\r
+ EntryCount = (SigList->SignatureListSize - SigList->SignatureHeaderSize -\r
+ sizeof (EFI_SIGNATURE_LIST)) / SigList->SignatureSize;\r
+ for (EntryIndex = 0; EntryIndex < EntryCount; Index++) {\r
+ //\r
+ // Check if the Certificate Hash is revoked.\r
+ //\r
+ if (CompareMem (SigData->SignatureData, CertHashVal,\r
+ SigList->SignatureSize - sizeof (EFI_GUID) - sizeof (EFI_TIME)) == 0) {\r
+ Status = TRUE;\r
+ //\r
+ // Return the revocation time of this revoked certificate.\r
+ //\r
+ CopyMem (\r
+ RevocationTime,\r
+ (EFI_TIME *)((UINT8 *)SigData + SigList->SignatureSize - sizeof (EFI_TIME)),\r
+ sizeof (EFI_TIME)\r
+ );\r
+ goto _Exit;\r
+ }\r
+\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigData + SigList->SignatureSize);\r
+ }\r
+ }\r
+\r
+_Exit:\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check if the given time value is zero.\r
+\r
+ @param[in] Time Pointer of a time value.\r
+\r
+ @retval TRUE The Time is Zero.\r
+ @retval FALSE The Time is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsTimeZero (\r
+ IN EFI_TIME *Time\r
+ )\r
+{\r
+ if ((Time->Year == 0) && (Time->Month == 0) && (Time->Day == 0) &&\r
+ (Time->Hour == 0) && (Time->Minute == 0) && (Time->Second == 0)) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Check whether the timestamp is valid by comparing the signing time and the revocation time.\r
+\r
+ @param SigningTime Pointer to the signing time.\r
+ @param RevocationTime Pointer to the revocation time.\r
+\r
+ @retval TRUE The SigningTime is not later than the RevocationTime.\r
+ @retval FALSE The SigningTime is later than the RevocationTime.\r
+\r
+**/\r
+BOOLEAN\r
+CompareTimestamp (\r
+ IN EFI_TIME *SigningTime,\r
+ IN EFI_TIME *RevocationTime\r
+ )\r
+{\r
+ if (SigningTime->Year != RevocationTime->Year) {\r
+ return (BOOLEAN) (SigningTime->Year < RevocationTime->Year);\r
+ } else if (SigningTime->Month != RevocationTime->Month) {\r
+ return (BOOLEAN) (SigningTime->Month < RevocationTime->Month);\r
+ } else if (SigningTime->Day != RevocationTime->Day) {\r
+ return (BOOLEAN) (SigningTime->Day < RevocationTime->Day);\r
+ } else if (SigningTime->Hour != RevocationTime->Hour) {\r
+ return (BOOLEAN) (SigningTime->Hour < RevocationTime->Hour);\r
+ } else if (SigningTime->Minute != RevocationTime->Minute) {\r
+ return (BOOLEAN) (SigningTime->Minute < RevocationTime->Minute);\r
+ }\r
+\r
+ return (BOOLEAN) (SigningTime->Second <= RevocationTime->Second);\r
+}\r
+\r
+/**\r
+ Check whether the timestamp signature embedded in PKCS7 signedData is valid and\r
+ the signing time is also earlier than the revocation time.\r
+\r
+ @param[in] SignedData Pointer to the PKCS#7 signedData.\r
+ @param[in] SignedDataSize Size of SignedData in bytes.\r
+ @param[in] TimeStampDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structures which is used to pass a list of X.509\r
+ certificates of trusted timestamp signers.\r
+ @param[in] RevocationTime The time that the certificate was revoked.\r
+\r
+ @retval TRUE Timestamp signature is valid and the signing time is no later\r
+ than the revocation time.\r
+ @retval FALSE Timestamp signature is not valid or the signing time is later\r
+ than the revocation time.\r
+\r
+**/\r
+BOOLEAN\r
+IsValidTimestamp (\r
+ IN UINT8 *SignedData,\r
+ IN UINTN SignedDataSize,\r
+ IN EFI_SIGNATURE_LIST **TimeStampDb,\r
+ IN EFI_TIME *RevocationTime\r
+ )\r
+{\r
+ BOOLEAN Status;\r
+ EFI_SIGNATURE_LIST *SigList;\r
+ EFI_SIGNATURE_DATA *SigData;\r
+ UINT8 *TsaCert;\r
+ UINTN TsaCertSize;\r
+ UINTN Index;\r
+ EFI_TIME SigningTime;\r
+\r
+ //\r
+ // If no supplied database for verification or RevocationTime is zero,\r
+ // the certificate shall be considered to always be revoked.\r
+ //\r
+ if ((TimeStampDb == NULL) || (IsTimeZero (RevocationTime))) {\r
+ return FALSE;\r
+ }\r
+\r
+ Status = FALSE;\r
+ //\r
+ // RevocationTime is non-zero, the certificate should be considered to be revoked\r
+ // from that time and onwards.\r
+ //\r
+ for (Index = 0; ; Index++) {\r
+ SigList = (EFI_SIGNATURE_LIST *) (TimeStampDb[Index]);\r
+\r
+ //\r
+ // The list is terminated by a NULL pointer.\r
+ //\r
+ if (SigList == NULL) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Ignore any non-X509-format entry in the list\r
+ //\r
+ if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {\r
+ continue;\r
+ }\r
+\r
+\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +\r
+ SigList->SignatureHeaderSize);\r
+ TsaCert = SigData->SignatureData;\r
+ TsaCertSize = SigList->SignatureSize - sizeof (EFI_GUID);\r
+\r
+ //\r
+ // Each TSA Certificate will normally be in a seperate EFI_SIGNATURE_LIST\r
+ // Leverage ImageTimestampVerify interface for Timestamp counterSignature Verification\r
+ //\r
+ if (ImageTimestampVerify (SignedData, SignedDataSize, TsaCert, TsaCertSize, &SigningTime)) {\r
+ //\r
+ // The signer signature is valid only when the signing time is earlier than revocation time.\r
+ //\r
+ if (CompareTimestamp (&SigningTime, RevocationTime)) {\r
+ Status = TRUE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check whether the PKCS7 signedData is revoked by verifying with the revoked\r
+ certificates database, and if the signedData is timestamped, the embedded timestamp\r
+ couterSignature will be checked with the supplied timestamp database.\r
+\r
+ @param[in] SignedData Pointer to buffer containing ASN.1 DER-encoded PKCS7\r
+ signature.\r
+ @param[in] SignedDataSize The size of SignedData buffer in bytes.\r
+ @param[in] InData Pointer to the buffer containing the raw message data\r
+ previously signed and to be verified.\r
+ @param[in] InDataSize The size of InData buffer in bytes.\r
+ @param[in] RevokedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structure which contains list of X.509 certificates\r
+ of revoked signers and revoked content hashes.\r
+ @param[in] TimeStampDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structures which is used to pass a list of X.509\r
+ certificates of trusted timestamp signers.\r
+\r
+ @retval EFI_SUCCESS The PKCS7 signedData is revoked.\r
+ @retval EFI_SECURITY_VIOLATION Fail to verify the signature in PKCS7 signedData.\r
+ @retval EFI_INVALID_PARAMETER SignedData is NULL or SignedDataSize is zero.\r
+ AllowedDb is NULL.\r
+ Content is not NULL and ContentSize is NULL.\r
+ @retval EFI_NOT_FOUND Content not found because InData is NULL and no\r
+ content embedded in PKCS7 signedData.\r
+ @retval EFI_UNSUPPORTED The PKCS7 signedData was not correctly formatted.\r
+\r
+**/\r
+EFI_STATUS\r
+P7CheckRevocation (\r
+ IN UINT8 *SignedData,\r
+ IN UINTN SignedDataSize,\r
+ IN UINT8 *InData,\r
+ IN UINTN InDataSize,\r
+ IN EFI_SIGNATURE_LIST **RevokedDb,\r
+ IN EFI_SIGNATURE_LIST **TimeStampDb\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SIGNATURE_LIST *SigList;\r
+ EFI_SIGNATURE_DATA *SigData;\r
+ UINT8 *RevokedCert;\r
+ UINTN RevokedCertSize;\r
+ UINTN Index;\r
+ UINT8 *CertBuffer;\r
+ UINTN BufferLength;\r
+ UINT8 *TrustedCert;\r
+ UINTN TrustedCertLength;\r
+ UINT8 CertNumber;\r
+ UINT8 *CertPtr;\r
+ UINT8 *Cert;\r
+ UINTN CertSize;\r
+ EFI_TIME RevocationTime;\r
+\r
+ Status = EFI_UNSUPPORTED;\r
+ SigData = NULL;\r
+ RevokedCert = NULL;\r
+ RevokedCertSize = 0;\r
+ CertBuffer = NULL;\r
+ TrustedCert = NULL;\r
+\r
+ //\r
+ // The signedData is revoked if the hash of content existed in RevokedDb\r
+ //\r
+ if (IsContentHashRevoked (InData, InDataSize, RevokedDb)) {\r
+ Status = EFI_SUCCESS;\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Check if the signer's certificate can be found in Revoked database\r
+ //\r
+ for (Index = 0; ; Index++) {\r
+ SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);\r
+\r
+ //\r
+ // The list is terminated by a NULL pointer.\r
+ //\r
+ if (SigList == NULL) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Ignore any non-X509-format entry in the list.\r
+ //\r
+ if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {\r
+ continue;\r
+ }\r
+\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +\r
+ SigList->SignatureHeaderSize);\r
+\r
+ RevokedCert = SigData->SignatureData;\r
+ RevokedCertSize = SigList->SignatureSize - sizeof (EFI_GUID);\r
+\r
+ //\r
+ // Verifying the PKCS#7 SignedData with the revoked certificate in RevokedDb\r
+ //\r
+ if (Pkcs7Verify (SignedData, SignedDataSize, RevokedCert, RevokedCertSize, InData, InDataSize)) {\r
+ //\r
+ // The signedData was verified by one entry in Revoked Database\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // The signedData was revoked, since it was hit by RevokedDb\r
+ //\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Now we will continue to check the X.509 Certificate Hash & Possible Timestamp\r
+ //\r
+ if ((TimeStampDb == NULL) || (*TimeStampDb == NULL)) {\r
+ goto _Exit;\r
+ }\r
+\r
+ Pkcs7GetSigners (SignedData, SignedDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);\r
+ if ((BufferLength == 0) || (CertBuffer == NULL)) {\r
+ Status = EFI_SUCCESS;\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Check if any hash of certificates embedded in P7 data is in the revoked database.\r
+ //\r
+ CertNumber = (UINT8) (*CertBuffer);\r
+ CertPtr = CertBuffer + 1;\r
+ for (Index = 0; Index < CertNumber; Index++) {\r
+ //\r
+ // Retrieve the Certificate data\r
+ //\r
+ CertSize = (UINTN) ReadUnaligned32 ((UINT32 *) CertPtr);\r
+ Cert = (UINT8 *)CertPtr + sizeof (UINT32);\r
+\r
+ if (IsCertHashRevoked (Cert, CertSize, RevokedDb, &RevocationTime)) {\r
+ //\r
+ // Check the timestamp signature and signing time to determine if p7 data can be trusted.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ if (IsValidTimestamp (SignedData, SignedDataSize, TimeStampDb, &RevocationTime)) {\r
+ //\r
+ // Use EFI_NOT_READY to identify the P7Data is not reovked, because the timestamping\r
+ // occured prior to the time of certificate revocation.\r
+ //\r
+ Status = EFI_NOT_READY;\r
+ }\r
+\r
+ goto _Exit;\r
+ }\r
+\r
+ CertPtr = CertPtr + sizeof (UINT32) + CertSize;\r
+ }\r
+\r
+_Exit:\r
+ Pkcs7FreeSigners (CertBuffer);\r
+ Pkcs7FreeSigners (TrustedCert);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check whether the PKCS7 signedData can be verified by the trusted certificates\r
+ database, and return the content of the signedData if requested.\r
+\r
+ @param[in] SignedData Pointer to buffer containing ASN.1 DER-encoded PKCS7\r
+ signature.\r
+ @param[in] SignedDataSize The size of SignedData buffer in bytes.\r
+ @param[in] InData Pointer to the buffer containing the raw message data\r
+ previously signed and to be verified.\r
+ @param[in] InDataSize The size of InData buffer in bytes.\r
+ @param[in] AllowedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structures which contains lists of X.509 certificates\r
+ of approved signers.\r
+ @param[out] Content An optional caller-allocated buffer into which the\r
+ function will copy the content of PKCS7 signedData.\r
+ @param[in,out] ContentSize On input, points of the size in bytes of the optional\r
+ buffer Content previously allocated by caller. On output,\r
+ the value will contain the actual size of the content\r
+ extracted from the signedData.\r
+\r
+ @retval EFI_SUCCESS The PKCS7 signedData is trusted.\r
+ @retval EFI_SECURITY_VIOLATION Fail to verify the signature in PKCS7 signedData.\r
+ @retval EFI_INVALID_PARAMETER SignedData is NULL or SignedDataSize is zero.\r
+ AllowedDb is NULL.\r
+ Content is not NULL and ContentSize is NULL.\r
+ @retval EFI_NOT_FOUND Content not found because InData is NULL and no\r
+ content embedded in PKCS7 signedData.\r
+ @retval EFI_UNSUPPORTED The PKCS7 signedData was not correctly formatted.\r
+ @retval EFI_BUFFER_TOO_SMALL The size of buffer indicated by ContentSize is too\r
+ small to hold the content. ContentSize updated to\r
+ the required size.\r
+\r
+**/\r
+EFI_STATUS\r
+P7CheckTrust (\r
+ IN UINT8 *SignedData,\r
+ IN UINTN SignedDataSize,\r
+ IN UINT8 *InData,\r
+ IN UINTN InDataSize,\r
+ IN EFI_SIGNATURE_LIST **AllowedDb\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SIGNATURE_LIST *SigList;\r
+ EFI_SIGNATURE_DATA *SigData;\r
+ UINT8 *TrustCert;\r
+ UINTN TrustCertSize;\r
+ UINTN Index;\r
+\r
+ Status = EFI_SECURITY_VIOLATION;\r
+ SigData = NULL;\r
+ TrustCert = NULL;\r
+ TrustCertSize = 0;\r
+\r
+ if (AllowedDb == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Build Certificate Stack with all valid X509 certificates in the supplied\r
+ // Signature List for PKCS7 Verification.\r
+ //\r
+ for (Index = 0; ; Index++) {\r
+ SigList = (EFI_SIGNATURE_LIST *)(AllowedDb[Index]);\r
+\r
+ //\r
+ // The list is terminated by a NULL pointer.\r
+ //\r
+ if (SigList == NULL) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Ignore any non-X509-format entry in the list.\r
+ //\r
+ if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {\r
+ continue;\r
+ }\r
+\r
+ SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +\r
+ SigList->SignatureHeaderSize);\r
+\r
+ TrustCert = SigData->SignatureData;\r
+ TrustCertSize = SigList->SignatureSize - sizeof (EFI_GUID);\r
+\r
+ //\r
+ // Verifying the PKCS#7 SignedData with the trusted certificate from AllowedDb\r
+ //\r
+ if (Pkcs7Verify (SignedData, SignedDataSize, TrustCert, TrustCertSize, InData, InDataSize)) {\r
+ //\r
+ // The SignedData was verified successfully by one entry in Trusted Database\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Processes a buffer containing binary DER-encoded PKCS7 signature.\r
+ The signed data content may be embedded within the buffer or separated. Function\r
+ verifies the signature of the content is valid and signing certificate was not\r
+ revoked and is contained within a list of trusted signers.\r
+\r
+ @param[in] This Pointer to EFI_PKCS7_VERIFY_PROTOCOL instance.\r
+ @param[in] SignedData Points to buffer containing ASN.1 DER-encoded PKCS7\r
+ signature.\r
+ @param[in] SignedDataSize The size of SignedData buffer in bytes.\r
+ @param[in] InData In case of detached signature, InData points to\r
+ buffer containing the raw message data previously\r
+ signed and to be verified by function. In case of\r
+ SignedData containing embedded data, InData must be\r
+ NULL.\r
+ @param[in] InDataSize When InData is used, the size of InData buffer in\r
+ bytes. When InData is NULL. This parameter must be\r
+ 0.\r
+ @param[in] AllowedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structures. The list is terminated by a null\r
+ pointer. The EFI_SIGNATURE_LIST structures contain\r
+ lists of X.509 certificates of approved signers.\r
+ Function recognizes signer certificates of type\r
+ EFI_CERT_X509_GUID. Any hash certificate in AllowedDb\r
+ list is ignored by this function. Function returns\r
+ success if signer of the buffer is within this list\r
+ (and not within RevokedDb). This parameter is\r
+ required.\r
+ @param[in] RevokedDb Optional pointer to a list of pointers to\r
+ EFI_SIGNATURE_LIST structures. The list is terminated\r
+ by a null pointer. List of X.509 certificates of\r
+ revoked signers and revoked file hashes. Except as\r
+ noted in description of TimeStampDb signature\r
+ verification will always fail if the signer of the\r
+ file or the hash of the data component of the buffer\r
+ is in RevokedDb list. This list is optional and\r
+ caller may pass Null or pointer to NULL if not\r
+ required.\r
+ @param[in] TimeStampDb Optional pointer to a list of pointers to\r
+ EFI_SIGNATURE_LIST structures. The list is terminated\r
+ by a null pointer. This parameter can be used to pass\r
+ a list of X.509 certificates of trusted time stamp\r
+ signers. This list is optional and caller must pass\r
+ Null or pointer to NULL if not required.\r
+ @param[out] Content On input, points to an optional caller-allocated\r
+ buffer into which the function will copy the content\r
+ portion of the file after verification succeeds.\r
+ This parameter is optional and if NULL, no copy of\r
+ content from file is performed.\r
+ @param[in,out] ContentSize On input, points to the size in bytes of the optional\r
+ buffer Content previously allocated by caller. On\r
+ output, if the verification succeeds, the value\r
+ referenced by ContentSize will contain the actual\r
+ size of the content from signed file. If ContentSize\r
+ indicates the caller-allocated buffer is too small\r
+ to contain content, an error is returned, and\r
+ ContentSize will be updated with the required size.\r
+ This parameter must be 0 if Content is Null.\r
+\r
+ @retval EFI_SUCCESS Content signature was verified against hash of\r
+ content, the signer's certificate was not found in\r
+ RevokedDb, and was found in AllowedDb or if in signer\r
+ is found in both AllowedDb and RevokedDb, the\r
+ signing was allowed by reference to TimeStampDb as\r
+ described above, and no hash matching content hash\r
+ was found in RevokedDb.\r
+ @retval EFI_SECURITY_VIOLATION The SignedData buffer was correctly formatted but\r
+ signer was in RevokedDb or not in AllowedDb. Also\r
+ returned if matching content hash found in RevokedDb.\r
+ @retval EFI_COMPROMISED_DATA Calculated hash differs from signed hash.\r
+ @retval EFI_INVALID_PARAMETER SignedData is NULL or SignedDataSize is zero.\r
+ AllowedDb is NULL.\r
+ @retval EFI_INVALID_PARAMETER Content is not NULL and ContentSize is NULL.\r
+ @retval EFI_ABORTED Unsupported or invalid format in TimeStampDb,\r
+ RevokedDb or AllowedDb list contents was detected.\r
+ @retval EFI_NOT_FOUND Content not found because InData is NULL and no\r
+ content embedded in SignedData.\r
+ @retval EFI_UNSUPPORTED The SignedData buffer was not correctly formatted\r
+ for processing by the function.\r
+ @retval EFI_UNSUPPORTED Signed data embedded in SignedData but InData is not\r
+ NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL The size of buffer indicated by ContentSize is too\r
+ small to hold the content. ContentSize updated to\r
+ required size.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VerifyBuffer (\r
+ IN EFI_PKCS7_VERIFY_PROTOCOL *This,\r
+ IN VOID *SignedData,\r
+ IN UINTN SignedDataSize,\r
+ IN VOID *InData OPTIONAL,\r
+ IN UINTN InDataSize,\r
+ IN EFI_SIGNATURE_LIST **AllowedDb,\r
+ IN EFI_SIGNATURE_LIST **RevokedDb OPTIONAL,\r
+ IN EFI_SIGNATURE_LIST **TimeStampDb OPTIONAL,\r
+ OUT VOID *Content OPTIONAL,\r
+ IN OUT UINTN *ContentSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *AttachedData;\r
+ UINTN AttachedDataSize;\r
+ UINT8 *DataPtr;\r
+ UINTN DataSize;\r
+\r
+ //\r
+ // Parameters Checking\r
+ //\r
+ if ((SignedData == NULL) || (SignedDataSize == 0) || (AllowedDb == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if ((Content != NULL) && (ContentSize == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Try to retrieve the attached content from PKCS7 signedData\r
+ //\r
+ AttachedData = NULL;\r
+ AttachedDataSize = 0;\r
+ if (!Pkcs7GetAttachedContent (\r
+ SignedData,\r
+ SignedDataSize,\r
+ (VOID **)&AttachedData,\r
+ &AttachedDataSize)) {\r
+ //\r
+ // The SignedData buffer was not correctly formatted for processing\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ if (AttachedData != NULL) {\r
+ //\r
+ // PKCS7-formatted signedData with attached content; Use the embedded\r
+ // content for verification\r
+ //\r
+ DataPtr = AttachedData;\r
+ DataSize = AttachedDataSize;\r
+\r
+ } else if (InData != NULL) {\r
+ //\r
+ // PKCS7-formatted signedData with detached content; Use the user-supplied\r
+ // input data for verification\r
+ //\r
+ DataPtr = (UINT8 *)InData;\r
+ DataSize = InDataSize;\r
+ } else {\r
+ //\r
+ // Content not found because InData is NULL and no content attached in SignedData\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ goto _Exit;\r
+ }\r
+\r
+ Status = EFI_UNSUPPORTED;\r
+\r
+ //\r
+ // Verify PKCS7 SignedData with Revoked database\r
+ //\r
+ if (RevokedDb != NULL) {\r
+ Status = P7CheckRevocation (\r
+ SignedData,\r
+ SignedDataSize,\r
+ DataPtr,\r
+ DataSize,\r
+ RevokedDb,\r
+ TimeStampDb\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // The PKCS7 SignedData is reovked\r
+ //\r
+ Status = EFI_SECURITY_VIOLATION;\r
+ goto _Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Verify PKCS7 SignedData with AllowedDB\r
+ //\r
+ Status = P7CheckTrust (\r
+ SignedData,\r
+ SignedDataSize,\r
+ DataPtr,\r
+ DataSize,\r
+ AllowedDb\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Verification failed with AllowedDb\r
+ //\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Copy the content portion after verification succeeds\r
+ //\r
+ if (Content != NULL) {\r
+ if (*ContentSize < DataSize) {\r
+ //\r
+ // Caller-allocated buffer is too small to contain content\r
+ //\r
+ *ContentSize = DataSize;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ } else {\r
+ *ContentSize = DataSize;\r
+ CopyMem (Content, DataPtr, DataSize);\r
+ }\r
+ }\r
+\r
+_Exit:\r
+ if (AttachedData != NULL) {\r
+ FreePool (AttachedData);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Processes a buffer containing binary DER-encoded detached PKCS7 signature.\r
+ The hash of the signed data content is calculated and passed by the caller. Function\r
+ verifies the signature of the content is valid and signing certificate was not revoked\r
+ and is contained within a list of trusted signers.\r
+\r
+ @param[in] This Pointer to EFI_PKCS7_VERIFY_PROTOCOL instance.\r
+ @param[in] Signature Points to buffer containing ASN.1 DER-encoded PKCS\r
+ detached signature.\r
+ @param[in] SignatureSize The size of Signature buffer in bytes.\r
+ @param[in] InHash InHash points to buffer containing the caller\r
+ calculated hash of the data. The parameter may not\r
+ be NULL.\r
+ @param[in] InHashSize The size in bytes of InHash buffer.\r
+ @param[in] AllowedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST\r
+ structures. The list is terminated by a null\r
+ pointer. The EFI_SIGNATURE_LIST structures contain\r
+ lists of X.509 certificates of approved signers.\r
+ Function recognizes signer certificates of type\r
+ EFI_CERT_X509_GUID. Any hash certificate in AllowedDb\r
+ list is ignored by this function. Function returns\r
+ success if signer of the buffer is within this list\r
+ (and not within RevokedDb). This parameter is\r
+ required.\r
+ @param[in] RevokedDb Optional pointer to a list of pointers to\r
+ EFI_SIGNATURE_LIST structures. The list is terminated\r
+ by a null pointer. List of X.509 certificates of\r
+ revoked signers and revoked file hashes. Signature\r
+ verification will always fail if the signer of the\r
+ file or the hash of the data component of the buffer\r
+ is in RevokedDb list. This parameter is optional\r
+ and caller may pass Null if not required.\r
+ @param[in] TimeStampDb Optional pointer to a list of pointers to\r
+ EFI_SIGNATURE_LIST structures. The list is terminated\r
+ by a null pointer. This parameter can be used to pass\r
+ a list of X.509 certificates of trusted time stamp\r
+ counter-signers.\r
+\r
+ @retval EFI_SUCCESS Signed hash was verified against caller-provided\r
+ hash of content, the signer's certificate was not\r
+ found in RevokedDb, and was found in AllowedDb or\r
+ if in signer is found in both AllowedDb and\r
+ RevokedDb, the signing was allowed by reference to\r
+ TimeStampDb as described above, and no hash matching\r
+ content hash was found in RevokedDb.\r
+ @retval EFI_SECURITY_VIOLATION The SignedData buffer was correctly formatted but\r
+ signer was in RevokedDb or not in AllowedDb. Also\r
+ returned if matching content hash found in RevokedDb.\r
+ @retval EFI_COMPROMISED_DATA Caller provided hash differs from signed hash. Or,\r
+ caller and encrypted hash are different sizes.\r
+ @retval EFI_INVALID_PARAMETER Signature is NULL or SignatureSize is zero. InHash\r
+ is NULL or InHashSize is zero. AllowedDb is NULL.\r
+ @retval EFI_ABORTED Unsupported or invalid format in TimeStampDb,\r
+ RevokedDb or AllowedDb list contents was detected.\r
+ @retval EFI_UNSUPPORTED The Signature buffer was not correctly formatted\r
+ for processing by the function.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VerifySignature (\r
+ IN EFI_PKCS7_VERIFY_PROTOCOL *This,\r
+ IN VOID *Signature,\r
+ IN UINTN SignatureSize,\r
+ IN VOID *InHash,\r
+ IN UINTN InHashSize,\r
+ IN EFI_SIGNATURE_LIST **AllowedDb,\r
+ IN EFI_SIGNATURE_LIST **RevokedDb OPTIONAL,\r
+ IN EFI_SIGNATURE_LIST **TimeStampDb OPTIONAL\r
+ )\r
+{\r
+ //\r
+ // NOTE: Current EDKII-OpenSSL interface cannot support VerifySignature\r
+ // directly. EFI_UNSUPPORTED is returned in this version.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+//\r
+// The PKCS7 Verification Protocol\r
+//\r
+EFI_PKCS7_VERIFY_PROTOCOL mPkcs7Verify = {\r
+ VerifyBuffer,\r
+ VerifySignature\r
+};\r
+\r
+/**\r
+ The user Entry Point for the PKCS7 Verification driver.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The entry point is executed successfully.\r
+ @retval EFI_NOT_SUPPORTED Platform does not support PKCS7 Verification.\r
+ @retval Other Some error occurs when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Pkcs7VerifyDriverEntry (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+\r
+ //\r
+ // Install UEFI Pkcs7 Verification Protocol\r
+ //\r
+ Handle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEfiPkcs7VerifyProtocolGuid,\r
+ &mPkcs7Verify,\r
+ NULL\r
+ );\r
+\r
+ return Status;\r
+}\r