]> git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
CryptoPkg/BaseCryptLib: Use X509_V_FLAG_NO_CHECK_TIME
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7Verify.c
1 /** @file
2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
3
4 Caution: This module requires additional review when modified.
5 This library will have external input - signature (e.g. UEFI Authenticated
6 Variable). It may by input in SMM mode.
7 This external input must be validated carefully to avoid security issue like
8 buffer overflow, integer overflow.
9
10 WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated
11 Variable and will do basic check for data structure.
12
13 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
14 This program and the accompanying materials
15 are licensed and made available under the terms and conditions of the BSD License
16 which accompanies this distribution. The full text of the license may be found at
17 http://opensource.org/licenses/bsd-license.php
18
19 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
20 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21
22 **/
23
24 #include "InternalCryptLib.h"
25
26 #include <openssl/objects.h>
27 #include <openssl/x509.h>
28 #include <openssl/x509v3.h>
29 #include <openssl/pkcs7.h>
30
31 UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
32
33 /**
34 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
35 a new structure to wrap P7Data.
36
37 Caution: This function may receive untrusted input.
38 UEFI Authenticated Variable is external input, so this function will do basic
39 check for PKCS#7 data structure.
40
41 @param[in] P7Data Pointer to the PKCS#7 message to verify.
42 @param[in] P7Length Length of the PKCS#7 message in bytes.
43 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
44 return FALSE.
45 @param[out] WrapData If return status of this function is TRUE:
46 1) when WrapFlag is TRUE, pointer to P7Data.
47 2) when WrapFlag is FALSE, pointer to a new ContentInfo
48 structure. It's caller's responsibility to free this
49 buffer.
50 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
51
52 @retval TRUE The operation is finished successfully.
53 @retval FALSE The operation is failed due to lack of resources.
54
55 **/
56 BOOLEAN
57 WrapPkcs7Data (
58 IN CONST UINT8 *P7Data,
59 IN UINTN P7Length,
60 OUT BOOLEAN *WrapFlag,
61 OUT UINT8 **WrapData,
62 OUT UINTN *WrapDataSize
63 )
64 {
65 BOOLEAN Wrapped;
66 UINT8 *SignedData;
67
68 //
69 // Check whether input P7Data is a wrapped ContentInfo structure or not.
70 //
71 Wrapped = FALSE;
72 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {
73 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {
74 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {
75 Wrapped = TRUE;
76 }
77 }
78 }
79
80 if (Wrapped) {
81 *WrapData = (UINT8 *) P7Data;
82 *WrapDataSize = P7Length;
83 } else {
84 //
85 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
86 //
87 *WrapDataSize = P7Length + 19;
88 *WrapData = malloc (*WrapDataSize);
89 if (*WrapData == NULL) {
90 *WrapFlag = Wrapped;
91 return FALSE;
92 }
93
94 SignedData = *WrapData;
95
96 //
97 // Part1: 0x30, 0x82.
98 //
99 SignedData[0] = 0x30;
100 SignedData[1] = 0x82;
101
102 //
103 // Part2: Length1 = P7Length + 19 - 4, in big endian.
104 //
105 SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);
106 SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);
107
108 //
109 // Part3: 0x06, 0x09.
110 //
111 SignedData[4] = 0x06;
112 SignedData[5] = 0x09;
113
114 //
115 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
116 //
117 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));
118
119 //
120 // Part5: 0xA0, 0x82.
121 //
122 SignedData[15] = 0xA0;
123 SignedData[16] = 0x82;
124
125 //
126 // Part6: Length2 = P7Length, in big endian.
127 //
128 SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);
129 SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);
130
131 //
132 // Part7: P7Data.
133 //
134 CopyMem (SignedData + 19, P7Data, P7Length);
135 }
136
137 *WrapFlag = Wrapped;
138 return TRUE;
139 }
140
141 /**
142 Pop single certificate from STACK_OF(X509).
143
144 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
145
146 @param[in] X509Stack Pointer to a X509 stack object.
147 @param[out] Cert Pointer to a X509 certificate.
148 @param[out] CertSize Length of output X509 certificate in bytes.
149
150 @retval TRUE The X509 stack pop succeeded.
151 @retval FALSE The pop operation failed.
152
153 **/
154 BOOLEAN
155 X509PopCertificate (
156 IN VOID *X509Stack,
157 OUT UINT8 **Cert,
158 OUT UINTN *CertSize
159 )
160 {
161 BIO *CertBio;
162 X509 *X509Cert;
163 STACK_OF(X509) *CertStack;
164 BOOLEAN Status;
165 INT32 Result;
166 INT32 Length;
167 VOID *Buffer;
168
169 Status = FALSE;
170
171 if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {
172 return Status;
173 }
174
175 CertStack = (STACK_OF(X509) *) X509Stack;
176
177 X509Cert = sk_X509_pop (CertStack);
178
179 if (X509Cert == NULL) {
180 return Status;
181 }
182
183 Buffer = NULL;
184
185 CertBio = BIO_new (BIO_s_mem ());
186 if (CertBio == NULL) {
187 return Status;
188 }
189
190 Result = i2d_X509_bio (CertBio, X509Cert);
191 if (Result == 0) {
192 goto _Exit;
193 }
194
195 Length = (INT32)(((BUF_MEM *) CertBio->ptr)->length);
196 if (Length <= 0) {
197 goto _Exit;
198 }
199
200 Buffer = malloc (Length);
201 if (Buffer == NULL) {
202 goto _Exit;
203 }
204
205 Result = BIO_read (CertBio, Buffer, Length);
206 if (Result != Length) {
207 goto _Exit;
208 }
209
210 *Cert = Buffer;
211 *CertSize = Length;
212
213 Status = TRUE;
214
215 _Exit:
216
217 BIO_free (CertBio);
218
219 if (!Status && (Buffer != NULL)) {
220 free (Buffer);
221 }
222
223 return Status;
224 }
225
226 /**
227 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
228 Cryptographic Message Syntax Standard". The input signed data could be wrapped
229 in a ContentInfo structure.
230
231 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
232 return FALSE. If P7Length overflow, then return FAlSE.
233
234 Caution: This function may receive untrusted input.
235 UEFI Authenticated Variable is external input, so this function will do basic
236 check for PKCS#7 data structure.
237
238 @param[in] P7Data Pointer to the PKCS#7 message to verify.
239 @param[in] P7Length Length of the PKCS#7 message in bytes.
240 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
241 It's caller's responsiblity to free the buffer.
242 @param[out] StackLength Length of signer's certificates in bytes.
243 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
244 It's caller's responsiblity to free the buffer.
245 @param[out] CertLength Length of the trusted certificate in bytes.
246
247 @retval TRUE The operation is finished successfully.
248 @retval FALSE Error occurs during the operation.
249
250 **/
251 BOOLEAN
252 EFIAPI
253 Pkcs7GetSigners (
254 IN CONST UINT8 *P7Data,
255 IN UINTN P7Length,
256 OUT UINT8 **CertStack,
257 OUT UINTN *StackLength,
258 OUT UINT8 **TrustedCert,
259 OUT UINTN *CertLength
260 )
261 {
262 PKCS7 *Pkcs7;
263 BOOLEAN Status;
264 UINT8 *SignedData;
265 CONST UINT8 *Temp;
266 UINTN SignedDataSize;
267 BOOLEAN Wrapped;
268 STACK_OF(X509) *Stack;
269 UINT8 Index;
270 UINT8 *CertBuf;
271 UINT8 *OldBuf;
272 UINTN BufferSize;
273 UINTN OldSize;
274 UINT8 *SingleCert;
275 UINTN SingleCertSize;
276
277 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
278 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {
279 return FALSE;
280 }
281
282 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
283 if (!Status) {
284 return Status;
285 }
286
287 Status = FALSE;
288 Pkcs7 = NULL;
289 Stack = NULL;
290 CertBuf = NULL;
291 OldBuf = NULL;
292 SingleCert = NULL;
293
294 //
295 // Retrieve PKCS#7 Data (DER encoding)
296 //
297 if (SignedDataSize > INT_MAX) {
298 goto _Exit;
299 }
300
301 Temp = SignedData;
302 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
303 if (Pkcs7 == NULL) {
304 goto _Exit;
305 }
306
307 //
308 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
309 //
310 if (!PKCS7_type_is_signed (Pkcs7)) {
311 goto _Exit;
312 }
313
314 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);
315 if (Stack == NULL) {
316 goto _Exit;
317 }
318
319 //
320 // Convert CertStack to buffer in following format:
321 // UINT8 CertNumber;
322 // UINT32 Cert1Length;
323 // UINT8 Cert1[];
324 // UINT32 Cert2Length;
325 // UINT8 Cert2[];
326 // ...
327 // UINT32 CertnLength;
328 // UINT8 Certn[];
329 //
330 BufferSize = sizeof (UINT8);
331 OldSize = BufferSize;
332
333 for (Index = 0; ; Index++) {
334 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);
335 if (!Status) {
336 break;
337 }
338
339 OldSize = BufferSize;
340 OldBuf = CertBuf;
341 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);
342 CertBuf = malloc (BufferSize);
343
344 if (CertBuf == NULL) {
345 goto _Exit;
346 }
347
348 if (OldBuf != NULL) {
349 CopyMem (CertBuf, OldBuf, OldSize);
350 free (OldBuf);
351 OldBuf = NULL;
352 }
353
354 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);
355 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);
356
357 free (SingleCert);
358 SingleCert = NULL;
359 }
360
361 if (CertBuf != NULL) {
362 //
363 // Update CertNumber.
364 //
365 CertBuf[0] = Index;
366
367 *CertLength = BufferSize - OldSize - sizeof (UINT32);
368 *TrustedCert = malloc (*CertLength);
369 if (*TrustedCert == NULL) {
370 goto _Exit;
371 }
372
373 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
374 *CertStack = CertBuf;
375 *StackLength = BufferSize;
376 Status = TRUE;
377 }
378
379 _Exit:
380 //
381 // Release Resources
382 //
383 if (!Wrapped) {
384 free (SignedData);
385 }
386
387 if (Pkcs7 != NULL) {
388 PKCS7_free (Pkcs7);
389 }
390
391 if (Stack != NULL) {
392 sk_X509_pop_free(Stack, X509_free);
393 }
394
395 if (SingleCert != NULL) {
396 free (SingleCert);
397 }
398
399 if (!Status && (CertBuf != NULL)) {
400 free (CertBuf);
401 *CertStack = NULL;
402 }
403
404 if (OldBuf != NULL) {
405 free (OldBuf);
406 }
407
408 return Status;
409 }
410
411 /**
412 Wrap function to use free() to free allocated memory for certificates.
413
414 @param[in] Certs Pointer to the certificates to be freed.
415
416 **/
417 VOID
418 EFIAPI
419 Pkcs7FreeSigners (
420 IN UINT8 *Certs
421 )
422 {
423 if (Certs == NULL) {
424 return;
425 }
426
427 free (Certs);
428 }
429
430 /**
431 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
432 Cryptographic Message Syntax Standard". The input signed data could be wrapped
433 in a ContentInfo structure.
434
435 If P7Data, TrustedCert or InData is NULL, then return FALSE.
436 If P7Length, CertLength or DataLength overflow, then return FAlSE.
437
438 Caution: This function may receive untrusted input.
439 UEFI Authenticated Variable is external input, so this function will do basic
440 check for PKCS#7 data structure.
441
442 @param[in] P7Data Pointer to the PKCS#7 message to verify.
443 @param[in] P7Length Length of the PKCS#7 message in bytes.
444 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
445 is used for certificate chain verification.
446 @param[in] CertLength Length of the trusted certificate in bytes.
447 @param[in] InData Pointer to the content to be verified.
448 @param[in] DataLength Length of InData in bytes.
449
450 @retval TRUE The specified PKCS#7 signed data is valid.
451 @retval FALSE Invalid PKCS#7 signed data.
452
453 **/
454 BOOLEAN
455 EFIAPI
456 Pkcs7Verify (
457 IN CONST UINT8 *P7Data,
458 IN UINTN P7Length,
459 IN CONST UINT8 *TrustedCert,
460 IN UINTN CertLength,
461 IN CONST UINT8 *InData,
462 IN UINTN DataLength
463 )
464 {
465 PKCS7 *Pkcs7;
466 BIO *DataBio;
467 BOOLEAN Status;
468 X509 *Cert;
469 X509_STORE *CertStore;
470 UINT8 *SignedData;
471 CONST UINT8 *Temp;
472 UINTN SignedDataSize;
473 BOOLEAN Wrapped;
474
475 //
476 // Check input parameters.
477 //
478 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||
479 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {
480 return FALSE;
481 }
482
483 Pkcs7 = NULL;
484 DataBio = NULL;
485 Cert = NULL;
486 CertStore = NULL;
487
488 //
489 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
490 //
491 if (EVP_add_digest (EVP_md5 ()) == 0) {
492 return FALSE;
493 }
494 if (EVP_add_digest (EVP_sha1 ()) == 0) {
495 return FALSE;
496 }
497 if (EVP_add_digest (EVP_sha256 ()) == 0) {
498 return FALSE;
499 }
500 if (EVP_add_digest (EVP_sha384 ()) == 0) {
501 return FALSE;
502 }
503 if (EVP_add_digest (EVP_sha512 ()) == 0) {
504 return FALSE;
505 }
506 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {
507 return FALSE;
508 }
509
510 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
511 if (!Status) {
512 return Status;
513 }
514
515 Status = FALSE;
516
517 //
518 // Retrieve PKCS#7 Data (DER encoding)
519 //
520 if (SignedDataSize > INT_MAX) {
521 goto _Exit;
522 }
523
524 Temp = SignedData;
525 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
526 if (Pkcs7 == NULL) {
527 goto _Exit;
528 }
529
530 //
531 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
532 //
533 if (!PKCS7_type_is_signed (Pkcs7)) {
534 goto _Exit;
535 }
536
537 //
538 // Read DER-encoded root certificate and Construct X509 Certificate
539 //
540 Temp = TrustedCert;
541 Cert = d2i_X509 (NULL, &Temp, (long) CertLength);
542 if (Cert == NULL) {
543 goto _Exit;
544 }
545
546 //
547 // Setup X509 Store for trusted certificate
548 //
549 CertStore = X509_STORE_new ();
550 if (CertStore == NULL) {
551 goto _Exit;
552 }
553 if (!(X509_STORE_add_cert (CertStore, Cert))) {
554 goto _Exit;
555 }
556
557 //
558 // For generic PKCS#7 handling, InData may be NULL if the content is present
559 // in PKCS#7 structure. So ignore NULL checking here.
560 //
561 DataBio = BIO_new (BIO_s_mem ());
562 if (DataBio == NULL) {
563 goto _Exit;
564 }
565
566 if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {
567 goto _Exit;
568 }
569
570 //
571 // Allow partial certificate chains, terminated by a non-self-signed but
572 // still trusted intermediate certificate. Also disable time checks.
573 //
574 X509_STORE_set_flags (CertStore,
575 X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);
576
577 //
578 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
579 // doesn't support the extended key usage for Authenticode Code Signing.
580 // Bypass the certificate purpose checking by enabling any purposes setting.
581 //
582 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);
583
584 //
585 // Verifies the PKCS#7 signedData structure
586 //
587 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);
588
589 _Exit:
590 //
591 // Release Resources
592 //
593 BIO_free (DataBio);
594 X509_free (Cert);
595 X509_STORE_free (CertStore);
596 PKCS7_free (Pkcs7);
597
598 if (!Wrapped) {
599 OPENSSL_free (SignedData);
600 }
601
602 return Status;
603 }
604
605 /**
606 Extracts the attached content from a PKCS#7 signed data if existed. The input signed
607 data could be wrapped in a ContentInfo structure.
608
609 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,
610 then return FAlSE. If the P7Data is not correctly formatted, then return FALSE.
611
612 Caution: This function may receive untrusted input. So this function will do
613 basic check for PKCS#7 data structure.
614
615 @param[in] P7Data Pointer to the PKCS#7 signed data to process.
616 @param[in] P7Length Length of the PKCS#7 signed data in bytes.
617 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.
618 It's caller's responsiblity to free the buffer.
619 @param[out] ContentSize The size of the extracted content in bytes.
620
621 @retval TRUE The P7Data was correctly formatted for processing.
622 @retval FALSE The P7Data was not correctly formatted for processing.
623
624 */
625 BOOLEAN
626 EFIAPI
627 Pkcs7GetAttachedContent (
628 IN CONST UINT8 *P7Data,
629 IN UINTN P7Length,
630 OUT VOID **Content,
631 OUT UINTN *ContentSize
632 )
633 {
634 BOOLEAN Status;
635 PKCS7 *Pkcs7;
636 UINT8 *SignedData;
637 UINTN SignedDataSize;
638 BOOLEAN Wrapped;
639 CONST UINT8 *Temp;
640 ASN1_OCTET_STRING *OctStr;
641
642 //
643 // Check input parameter.
644 //
645 if ((P7Data == NULL) || (P7Length > INT_MAX) || (Content == NULL) || (ContentSize == NULL)) {
646 return FALSE;
647 }
648
649 *Content = NULL;
650 Pkcs7 = NULL;
651 SignedData = NULL;
652 OctStr = NULL;
653
654 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
655 if (!Status || (SignedDataSize > INT_MAX)) {
656 goto _Exit;
657 }
658
659 Status = FALSE;
660
661 //
662 // Decoding PKCS#7 SignedData
663 //
664 Temp = SignedData;
665 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);
666 if (Pkcs7 == NULL) {
667 goto _Exit;
668 }
669
670 //
671 // The type of Pkcs7 must be signedData
672 //
673 if (!PKCS7_type_is_signed (Pkcs7)) {
674 goto _Exit;
675 }
676
677 //
678 // Check for detached or attached content
679 //
680 if (PKCS7_get_detached (Pkcs7)) {
681 //
682 // No Content supplied for PKCS7 detached signedData
683 //
684 *Content = NULL;
685 *ContentSize = 0;
686 } else {
687 //
688 // Retrieve the attached content in PKCS7 signedData
689 //
690 OctStr = Pkcs7->d.sign->contents->d.data;
691 if ((OctStr->length > 0) && (OctStr->data != NULL)) {
692 *ContentSize = OctStr->length;
693 *Content = malloc (*ContentSize);
694 if (*Content == NULL) {
695 *ContentSize = 0;
696 goto _Exit;
697 }
698 CopyMem (*Content, OctStr->data, *ContentSize);
699 }
700 }
701 Status = TRUE;
702
703 _Exit:
704 //
705 // Release Resources
706 //
707 PKCS7_free (Pkcs7);
708
709 if (!Wrapped) {
710 OPENSSL_free (SignedData);
711 }
712
713 return Status;
714 }