Home | History | Annotate | Download | only in Pkcs7VerifyDxe
      1 /** @file
      2   Pkcs7Verify Driver to produce the UEFI PKCS7 Verification Protocol.
      3 
      4   The driver will produce the UEFI PKCS7 Verification Protocol which is used to
      5   verify data signed using PKCS7 structure. The PKCS7 data to be verified must
      6   be ASN.1 (DER) encoded.
      7 
      8 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
      9 This program and the accompanying materials
     10 are licensed and made available under the terms and conditions of the BSD License
     11 which accompanies this distribution.  The full text of the license may be found at
     12 http://opensource.org/licenses/bsd-license.php
     13 
     14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     16 
     17 **/
     18 
     19 #include <Library/BaseLib.h>
     20 #include <Library/BaseMemoryLib.h>
     21 #include <Library/MemoryAllocationLib.h>
     22 #include <Library/UefiBootServicesTableLib.h>
     23 #include <Library/BaseCryptLib.h>
     24 #include <Protocol/Pkcs7Verify.h>
     25 
     26 #define MAX_DIGEST_SIZE  SHA512_DIGEST_SIZE
     27 
     28 /**
     29   Calculates the hash of the given data based on the specified hash GUID.
     30 
     31   @param[in]  Data      Pointer to the data buffer to be hashed.
     32   @param[in]  DataSize  The size of data buffer in bytes.
     33   @param[in]  CertGuid  The GUID to identify the hash algorithm to be used.
     34   @param[out] HashValue Pointer to a buffer that receives the hash result.
     35 
     36   @retval TRUE          Data hash calculation succeeded.
     37   @retval FALSE         Data hash calculation failed.
     38 
     39 **/
     40 BOOLEAN
     41 CalculateDataHash (
     42   IN  VOID               *Data,
     43   IN  UINTN              DataSize,
     44   IN  EFI_GUID           *CertGuid,
     45   OUT UINT8              *HashValue
     46   )
     47 {
     48   BOOLEAN  Status;
     49   VOID     *HashCtx;
     50   UINTN    CtxSize;
     51 
     52   Status  = FALSE;
     53   HashCtx = NULL;
     54 
     55   if (CompareGuid (CertGuid, &gEfiCertSha1Guid)) {
     56     //
     57     // SHA-1 Hash
     58     //
     59     CtxSize = Sha1GetContextSize ();
     60     HashCtx = AllocatePool (CtxSize);
     61     if (HashCtx == NULL) {
     62       goto _Exit;
     63     }
     64     Status = Sha1Init   (HashCtx);
     65     Status = Sha1Update (HashCtx, Data, DataSize);
     66     Status = Sha1Final  (HashCtx, HashValue);
     67 
     68   } else if (CompareGuid (CertGuid, &gEfiCertSha256Guid)) {
     69     //
     70     // SHA256 Hash
     71     //
     72     CtxSize = Sha256GetContextSize ();
     73     HashCtx = AllocatePool (CtxSize);
     74     if (HashCtx == NULL) {
     75       goto _Exit;
     76     }
     77     Status = Sha256Init   (HashCtx);
     78     Status = Sha256Update (HashCtx, Data, DataSize);
     79     Status = Sha256Final  (HashCtx, HashValue);
     80 
     81   } else if (CompareGuid (CertGuid, &gEfiCertSha384Guid)) {
     82     //
     83     // SHA384 Hash
     84     //
     85     CtxSize = Sha384GetContextSize ();
     86     HashCtx = AllocatePool (CtxSize);
     87     if (HashCtx == NULL) {
     88       goto _Exit;
     89     }
     90     Status = Sha384Init   (HashCtx);
     91     Status = Sha384Update (HashCtx, Data, DataSize);
     92     Status = Sha384Final  (HashCtx, HashValue);
     93 
     94   } else if (CompareGuid (CertGuid, &gEfiCertSha512Guid)) {
     95     //
     96     // SHA512 Hash
     97     //
     98     CtxSize = Sha512GetContextSize ();
     99     HashCtx = AllocatePool (CtxSize);
    100     if (HashCtx == NULL) {
    101       goto _Exit;
    102     }
    103     Status = Sha512Init   (HashCtx);
    104     Status = Sha512Update (HashCtx, Data, DataSize);
    105     Status = Sha512Final  (HashCtx, HashValue);
    106   }
    107 
    108 _Exit:
    109   if (HashCtx != NULL) {
    110     FreePool (HashCtx);
    111   }
    112 
    113   return Status;
    114 }
    115 
    116 /**
    117   Check whether the hash of data content is revoked by the revocation database.
    118 
    119   @param[in]  Content       Pointer to the content buffer that is searched for.
    120   @param[in]  ContentSize   The size of data content in bytes.
    121   @param[in]  RevokedDb     Pointer to a list of pointers to EFI_SIGNATURE_LIST
    122                             structure which contains list of X.509 certificates
    123                             of revoked signers and revoked content hashes.
    124 
    125   @return TRUE   The matched content hash is found in the revocation database.
    126   @return FALSE  The matched content hash is not found in the revocation database.
    127 
    128 **/
    129 BOOLEAN
    130 IsContentHashRevoked (
    131   IN  UINT8              *Content,
    132   IN  UINTN              ContentSize,
    133   IN  EFI_SIGNATURE_LIST **RevokedDb
    134   )
    135 {
    136   EFI_SIGNATURE_LIST  *SigList;
    137   EFI_SIGNATURE_DATA  *SigData;
    138   UINTN               Index;
    139   UINT8               HashVal[MAX_DIGEST_SIZE];
    140   UINTN               EntryIndex;
    141   UINTN               EntryCount;
    142   BOOLEAN             Status;
    143 
    144   if (RevokedDb == NULL) {
    145     return FALSE;
    146   }
    147 
    148   Status = FALSE;
    149   //
    150   // Check if any hash matching content hash can be found in RevokedDB
    151   //
    152   for (Index = 0; ; Index++) {
    153     SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);
    154 
    155     //
    156     // The list is terminated by a NULL pointer.
    157     //
    158     if (SigList == NULL) {
    159       break;
    160     }
    161 
    162     //
    163     // Calculate the digest of supplied data based on the signature hash type.
    164     //
    165     if (!CalculateDataHash (Content, ContentSize, &SigList->SignatureType, HashVal)) {
    166       //
    167       // Un-matched Hash GUID or other failure.
    168       //
    169       continue;
    170     }
    171 
    172     //
    173     // Search the signature database to search the revoked content hash
    174     //
    175     SigData    = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
    176                                         SigList->SignatureHeaderSize);
    177     EntryCount = (SigList->SignatureListSize - SigList->SignatureHeaderSize -
    178                  sizeof (EFI_SIGNATURE_LIST)) / SigList->SignatureSize;
    179     for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex++) {
    180       //
    181       // Compare Data Hash with Signature Data
    182       //
    183       if (CompareMem (SigData->SignatureData, HashVal, (SigList->SignatureSize - sizeof (EFI_GUID))) == 0) {
    184         Status = TRUE;
    185         goto _Exit;
    186       }
    187 
    188       SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigData + SigList->SignatureSize);
    189     }
    190   }
    191 
    192 _Exit:
    193   return Status;
    194 }
    195 
    196 /**
    197   Check whether the hash of an given certificate is revoked by the revocation database.
    198 
    199   @param[in]  Certificate     Pointer to the certificate that is searched for.
    200   @param[in]  CertSize        Size of certificate in bytes.
    201   @param[in]  RevokedDb       Pointer to a list of pointers to EFI_SIGNATURE_LIST
    202                               structures which contains list of X.509 certificate
    203                               of revoked signers and revoked content hashes.
    204   @param[out] RevocationTime  Return the time that the certificate was revoked.
    205 
    206   @return TRUE   The certificate hash is found in the revocation database.
    207   @return FALSE  The certificate hash is not found in the revocation database.
    208 
    209 **/
    210 BOOLEAN
    211 IsCertHashRevoked (
    212   IN  UINT8              *Certificate,
    213   IN  UINTN              CertSize,
    214   IN  EFI_SIGNATURE_LIST **RevokedDb,
    215   OUT EFI_TIME           *RevocationTime
    216   )
    217 {
    218   BOOLEAN             Status;
    219   EFI_SIGNATURE_LIST  *SigList;
    220   EFI_SIGNATURE_DATA  *SigData;
    221   UINT8               *TBSCert;
    222   UINTN               TBSCertSize;
    223   UINTN               Index;
    224   UINTN               EntryIndex;
    225   UINTN               EntryCount;
    226   UINT8               CertHashVal[MAX_DIGEST_SIZE];
    227 
    228   if ((RevocationTime == NULL) || (RevokedDb == NULL)) {
    229     return FALSE;
    230   }
    231 
    232   //
    233   // Retrieve the TBSCertificate from the X.509 Certificate for hash calculation
    234   //
    235   if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {
    236     return FALSE;
    237   }
    238 
    239   Status = FALSE;
    240   for (Index = 0; ; Index++) {
    241 
    242     SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);
    243     //
    244     // The list is terminated by a NULL pointer.
    245     //
    246     if (SigList == NULL) {
    247       break;
    248     }
    249 
    250     //
    251     // Determine Hash Algorithm based on the entry type in revocation database, and
    252     // calculate the certificate hash.
    253     //
    254     if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Sha256Guid)) {
    255       Status = CalculateDataHash (TBSCert, TBSCertSize, &gEfiCertSha256Guid, CertHashVal);
    256 
    257     } else if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Sha384Guid)) {
    258       Status = CalculateDataHash (TBSCert, TBSCertSize, &gEfiCertSha384Guid, CertHashVal);
    259 
    260     } else if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Sha512Guid)) {
    261       Status = CalculateDataHash (TBSCert, TBSCertSize, &gEfiCertSha512Guid, CertHashVal);
    262 
    263     } else {
    264       //
    265       // Un-matched Cert Hash GUID
    266       //
    267       continue;
    268     }
    269 
    270     if (!Status) {
    271       continue;
    272     }
    273 
    274     SigData    = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
    275                                       SigList->SignatureHeaderSize);
    276     EntryCount = (SigList->SignatureListSize - SigList->SignatureHeaderSize -
    277                   sizeof (EFI_SIGNATURE_LIST)) / SigList->SignatureSize;
    278     for (EntryIndex = 0; EntryIndex < EntryCount; Index++) {
    279       //
    280       // Check if the Certificate Hash is revoked.
    281       //
    282       if (CompareMem (SigData->SignatureData, CertHashVal,
    283                       SigList->SignatureSize - sizeof (EFI_GUID) - sizeof (EFI_TIME)) == 0) {
    284         Status = TRUE;
    285         //
    286         // Return the revocation time of this revoked certificate.
    287         //
    288         CopyMem (
    289           RevocationTime,
    290           (EFI_TIME *)((UINT8 *)SigData + SigList->SignatureSize - sizeof (EFI_TIME)),
    291           sizeof (EFI_TIME)
    292           );
    293         goto _Exit;
    294       }
    295 
    296       SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigData + SigList->SignatureSize);
    297     }
    298   }
    299 
    300 _Exit:
    301   return Status;
    302 }
    303 
    304 /**
    305   Check if the given time value is zero.
    306 
    307   @param[in]  Time      Pointer of a time value.
    308 
    309   @retval     TRUE      The Time is Zero.
    310   @retval     FALSE     The Time is not Zero.
    311 
    312 **/
    313 BOOLEAN
    314 IsTimeZero (
    315   IN EFI_TIME            *Time
    316   )
    317 {
    318   if ((Time->Year == 0) && (Time->Month == 0) &&  (Time->Day == 0) &&
    319       (Time->Hour == 0) && (Time->Minute == 0) && (Time->Second == 0)) {
    320     return TRUE;
    321   }
    322 
    323   return FALSE;
    324 }
    325 
    326 /**
    327   Check whether the timestamp is valid by comparing the signing time and the revocation time.
    328 
    329   @param SigningTime     Pointer to the signing time.
    330   @param RevocationTime  Pointer to the revocation time.
    331 
    332   @retval  TRUE          The SigningTime is not later than the RevocationTime.
    333   @retval  FALSE         The SigningTime is later than the RevocationTime.
    334 
    335 **/
    336 BOOLEAN
    337 CompareTimestamp (
    338   IN EFI_TIME            *SigningTime,
    339   IN EFI_TIME            *RevocationTime
    340   )
    341 {
    342   if (SigningTime->Year != RevocationTime->Year) {
    343     return (BOOLEAN) (SigningTime->Year < RevocationTime->Year);
    344   } else if (SigningTime->Month != RevocationTime->Month) {
    345     return (BOOLEAN) (SigningTime->Month < RevocationTime->Month);
    346   } else if (SigningTime->Day != RevocationTime->Day) {
    347     return (BOOLEAN) (SigningTime->Day < RevocationTime->Day);
    348   } else if (SigningTime->Hour != RevocationTime->Hour) {
    349     return (BOOLEAN) (SigningTime->Hour < RevocationTime->Hour);
    350   } else if (SigningTime->Minute != RevocationTime->Minute) {
    351     return (BOOLEAN) (SigningTime->Minute < RevocationTime->Minute);
    352   }
    353 
    354   return (BOOLEAN) (SigningTime->Second <= RevocationTime->Second);
    355 }
    356 
    357 /**
    358   Check whether the timestamp signature embedded in PKCS7 signedData is valid and
    359   the signing time is also earlier than the revocation time.
    360 
    361   @param[in]  SignedData        Pointer to the PKCS#7 signedData.
    362   @param[in]  SignedDataSize    Size of SignedData in bytes.
    363   @param[in]  TimeStampDb       Pointer to a list of pointers to EFI_SIGNATURE_LIST
    364                                 structures which is used to pass a list of X.509
    365                                 certificates of trusted timestamp signers.
    366   @param[in]  RevocationTime    The time that the certificate was revoked.
    367 
    368   @retval TRUE      Timestamp signature is valid and the signing time is no later
    369                     than the revocation time.
    370   @retval FALSE     Timestamp signature is not valid or the signing time is later
    371                     than the revocation time.
    372 
    373 **/
    374 BOOLEAN
    375 IsValidTimestamp (
    376   IN UINT8               *SignedData,
    377   IN UINTN               SignedDataSize,
    378   IN EFI_SIGNATURE_LIST  **TimeStampDb,
    379   IN EFI_TIME            *RevocationTime
    380   )
    381 {
    382   BOOLEAN             Status;
    383   EFI_SIGNATURE_LIST  *SigList;
    384   EFI_SIGNATURE_DATA  *SigData;
    385   UINT8               *TsaCert;
    386   UINTN               TsaCertSize;
    387   UINTN               Index;
    388   EFI_TIME            SigningTime;
    389 
    390   //
    391   // If no supplied database for verification or RevocationTime is zero,
    392   // the certificate shall be considered to always be revoked.
    393   //
    394   if ((TimeStampDb == NULL) || (IsTimeZero (RevocationTime))) {
    395     return FALSE;
    396   }
    397 
    398   Status = FALSE;
    399   //
    400   // RevocationTime is non-zero, the certificate should be considered to be revoked
    401   // from that time and onwards.
    402   //
    403   for (Index = 0; ; Index++) {
    404     SigList = (EFI_SIGNATURE_LIST *) (TimeStampDb[Index]);
    405 
    406     //
    407     // The list is terminated by a NULL pointer.
    408     //
    409     if (SigList == NULL) {
    410       break;
    411     }
    412 
    413     //
    414     // Ignore any non-X509-format entry in the list
    415     //
    416     if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
    417       continue;
    418     }
    419 
    420 
    421     SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
    422                                       SigList->SignatureHeaderSize);
    423     TsaCert     = SigData->SignatureData;
    424     TsaCertSize = SigList->SignatureSize - sizeof (EFI_GUID);
    425 
    426     //
    427     // Each TSA Certificate will normally be in a seperate EFI_SIGNATURE_LIST
    428     // Leverage ImageTimestampVerify interface for Timestamp counterSignature Verification
    429     //
    430     if (ImageTimestampVerify (SignedData, SignedDataSize, TsaCert, TsaCertSize, &SigningTime)) {
    431       //
    432       // The signer signature is valid only when the signing time is earlier than revocation time.
    433       //
    434       if (CompareTimestamp (&SigningTime, RevocationTime)) {
    435         Status = TRUE;
    436         break;
    437       }
    438     }
    439   }
    440 
    441   return Status;
    442 }
    443 
    444 /**
    445   Check whether the PKCS7 signedData is revoked by verifying with the revoked
    446   certificates database, and if the signedData is timestamped, the embedded timestamp
    447   couterSignature will be checked with the supplied timestamp database.
    448 
    449   @param[in]  SignedData      Pointer to buffer containing ASN.1 DER-encoded PKCS7
    450                               signature.
    451   @param[in]  SignedDataSize  The size of SignedData buffer in bytes.
    452   @param[in]  InData          Pointer to the buffer containing the raw message data
    453                               previously signed and to be verified.
    454   @param[in]  InDataSize      The size of InData buffer in bytes.
    455   @param[in]  RevokedDb       Pointer to a list of pointers to EFI_SIGNATURE_LIST
    456                               structure which contains list of X.509 certificates
    457                               of revoked signers and revoked content hashes.
    458   @param[in]  TimeStampDb     Pointer to a list of pointers to EFI_SIGNATURE_LIST
    459                               structures which is used to pass a list of X.509
    460                               certificates of trusted timestamp signers.
    461 
    462   @retval  EFI_SUCCESS             The PKCS7 signedData is revoked.
    463   @retval  EFI_SECURITY_VIOLATION  Fail to verify the signature in PKCS7 signedData.
    464   @retval  EFI_INVALID_PARAMETER   SignedData is NULL or SignedDataSize is zero.
    465                                    AllowedDb is NULL.
    466                                    Content is not NULL and ContentSize is NULL.
    467   @retval  EFI_NOT_FOUND           Content not found because InData is NULL and no
    468                                    content embedded in PKCS7 signedData.
    469   @retval  EFI_UNSUPPORTED         The PKCS7 signedData was not correctly formatted.
    470 
    471 **/
    472 EFI_STATUS
    473 P7CheckRevocation (
    474   IN UINT8                *SignedData,
    475   IN UINTN                SignedDataSize,
    476   IN UINT8                *InData,
    477   IN UINTN                InDataSize,
    478   IN EFI_SIGNATURE_LIST   **RevokedDb,
    479   IN EFI_SIGNATURE_LIST   **TimeStampDb
    480   )
    481 {
    482   EFI_STATUS          Status;
    483   EFI_SIGNATURE_LIST  *SigList;
    484   EFI_SIGNATURE_DATA  *SigData;
    485   UINT8               *RevokedCert;
    486   UINTN               RevokedCertSize;
    487   UINTN               Index;
    488   UINT8               *CertBuffer;
    489   UINTN               BufferLength;
    490   UINT8               *TrustedCert;
    491   UINTN               TrustedCertLength;
    492   UINT8               CertNumber;
    493   UINT8               *CertPtr;
    494   UINT8               *Cert;
    495   UINTN               CertSize;
    496   EFI_TIME            RevocationTime;
    497 
    498   Status          = EFI_UNSUPPORTED;
    499   SigData         = NULL;
    500   RevokedCert     = NULL;
    501   RevokedCertSize = 0;
    502   CertBuffer      = NULL;
    503   TrustedCert     = NULL;
    504 
    505   //
    506   // The signedData is revoked if the hash of content existed in RevokedDb
    507   //
    508   if (IsContentHashRevoked (InData, InDataSize, RevokedDb)) {
    509     Status = EFI_SUCCESS;
    510     goto _Exit;
    511   }
    512 
    513   //
    514   // Check if the signer's certificate can be found in Revoked database
    515   //
    516   for (Index = 0; ; Index++) {
    517     SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);
    518 
    519     //
    520     // The list is terminated by a NULL pointer.
    521     //
    522     if (SigList == NULL) {
    523       break;
    524     }
    525 
    526     //
    527     // Ignore any non-X509-format entry in the list.
    528     //
    529     if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
    530       continue;
    531     }
    532 
    533     SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
    534                                       SigList->SignatureHeaderSize);
    535 
    536     RevokedCert     = SigData->SignatureData;
    537     RevokedCertSize = SigList->SignatureSize - sizeof (EFI_GUID);
    538 
    539     //
    540     // Verifying the PKCS#7 SignedData with the revoked certificate in RevokedDb
    541     //
    542     if (Pkcs7Verify (SignedData, SignedDataSize, RevokedCert, RevokedCertSize, InData, InDataSize)) {
    543       //
    544       // The signedData was verified by one entry in Revoked Database
    545       //
    546       Status = EFI_SUCCESS;
    547       break;
    548     }
    549   }
    550 
    551   if (!EFI_ERROR (Status)) {
    552     //
    553     // The signedData was revoked, since it was hit by RevokedDb
    554     //
    555     goto _Exit;
    556   }
    557 
    558   //
    559   // Now we will continue to check the X.509 Certificate Hash & Possible Timestamp
    560   //
    561   if ((TimeStampDb == NULL) || (*TimeStampDb == NULL)) {
    562     goto _Exit;
    563   }
    564 
    565   Pkcs7GetSigners (SignedData, SignedDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);
    566   if ((BufferLength == 0) || (CertBuffer == NULL)) {
    567     Status = EFI_SUCCESS;
    568     goto _Exit;
    569   }
    570 
    571   //
    572   // Check if any hash of certificates embedded in P7 data is in the revoked database.
    573   //
    574   CertNumber = (UINT8) (*CertBuffer);
    575   CertPtr    = CertBuffer + 1;
    576   for (Index = 0; Index < CertNumber; Index++) {
    577     //
    578     // Retrieve the Certificate data
    579     //
    580     CertSize = (UINTN) ReadUnaligned32 ((UINT32 *) CertPtr);
    581     Cert     = (UINT8 *)CertPtr + sizeof (UINT32);
    582 
    583     if (IsCertHashRevoked (Cert, CertSize, RevokedDb, &RevocationTime)) {
    584       //
    585       // Check the timestamp signature and signing time to determine if p7 data can be trusted.
    586       //
    587       Status = EFI_SUCCESS;
    588       if (IsValidTimestamp (SignedData, SignedDataSize, TimeStampDb, &RevocationTime)) {
    589         //
    590         // Use EFI_NOT_READY to identify the P7Data is not reovked, because the timestamping
    591         // occured prior to the time of certificate revocation.
    592         //
    593         Status = EFI_NOT_READY;
    594       }
    595 
    596       goto _Exit;
    597     }
    598 
    599     CertPtr = CertPtr + sizeof (UINT32) + CertSize;
    600   }
    601 
    602 _Exit:
    603   Pkcs7FreeSigners (CertBuffer);
    604   Pkcs7FreeSigners (TrustedCert);
    605 
    606   return Status;
    607 }
    608 
    609 /**
    610   Check whether the PKCS7 signedData can be verified by the trusted certificates
    611   database, and return the content of the signedData if requested.
    612 
    613   @param[in]  SignedData      Pointer to buffer containing ASN.1 DER-encoded PKCS7
    614                               signature.
    615   @param[in]  SignedDataSize  The size of SignedData buffer in bytes.
    616   @param[in]  InData          Pointer to the buffer containing the raw message data
    617                               previously signed and to be verified.
    618   @param[in]  InDataSize      The size of InData buffer in bytes.
    619   @param[in]  AllowedDb       Pointer to a list of pointers to EFI_SIGNATURE_LIST
    620                               structures which contains lists of X.509 certificates
    621                               of approved signers.
    622 
    623   @retval  EFI_SUCCESS             The PKCS7 signedData is trusted.
    624   @retval  EFI_SECURITY_VIOLATION  Fail to verify the signature in PKCS7 signedData.
    625   @retval  EFI_INVALID_PARAMETER   SignedData is NULL or SignedDataSize is zero.
    626                                    AllowedDb is NULL.
    627                                    Content is not NULL and ContentSize is NULL.
    628   @retval  EFI_NOT_FOUND           Content not found because InData is NULL and no
    629                                    content embedded in PKCS7 signedData.
    630   @retval  EFI_UNSUPPORTED         The PKCS7 signedData was not correctly formatted.
    631   @retval  EFI_BUFFER_TOO_SMALL    The size of buffer indicated by ContentSize is too
    632                                    small to hold the content. ContentSize updated to
    633                                    the required size.
    634 
    635 **/
    636 EFI_STATUS
    637 P7CheckTrust (
    638   IN UINT8               *SignedData,
    639   IN UINTN               SignedDataSize,
    640   IN UINT8               *InData,
    641   IN UINTN               InDataSize,
    642   IN EFI_SIGNATURE_LIST  **AllowedDb
    643   )
    644 {
    645   EFI_STATUS          Status;
    646   EFI_SIGNATURE_LIST  *SigList;
    647   EFI_SIGNATURE_DATA  *SigData;
    648   UINT8               *TrustCert;
    649   UINTN               TrustCertSize;
    650   UINTN               Index;
    651 
    652   Status        = EFI_SECURITY_VIOLATION;
    653   SigData       = NULL;
    654   TrustCert     = NULL;
    655   TrustCertSize = 0;
    656 
    657   if (AllowedDb == NULL) {
    658     return EFI_INVALID_PARAMETER;
    659   }
    660 
    661   //
    662   // Build Certificate Stack with all valid X509 certificates in the supplied
    663   // Signature List for PKCS7 Verification.
    664   //
    665   for (Index = 0; ; Index++) {
    666     SigList = (EFI_SIGNATURE_LIST *)(AllowedDb[Index]);
    667 
    668     //
    669     // The list is terminated by a NULL pointer.
    670     //
    671     if (SigList == NULL) {
    672       break;
    673     }
    674 
    675     //
    676     // Ignore any non-X509-format entry in the list.
    677     //
    678     if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
    679       continue;
    680     }
    681 
    682     SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
    683                                       SigList->SignatureHeaderSize);
    684 
    685     TrustCert     = SigData->SignatureData;
    686     TrustCertSize = SigList->SignatureSize - sizeof (EFI_GUID);
    687 
    688     //
    689     // Verifying the PKCS#7 SignedData with the trusted certificate from AllowedDb
    690     //
    691     if (Pkcs7Verify (SignedData, SignedDataSize, TrustCert, TrustCertSize, InData, InDataSize)) {
    692       //
    693       // The SignedData was verified successfully by one entry in Trusted Database
    694       //
    695       Status = EFI_SUCCESS;
    696       break;
    697     }
    698   }
    699 
    700   return Status;
    701 }
    702 
    703 /**
    704   Processes a buffer containing binary DER-encoded PKCS7 signature.
    705   The signed data content may be embedded within the buffer or separated. Function
    706   verifies the signature of the content is valid and signing certificate was not
    707   revoked and is contained within a list of trusted signers.
    708 
    709   @param[in]     This                 Pointer to EFI_PKCS7_VERIFY_PROTOCOL instance.
    710   @param[in]     SignedData           Points to buffer containing ASN.1 DER-encoded PKCS7
    711                                       signature.
    712   @param[in]     SignedDataSize       The size of SignedData buffer in bytes.
    713   @param[in]     InData               In case of detached signature, InData points to
    714                                       buffer containing the raw message data previously
    715                                       signed and to be verified by function. In case of
    716                                       SignedData containing embedded data, InData must be
    717                                       NULL.
    718   @param[in]     InDataSize           When InData is used, the size of InData buffer in
    719                                       bytes. When InData is NULL. This parameter must be
    720                                       0.
    721   @param[in]     AllowedDb            Pointer to a list of pointers to EFI_SIGNATURE_LIST
    722                                       structures. The list is terminated by a null
    723                                       pointer. The EFI_SIGNATURE_LIST structures contain
    724                                       lists of X.509 certificates of approved signers.
    725                                       Function recognizes signer certificates of type
    726                                       EFI_CERT_X509_GUID. Any hash certificate in AllowedDb
    727                                       list is ignored by this function. Function returns
    728                                       success if signer of the buffer is within this list
    729                                       (and not within RevokedDb). This parameter is
    730                                       required.
    731   @param[in]     RevokedDb            Optional pointer to a list of pointers to
    732                                       EFI_SIGNATURE_LIST structures. The list is terminated
    733                                       by a null pointer. List of X.509 certificates of
    734                                       revoked signers and revoked file hashes. Except as
    735                                       noted in description of TimeStampDb signature
    736                                       verification will always fail if the signer of the
    737                                       file or the hash of the data component of the buffer
    738                                       is in RevokedDb list. This list is optional and
    739                                       caller may pass Null or pointer to NULL if not
    740                                       required.
    741   @param[in]     TimeStampDb          Optional pointer to a list of pointers to
    742                                       EFI_SIGNATURE_LIST structures. The list is terminated
    743                                       by a null pointer. This parameter can be used to pass
    744                                       a list of X.509 certificates of trusted time stamp
    745                                       signers. This list is optional and caller must pass
    746                                       Null or pointer to NULL if not required.
    747   @param[out]    Content              On input, points to an optional caller-allocated
    748                                       buffer into which the function will copy the content
    749                                       portion of the file after verification succeeds.
    750                                       This parameter is optional and if NULL, no copy of
    751                                       content from file is performed.
    752   @param[in,out] ContentSize          On input, points to the size in bytes of the optional
    753                                       buffer Content previously allocated by caller. On
    754                                       output, if the verification succeeds, the value
    755                                       referenced by ContentSize will contain the actual
    756                                       size of the content from signed file. If ContentSize
    757                                       indicates the caller-allocated buffer is too small
    758                                       to contain content, an error is returned, and
    759                                       ContentSize will be updated with the required size.
    760                                       This parameter must be 0 if Content is Null.
    761 
    762   @retval EFI_SUCCESS                 Content signature was verified against hash of
    763                                       content, the signer's certificate was not found in
    764                                       RevokedDb, and was found in AllowedDb or if in signer
    765                                       is found in both AllowedDb and RevokedDb, the
    766                                       signing was allowed by reference to TimeStampDb as
    767                                       described above, and no hash matching content hash
    768                                       was found in RevokedDb.
    769   @retval EFI_SECURITY_VIOLATION      The SignedData buffer was correctly formatted but
    770                                       signer was in RevokedDb or not in AllowedDb. Also
    771                                       returned if matching content hash found in RevokedDb.
    772   @retval EFI_COMPROMISED_DATA        Calculated hash differs from signed hash.
    773   @retval EFI_INVALID_PARAMETER       SignedData is NULL or SignedDataSize is zero.
    774                                       AllowedDb is NULL.
    775   @retval EFI_INVALID_PARAMETER       Content is not NULL and ContentSize is NULL.
    776   @retval EFI_ABORTED                 Unsupported or invalid format in TimeStampDb,
    777                                       RevokedDb or AllowedDb list contents was detected.
    778   @retval EFI_NOT_FOUND               Content not found because InData is NULL and no
    779                                       content embedded in SignedData.
    780   @retval EFI_UNSUPPORTED             The SignedData buffer was not correctly formatted
    781                                       for processing by the function.
    782   @retval EFI_UNSUPPORTED             Signed data embedded in SignedData but InData is not
    783                                       NULL.
    784   @retval EFI_BUFFER_TOO_SMALL        The size of buffer indicated by ContentSize is too
    785                                       small to hold the content. ContentSize updated to
    786                                       required size.
    787 
    788 **/
    789 EFI_STATUS
    790 EFIAPI
    791 VerifyBuffer (
    792   IN EFI_PKCS7_VERIFY_PROTOCOL    *This,
    793   IN VOID                         *SignedData,
    794   IN UINTN                        SignedDataSize,
    795   IN VOID                         *InData          OPTIONAL,
    796   IN UINTN                        InDataSize,
    797   IN EFI_SIGNATURE_LIST           **AllowedDb,
    798   IN EFI_SIGNATURE_LIST           **RevokedDb      OPTIONAL,
    799   IN EFI_SIGNATURE_LIST           **TimeStampDb    OPTIONAL,
    800   OUT VOID                        *Content         OPTIONAL,
    801   IN OUT UINTN                    *ContentSize
    802   )
    803 {
    804   EFI_STATUS  Status;
    805   UINT8       *AttachedData;
    806   UINTN       AttachedDataSize;
    807   UINT8       *DataPtr;
    808   UINTN       DataSize;
    809 
    810   //
    811   // Parameters Checking
    812   //
    813   if ((SignedData == NULL) || (SignedDataSize == 0) || (AllowedDb == NULL)) {
    814     return EFI_INVALID_PARAMETER;
    815   }
    816   if ((Content != NULL) && (ContentSize == NULL)) {
    817     return EFI_INVALID_PARAMETER;
    818   }
    819 
    820   //
    821   // Try to retrieve the attached content from PKCS7 signedData
    822   //
    823   AttachedData     = NULL;
    824   AttachedDataSize = 0;
    825   if (!Pkcs7GetAttachedContent (
    826          SignedData,
    827          SignedDataSize,
    828          (VOID **)&AttachedData,
    829          &AttachedDataSize)) {
    830     //
    831     // The SignedData buffer was not correctly formatted for processing
    832     //
    833     return EFI_UNSUPPORTED;
    834   }
    835   if (AttachedData != NULL) {
    836     if (InData != NULL) {
    837       //
    838       // The embedded content is found in SignedData but InData is not NULL
    839       //
    840       Status = EFI_UNSUPPORTED;
    841       goto _Exit;
    842     }
    843     //
    844     // PKCS7-formatted signedData with attached content; Use the embedded
    845     // content for verification
    846     //
    847     DataPtr  = AttachedData;
    848     DataSize = AttachedDataSize;
    849 
    850   } else if (InData != NULL) {
    851     //
    852     // PKCS7-formatted signedData with detached content; Use the user-supplied
    853     // input data for verification
    854     //
    855     DataPtr  = (UINT8 *)InData;
    856     DataSize = InDataSize;
    857   } else {
    858     //
    859     // Content not found because InData is NULL and no content attached in SignedData
    860     //
    861     Status = EFI_NOT_FOUND;
    862     goto _Exit;
    863   }
    864 
    865   Status = EFI_UNSUPPORTED;
    866 
    867   //
    868   // Verify PKCS7 SignedData with Revoked database
    869   //
    870   if (RevokedDb != NULL) {
    871     Status = P7CheckRevocation (
    872                SignedData,
    873                SignedDataSize,
    874                DataPtr,
    875                DataSize,
    876                RevokedDb,
    877                TimeStampDb
    878                );
    879     if (!EFI_ERROR (Status)) {
    880       //
    881       // The PKCS7 SignedData is reovked
    882       //
    883       Status = EFI_SECURITY_VIOLATION;
    884       goto _Exit;
    885     }
    886   }
    887 
    888   //
    889   // Verify PKCS7 SignedData with AllowedDB
    890   //
    891   Status = P7CheckTrust (
    892              SignedData,
    893              SignedDataSize,
    894              DataPtr,
    895              DataSize,
    896              AllowedDb
    897              );
    898   if (EFI_ERROR (Status)) {
    899       //
    900       // Verification failed with AllowedDb
    901       //
    902       goto _Exit;
    903   }
    904 
    905   //
    906   // Copy the content portion after verification succeeds
    907   //
    908   if (Content != NULL) {
    909     if (*ContentSize < DataSize) {
    910       //
    911       // Caller-allocated buffer is too small to contain content
    912       //
    913       *ContentSize = DataSize;
    914       Status = EFI_BUFFER_TOO_SMALL;
    915     } else {
    916       *ContentSize = DataSize;
    917       CopyMem (Content, DataPtr, DataSize);
    918     }
    919   }
    920 
    921 _Exit:
    922   if (AttachedData != NULL) {
    923     FreePool (AttachedData);
    924   }
    925 
    926   return Status;
    927 }
    928 
    929 /**
    930   Processes a buffer containing binary DER-encoded detached PKCS7 signature.
    931   The hash of the signed data content is calculated and passed by the caller. Function
    932   verifies the signature of the content is valid and signing certificate was not revoked
    933   and is contained within a list of trusted signers.
    934 
    935   @param[in]     This                 Pointer to EFI_PKCS7_VERIFY_PROTOCOL instance.
    936   @param[in]     Signature            Points to buffer containing ASN.1 DER-encoded PKCS
    937                                       detached signature.
    938   @param[in]     SignatureSize        The size of Signature buffer in bytes.
    939   @param[in]     InHash               InHash points to buffer containing the caller
    940                                       calculated hash of the data. The parameter may not
    941                                       be NULL.
    942   @param[in]     InHashSize           The size in bytes of InHash buffer.
    943   @param[in]     AllowedDb            Pointer to a list of pointers to EFI_SIGNATURE_LIST
    944                                       structures. The list is terminated by a null
    945                                       pointer. The EFI_SIGNATURE_LIST structures contain
    946                                       lists of X.509 certificates of approved signers.
    947                                       Function recognizes signer certificates of type
    948                                       EFI_CERT_X509_GUID. Any hash certificate in AllowedDb
    949                                       list is ignored by this function. Function returns
    950                                       success if signer of the buffer is within this list
    951                                       (and not within RevokedDb). This parameter is
    952                                       required.
    953   @param[in]     RevokedDb            Optional pointer to a list of pointers to
    954                                       EFI_SIGNATURE_LIST structures. The list is terminated
    955                                       by a null pointer. List of X.509 certificates of
    956                                       revoked signers and revoked file hashes. Signature
    957                                       verification will always fail if the signer of the
    958                                       file or the hash of the data component of the buffer
    959                                       is in RevokedDb list. This parameter is optional
    960                                       and caller may pass Null if not required.
    961   @param[in]     TimeStampDb          Optional pointer to a list of pointers to
    962                                       EFI_SIGNATURE_LIST structures. The list is terminated
    963                                       by a null pointer. This parameter can be used to pass
    964                                       a list of X.509 certificates of trusted time stamp
    965                                       counter-signers.
    966 
    967   @retval EFI_SUCCESS                 Signed hash was verified against caller-provided
    968                                       hash of content, the signer's certificate was not
    969                                       found in RevokedDb, and was found in AllowedDb or
    970                                       if in signer is found in both AllowedDb and
    971                                       RevokedDb, the signing was allowed by reference to
    972                                       TimeStampDb as described above, and no hash matching
    973                                       content hash was found in RevokedDb.
    974   @retval EFI_SECURITY_VIOLATION      The SignedData buffer was correctly formatted but
    975                                       signer was in RevokedDb or not in AllowedDb. Also
    976                                       returned if matching content hash found in RevokedDb.
    977   @retval EFI_COMPROMISED_DATA        Caller provided hash differs from signed hash. Or,
    978                                       caller and encrypted hash are different sizes.
    979   @retval EFI_INVALID_PARAMETER       Signature is NULL or SignatureSize is zero. InHash
    980                                       is NULL or InHashSize is zero. AllowedDb is NULL.
    981   @retval EFI_ABORTED                 Unsupported or invalid format in TimeStampDb,
    982                                       RevokedDb or AllowedDb list contents was detected.
    983   @retval EFI_UNSUPPORTED             The Signature buffer was not correctly formatted
    984                                       for processing by the function.
    985 
    986 **/
    987 EFI_STATUS
    988 EFIAPI
    989 VerifySignature (
    990   IN EFI_PKCS7_VERIFY_PROTOCOL    *This,
    991   IN VOID                         *Signature,
    992   IN UINTN                        SignatureSize,
    993   IN VOID                         *InHash,
    994   IN UINTN                        InHashSize,
    995   IN EFI_SIGNATURE_LIST           **AllowedDb,
    996   IN EFI_SIGNATURE_LIST           **RevokedDb       OPTIONAL,
    997   IN EFI_SIGNATURE_LIST           **TimeStampDb     OPTIONAL
    998   )
    999 {
   1000   //
   1001   // NOTE: Current EDKII-OpenSSL interface cannot support VerifySignature
   1002   //       directly. EFI_UNSUPPORTED is returned in this version.
   1003   //
   1004   return EFI_UNSUPPORTED;
   1005 }
   1006 
   1007 //
   1008 // The PKCS7 Verification Protocol
   1009 //
   1010 EFI_PKCS7_VERIFY_PROTOCOL mPkcs7Verify = {
   1011   VerifyBuffer,
   1012   VerifySignature
   1013 };
   1014 
   1015 /**
   1016   The user Entry Point for the PKCS7 Verification driver.
   1017 
   1018   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
   1019   @param[in] SystemTable    A pointer to the EFI System Table.
   1020 
   1021   @retval EFI_SUCCESS       The entry point is executed successfully.
   1022   @retval EFI_NOT_SUPPORTED Platform does not support PKCS7 Verification.
   1023   @retval Other             Some error occurs when executing this entry point.
   1024 
   1025 **/
   1026 EFI_STATUS
   1027 EFIAPI
   1028 Pkcs7VerifyDriverEntry (
   1029   IN EFI_HANDLE          ImageHandle,
   1030   IN EFI_SYSTEM_TABLE    *SystemTable
   1031   )
   1032 {
   1033   EFI_STATUS    Status;
   1034   EFI_HANDLE    Handle;
   1035 
   1036   //
   1037   // Install UEFI Pkcs7 Verification Protocol
   1038   //
   1039   Handle = NULL;
   1040   Status = gBS->InstallMultipleProtocolInterfaces (
   1041                   &Handle,
   1042                   &gEfiPkcs7VerifyProtocolGuid,
   1043                   &mPkcs7Verify,
   1044                   NULL
   1045                   );
   1046 
   1047   return Status;
   1048 }
   1049