File pkcs7verify-add-signature.diff of Package OVMF

From a7c4ce23f5aea8c48a2772b6cfac8dab37c0199b Mon Sep 17 00:00:00 2001
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Date: Tue, 23 Feb 2016 15:53:50 -0800
Subject: [PATCH 3/3] Pkcs7VerifyDxe: Preliminary additions for implementing
 VerifySignature

Implement verification by hash in spite of the flaws (if the hash is
the wrong algorithm, revocation by hash will not work).

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 .../Pkcs7Verify/Pkcs7VerifyDxe/Pkcs7VerifyDxe.c    | 378 ++++++++++++++++++++-
 1 file changed, 375 insertions(+), 3 deletions(-)

diff --git a/SecurityPkg/Pkcs7Verify/Pkcs7VerifyDxe/Pkcs7VerifyDxe.c b/SecurityPkg/Pkcs7Verify/Pkcs7VerifyDxe/Pkcs7VerifyDxe.c
index 07fdf55..319e11a 100644
--- a/SecurityPkg/Pkcs7Verify/Pkcs7VerifyDxe/Pkcs7VerifyDxe.c
+++ b/SecurityPkg/Pkcs7Verify/Pkcs7VerifyDxe/Pkcs7VerifyDxe.c
@@ -116,6 +116,81 @@ _Exit:
 /**
   Check whether the hash of data content is revoked by the revocation database.
 
+  @param[in]  Hash          Pointer to the hash that is searched for.
+  @param[in]  HashSize      The size of the hash in bytes.
+  @param[in]  RevokedDb     Pointer to a list of pointers to EFI_SIGNATURE_LIST
+                            structure which contains list of X.509 certificates
+                            of revoked signers and revoked content hashes.
+
+  @return TRUE   The matched content hash is found in the revocation database.
+  @return FALSE  The matched content hash is not found in the revocation database.
+
+**/
+BOOLEAN
+IsContentHashRevokedByHash (
+  IN  UINT8              *Hash,
+  IN  UINTN              HashSize,
+  IN  EFI_SIGNATURE_LIST **RevokedDb
+  )
+{
+  EFI_SIGNATURE_LIST  *SigList;
+  EFI_SIGNATURE_DATA  *SigData;
+  UINTN               Index;
+  UINTN               EntryIndex;
+  UINTN               EntryCount;
+  BOOLEAN             Status;
+
+  if (RevokedDb == NULL) {
+    return FALSE;
+  }
+
+  Status = FALSE;
+  //
+  // Check if any hash matching content hash can be found in RevokedDB
+  //
+  for (Index = 0; ; Index++) {
+    SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);
+
+    //
+    // The list is terminated by a NULL pointer.
+    //
+    if (SigList == NULL) {
+      break;
+    }
+
+    //
+    // Search the signature database to search the revoked content hash
+    //
+    SigData    = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
+                                        SigList->SignatureHeaderSize);
+    EntryCount = (SigList->SignatureListSize - SigList->SignatureHeaderSize -
+                 sizeof (EFI_SIGNATURE_LIST)) / SigList->SignatureSize;
+    for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex++) {
+      //
+      // The problem case.  There's a revocation hash but the sizes
+      // don't match, meaning it's a different hash algorithm and we
+      // can't tell if it's revoking our binary or not.  Assume not.
+      //
+      if (SigList->SignatureSize - sizeof(EFI_GUID) == HashSize) {
+	//
+	// Compare Data Hash with Signature Data
+	//
+	if (CompareMem (SigData->SignatureData, Hash, HashSize) == 0) {
+	  Status = TRUE;
+	  goto _Exit;
+	}
+      }
+
+      SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigData + SigList->SignatureSize);
+    }
+  }
+
+_Exit:
+  return Status;
+}
+/**
+  Check whether the hash of data content is revoked by the revocation database.
+
   @param[in]  Content       Pointer to the content buffer that is searched for.
   @param[in]  ContentSize   The size of data content in bytes.
   @param[in]  RevokedDb     Pointer to a list of pointers to EFI_SIGNATURE_LIST
@@ -449,6 +524,171 @@ IsValidTimestamp (
   @param[in]  SignedData      Pointer to buffer containing ASN.1 DER-encoded PKCS7
                               signature.
   @param[in]  SignedDataSize  The size of SignedData buffer in bytes.
+  @param[in]  InHash          Pointer to the buffer containing the hash of the mesage data
+                              previously signed and to be verified.
+  @param[in]  InHashSize      The size of InHash buffer in bytes.
+  @param[in]  RevokedDb       Pointer to a list of pointers to EFI_SIGNATURE_LIST
+                              structure which contains list of X.509 certificates
+                              of revoked signers and revoked content hashes.
+  @param[in]  TimeStampDb     Pointer to a list of pointers to EFI_SIGNATURE_LIST
+                              structures which is used to pass a list of X.509
+                              certificates of trusted timestamp signers.
+
+  @retval  EFI_SUCCESS             The PKCS7 signedData is revoked.
+  @retval  EFI_SECURITY_VIOLATION  Fail to verify the signature in PKCS7 signedData.
+  @retval  EFI_INVALID_PARAMETER   SignedData is NULL or SignedDataSize is zero.
+                                   AllowedDb is NULL.
+                                   Content is not NULL and ContentSize is NULL.
+  @retval  EFI_NOT_FOUND           Content not found because InData is NULL and no
+                                   content embedded in PKCS7 signedData.
+  @retval  EFI_UNSUPPORTED         The PKCS7 signedData was not correctly formatted.
+
+**/
+EFI_STATUS
+P7CheckRevocationByHash (
+  IN UINT8                *SignedData,
+  IN UINTN                SignedDataSize,
+  IN UINT8                *InHash,
+  IN UINTN                InHashSize,
+  IN EFI_SIGNATURE_LIST   **RevokedDb,
+  IN EFI_SIGNATURE_LIST   **TimeStampDb
+  )
+{
+  EFI_STATUS          Status;
+  EFI_SIGNATURE_LIST  *SigList;
+  EFI_SIGNATURE_DATA  *SigData;
+  UINT8               *RevokedCert;
+  UINTN               RevokedCertSize;
+  UINTN               Index;
+  UINT8               *CertBuffer;
+  UINTN               BufferLength;
+  UINT8               *TrustedCert;
+  UINTN               TrustedCertLength;
+  UINT8               CertNumber;
+  UINT8               *CertPtr;
+  UINT8               *Cert;
+  UINTN               CertSize;
+  EFI_TIME            RevocationTime;
+
+  Status          = EFI_SECURITY_VIOLATION;
+  SigData         = NULL;
+  RevokedCert     = NULL;
+  RevokedCertSize = 0;
+  CertBuffer      = NULL;
+  TrustedCert     = NULL;
+
+  //
+  // The signedData is revoked if the hash of content existed in RevokedDb
+  //
+  if (IsContentHashRevokedByHash (InHash, InHashSize, RevokedDb)) {
+    Status = EFI_SUCCESS;
+    goto _Exit;
+  }
+
+  //
+  // Check if the signer's certificate can be found in Revoked database
+  //
+  for (Index = 0; ; Index++) {
+    SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);
+
+    //
+    // The list is terminated by a NULL pointer.
+    //
+    if (SigList == NULL) {
+      break;
+    }
+
+    //
+    // Ignore any non-X509-format entry in the list.
+    //
+    if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
+      continue;
+    }
+
+    SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
+                                      SigList->SignatureHeaderSize);
+
+    RevokedCert     = SigData->SignatureData;
+    RevokedCertSize = SigList->SignatureSize - sizeof (EFI_GUID);
+
+    //
+    // Verifying the PKCS#7 SignedData with the revoked certificate in RevokedDb
+    //
+    if (AuthenticodeVerify (SignedData, SignedDataSize, RevokedCert, RevokedCertSize, InHash, InHashSize)) {
+      //
+      // The signedData was verified by one entry in Revoked Database
+      //
+      Status = EFI_SUCCESS;
+      break;
+    }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // The signedData was revoked, since it was hit by RevokedDb
+    //
+    goto _Exit;
+  }
+
+  //
+  // Now we will continue to check the X.509 Certificate Hash & Possible Timestamp
+  //
+  if ((TimeStampDb == NULL) || (*TimeStampDb == NULL)) {
+    goto _Exit;
+  }
+
+  Pkcs7GetSigners (SignedData, SignedDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);
+  if ((BufferLength == 0) || (CertBuffer == NULL)) {
+    Status = EFI_SUCCESS;
+    goto _Exit;
+  }
+
+  //
+  // Check if any hash of certificates embedded in P7 data is in the revoked database.
+  //
+  CertNumber = (UINT8) (*CertBuffer);
+  CertPtr    = CertBuffer + 1;
+  for (Index = 0; Index < CertNumber; Index++) {
+    //
+    // Retrieve the Certificate data
+    //
+    CertSize = (UINTN) ReadUnaligned32 ((UINT32 *) CertPtr);
+    Cert     = (UINT8 *)CertPtr + sizeof (UINT32);
+
+    if (IsCertHashRevoked (Cert, CertSize, RevokedDb, &RevocationTime)) {
+      //
+      // Check the timestamp signature and signing time to determine if p7 data can be trusted.
+      //
+      Status = EFI_SUCCESS;
+      if (IsValidTimestamp (SignedData, SignedDataSize, TimeStampDb, &RevocationTime)) {
+        //
+        // Use EFI_NOT_READY to identify the P7Data is not reovked, because the timestamping
+        // occured prior to the time of certificate revocation.
+        //
+        Status = EFI_NOT_READY;
+      }
+
+      goto _Exit;
+    }
+
+    CertPtr = CertPtr + sizeof (UINT32) + CertSize;
+  }
+
+_Exit:
+  Pkcs7FreeSigners (CertBuffer);
+  Pkcs7FreeSigners (TrustedCert);
+
+  return Status;
+}
+
+/**
+  Check whether the PKCS7 signedData is revoked by verifying with the revoked
+  certificates database, and if the signedData is timestamped, the embedded timestamp
+  couterSignature will be checked with the supplied timestamp database.
+
+  @param[in]  SignedData      Pointer to buffer containing ASN.1 DER-encoded PKCS7
+                              signature.
+  @param[in]  SignedDataSize  The size of SignedData buffer in bytes.
   @param[in]  InData          Pointer to the buffer containing the raw message data
                               previously signed and to be verified.
   @param[in]  InDataSize      The size of InData buffer in bytes.
@@ -613,6 +853,100 @@ _Exit:
   @param[in]  SignedData      Pointer to buffer containing ASN.1 DER-encoded PKCS7
                               signature.
   @param[in]  SignedDataSize  The size of SignedData buffer in bytes.
+  @param[in]  InHash          Pointer to the buffer containing the hash of the message data
+                              previously signed and to be verified.
+  @param[in]  InHashSize      The size of InHash buffer in bytes.
+  @param[in]  AllowedDb       Pointer to a list of pointers to EFI_SIGNATURE_LIST
+                              structures which contains lists of X.509 certificates
+                              of approved signers.
+
+  @retval  EFI_SUCCESS             The PKCS7 signedData is trusted.
+  @retval  EFI_SECURITY_VIOLATION  Fail to verify the signature in PKCS7 signedData.
+  @retval  EFI_INVALID_PARAMETER   SignedData is NULL or SignedDataSize is zero.
+                                   AllowedDb is NULL.
+                                   Content is not NULL and ContentSize is NULL.
+  @retval  EFI_NOT_FOUND           Content not found because InData is NULL and no
+                                   content embedded in PKCS7 signedData.
+  @retval  EFI_UNSUPPORTED         The PKCS7 signedData was not correctly formatted.
+  @retval  EFI_BUFFER_TOO_SMALL    The size of buffer indicated by ContentSize is too
+                                   small to hold the content. ContentSize updated to
+                                   the required size.
+
+**/
+EFI_STATUS
+P7CheckTrustByHash (
+  IN UINT8               *SignedData,
+  IN UINTN               SignedDataSize,
+  IN UINT8               *InHash,
+  IN UINTN               InHashSize,
+  IN EFI_SIGNATURE_LIST  **AllowedDb
+  )
+{
+  EFI_STATUS          Status;
+  EFI_SIGNATURE_LIST  *SigList;
+  EFI_SIGNATURE_DATA  *SigData;
+  UINT8               *TrustCert;
+  UINTN               TrustCertSize;
+  UINTN               Index;
+
+  Status        = EFI_SECURITY_VIOLATION;
+  SigData       = NULL;
+  TrustCert     = NULL;
+  TrustCertSize = 0;
+
+  if (AllowedDb == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Build Certificate Stack with all valid X509 certificates in the supplied
+  // Signature List for PKCS7 Verification.
+  //
+  for (Index = 0; ; Index++) {
+    SigList = (EFI_SIGNATURE_LIST *)(AllowedDb[Index]);
+
+    //
+    // The list is terminated by a NULL pointer.
+    //
+    if (SigList == NULL) {
+      break;
+    }
+
+    //
+    // Ignore any non-X509-format entry in the list.
+    //
+    if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
+      continue;
+    }
+
+    SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
+                                      SigList->SignatureHeaderSize);
+
+    TrustCert     = SigData->SignatureData;
+    TrustCertSize = SigList->SignatureSize - sizeof (EFI_GUID);
+
+    //
+    // Verifying the PKCS#7 SignedData with the trusted certificate from AllowedDb
+    //
+    if (AuthenticodeVerify (SignedData, SignedDataSize, TrustCert, TrustCertSize, InHash, InHashSize)) {
+      //
+      // The SignedData was verified successfully by one entry in Trusted Database
+      //
+      Status = EFI_SUCCESS;
+      break;
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Check whether the PKCS7 signedData can be verified by the trusted certificates
+  database, and return the content of the signedData if requested.
+
+  @param[in]  SignedData      Pointer to buffer containing ASN.1 DER-encoded PKCS7
+                              signature.
+  @param[in]  SignedDataSize  The size of SignedData buffer in bytes.
   @param[in]  InData          Pointer to the buffer containing the raw message data
                               previously signed and to be verified.
   @param[in]  InDataSize      The size of InData buffer in bytes.
@@ -997,11 +1331,49 @@ VerifySignature (
   IN EFI_SIGNATURE_LIST           **TimeStampDb     OPTIONAL
   )
 {
+  EFI_STATUS  Status;
+
+  //
+  // Parameters Checking
   //
-  // NOTE: Current EDKII-OpenSSL interface cannot support VerifySignature
-  //       directly. EFI_UNSUPPORTED is returned in this version.
+  if ((Signature == NULL) || (SignatureSize == 0) || (AllowedDb == NULL)
+      || (InHash == NULL) || (InHashSize == 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
   //
-  return EFI_UNSUPPORTED;
+  // Verify PKCS7 SignedData with Revoked database
+  //
+  if (RevokedDb != NULL) {
+    Status = P7CheckRevocationByHash (
+               Signature,
+               SignatureSize,
+               InHash,
+               InHashSize,
+               RevokedDb,
+               TimeStampDb
+               );
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // The PKCS7 SignedData is reovked
+      //
+      return EFI_SECURITY_VIOLATION;
+    }
+  }
+
+  //
+  // Verify PKCS7 SignedData with AllowedDB
+  //
+  Status = P7CheckTrustByHash (
+             Signature,
+             SignatureSize,
+             InHash,
+             InHashSize,
+             AllowedDb
+             );
+
+  return Status;
 }
 
 //
-- 
2.6.6