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.
158 STACK_OF (X509
) *CertStack
;
167 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
171 CertStack
= (STACK_OF (X509
) *) X509Stack
;
173 X509Cert
= sk_X509_pop (CertStack
);
175 if (X509Cert
== NULL
) {
181 CertBio
= BIO_new (BIO_s_mem ());
182 if (CertBio
== NULL
) {
186 Result
= i2d_X509_bio (CertBio
, X509Cert
);
191 BIO_get_mem_ptr (CertBio
, &Ptr
);
192 Length
= (INT32
)(Ptr
->length
);
197 Buffer
= malloc (Length
);
198 if (Buffer
== NULL
) {
202 Result
= BIO_read (CertBio
, Buffer
, Length
);
203 if (Result
!= Length
) {
216 if (!Status
&& (Buffer
!= NULL
)) {
224 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
225 Cryptographic Message Syntax Standard". The input signed data could be wrapped
226 in a ContentInfo structure.
228 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
229 return FALSE. If P7Length overflow, then return FALSE.
231 Caution: This function may receive untrusted input.
232 UEFI Authenticated Variable is external input, so this function will do basic
233 check for PKCS#7 data structure.
235 @param[in] P7Data Pointer to the PKCS#7 message to verify.
236 @param[in] P7Length Length of the PKCS#7 message in bytes.
237 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
238 It's caller's responsibility to free the buffer with
240 This data structure is EFI_CERT_STACK type.
241 @param[out] StackLength Length of signer's certificates in bytes.
242 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
243 It's caller's responsibility to free the buffer with
245 @param[out] CertLength Length of the trusted certificate in bytes.
247 @retval TRUE The operation is finished successfully.
248 @retval FALSE Error occurs during the operation.
254 IN CONST UINT8
*P7Data
,
256 OUT UINT8
**CertStack
,
257 OUT UINTN
*StackLength
,
258 OUT UINT8
**TrustedCert
,
259 OUT UINTN
*CertLength
266 UINTN SignedDataSize
;
269 STACK_OF (X509
) *Stack
;
276 UINTN SingleCertSize
;
278 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
279 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
))
284 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
297 // Retrieve PKCS#7 Data (DER encoding)
299 if (SignedDataSize
> INT_MAX
) {
304 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
310 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
312 if (!PKCS7_type_is_signed (Pkcs7
)) {
316 Stack
= PKCS7_get0_signers (Pkcs7
, NULL
, PKCS7_BINARY
);
322 // Convert CertStack to buffer in following format:
324 // UINT32 Cert1Length;
326 // UINT32 Cert2Length;
329 // UINT32 CertnLength;
332 BufferSize
= sizeof (UINT8
);
333 OldSize
= BufferSize
;
335 for (Index
= 0; ; Index
++) {
336 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
341 OldSize
= BufferSize
;
343 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
344 CertBuf
= malloc (BufferSize
);
346 if (CertBuf
== NULL
) {
350 if (OldBuf
!= NULL
) {
351 CopyMem (CertBuf
, OldBuf
, OldSize
);
356 WriteUnaligned32 ((UINT32
*)(CertBuf
+ OldSize
), (UINT32
)SingleCertSize
);
357 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
363 if (CertBuf
!= NULL
) {
365 // Update CertNumber.
369 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
370 *TrustedCert
= malloc (*CertLength
);
371 if (*TrustedCert
== NULL
) {
375 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
376 *CertStack
= CertBuf
;
377 *StackLength
= BufferSize
;
394 sk_X509_pop_free (Stack
, X509_free
);
397 if (SingleCert
!= NULL
) {
401 if (!Status
&& (CertBuf
!= NULL
)) {
406 if (OldBuf
!= NULL
) {
414 Wrap function to use free() to free allocated memory for certificates.
416 @param[in] Certs Pointer to the certificates to be freed.
433 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
434 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
435 unchained to the signer's certificates.
436 The input signed data could be wrapped in a ContentInfo structure.
438 @param[in] P7Data Pointer to the PKCS#7 message.
439 @param[in] P7Length Length of the PKCS#7 message in bytes.
440 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's
441 certificate. It's caller's responsibility to free the buffer
442 with Pkcs7FreeSigners().
443 This data structure is EFI_CERT_STACK type.
444 @param[out] ChainLength Length of the chained certificates list buffer in bytes.
445 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's
446 responsibility to free the buffer with Pkcs7FreeSigners().
447 This data structure is EFI_CERT_STACK type.
448 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.
450 @retval TRUE The operation is finished successfully.
451 @retval FALSE Error occurs during the operation.
456 Pkcs7GetCertificatesList (
457 IN CONST UINT8
*P7Data
,
459 OUT UINT8
**SignerChainCerts
,
460 OUT UINTN
*ChainLength
,
461 OUT UINT8
**UnchainCerts
,
462 OUT UINTN
*UnchainLength
471 X509_STORE_CTX
*CertCtx
;
473 STACK_OF (X509
) *CtxChain
;
474 STACK_OF (X509
) *CtxUntrusted
;
477 STACK_OF (X509
) *Signers
;
481 X509_NAME
*IssuerName
;
505 ZeroMem (&CertCtx
, sizeof (CertCtx
));
508 // Parameter Checking
510 if ((P7Data
== NULL
) || (SignerChainCerts
== NULL
) || (ChainLength
== NULL
) ||
511 (UnchainCerts
== NULL
) || (UnchainLength
== NULL
) || (P7Length
> INT_MAX
))
516 *SignerChainCerts
= NULL
;
518 *UnchainCerts
= NULL
;
522 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.
524 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &NewP7Data
, &NewP7Length
);
525 if (!Status
|| (NewP7Length
> INT_MAX
)) {
530 // Decodes PKCS#7 SignedData
532 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&NewP7Data
, (int)NewP7Length
);
533 if ((Pkcs7
== NULL
) || (!PKCS7_type_is_signed (Pkcs7
))) {
538 // Obtains Signer's Certificate from PKCS#7 data
539 // NOTE: Only one signer case will be handled in this function, which means SignerInfos
540 // should include only one signer's certificate.
542 Signers
= PKCS7_get0_signers (Pkcs7
, NULL
, PKCS7_BINARY
);
543 if ((Signers
== NULL
) || (sk_X509_num (Signers
) != 1)) {
547 Signer
= sk_X509_value (Signers
, 0);
549 CertCtx
= X509_STORE_CTX_new ();
550 if (CertCtx
== NULL
) {
554 if (!X509_STORE_CTX_init (CertCtx
, NULL
, Signer
, Pkcs7
->d
.sign
->cert
)) {
559 // Initialize Chained & Untrusted stack
561 CtxChain
= X509_STORE_CTX_get0_chain (CertCtx
);
562 CtxCert
= X509_STORE_CTX_get0_cert (CertCtx
);
563 if (CtxChain
== NULL
) {
564 if (((CtxChain
= sk_X509_new_null ()) == NULL
) ||
565 (!sk_X509_push (CtxChain
, CtxCert
)))
571 CtxUntrusted
= X509_STORE_CTX_get0_untrusted (CertCtx
);
572 if (CtxUntrusted
!= NULL
) {
573 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Signer
);
577 // Build certificates stack chained from Signer's certificate.
582 // Self-Issue checking
585 if (X509_STORE_CTX_get1_issuer (&Issuer
, CertCtx
, Cert
) == 1) {
586 if (X509_cmp (Issuer
, Cert
) == 0) {
592 // Found the issuer of the current certificate
594 if (CtxUntrusted
!= NULL
) {
596 IssuerName
= X509_get_issuer_name (Cert
);
597 Issuer
= X509_find_by_subject (CtxUntrusted
, IssuerName
);
598 if (Issuer
!= NULL
) {
599 if (!sk_X509_push (CtxChain
, Issuer
)) {
603 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Issuer
);
614 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:
616 // UINT32 Cert1Length;
618 // UINT32 Cert2Length;
621 // UINT32 CertnLength;
625 if (CtxChain
!= NULL
) {
626 BufferSize
= sizeof (UINT8
);
629 for (Index
= 0; ; Index
++) {
630 Status
= X509PopCertificate (CtxChain
, &SingleCert
, &CertSize
);
635 OldSize
= BufferSize
;
637 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
638 CertBuf
= malloc (BufferSize
);
640 if (CertBuf
== NULL
) {
645 if (OldBuf
!= NULL
) {
646 CopyMem (CertBuf
, OldBuf
, OldSize
);
651 WriteUnaligned32 ((UINT32
*)(CertBuf
+ OldSize
), (UINT32
)CertSize
);
652 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
658 if (CertBuf
!= NULL
) {
660 // Update CertNumber.
664 *SignerChainCerts
= CertBuf
;
665 *ChainLength
= BufferSize
;
669 if (CtxUntrusted
!= NULL
) {
670 BufferSize
= sizeof (UINT8
);
673 for (Index
= 0; ; Index
++) {
674 Status
= X509PopCertificate (CtxUntrusted
, &SingleCert
, &CertSize
);
679 OldSize
= BufferSize
;
681 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
682 CertBuf
= malloc (BufferSize
);
684 if (CertBuf
== NULL
) {
689 if (OldBuf
!= NULL
) {
690 CopyMem (CertBuf
, OldBuf
, OldSize
);
695 WriteUnaligned32 ((UINT32
*)(CertBuf
+ OldSize
), (UINT32
)CertSize
);
696 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
702 if (CertBuf
!= NULL
) {
704 // Update CertNumber.
708 *UnchainCerts
= CertBuf
;
709 *UnchainLength
= BufferSize
;
717 // Release Resources.
719 if (!Wrapped
&& (NewP7Data
!= NULL
)) {
727 sk_X509_free (Signers
);
729 if (CertCtx
!= NULL
) {
730 X509_STORE_CTX_cleanup (CertCtx
);
731 X509_STORE_CTX_free (CertCtx
);
734 if (SingleCert
!= NULL
) {
738 if (OldBuf
!= NULL
) {
742 if (!Status
&& (CertBuf
!= NULL
)) {
744 *SignerChainCerts
= NULL
;
745 *UnchainCerts
= NULL
;
752 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:
753 Cryptographic Message Syntax Standard". The input signed data could be wrapped
754 in a ContentInfo structure.
756 If P7Data, TrustedCert or InData is NULL, then return FALSE.
757 If P7Length, CertLength or DataLength overflow, then return FALSE.
759 Caution: This function may receive untrusted input.
760 UEFI Authenticated Variable is external input, so this function will do basic
761 check for PKCS#7 data structure.
763 @param[in] P7Data Pointer to the PKCS#7 message to verify.
764 @param[in] P7Length Length of the PKCS#7 message in bytes.
765 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
766 is used for certificate chain verification.
767 @param[in] CertLength Length of the trusted certificate in bytes.
768 @param[in] InData Pointer to the content to be verified.
769 @param[in] DataLength Length of InData in bytes.
771 @retval TRUE The specified PKCS#7 signed data is valid.
772 @retval FALSE Invalid PKCS#7 signed data.
778 IN CONST UINT8
*P7Data
,
780 IN CONST UINT8
*TrustedCert
,
782 IN CONST UINT8
*InData
,
790 X509_STORE
*CertStore
;
793 UINTN SignedDataSize
;
797 // Check input parameters.
799 if ((P7Data
== NULL
) || (TrustedCert
== NULL
) || (InData
== NULL
) ||
800 (P7Length
> INT_MAX
) || (CertLength
> INT_MAX
) || (DataLength
> INT_MAX
))
811 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
813 if (EVP_add_digest (EVP_md5 ()) == 0) {
817 if (EVP_add_digest (EVP_sha1 ()) == 0) {
821 if (EVP_add_digest (EVP_sha256 ()) == 0) {
825 if (EVP_add_digest (EVP_sha384 ()) == 0) {
829 if (EVP_add_digest (EVP_sha512 ()) == 0) {
833 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
837 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
845 // Retrieve PKCS#7 Data (DER encoding)
847 if (SignedDataSize
> INT_MAX
) {
852 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
858 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
860 if (!PKCS7_type_is_signed (Pkcs7
)) {
865 // Read DER-encoded root certificate and Construct X509 Certificate
868 Cert
= d2i_X509 (NULL
, &Temp
, (long)CertLength
);
874 // Setup X509 Store for trusted certificate
876 CertStore
= X509_STORE_new ();
877 if (CertStore
== NULL
) {
881 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
886 // For generic PKCS#7 handling, InData may be NULL if the content is present
887 // in PKCS#7 structure. So ignore NULL checking here.
889 DataBio
= BIO_new_mem_buf (InData
, (int)DataLength
);
890 if (DataBio
== NULL
) {
895 // Allow partial certificate chains, terminated by a non-self-signed but
896 // still trusted intermediate certificate. Also disable time checks.
898 X509_STORE_set_flags (
900 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
904 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
905 // doesn't support the extended key usage for Authenticode Code Signing.
906 // Bypass the certificate purpose checking by enabling any purposes setting.
908 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
911 // Verifies the PKCS#7 signedData structure
913 Status
= (BOOLEAN
)PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
921 X509_STORE_free (CertStore
);
925 OPENSSL_free (SignedData
);