2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
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.
10 WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated
11 Variable and will do basic check for data structure.
13 Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
14 SPDX-License-Identifier: BSD-2-Clause-Patent
18 #include "InternalCryptLib.h"
20 #include <openssl/objects.h>
21 #include <openssl/x509.h>
22 #include <openssl/x509v3.h>
23 #include <openssl/pkcs7.h>
25 UINT8 mOidValue
[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
28 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
29 a new structure to wrap P7Data.
31 Caution: This function may receive untrusted input.
32 UEFI Authenticated Variable is external input, so this function will do basic
33 check for PKCS#7 data structure.
35 @param[in] P7Data Pointer to the PKCS#7 message to verify.
36 @param[in] P7Length Length of the PKCS#7 message in bytes.
37 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
39 @param[out] WrapData If return status of this function is TRUE:
40 1) when WrapFlag is TRUE, pointer to P7Data.
41 2) when WrapFlag is FALSE, pointer to a new ContentInfo
42 structure. It's caller's responsibility to free this
44 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
46 @retval TRUE The operation is finished successfully.
47 @retval FALSE The operation is failed due to lack of resources.
52 IN CONST UINT8
*P7Data
,
54 OUT BOOLEAN
*WrapFlag
,
56 OUT UINTN
*WrapDataSize
63 // Check whether input P7Data is a wrapped ContentInfo structure or not.
66 if ((P7Data
[4] == 0x06) && (P7Data
[5] == 0x09)) {
67 if (CompareMem (P7Data
+ 6, mOidValue
, sizeof (mOidValue
)) == 0) {
68 if ((P7Data
[15] == 0xA0) && (P7Data
[16] == 0x82)) {
75 *WrapData
= (UINT8
*) P7Data
;
76 *WrapDataSize
= P7Length
;
79 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
81 *WrapDataSize
= P7Length
+ 19;
82 *WrapData
= malloc (*WrapDataSize
);
83 if (*WrapData
== NULL
) {
88 SignedData
= *WrapData
;
97 // Part2: Length1 = P7Length + 19 - 4, in big endian.
99 SignedData
[2] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) >> 8);
100 SignedData
[3] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) & 0xff);
103 // Part3: 0x06, 0x09.
105 SignedData
[4] = 0x06;
106 SignedData
[5] = 0x09;
109 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
111 CopyMem (SignedData
+ 6, mOidValue
, sizeof (mOidValue
));
114 // Part5: 0xA0, 0x82.
116 SignedData
[15] = 0xA0;
117 SignedData
[16] = 0x82;
120 // Part6: Length2 = P7Length, in big endian.
122 SignedData
[17] = (UINT8
) (((UINT16
) P7Length
) >> 8);
123 SignedData
[18] = (UINT8
) (((UINT16
) P7Length
) & 0xff);
128 CopyMem (SignedData
+ 19, P7Data
, P7Length
);
136 Pop single certificate from STACK_OF(X509).
138 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
140 @param[in] X509Stack Pointer to a X509 stack object.
141 @param[out] Cert Pointer to a X509 certificate.
142 @param[out] CertSize Length of output X509 certificate in bytes.
144 @retval TRUE The X509 stack pop succeeded.
145 @retval FALSE The pop operation failed.
157 STACK_OF(X509
) *CertStack
;
166 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
170 CertStack
= (STACK_OF(X509
) *) X509Stack
;
172 X509Cert
= sk_X509_pop (CertStack
);
174 if (X509Cert
== NULL
) {
180 CertBio
= BIO_new (BIO_s_mem ());
181 if (CertBio
== NULL
) {
185 Result
= i2d_X509_bio (CertBio
, X509Cert
);
190 BIO_get_mem_ptr (CertBio
, &Ptr
);
191 Length
= (INT32
)(Ptr
->length
);
196 Buffer
= malloc (Length
);
197 if (Buffer
== NULL
) {
201 Result
= BIO_read (CertBio
, Buffer
, Length
);
202 if (Result
!= Length
) {
215 if (!Status
&& (Buffer
!= NULL
)) {
223 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
224 Cryptographic Message Syntax Standard". The input signed data could be wrapped
225 in a ContentInfo structure.
227 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
228 return FALSE. If P7Length overflow, then return FALSE.
230 Caution: This function may receive untrusted input.
231 UEFI Authenticated Variable is external input, so this function will do basic
232 check for PKCS#7 data structure.
234 @param[in] P7Data Pointer to the PKCS#7 message to verify.
235 @param[in] P7Length Length of the PKCS#7 message in bytes.
236 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
237 It's caller's responsibility to free the buffer with
239 This data structure is EFI_CERT_STACK type.
240 @param[out] StackLength Length of signer's certificates in bytes.
241 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
242 It's caller's responsibility to free the buffer with
244 @param[out] CertLength Length of the trusted certificate in bytes.
246 @retval TRUE The operation is finished successfully.
247 @retval FALSE Error occurs during the operation.
253 IN CONST UINT8
*P7Data
,
255 OUT UINT8
**CertStack
,
256 OUT UINTN
*StackLength
,
257 OUT UINT8
**TrustedCert
,
258 OUT UINTN
*CertLength
265 UINTN SignedDataSize
;
267 STACK_OF(X509
) *Stack
;
274 UINTN SingleCertSize
;
276 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
277 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
281 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
294 // Retrieve PKCS#7 Data (DER encoding)
296 if (SignedDataSize
> INT_MAX
) {
301 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
307 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
309 if (!PKCS7_type_is_signed (Pkcs7
)) {
313 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
319 // Convert CertStack to buffer in following format:
321 // UINT32 Cert1Length;
323 // UINT32 Cert2Length;
326 // UINT32 CertnLength;
329 BufferSize
= sizeof (UINT8
);
330 OldSize
= BufferSize
;
332 for (Index
= 0; ; Index
++) {
333 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
338 OldSize
= BufferSize
;
340 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
341 CertBuf
= malloc (BufferSize
);
343 if (CertBuf
== NULL
) {
347 if (OldBuf
!= NULL
) {
348 CopyMem (CertBuf
, OldBuf
, OldSize
);
353 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
354 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
360 if (CertBuf
!= NULL
) {
362 // Update CertNumber.
366 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
367 *TrustedCert
= malloc (*CertLength
);
368 if (*TrustedCert
== NULL
) {
372 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
373 *CertStack
= CertBuf
;
374 *StackLength
= BufferSize
;
391 sk_X509_pop_free(Stack
, X509_free
);
394 if (SingleCert
!= NULL
) {
398 if (!Status
&& (CertBuf
!= NULL
)) {
403 if (OldBuf
!= NULL
) {
411 Wrap function to use free() to free allocated memory for certificates.
413 @param[in] Certs Pointer to the certificates to be freed.
430 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
431 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
432 unchained to the signer's certificates.
433 The input signed data could be wrapped in a ContentInfo structure.
435 @param[in] P7Data Pointer to the PKCS#7 message.
436 @param[in] P7Length Length of the PKCS#7 message in bytes.
437 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's
438 certificate. It's caller's responsibility to free the buffer
439 with Pkcs7FreeSigners().
440 This data structure is EFI_CERT_STACK type.
441 @param[out] ChainLength Length of the chained certificates list buffer in bytes.
442 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's
443 responsibility to free the buffer with Pkcs7FreeSigners().
444 This data structure is EFI_CERT_STACK type.
445 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.
447 @retval TRUE The operation is finished successfully.
448 @retval FALSE Error occurs during the operation.
453 Pkcs7GetCertificatesList (
454 IN CONST UINT8
*P7Data
,
456 OUT UINT8
**SignerChainCerts
,
457 OUT UINTN
*ChainLength
,
458 OUT UINT8
**UnchainCerts
,
459 OUT UINTN
*UnchainLength
468 X509_STORE_CTX
*CertCtx
;
469 STACK_OF(X509
) *CtxChain
;
470 STACK_OF(X509
) *CtxUntrusted
;
472 STACK_OF(X509
) *Signers
;
476 X509_NAME
*IssuerName
;
500 ZeroMem (&CertCtx
, sizeof (CertCtx
));
503 // Parameter Checking
505 if ((P7Data
== NULL
) || (SignerChainCerts
== NULL
) || (ChainLength
== NULL
) ||
506 (UnchainCerts
== NULL
) || (UnchainLength
== NULL
) || (P7Length
> INT_MAX
)) {
510 *SignerChainCerts
= NULL
;
512 *UnchainCerts
= NULL
;
516 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.
518 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &NewP7Data
, &NewP7Length
);
519 if (!Status
|| (NewP7Length
> INT_MAX
)) {
524 // Decodes PKCS#7 SignedData
526 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &NewP7Data
, (int) NewP7Length
);
527 if ((Pkcs7
== NULL
) || (!PKCS7_type_is_signed (Pkcs7
))) {
532 // Obtains Signer's Certificate from PKCS#7 data
533 // NOTE: Only one signer case will be handled in this function, which means SignerInfos
534 // should include only one signer's certificate.
536 Signers
= PKCS7_get0_signers (Pkcs7
, NULL
, PKCS7_BINARY
);
537 if ((Signers
== NULL
) || (sk_X509_num (Signers
) != 1)) {
540 Signer
= sk_X509_value (Signers
, 0);
542 CertCtx
= X509_STORE_CTX_new ();
543 if (CertCtx
== NULL
) {
546 if (!X509_STORE_CTX_init (CertCtx
, NULL
, Signer
, Pkcs7
->d
.sign
->cert
)) {
550 // Initialize Chained & Untrusted stack
552 CtxChain
= X509_STORE_CTX_get0_chain (CertCtx
);
553 CtxCert
= X509_STORE_CTX_get0_cert (CertCtx
);
554 if (CtxChain
== NULL
) {
555 if (((CtxChain
= sk_X509_new_null ()) == NULL
) ||
556 (!sk_X509_push (CtxChain
, CtxCert
))) {
560 CtxUntrusted
= X509_STORE_CTX_get0_untrusted (CertCtx
);
561 if (CtxUntrusted
!= NULL
) {
562 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Signer
);
566 // Build certificates stack chained from Signer's certificate.
571 // Self-Issue checking
574 if (X509_STORE_CTX_get1_issuer (&Issuer
, CertCtx
, Cert
) == 1) {
575 if (X509_cmp (Issuer
, Cert
) == 0) {
581 // Found the issuer of the current certificate
583 if (CtxUntrusted
!= NULL
) {
585 IssuerName
= X509_get_issuer_name (Cert
);
586 Issuer
= X509_find_by_subject (CtxUntrusted
, IssuerName
);
587 if (Issuer
!= NULL
) {
588 if (!sk_X509_push (CtxChain
, Issuer
)) {
591 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Issuer
);
602 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:
604 // UINT32 Cert1Length;
606 // UINT32 Cert2Length;
609 // UINT32 CertnLength;
613 if (CtxChain
!= NULL
) {
614 BufferSize
= sizeof (UINT8
);
617 for (Index
= 0; ; Index
++) {
618 Status
= X509PopCertificate (CtxChain
, &SingleCert
, &CertSize
);
623 OldSize
= BufferSize
;
625 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
626 CertBuf
= malloc (BufferSize
);
628 if (CertBuf
== NULL
) {
632 if (OldBuf
!= NULL
) {
633 CopyMem (CertBuf
, OldBuf
, OldSize
);
638 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) CertSize
);
639 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
645 if (CertBuf
!= NULL
) {
647 // Update CertNumber.
651 *SignerChainCerts
= CertBuf
;
652 *ChainLength
= BufferSize
;
656 if (CtxUntrusted
!= NULL
) {
657 BufferSize
= sizeof (UINT8
);
660 for (Index
= 0; ; Index
++) {
661 Status
= X509PopCertificate (CtxUntrusted
, &SingleCert
, &CertSize
);
666 OldSize
= BufferSize
;
668 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
669 CertBuf
= malloc (BufferSize
);
671 if (CertBuf
== NULL
) {
675 if (OldBuf
!= NULL
) {
676 CopyMem (CertBuf
, OldBuf
, OldSize
);
681 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) CertSize
);
682 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
688 if (CertBuf
!= NULL
) {
690 // Update CertNumber.
694 *UnchainCerts
= CertBuf
;
695 *UnchainLength
= BufferSize
;
703 // Release Resources.
705 if (!Wrapped
&& (NewP7Data
!= NULL
)) {
712 sk_X509_free (Signers
);
714 if (CertCtx
!= NULL
) {
715 X509_STORE_CTX_cleanup (CertCtx
);
716 X509_STORE_CTX_free (CertCtx
);
719 if (SingleCert
!= NULL
) {
723 if (OldBuf
!= NULL
) {
727 if (!Status
&& (CertBuf
!= NULL
)) {
729 *SignerChainCerts
= NULL
;
730 *UnchainCerts
= NULL
;
737 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:
738 Cryptographic Message Syntax Standard". The input signed data could be wrapped
739 in a ContentInfo structure.
741 If P7Data, TrustedCert or InData is NULL, then return FALSE.
742 If P7Length, CertLength or DataLength overflow, then return FALSE.
744 Caution: This function may receive untrusted input.
745 UEFI Authenticated Variable is external input, so this function will do basic
746 check for PKCS#7 data structure.
748 @param[in] P7Data Pointer to the PKCS#7 message to verify.
749 @param[in] P7Length Length of the PKCS#7 message in bytes.
750 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
751 is used for certificate chain verification.
752 @param[in] CertLength Length of the trusted certificate in bytes.
753 @param[in] InData Pointer to the content to be verified.
754 @param[in] DataLength Length of InData in bytes.
756 @retval TRUE The specified PKCS#7 signed data is valid.
757 @retval FALSE Invalid PKCS#7 signed data.
763 IN CONST UINT8
*P7Data
,
765 IN CONST UINT8
*TrustedCert
,
767 IN CONST UINT8
*InData
,
775 X509_STORE
*CertStore
;
778 UINTN SignedDataSize
;
782 // Check input parameters.
784 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
785 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
795 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
797 if (EVP_add_digest (EVP_md5 ()) == 0) {
800 if (EVP_add_digest (EVP_sha1 ()) == 0) {
803 if (EVP_add_digest (EVP_sha256 ()) == 0) {
806 if (EVP_add_digest (EVP_sha384 ()) == 0) {
809 if (EVP_add_digest (EVP_sha512 ()) == 0) {
812 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
816 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
824 // Retrieve PKCS#7 Data (DER encoding)
826 if (SignedDataSize
> INT_MAX
) {
831 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
837 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
839 if (!PKCS7_type_is_signed (Pkcs7
)) {
844 // Read DER-encoded root certificate and Construct X509 Certificate
847 Cert
= d2i_X509 (NULL
, &Temp
, (long) CertLength
);
853 // Setup X509 Store for trusted certificate
855 CertStore
= X509_STORE_new ();
856 if (CertStore
== NULL
) {
859 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
864 // For generic PKCS#7 handling, InData may be NULL if the content is present
865 // in PKCS#7 structure. So ignore NULL checking here.
867 DataBio
= BIO_new (BIO_s_mem ());
868 if (DataBio
== NULL
) {
872 if (BIO_write (DataBio
, InData
, (int) DataLength
) <= 0) {
877 // Allow partial certificate chains, terminated by a non-self-signed but
878 // still trusted intermediate certificate. Also disable time checks.
880 X509_STORE_set_flags (CertStore
,
881 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
);
884 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
885 // doesn't support the extended key usage for Authenticode Code Signing.
886 // Bypass the certificate purpose checking by enabling any purposes setting.
888 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
891 // Verifies the PKCS#7 signedData structure
893 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
901 X509_STORE_free (CertStore
);
905 OPENSSL_free (SignedData
);