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 GLOBAL_REMOVE_IF_UNREFERENCED
const 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.
159 STACK_OF (X509
) *CertStack
;
168 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
172 CertStack
= (STACK_OF (X509
) *) X509Stack
;
174 X509Cert
= sk_X509_pop (CertStack
);
176 if (X509Cert
== NULL
) {
182 CertBio
= BIO_new (BIO_s_mem ());
183 if (CertBio
== NULL
) {
187 Result
= i2d_X509_bio (CertBio
, X509Cert
);
192 BIO_get_mem_ptr (CertBio
, &Ptr
);
193 Length
= (INT32
)(Ptr
->length
);
198 Buffer
= malloc (Length
);
199 if (Buffer
== NULL
) {
203 Result
= BIO_read (CertBio
, Buffer
, Length
);
204 if (Result
!= Length
) {
217 if (!Status
&& (Buffer
!= NULL
)) {
225 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
226 Cryptographic Message Syntax Standard". The input signed data could be wrapped
227 in a ContentInfo structure.
229 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
230 return FALSE. If P7Length overflow, then return FALSE.
232 Caution: This function may receive untrusted input.
233 UEFI Authenticated Variable is external input, so this function will do basic
234 check for PKCS#7 data structure.
236 @param[in] P7Data Pointer to the PKCS#7 message to verify.
237 @param[in] P7Length Length of the PKCS#7 message in bytes.
238 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
239 It's caller's responsibility to free the buffer with
241 This data structure is EFI_CERT_STACK type.
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 responsibility to free the buffer with
246 @param[out] CertLength Length of the trusted certificate in bytes.
248 @retval TRUE The operation is finished successfully.
249 @retval FALSE Error occurs during the operation.
255 IN CONST UINT8
*P7Data
,
257 OUT UINT8
**CertStack
,
258 OUT UINTN
*StackLength
,
259 OUT UINT8
**TrustedCert
,
260 OUT UINTN
*CertLength
267 UINTN SignedDataSize
;
270 STACK_OF (X509
) *Stack
;
277 UINTN SingleCertSize
;
279 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
280 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
))
285 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
298 // Retrieve PKCS#7 Data (DER encoding)
300 if (SignedDataSize
> INT_MAX
) {
305 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
311 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
313 if (!PKCS7_type_is_signed (Pkcs7
)) {
317 Stack
= PKCS7_get0_signers (Pkcs7
, NULL
, PKCS7_BINARY
);
323 // Convert CertStack to buffer in following format:
325 // UINT32 Cert1Length;
327 // UINT32 Cert2Length;
330 // UINT32 CertnLength;
333 BufferSize
= sizeof (UINT8
);
334 OldSize
= BufferSize
;
336 for (Index
= 0; ; Index
++) {
337 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
342 OldSize
= BufferSize
;
344 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
345 CertBuf
= malloc (BufferSize
);
347 if (CertBuf
== NULL
) {
351 if (OldBuf
!= NULL
) {
352 CopyMem (CertBuf
, OldBuf
, OldSize
);
357 WriteUnaligned32 ((UINT32
*)(CertBuf
+ OldSize
), (UINT32
)SingleCertSize
);
358 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
364 if (CertBuf
!= NULL
) {
366 // Update CertNumber.
370 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
371 *TrustedCert
= malloc (*CertLength
);
372 if (*TrustedCert
== NULL
) {
376 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
377 *CertStack
= CertBuf
;
378 *StackLength
= BufferSize
;
395 sk_X509_pop_free (Stack
, X509_free
);
398 if (SingleCert
!= NULL
) {
402 if (!Status
&& (CertBuf
!= NULL
)) {
407 if (OldBuf
!= NULL
) {
415 Wrap function to use free() to free allocated memory for certificates.
417 @param[in] Certs Pointer to the certificates to be freed.
434 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
435 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
436 unchained to the signer's certificates.
437 The input signed data could be wrapped in a ContentInfo structure.
439 @param[in] P7Data Pointer to the PKCS#7 message.
440 @param[in] P7Length Length of the PKCS#7 message in bytes.
441 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's
442 certificate. It's caller's responsibility to free the buffer
443 with Pkcs7FreeSigners().
444 This data structure is EFI_CERT_STACK type.
445 @param[out] ChainLength Length of the chained certificates list buffer in bytes.
446 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's
447 responsibility to free the buffer with Pkcs7FreeSigners().
448 This data structure is EFI_CERT_STACK type.
449 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.
451 @retval TRUE The operation is finished successfully.
452 @retval FALSE Error occurs during the operation.
457 Pkcs7GetCertificatesList (
458 IN CONST UINT8
*P7Data
,
460 OUT UINT8
**SignerChainCerts
,
461 OUT UINTN
*ChainLength
,
462 OUT UINT8
**UnchainCerts
,
463 OUT UINTN
*UnchainLength
472 X509_STORE_CTX
*CertCtx
;
474 STACK_OF (X509
) *CtxChain
;
475 STACK_OF (X509
) *CtxUntrusted
;
478 STACK_OF (X509
) *Signers
;
482 X509_NAME
*IssuerName
;
507 // Parameter Checking
509 if ((P7Data
== NULL
) || (SignerChainCerts
== NULL
) || (ChainLength
== NULL
) ||
510 (UnchainCerts
== NULL
) || (UnchainLength
== NULL
) || (P7Length
> INT_MAX
))
515 *SignerChainCerts
= NULL
;
517 *UnchainCerts
= NULL
;
521 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.
523 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &NewP7Data
, &NewP7Length
);
524 if (!Status
|| (NewP7Length
> INT_MAX
)) {
529 // Decodes PKCS#7 SignedData
531 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&NewP7Data
, (int)NewP7Length
);
532 if ((Pkcs7
== NULL
) || (!PKCS7_type_is_signed (Pkcs7
))) {
537 // Obtains Signer's Certificate from PKCS#7 data
538 // NOTE: Only one signer case will be handled in this function, which means SignerInfos
539 // should include only one signer's certificate.
541 Signers
= PKCS7_get0_signers (Pkcs7
, NULL
, PKCS7_BINARY
);
542 if ((Signers
== NULL
) || (sk_X509_num (Signers
) != 1)) {
546 Signer
= sk_X509_value (Signers
, 0);
548 CertCtx
= X509_STORE_CTX_new ();
549 if (CertCtx
== NULL
) {
553 if (!X509_STORE_CTX_init (CertCtx
, NULL
, Signer
, Pkcs7
->d
.sign
->cert
)) {
558 // Initialize Chained & Untrusted stack
560 CtxChain
= X509_STORE_CTX_get0_chain (CertCtx
);
561 CtxCert
= X509_STORE_CTX_get0_cert (CertCtx
);
562 if (CtxChain
== NULL
) {
563 if (((CtxChain
= sk_X509_new_null ()) == NULL
) ||
564 (!sk_X509_push (CtxChain
, CtxCert
)))
570 CtxUntrusted
= X509_STORE_CTX_get0_untrusted (CertCtx
);
571 if (CtxUntrusted
!= NULL
) {
572 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Signer
);
576 // Build certificates stack chained from Signer's certificate.
581 // Self-Issue checking
584 if (X509_STORE_CTX_get1_issuer (&Issuer
, CertCtx
, Cert
) == 1) {
585 if (X509_cmp (Issuer
, Cert
) == 0) {
591 // Found the issuer of the current certificate
593 if (CtxUntrusted
!= NULL
) {
595 IssuerName
= X509_get_issuer_name (Cert
);
596 Issuer
= X509_find_by_subject (CtxUntrusted
, IssuerName
);
597 if (Issuer
!= NULL
) {
598 if (!sk_X509_push (CtxChain
, Issuer
)) {
602 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Issuer
);
613 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:
615 // UINT32 Cert1Length;
617 // UINT32 Cert2Length;
620 // UINT32 CertnLength;
624 if (CtxChain
!= NULL
) {
625 BufferSize
= sizeof (UINT8
);
628 for (Index
= 0; ; Index
++) {
629 Status
= X509PopCertificate (CtxChain
, &SingleCert
, &CertSize
);
634 OldSize
= BufferSize
;
636 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
637 CertBuf
= malloc (BufferSize
);
639 if (CertBuf
== NULL
) {
644 if (OldBuf
!= NULL
) {
645 CopyMem (CertBuf
, OldBuf
, OldSize
);
650 WriteUnaligned32 ((UINT32
*)(CertBuf
+ OldSize
), (UINT32
)CertSize
);
651 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
657 if (CertBuf
!= NULL
) {
659 // Update CertNumber.
663 *SignerChainCerts
= CertBuf
;
664 *ChainLength
= BufferSize
;
668 if (CtxUntrusted
!= NULL
) {
669 BufferSize
= sizeof (UINT8
);
672 for (Index
= 0; ; Index
++) {
673 Status
= X509PopCertificate (CtxUntrusted
, &SingleCert
, &CertSize
);
678 OldSize
= BufferSize
;
680 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
681 CertBuf
= malloc (BufferSize
);
683 if (CertBuf
== NULL
) {
688 if (OldBuf
!= NULL
) {
689 CopyMem (CertBuf
, OldBuf
, OldSize
);
694 WriteUnaligned32 ((UINT32
*)(CertBuf
+ OldSize
), (UINT32
)CertSize
);
695 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
701 if (CertBuf
!= NULL
) {
703 // Update CertNumber.
707 *UnchainCerts
= CertBuf
;
708 *UnchainLength
= BufferSize
;
716 // Release Resources.
718 if (!Wrapped
&& (NewP7Data
!= NULL
)) {
726 sk_X509_free (Signers
);
728 if (CertCtx
!= NULL
) {
729 X509_STORE_CTX_cleanup (CertCtx
);
730 X509_STORE_CTX_free (CertCtx
);
733 if (SingleCert
!= NULL
) {
737 if (OldBuf
!= NULL
) {
741 if (!Status
&& (CertBuf
!= NULL
)) {
743 *SignerChainCerts
= NULL
;
744 *UnchainCerts
= NULL
;
751 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:
752 Cryptographic Message Syntax Standard". The input signed data could be wrapped
753 in a ContentInfo structure.
755 If P7Data, TrustedCert or InData is NULL, then return FALSE.
756 If P7Length, CertLength or DataLength overflow, then return FALSE.
758 Caution: This function may receive untrusted input.
759 UEFI Authenticated Variable is external input, so this function will do basic
760 check for PKCS#7 data structure.
762 @param[in] P7Data Pointer to the PKCS#7 message to verify.
763 @param[in] P7Length Length of the PKCS#7 message in bytes.
764 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
765 is used for certificate chain verification.
766 @param[in] CertLength Length of the trusted certificate in bytes.
767 @param[in] InData Pointer to the content to be verified.
768 @param[in] DataLength Length of InData in bytes.
770 @retval TRUE The specified PKCS#7 signed data is valid.
771 @retval FALSE Invalid PKCS#7 signed data.
777 IN CONST UINT8
*P7Data
,
779 IN CONST UINT8
*TrustedCert
,
781 IN CONST UINT8
*InData
,
789 X509_STORE
*CertStore
;
792 UINTN SignedDataSize
;
796 // Check input parameters.
798 if ((P7Data
== NULL
) || (TrustedCert
== NULL
) || (InData
== NULL
) ||
799 (P7Length
> INT_MAX
) || (CertLength
> INT_MAX
) || (DataLength
> INT_MAX
))
810 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
812 if (EVP_add_digest (EVP_md5 ()) == 0) {
816 if (EVP_add_digest (EVP_sha1 ()) == 0) {
820 if (EVP_add_digest (EVP_sha256 ()) == 0) {
824 if (EVP_add_digest (EVP_sha384 ()) == 0) {
828 if (EVP_add_digest (EVP_sha512 ()) == 0) {
832 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
836 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
844 // Retrieve PKCS#7 Data (DER encoding)
846 if (SignedDataSize
> INT_MAX
) {
851 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
857 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
859 if (!PKCS7_type_is_signed (Pkcs7
)) {
864 // Read DER-encoded root certificate and Construct X509 Certificate
867 Cert
= d2i_X509 (NULL
, &Temp
, (long)CertLength
);
873 // Setup X509 Store for trusted certificate
875 CertStore
= X509_STORE_new ();
876 if (CertStore
== NULL
) {
880 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
885 // For generic PKCS#7 handling, InData may be NULL if the content is present
886 // in PKCS#7 structure. So ignore NULL checking here.
888 DataBio
= BIO_new_mem_buf (InData
, (int)DataLength
);
889 if (DataBio
== NULL
) {
894 // Allow partial certificate chains, terminated by a non-self-signed but
895 // still trusted intermediate certificate. Also disable time checks.
897 X509_STORE_set_flags (
899 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
903 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
904 // doesn't support the extended key usage for Authenticode Code Signing.
905 // Bypass the certificate purpose checking by enabling any purposes setting.
907 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
910 // Verifies the PKCS#7 signedData structure
912 Status
= (BOOLEAN
)PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
920 X509_STORE_free (CertStore
);
924 OPENSSL_free (SignedData
);