]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c
CryptoPkg: add new X509 function.
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7VerifyCommon.c
CommitLineData
532616bb 1/** @file\r
2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.\r
3\r
4 Caution: This module requires additional review when modified.\r
5 This library will have external input - signature (e.g. UEFI Authenticated\r
6 Variable). It may by input in SMM mode.\r
7 This external input must be validated carefully to avoid security issue like\r
8 buffer overflow, integer overflow.\r
9\r
10 WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated\r
11 Variable and will do basic check for data structure.\r
12\r
cc01b26e 13Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
2009f6b4 14SPDX-License-Identifier: BSD-2-Clause-Patent\r
532616bb 15\r
16**/\r
17\r
18#include "InternalCryptLib.h"\r
19\r
20#include <openssl/objects.h>\r
21#include <openssl/x509.h>\r
02ee8d3b 22#include <openssl/x509v3.h>\r
532616bb 23#include <openssl/pkcs7.h>\r
24\r
7c342378 25UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };\r
532616bb 26\r
532616bb 27/**\r
28 Check input P7Data is a wrapped ContentInfo structure or not. If not construct\r
29 a new structure to wrap P7Data.\r
30\r
31 Caution: This function may receive untrusted input.\r
32 UEFI Authenticated Variable is external input, so this function will do basic\r
33 check for PKCS#7 data structure.\r
34\r
35 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
36 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
37 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise\r
38 return FALSE.\r
2ac68e8b 39 @param[out] WrapData If return status of this function is TRUE:\r
532616bb 40 1) when WrapFlag is TRUE, pointer to P7Data.\r
41 2) when WrapFlag is FALSE, pointer to a new ContentInfo\r
42 structure. It's caller's responsibility to free this\r
43 buffer.\r
44 @param[out] WrapDataSize Length of ContentInfo structure in bytes.\r
45\r
46 @retval TRUE The operation is finished successfully.\r
47 @retval FALSE The operation is failed due to lack of resources.\r
48\r
49**/\r
50BOOLEAN\r
51WrapPkcs7Data (\r
52 IN CONST UINT8 *P7Data,\r
53 IN UINTN P7Length,\r
54 OUT BOOLEAN *WrapFlag,\r
55 OUT UINT8 **WrapData,\r
56 OUT UINTN *WrapDataSize\r
57 )\r
58{\r
7c342378
MK
59 BOOLEAN Wrapped;\r
60 UINT8 *SignedData;\r
532616bb 61\r
62 //\r
63 // Check whether input P7Data is a wrapped ContentInfo structure or not.\r
64 //\r
65 Wrapped = FALSE;\r
66 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {\r
67 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {\r
68 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {\r
69 Wrapped = TRUE;\r
70 }\r
71 }\r
72 }\r
73\r
74 if (Wrapped) {\r
7c342378 75 *WrapData = (UINT8 *)P7Data;\r
532616bb 76 *WrapDataSize = P7Length;\r
77 } else {\r
78 //\r
79 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.\r
80 //\r
81 *WrapDataSize = P7Length + 19;\r
82 *WrapData = malloc (*WrapDataSize);\r
83 if (*WrapData == NULL) {\r
84 *WrapFlag = Wrapped;\r
85 return FALSE;\r
86 }\r
87\r
88 SignedData = *WrapData;\r
89\r
90 //\r
91 // Part1: 0x30, 0x82.\r
92 //\r
93 SignedData[0] = 0x30;\r
94 SignedData[1] = 0x82;\r
95\r
96 //\r
97 // Part2: Length1 = P7Length + 19 - 4, in big endian.\r
98 //\r
7c342378
MK
99 SignedData[2] = (UINT8)(((UINT16)(*WrapDataSize - 4)) >> 8);\r
100 SignedData[3] = (UINT8)(((UINT16)(*WrapDataSize - 4)) & 0xff);\r
532616bb 101\r
102 //\r
103 // Part3: 0x06, 0x09.\r
104 //\r
105 SignedData[4] = 0x06;\r
106 SignedData[5] = 0x09;\r
107\r
108 //\r
109 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.\r
110 //\r
111 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));\r
112\r
113 //\r
114 // Part5: 0xA0, 0x82.\r
115 //\r
116 SignedData[15] = 0xA0;\r
117 SignedData[16] = 0x82;\r
118\r
119 //\r
120 // Part6: Length2 = P7Length, in big endian.\r
121 //\r
7c342378
MK
122 SignedData[17] = (UINT8)(((UINT16)P7Length) >> 8);\r
123 SignedData[18] = (UINT8)(((UINT16)P7Length) & 0xff);\r
532616bb 124\r
125 //\r
126 // Part7: P7Data.\r
127 //\r
128 CopyMem (SignedData + 19, P7Data, P7Length);\r
129 }\r
130\r
131 *WrapFlag = Wrapped;\r
132 return TRUE;\r
133}\r
134\r
efad60c5 135/**\r
136 Pop single certificate from STACK_OF(X509).\r
137\r
138 If X509Stack, Cert, or CertSize is NULL, then return FALSE.\r
139\r
140 @param[in] X509Stack Pointer to a X509 stack object.\r
141 @param[out] Cert Pointer to a X509 certificate.\r
142 @param[out] CertSize Length of output X509 certificate in bytes.\r
2ac68e8b 143\r
efad60c5 144 @retval TRUE The X509 stack pop succeeded.\r
145 @retval FALSE The pop operation failed.\r
146\r
147**/\r
148BOOLEAN\r
149X509PopCertificate (\r
7c342378
MK
150 IN VOID *X509Stack,\r
151 OUT UINT8 **Cert,\r
152 OUT UINTN *CertSize\r
efad60c5 153 )\r
154{\r
7c342378
MK
155 BIO *CertBio;\r
156 X509 *X509Cert;\r
157\r
158 STACK_OF (X509) *CertStack;\r
159 BOOLEAN Status;\r
160 INT32 Result;\r
161 BUF_MEM *Ptr;\r
162 INT32 Length;\r
163 VOID *Buffer;\r
efad60c5 164\r
165 Status = FALSE;\r
166\r
167 if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {\r
168 return Status;\r
169 }\r
170\r
7c342378 171 CertStack = (STACK_OF (X509) *) X509Stack;\r
efad60c5 172\r
173 X509Cert = sk_X509_pop (CertStack);\r
174\r
175 if (X509Cert == NULL) {\r
176 return Status;\r
177 }\r
178\r
179 Buffer = NULL;\r
180\r
181 CertBio = BIO_new (BIO_s_mem ());\r
182 if (CertBio == NULL) {\r
183 return Status;\r
184 }\r
185\r
186 Result = i2d_X509_bio (CertBio, X509Cert);\r
187 if (Result == 0) {\r
188 goto _Exit;\r
189 }\r
190\r
f56b11d2
QL
191 BIO_get_mem_ptr (CertBio, &Ptr);\r
192 Length = (INT32)(Ptr->length);\r
efad60c5 193 if (Length <= 0) {\r
194 goto _Exit;\r
195 }\r
196\r
197 Buffer = malloc (Length);\r
198 if (Buffer == NULL) {\r
199 goto _Exit;\r
200 }\r
201\r
202 Result = BIO_read (CertBio, Buffer, Length);\r
203 if (Result != Length) {\r
204 goto _Exit;\r
205 }\r
206\r
207 *Cert = Buffer;\r
208 *CertSize = Length;\r
209\r
210 Status = TRUE;\r
211\r
212_Exit:\r
213\r
214 BIO_free (CertBio);\r
215\r
216 if (!Status && (Buffer != NULL)) {\r
217 free (Buffer);\r
218 }\r
219\r
220 return Status;\r
221}\r
222\r
532616bb 223/**\r
224 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:\r
225 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
226 in a ContentInfo structure.\r
227\r
228 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then\r
2998af86 229 return FALSE. If P7Length overflow, then return FALSE.\r
532616bb 230\r
231 Caution: This function may receive untrusted input.\r
232 UEFI Authenticated Variable is external input, so this function will do basic\r
233 check for PKCS#7 data structure.\r
234\r
235 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
236 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
237 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.\r
6fe575d0
LQ
238 It's caller's responsibility to free the buffer with\r
239 Pkcs7FreeSigners().\r
3702637a 240 This data structure is EFI_CERT_STACK type.\r
532616bb 241 @param[out] StackLength Length of signer's certificates in bytes.\r
242 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.\r
6fe575d0
LQ
243 It's caller's responsibility to free the buffer with\r
244 Pkcs7FreeSigners().\r
532616bb 245 @param[out] CertLength Length of the trusted certificate in bytes.\r
246\r
247 @retval TRUE The operation is finished successfully.\r
248 @retval FALSE Error occurs during the operation.\r
249\r
250**/\r
251BOOLEAN\r
252EFIAPI\r
253Pkcs7GetSigners (\r
254 IN CONST UINT8 *P7Data,\r
255 IN UINTN P7Length,\r
256 OUT UINT8 **CertStack,\r
257 OUT UINTN *StackLength,\r
258 OUT UINT8 **TrustedCert,\r
259 OUT UINTN *CertLength\r
260 )\r
261{\r
7c342378
MK
262 PKCS7 *Pkcs7;\r
263 BOOLEAN Status;\r
264 UINT8 *SignedData;\r
265 CONST UINT8 *Temp;\r
266 UINTN SignedDataSize;\r
267 BOOLEAN Wrapped;\r
268\r
269 STACK_OF (X509) *Stack;\r
270 UINT8 Index;\r
271 UINT8 *CertBuf;\r
272 UINT8 *OldBuf;\r
273 UINTN BufferSize;\r
274 UINTN OldSize;\r
275 UINT8 *SingleCert;\r
276 UINTN SingleCertSize;\r
532616bb 277\r
278 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||\r
7c342378
MK
279 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX))\r
280 {\r
532616bb 281 return FALSE;\r
282 }\r
2ac68e8b 283\r
532616bb 284 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
285 if (!Status) {\r
286 return Status;\r
287 }\r
288\r
289 Status = FALSE;\r
290 Pkcs7 = NULL;\r
291 Stack = NULL;\r
292 CertBuf = NULL;\r
293 OldBuf = NULL;\r
294 SingleCert = NULL;\r
295\r
296 //\r
297 // Retrieve PKCS#7 Data (DER encoding)\r
298 //\r
299 if (SignedDataSize > INT_MAX) {\r
300 goto _Exit;\r
301 }\r
302\r
7c342378
MK
303 Temp = SignedData;\r
304 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);\r
532616bb 305 if (Pkcs7 == NULL) {\r
306 goto _Exit;\r
307 }\r
308\r
309 //\r
310 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
311 //\r
312 if (!PKCS7_type_is_signed (Pkcs7)) {\r
313 goto _Exit;\r
314 }\r
315\r
7c342378 316 Stack = PKCS7_get0_signers (Pkcs7, NULL, PKCS7_BINARY);\r
532616bb 317 if (Stack == NULL) {\r
318 goto _Exit;\r
319 }\r
320\r
321 //\r
322 // Convert CertStack to buffer in following format:\r
323 // UINT8 CertNumber;\r
324 // UINT32 Cert1Length;\r
325 // UINT8 Cert1[];\r
326 // UINT32 Cert2Length;\r
327 // UINT8 Cert2[];\r
328 // ...\r
329 // UINT32 CertnLength;\r
330 // UINT8 Certn[];\r
331 //\r
332 BufferSize = sizeof (UINT8);\r
333 OldSize = BufferSize;\r
2ac68e8b 334\r
532616bb 335 for (Index = 0; ; Index++) {\r
336 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);\r
337 if (!Status) {\r
338 break;\r
339 }\r
340\r
341 OldSize = BufferSize;\r
342 OldBuf = CertBuf;\r
343 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);\r
344 CertBuf = malloc (BufferSize);\r
345\r
346 if (CertBuf == NULL) {\r
347 goto _Exit;\r
348 }\r
349\r
350 if (OldBuf != NULL) {\r
351 CopyMem (CertBuf, OldBuf, OldSize);\r
352 free (OldBuf);\r
353 OldBuf = NULL;\r
354 }\r
355\r
7c342378 356 WriteUnaligned32 ((UINT32 *)(CertBuf + OldSize), (UINT32)SingleCertSize);\r
532616bb 357 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);\r
358\r
359 free (SingleCert);\r
360 SingleCert = NULL;\r
361 }\r
362\r
363 if (CertBuf != NULL) {\r
364 //\r
365 // Update CertNumber.\r
366 //\r
367 CertBuf[0] = Index;\r
368\r
7c342378 369 *CertLength = BufferSize - OldSize - sizeof (UINT32);\r
532616bb 370 *TrustedCert = malloc (*CertLength);\r
371 if (*TrustedCert == NULL) {\r
372 goto _Exit;\r
373 }\r
374\r
375 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);\r
376 *CertStack = CertBuf;\r
377 *StackLength = BufferSize;\r
7c342378 378 Status = TRUE;\r
2ac68e8b 379 }\r
532616bb 380\r
381_Exit:\r
382 //\r
383 // Release Resources\r
384 //\r
385 if (!Wrapped) {\r
386 free (SignedData);\r
387 }\r
388\r
389 if (Pkcs7 != NULL) {\r
390 PKCS7_free (Pkcs7);\r
391 }\r
392\r
393 if (Stack != NULL) {\r
7c342378 394 sk_X509_pop_free (Stack, X509_free);\r
532616bb 395 }\r
396\r
397 if (SingleCert != NULL) {\r
398 free (SingleCert);\r
399 }\r
400\r
401 if (!Status && (CertBuf != NULL)) {\r
402 free (CertBuf);\r
403 *CertStack = NULL;\r
404 }\r
405\r
406 if (OldBuf != NULL) {\r
407 free (OldBuf);\r
408 }\r
2ac68e8b 409\r
532616bb 410 return Status;\r
411}\r
412\r
413/**\r
414 Wrap function to use free() to free allocated memory for certificates.\r
415\r
416 @param[in] Certs Pointer to the certificates to be freed.\r
417\r
418**/\r
419VOID\r
420EFIAPI\r
421Pkcs7FreeSigners (\r
7c342378 422 IN UINT8 *Certs\r
532616bb 423 )\r
424{\r
425 if (Certs == NULL) {\r
426 return;\r
427 }\r
428\r
429 free (Certs);\r
430}\r
431\r
45419de6
QL
432/**\r
433 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:\r
434 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and\r
435 unchained to the signer's certificates.\r
436 The input signed data could be wrapped in a ContentInfo structure.\r
437\r
438 @param[in] P7Data Pointer to the PKCS#7 message.\r
439 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
0f5f6b3d 440 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's\r
6fe575d0
LQ
441 certificate. It's caller's responsibility to free the buffer\r
442 with Pkcs7FreeSigners().\r
3702637a 443 This data structure is EFI_CERT_STACK type.\r
45419de6
QL
444 @param[out] ChainLength Length of the chained certificates list buffer in bytes.\r
445 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's\r
6fe575d0 446 responsibility to free the buffer with Pkcs7FreeSigners().\r
3702637a 447 This data structure is EFI_CERT_STACK type.\r
45419de6
QL
448 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.\r
449\r
450 @retval TRUE The operation is finished successfully.\r
451 @retval FALSE Error occurs during the operation.\r
452\r
453**/\r
454BOOLEAN\r
455EFIAPI\r
456Pkcs7GetCertificatesList (\r
457 IN CONST UINT8 *P7Data,\r
458 IN UINTN P7Length,\r
459 OUT UINT8 **SignerChainCerts,\r
460 OUT UINTN *ChainLength,\r
461 OUT UINT8 **UnchainCerts,\r
462 OUT UINTN *UnchainLength\r
463 )\r
464{\r
7c342378
MK
465 BOOLEAN Status;\r
466 UINT8 *NewP7Data;\r
467 UINTN NewP7Length;\r
468 BOOLEAN Wrapped;\r
469 UINT8 Index;\r
470 PKCS7 *Pkcs7;\r
471 X509_STORE_CTX *CertCtx;\r
472\r
473 STACK_OF (X509) *CtxChain;\r
474 STACK_OF (X509) *CtxUntrusted;\r
475 X509 *CtxCert;\r
476\r
477 STACK_OF (X509) *Signers;\r
478 X509 *Signer;\r
479 X509 *Cert;\r
480 X509 *Issuer;\r
481 X509_NAME *IssuerName;\r
482 UINT8 *CertBuf;\r
483 UINT8 *OldBuf;\r
484 UINTN BufferSize;\r
485 UINTN OldSize;\r
486 UINT8 *SingleCert;\r
487 UINTN CertSize;\r
45419de6
QL
488\r
489 //\r
490 // Initializations\r
491 //\r
7c342378
MK
492 Status = FALSE;\r
493 NewP7Data = NULL;\r
494 Pkcs7 = NULL;\r
495 CertCtx = NULL;\r
496 CtxChain = NULL;\r
497 CtxCert = NULL;\r
498 CtxUntrusted = NULL;\r
499 Cert = NULL;\r
500 SingleCert = NULL;\r
501 CertBuf = NULL;\r
502 OldBuf = NULL;\r
503 Signers = NULL;\r
45419de6
QL
504\r
505 //\r
506 // Parameter Checking\r
507 //\r
508 if ((P7Data == NULL) || (SignerChainCerts == NULL) || (ChainLength == NULL) ||\r
7c342378
MK
509 (UnchainCerts == NULL) || (UnchainLength == NULL) || (P7Length > INT_MAX))\r
510 {\r
45419de6
QL
511 return Status;\r
512 }\r
513\r
514 *SignerChainCerts = NULL;\r
515 *ChainLength = 0;\r
516 *UnchainCerts = NULL;\r
517 *UnchainLength = 0;\r
518\r
519 //\r
520 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.\r
521 //\r
522 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &NewP7Data, &NewP7Length);\r
523 if (!Status || (NewP7Length > INT_MAX)) {\r
524 goto _Error;\r
525 }\r
526\r
527 //\r
528 // Decodes PKCS#7 SignedData\r
529 //\r
7c342378 530 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&NewP7Data, (int)NewP7Length);\r
45419de6
QL
531 if ((Pkcs7 == NULL) || (!PKCS7_type_is_signed (Pkcs7))) {\r
532 goto _Error;\r
533 }\r
534\r
535 //\r
536 // Obtains Signer's Certificate from PKCS#7 data\r
537 // NOTE: Only one signer case will be handled in this function, which means SignerInfos\r
0f5f6b3d 538 // should include only one signer's certificate.\r
45419de6
QL
539 //\r
540 Signers = PKCS7_get0_signers (Pkcs7, NULL, PKCS7_BINARY);\r
541 if ((Signers == NULL) || (sk_X509_num (Signers) != 1)) {\r
542 goto _Error;\r
543 }\r
7c342378 544\r
45419de6
QL
545 Signer = sk_X509_value (Signers, 0);\r
546\r
f56b11d2
QL
547 CertCtx = X509_STORE_CTX_new ();\r
548 if (CertCtx == NULL) {\r
549 goto _Error;\r
550 }\r
7c342378 551\r
f56b11d2 552 if (!X509_STORE_CTX_init (CertCtx, NULL, Signer, Pkcs7->d.sign->cert)) {\r
45419de6
QL
553 goto _Error;\r
554 }\r
7c342378 555\r
45419de6
QL
556 //\r
557 // Initialize Chained & Untrusted stack\r
558 //\r
f56b11d2
QL
559 CtxChain = X509_STORE_CTX_get0_chain (CertCtx);\r
560 CtxCert = X509_STORE_CTX_get0_cert (CertCtx);\r
561 if (CtxChain == NULL) {\r
562 if (((CtxChain = sk_X509_new_null ()) == NULL) ||\r
7c342378
MK
563 (!sk_X509_push (CtxChain, CtxCert)))\r
564 {\r
45419de6
QL
565 goto _Error;\r
566 }\r
567 }\r
7c342378 568\r
f56b11d2 569 CtxUntrusted = X509_STORE_CTX_get0_untrusted (CertCtx);\r
a9fb7b78
LQ
570 if (CtxUntrusted != NULL) {\r
571 (VOID)sk_X509_delete_ptr (CtxUntrusted, Signer);\r
572 }\r
45419de6
QL
573\r
574 //\r
575 // Build certificates stack chained from Signer's certificate.\r
576 //\r
577 Cert = Signer;\r
7c342378 578 for ( ; ;) {\r
45419de6
QL
579 //\r
580 // Self-Issue checking\r
581 //\r
f56b11d2
QL
582 Issuer = NULL;\r
583 if (X509_STORE_CTX_get1_issuer (&Issuer, CertCtx, Cert) == 1) {\r
584 if (X509_cmp (Issuer, Cert) == 0) {\r
585 break;\r
586 }\r
45419de6
QL
587 }\r
588\r
589 //\r
590 // Found the issuer of the current certificate\r
591 //\r
f56b11d2 592 if (CtxUntrusted != NULL) {\r
7c342378 593 Issuer = NULL;\r
f56b11d2
QL
594 IssuerName = X509_get_issuer_name (Cert);\r
595 Issuer = X509_find_by_subject (CtxUntrusted, IssuerName);\r
45419de6 596 if (Issuer != NULL) {\r
f56b11d2 597 if (!sk_X509_push (CtxChain, Issuer)) {\r
45419de6
QL
598 goto _Error;\r
599 }\r
7c342378 600\r
f56b11d2 601 (VOID)sk_X509_delete_ptr (CtxUntrusted, Issuer);\r
45419de6
QL
602\r
603 Cert = Issuer;\r
604 continue;\r
605 }\r
606 }\r
607\r
608 break;\r
609 }\r
610\r
611 //\r
612 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:\r
613 // UINT8 CertNumber;\r
614 // UINT32 Cert1Length;\r
615 // UINT8 Cert1[];\r
616 // UINT32 Cert2Length;\r
617 // UINT8 Cert2[];\r
618 // ...\r
619 // UINT32 CertnLength;\r
620 // UINT8 Certn[];\r
621 //\r
622\r
f56b11d2 623 if (CtxChain != NULL) {\r
45419de6 624 BufferSize = sizeof (UINT8);\r
45419de6
QL
625 CertBuf = NULL;\r
626\r
627 for (Index = 0; ; Index++) {\r
f56b11d2 628 Status = X509PopCertificate (CtxChain, &SingleCert, &CertSize);\r
45419de6
QL
629 if (!Status) {\r
630 break;\r
631 }\r
632\r
633 OldSize = BufferSize;\r
634 OldBuf = CertBuf;\r
635 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
636 CertBuf = malloc (BufferSize);\r
637\r
638 if (CertBuf == NULL) {\r
639 Status = FALSE;\r
640 goto _Error;\r
641 }\r
7c342378 642\r
45419de6
QL
643 if (OldBuf != NULL) {\r
644 CopyMem (CertBuf, OldBuf, OldSize);\r
645 free (OldBuf);\r
646 OldBuf = NULL;\r
647 }\r
648\r
7c342378 649 WriteUnaligned32 ((UINT32 *)(CertBuf + OldSize), (UINT32)CertSize);\r
45419de6
QL
650 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
651\r
652 free (SingleCert);\r
653 SingleCert = NULL;\r
654 }\r
655\r
656 if (CertBuf != NULL) {\r
657 //\r
658 // Update CertNumber.\r
659 //\r
660 CertBuf[0] = Index;\r
661\r
662 *SignerChainCerts = CertBuf;\r
663 *ChainLength = BufferSize;\r
664 }\r
665 }\r
666\r
f56b11d2 667 if (CtxUntrusted != NULL) {\r
45419de6 668 BufferSize = sizeof (UINT8);\r
45419de6
QL
669 CertBuf = NULL;\r
670\r
671 for (Index = 0; ; Index++) {\r
f56b11d2 672 Status = X509PopCertificate (CtxUntrusted, &SingleCert, &CertSize);\r
45419de6
QL
673 if (!Status) {\r
674 break;\r
675 }\r
676\r
677 OldSize = BufferSize;\r
678 OldBuf = CertBuf;\r
679 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
680 CertBuf = malloc (BufferSize);\r
681\r
682 if (CertBuf == NULL) {\r
683 Status = FALSE;\r
684 goto _Error;\r
685 }\r
7c342378 686\r
45419de6
QL
687 if (OldBuf != NULL) {\r
688 CopyMem (CertBuf, OldBuf, OldSize);\r
689 free (OldBuf);\r
690 OldBuf = NULL;\r
691 }\r
692\r
7c342378 693 WriteUnaligned32 ((UINT32 *)(CertBuf + OldSize), (UINT32)CertSize);\r
45419de6
QL
694 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
695\r
696 free (SingleCert);\r
697 SingleCert = NULL;\r
698 }\r
699\r
700 if (CertBuf != NULL) {\r
701 //\r
702 // Update CertNumber.\r
703 //\r
704 CertBuf[0] = Index;\r
705\r
706 *UnchainCerts = CertBuf;\r
707 *UnchainLength = BufferSize;\r
708 }\r
709 }\r
710\r
711 Status = TRUE;\r
712\r
713_Error:\r
714 //\r
715 // Release Resources.\r
716 //\r
717 if (!Wrapped && (NewP7Data != NULL)) {\r
718 free (NewP7Data);\r
719 }\r
720\r
721 if (Pkcs7 != NULL) {\r
722 PKCS7_free (Pkcs7);\r
723 }\r
7c342378 724\r
45419de6
QL
725 sk_X509_free (Signers);\r
726\r
a9fb7b78
LQ
727 if (CertCtx != NULL) {\r
728 X509_STORE_CTX_cleanup (CertCtx);\r
729 X509_STORE_CTX_free (CertCtx);\r
730 }\r
45419de6
QL
731\r
732 if (SingleCert != NULL) {\r
733 free (SingleCert);\r
734 }\r
735\r
736 if (OldBuf != NULL) {\r
737 free (OldBuf);\r
738 }\r
739\r
740 if (!Status && (CertBuf != NULL)) {\r
741 free (CertBuf);\r
742 *SignerChainCerts = NULL;\r
743 *UnchainCerts = NULL;\r
744 }\r
745\r
746 return Status;\r
747}\r
748\r
532616bb 749/**\r
2998af86 750 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:\r
532616bb 751 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
752 in a ContentInfo structure.\r
753\r
754 If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
2998af86 755 If P7Length, CertLength or DataLength overflow, then return FALSE.\r
532616bb 756\r
757 Caution: This function may receive untrusted input.\r
758 UEFI Authenticated Variable is external input, so this function will do basic\r
759 check for PKCS#7 data structure.\r
760\r
761 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
762 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
763 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
764 is used for certificate chain verification.\r
765 @param[in] CertLength Length of the trusted certificate in bytes.\r
766 @param[in] InData Pointer to the content to be verified.\r
767 @param[in] DataLength Length of InData in bytes.\r
768\r
769 @retval TRUE The specified PKCS#7 signed data is valid.\r
770 @retval FALSE Invalid PKCS#7 signed data.\r
771\r
772**/\r
773BOOLEAN\r
774EFIAPI\r
775Pkcs7Verify (\r
776 IN CONST UINT8 *P7Data,\r
777 IN UINTN P7Length,\r
778 IN CONST UINT8 *TrustedCert,\r
779 IN UINTN CertLength,\r
780 IN CONST UINT8 *InData,\r
781 IN UINTN DataLength\r
782 )\r
783{\r
7c342378
MK
784 PKCS7 *Pkcs7;\r
785 BIO *DataBio;\r
786 BOOLEAN Status;\r
787 X509 *Cert;\r
788 X509_STORE *CertStore;\r
789 UINT8 *SignedData;\r
790 CONST UINT8 *Temp;\r
791 UINTN SignedDataSize;\r
792 BOOLEAN Wrapped;\r
532616bb 793\r
794 //\r
795 // Check input parameters.\r
796 //\r
7c342378
MK
797 if ((P7Data == NULL) || (TrustedCert == NULL) || (InData == NULL) ||\r
798 (P7Length > INT_MAX) || (CertLength > INT_MAX) || (DataLength > INT_MAX))\r
799 {\r
532616bb 800 return FALSE;\r
801 }\r
2ac68e8b 802\r
532616bb 803 Pkcs7 = NULL;\r
532616bb 804 DataBio = NULL;\r
805 Cert = NULL;\r
806 CertStore = NULL;\r
807\r
808 //\r
809 // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
810 //\r
dda39f3a 811 if (EVP_add_digest (EVP_md5 ()) == 0) {\r
812 return FALSE;\r
813 }\r
7c342378 814\r
dda39f3a 815 if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
816 return FALSE;\r
817 }\r
7c342378 818\r
dda39f3a 819 if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
820 return FALSE;\r
821 }\r
7c342378 822\r
2ac68e8b
QL
823 if (EVP_add_digest (EVP_sha384 ()) == 0) {\r
824 return FALSE;\r
825 }\r
7c342378 826\r
2ac68e8b
QL
827 if (EVP_add_digest (EVP_sha512 ()) == 0) {\r
828 return FALSE;\r
829 }\r
7c342378 830\r
dda39f3a 831 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
832 return FALSE;\r
833 }\r
834\r
532616bb 835 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
836 if (!Status) {\r
837 return Status;\r
838 }\r
839\r
840 Status = FALSE;\r
2ac68e8b 841\r
532616bb 842 //\r
843 // Retrieve PKCS#7 Data (DER encoding)\r
844 //\r
845 if (SignedDataSize > INT_MAX) {\r
846 goto _Exit;\r
847 }\r
848\r
7c342378
MK
849 Temp = SignedData;\r
850 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);\r
532616bb 851 if (Pkcs7 == NULL) {\r
852 goto _Exit;\r
853 }\r
854\r
855 //\r
856 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
857 //\r
858 if (!PKCS7_type_is_signed (Pkcs7)) {\r
859 goto _Exit;\r
860 }\r
861\r
862 //\r
863 // Read DER-encoded root certificate and Construct X509 Certificate\r
864 //\r
1463ce18 865 Temp = TrustedCert;\r
7c342378 866 Cert = d2i_X509 (NULL, &Temp, (long)CertLength);\r
532616bb 867 if (Cert == NULL) {\r
868 goto _Exit;\r
869 }\r
870\r
871 //\r
872 // Setup X509 Store for trusted certificate\r
873 //\r
874 CertStore = X509_STORE_new ();\r
875 if (CertStore == NULL) {\r
876 goto _Exit;\r
877 }\r
7c342378 878\r
532616bb 879 if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
880 goto _Exit;\r
881 }\r
882\r
532616bb 883 //\r
884 // For generic PKCS#7 handling, InData may be NULL if the content is present\r
885 // in PKCS#7 structure. So ignore NULL checking here.\r
886 //\r
7c342378 887 DataBio = BIO_new_mem_buf (InData, (int)DataLength);\r
5b2956ea
YT
888 if (DataBio == NULL) {\r
889 goto _Exit;\r
890 }\r
891\r
68547181
DW
892 //\r
893 // Allow partial certificate chains, terminated by a non-self-signed but\r
de0408be 894 // still trusted intermediate certificate. Also disable time checks.\r
68547181 895 //\r
7c342378
MK
896 X509_STORE_set_flags (\r
897 CertStore,\r
898 X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME\r
899 );\r
68547181 900\r
02ee8d3b 901 //\r
902 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and\r
903 // doesn't support the extended key usage for Authenticode Code Signing.\r
904 // Bypass the certificate purpose checking by enabling any purposes setting.\r
905 //\r
906 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);\r
907\r
532616bb 908 //\r
909 // Verifies the PKCS#7 signedData structure\r
910 //\r
7c342378 911 Status = (BOOLEAN)PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
532616bb 912\r
913_Exit:\r
914 //\r
915 // Release Resources\r
916 //\r
917 BIO_free (DataBio);\r
532616bb 918 X509_free (Cert);\r
919 X509_STORE_free (CertStore);\r
920 PKCS7_free (Pkcs7);\r
921\r
922 if (!Wrapped) {\r
923 OPENSSL_free (SignedData);\r
924 }\r
925\r
926 return Status;\r
afeb55e4 927}\r