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
CalculateDataHash(IN VOID * Data,IN UINTN DataSize,IN EFI_GUID * CertGuid,OUT UINT8 * HashValue)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
IsContentHashRevoked(IN UINT8 * Content,IN UINTN ContentSize,IN EFI_SIGNATURE_LIST ** RevokedDb)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
IsCertHashRevoked(IN UINT8 * Certificate,IN UINTN CertSize,IN EFI_SIGNATURE_LIST ** RevokedDb,OUT EFI_TIME * RevocationTime)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
IsTimeZero(IN EFI_TIME * Time)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
CompareTimestamp(IN EFI_TIME * SigningTime,IN EFI_TIME * RevocationTime)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
IsValidTimestamp(IN UINT8 * SignedData,IN UINTN SignedDataSize,IN EFI_SIGNATURE_LIST ** TimeStampDb,IN EFI_TIME * RevocationTime)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
P7CheckRevocation(IN UINT8 * SignedData,IN UINTN SignedDataSize,IN UINT8 * InData,IN UINTN InDataSize,IN EFI_SIGNATURE_LIST ** RevokedDb,IN EFI_SIGNATURE_LIST ** TimeStampDb)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
P7CheckTrust(IN UINT8 * SignedData,IN UINTN SignedDataSize,IN UINT8 * InData,IN UINTN InDataSize,IN EFI_SIGNATURE_LIST ** AllowedDb)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
VerifyBuffer(IN EFI_PKCS7_VERIFY_PROTOCOL * This,IN VOID * SignedData,IN UINTN SignedDataSize,IN VOID * InData OPTIONAL,IN UINTN InDataSize,IN EFI_SIGNATURE_LIST ** AllowedDb,IN EFI_SIGNATURE_LIST ** RevokedDb OPTIONAL,IN EFI_SIGNATURE_LIST ** TimeStampDb OPTIONAL,OUT VOID * Content OPTIONAL,IN OUT UINTN * ContentSize)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
VerifySignature(IN EFI_PKCS7_VERIFY_PROTOCOL * This,IN VOID * Signature,IN UINTN SignatureSize,IN VOID * InHash,IN UINTN InHashSize,IN EFI_SIGNATURE_LIST ** AllowedDb,IN EFI_SIGNATURE_LIST ** RevokedDb OPTIONAL,IN EFI_SIGNATURE_LIST ** TimeStampDb OPTIONAL)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
Pkcs7VerifyDriverEntry(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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