]> git.proxmox.com Git - mirror_edk2.git/commitdiff
SecurityPkg/Pkcs7Verify: Complete the Pkcs7VerifyDxe protocol
authorLong Qin <qin.long@intel.com>
Tue, 5 Sep 2017 07:46:21 +0000 (15:46 +0800)
committerLong Qin <qin.long@intel.com>
Tue, 5 Sep 2017 07:47:31 +0000 (15:47 +0800)
VerifySignature can be implemented using a mirror of the
AuthenticodeVerify function that's already in use in the
ImageVerificationDXE environment, so this patch simply wires up
VerifySignature using that code.
<NOTE: Only Authenticode-signature verification was supported by
       this VerifySignature() implementation now.)

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: Long Qin <qin.long@intel.com>
SecurityPkg/Pkcs7Verify/Pkcs7VerifyDxe/Pkcs7VerifyDxe.c

index 3776f903d4429973cb7788ce2a83831e6e515618..d9013212c137b9f6daaa166080b816d3f022bce1 100644 (file)
@@ -113,6 +113,82 @@ _Exit:
   return Status;\r
 }\r
 \r
+/**\r
+  Check whether the hash of data content is revoked by the revocation database.\r
+\r
+  @param[in]  Hash          Pointer to the hash that is searched for.\r
+  @param[in]  HashSize      The size of the hash 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
+IsContentHashRevokedByHash (\r
+  IN  UINT8              *Hash,\r
+  IN  UINTN              HashSize,\r
+  IN  EFI_SIGNATURE_LIST **RevokedDb\r
+  )\r
+{\r
+  EFI_SIGNATURE_LIST  *SigList;\r
+  EFI_SIGNATURE_DATA  *SigData;\r
+  UINTN               Index;\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
+    // 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
+      // The problem case.  There's a revocation hash but the sizes\r
+      // don't match, meaning it's a different hash algorithm and we\r
+      // can't tell if it's revoking our binary or not.  Assume not.\r
+      //\r
+      if (SigList->SignatureSize - sizeof(EFI_GUID) == HashSize) {\r
+        //\r
+        // Compare Data Hash with Signature Data\r
+        //\r
+        if (CompareMem (SigData->SignatureData, Hash, HashSize) == 0) {\r
+          Status = TRUE;\r
+          goto _Exit;\r
+        }\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 data content is revoked by the revocation database.\r
 \r
@@ -441,6 +517,171 @@ IsValidTimestamp (
   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]  InHash          Pointer to the buffer containing the hash of the mesage data\r
+                              previously signed and to be verified.\r
+  @param[in]  InHashSize      The size of InHash 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
+P7CheckRevocationByHash (\r
+  IN UINT8                *SignedData,\r
+  IN UINTN                SignedDataSize,\r
+  IN UINT8                *InHash,\r
+  IN UINTN                InHashSize,\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_SECURITY_VIOLATION;\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 (IsContentHashRevokedByHash (InHash, InHashSize, 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 (AuthenticodeVerify (SignedData, SignedDataSize, RevokedCert, RevokedCertSize, InHash, InHashSize)) {\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 is revoked by verifying with the revoked\r
   certificates database, and if the signedData is timestamped, the embedded timestamp\r
@@ -606,6 +847,100 @@ _Exit:
   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]  InHash          Pointer to the buffer containing the hash of the message data\r
+                              previously signed and to be verified.\r
+  @param[in]  InHashSize      The size of InHash 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
+\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
+P7CheckTrustByHash (\r
+  IN UINT8               *SignedData,\r
+  IN UINTN               SignedDataSize,\r
+  IN UINT8               *InHash,\r
+  IN UINTN               InHashSize,\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 (AuthenticodeVerify (SignedData, SignedDataSize, TrustCert, TrustCertSize, InHash, InHashSize)) {\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
   Check whether the PKCS7 signedData can be verified by the trusted certificates\r
   database, and return the content of the signedData if requested.\r
@@ -1051,11 +1386,49 @@ VerifySignature (
   IN EFI_SIGNATURE_LIST           **TimeStampDb     OPTIONAL\r
   )\r
 {\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Parameters Checking\r
   //\r
-  // NOTE: Current EDKII-OpenSSL interface cannot support VerifySignature\r
-  //       directly. EFI_UNSUPPORTED is returned in this version.\r
+  if ((Signature == NULL) || (SignatureSize == 0) || (AllowedDb == NULL)\r
+      || (InHash == NULL) || (InHashSize == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
   //\r
-  return EFI_UNSUPPORTED;\r
+  // Verify PKCS7 SignedData with Revoked database\r
+  //\r
+  if (RevokedDb != NULL) {\r
+    Status = P7CheckRevocationByHash (\r
+               Signature,\r
+               SignatureSize,\r
+               InHash,\r
+               InHashSize,\r
+               RevokedDb,\r
+               TimeStampDb\r
+               );\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // The PKCS7 SignedData is reovked\r
+      //\r
+      return EFI_SECURITY_VIOLATION;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Verify PKCS7 SignedData with AllowedDB\r
+  //\r
+  Status = P7CheckTrustByHash (\r
+             Signature,\r
+             SignatureSize,\r
+             InHash,\r
+             InHashSize,\r
+             AllowedDb\r
+             );\r
+\r
+  return Status;\r
 }\r
 \r
 //\r