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 - 2017, 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
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.
24 #include "InternalCryptLib.h"
26 #include <openssl/objects.h>
27 #include <openssl/x509.h>
28 #include <openssl/x509v3.h>
29 #include <openssl/pkcs7.h>
31 UINT8 mOidValue
[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
34 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
35 a new structure to wrap P7Data.
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.
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
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
50 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
52 @retval TRUE The operation is finished successfully.
53 @retval FALSE The operation is failed due to lack of resources.
58 IN CONST UINT8
*P7Data
,
60 OUT BOOLEAN
*WrapFlag
,
62 OUT UINTN
*WrapDataSize
69 // Check whether input P7Data is a wrapped ContentInfo structure or not.
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)) {
81 *WrapData
= (UINT8
*) P7Data
;
82 *WrapDataSize
= P7Length
;
85 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
87 *WrapDataSize
= P7Length
+ 19;
88 *WrapData
= malloc (*WrapDataSize
);
89 if (*WrapData
== NULL
) {
94 SignedData
= *WrapData
;
100 SignedData
[1] = 0x82;
103 // Part2: Length1 = P7Length + 19 - 4, in big endian.
105 SignedData
[2] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) >> 8);
106 SignedData
[3] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) & 0xff);
109 // Part3: 0x06, 0x09.
111 SignedData
[4] = 0x06;
112 SignedData
[5] = 0x09;
115 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
117 CopyMem (SignedData
+ 6, mOidValue
, sizeof (mOidValue
));
120 // Part5: 0xA0, 0x82.
122 SignedData
[15] = 0xA0;
123 SignedData
[16] = 0x82;
126 // Part6: Length2 = P7Length, in big endian.
128 SignedData
[17] = (UINT8
) (((UINT16
) P7Length
) >> 8);
129 SignedData
[18] = (UINT8
) (((UINT16
) P7Length
) & 0xff);
134 CopyMem (SignedData
+ 19, P7Data
, P7Length
);
142 Pop single certificate from STACK_OF(X509).
144 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
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.
150 @retval TRUE The X509 stack pop succeeded.
151 @retval FALSE The pop operation failed.
163 STACK_OF(X509
) *CertStack
;
172 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
176 CertStack
= (STACK_OF(X509
) *) X509Stack
;
178 X509Cert
= sk_X509_pop (CertStack
);
180 if (X509Cert
== NULL
) {
186 CertBio
= BIO_new (BIO_s_mem ());
187 if (CertBio
== NULL
) {
191 Result
= i2d_X509_bio (CertBio
, X509Cert
);
196 BIO_get_mem_ptr (CertBio
, &Ptr
);
197 Length
= (INT32
)(Ptr
->length
);
202 Buffer
= malloc (Length
);
203 if (Buffer
== NULL
) {
207 Result
= BIO_read (CertBio
, Buffer
, Length
);
208 if (Result
!= Length
) {
221 if (!Status
&& (Buffer
!= NULL
)) {
229 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
230 Cryptographic Message Syntax Standard". The input signed data could be wrapped
231 in a ContentInfo structure.
233 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
234 return FALSE. If P7Length overflow, then return FALSE.
236 Caution: This function may receive untrusted input.
237 UEFI Authenticated Variable is external input, so this function will do basic
238 check for PKCS#7 data structure.
240 @param[in] P7Data Pointer to the PKCS#7 message to verify.
241 @param[in] P7Length Length of the PKCS#7 message in bytes.
242 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
243 It's caller's responsibility to free the buffer with
245 This data structure is EFI_CERT_STACK type.
246 @param[out] StackLength Length of signer's certificates in bytes.
247 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
248 It's caller's responsibility to free the buffer with
250 @param[out] CertLength Length of the trusted certificate in bytes.
252 @retval TRUE The operation is finished successfully.
253 @retval FALSE Error occurs during the operation.
259 IN CONST UINT8
*P7Data
,
261 OUT UINT8
**CertStack
,
262 OUT UINTN
*StackLength
,
263 OUT UINT8
**TrustedCert
,
264 OUT UINTN
*CertLength
271 UINTN SignedDataSize
;
273 STACK_OF(X509
) *Stack
;
280 UINTN SingleCertSize
;
282 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
283 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
287 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
300 // Retrieve PKCS#7 Data (DER encoding)
302 if (SignedDataSize
> INT_MAX
) {
307 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
313 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
315 if (!PKCS7_type_is_signed (Pkcs7
)) {
319 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
325 // Convert CertStack to buffer in following format:
327 // UINT32 Cert1Length;
329 // UINT32 Cert2Length;
332 // UINT32 CertnLength;
335 BufferSize
= sizeof (UINT8
);
336 OldSize
= BufferSize
;
338 for (Index
= 0; ; Index
++) {
339 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
344 OldSize
= BufferSize
;
346 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
347 CertBuf
= malloc (BufferSize
);
349 if (CertBuf
== NULL
) {
353 if (OldBuf
!= NULL
) {
354 CopyMem (CertBuf
, OldBuf
, OldSize
);
359 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
360 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
366 if (CertBuf
!= NULL
) {
368 // Update CertNumber.
372 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
373 *TrustedCert
= malloc (*CertLength
);
374 if (*TrustedCert
== NULL
) {
378 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
379 *CertStack
= CertBuf
;
380 *StackLength
= BufferSize
;
397 sk_X509_pop_free(Stack
, X509_free
);
400 if (SingleCert
!= NULL
) {
404 if (!Status
&& (CertBuf
!= NULL
)) {
409 if (OldBuf
!= NULL
) {
417 Wrap function to use free() to free allocated memory for certificates.
419 @param[in] Certs Pointer to the certificates to be freed.
436 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
437 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
438 unchained to the signer's certificates.
439 The input signed data could be wrapped in a ContentInfo structure.
441 @param[in] P7Data Pointer to the PKCS#7 message.
442 @param[in] P7Length Length of the PKCS#7 message in bytes.
443 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's
444 certificate. It's caller's responsibility to free the buffer
445 with Pkcs7FreeSigners().
446 This data structure is EFI_CERT_STACK type.
447 @param[out] ChainLength Length of the chained certificates list buffer in bytes.
448 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's
449 responsibility to free the buffer with Pkcs7FreeSigners().
450 This data structure is EFI_CERT_STACK type.
451 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.
453 @retval TRUE The operation is finished successfully.
454 @retval FALSE Error occurs during the operation.
459 Pkcs7GetCertificatesList (
460 IN CONST UINT8
*P7Data
,
462 OUT UINT8
**SignerChainCerts
,
463 OUT UINTN
*ChainLength
,
464 OUT UINT8
**UnchainCerts
,
465 OUT UINTN
*UnchainLength
474 X509_STORE_CTX
*CertCtx
;
475 STACK_OF(X509
) *CtxChain
;
476 STACK_OF(X509
) *CtxUntrusted
;
478 STACK_OF(X509
) *Signers
;
482 X509_NAME
*IssuerName
;
506 ZeroMem (&CertCtx
, sizeof (CertCtx
));
509 // Parameter Checking
511 if ((P7Data
== NULL
) || (SignerChainCerts
== NULL
) || (ChainLength
== NULL
) ||
512 (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)) {
546 Signer
= sk_X509_value (Signers
, 0);
548 CertCtx
= X509_STORE_CTX_new ();
549 if (CertCtx
== NULL
) {
552 if (!X509_STORE_CTX_init (CertCtx
, NULL
, Signer
, Pkcs7
->d
.sign
->cert
)) {
556 // Initialize Chained & Untrusted stack
558 CtxChain
= X509_STORE_CTX_get0_chain (CertCtx
);
559 CtxCert
= X509_STORE_CTX_get0_cert (CertCtx
);
560 if (CtxChain
== NULL
) {
561 if (((CtxChain
= sk_X509_new_null ()) == NULL
) ||
562 (!sk_X509_push (CtxChain
, CtxCert
))) {
566 CtxUntrusted
= X509_STORE_CTX_get0_untrusted (CertCtx
);
567 if (CtxUntrusted
!= NULL
) {
568 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Signer
);
572 // Build certificates stack chained from Signer's certificate.
577 // Self-Issue checking
580 if (X509_STORE_CTX_get1_issuer (&Issuer
, CertCtx
, Cert
) == 1) {
581 if (X509_cmp (Issuer
, Cert
) == 0) {
587 // Found the issuer of the current certificate
589 if (CtxUntrusted
!= NULL
) {
591 IssuerName
= X509_get_issuer_name (Cert
);
592 Issuer
= X509_find_by_subject (CtxUntrusted
, IssuerName
);
593 if (Issuer
!= NULL
) {
594 if (!sk_X509_push (CtxChain
, Issuer
)) {
597 (VOID
)sk_X509_delete_ptr (CtxUntrusted
, Issuer
);
608 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:
610 // UINT32 Cert1Length;
612 // UINT32 Cert2Length;
615 // UINT32 CertnLength;
619 if (CtxChain
!= NULL
) {
620 BufferSize
= sizeof (UINT8
);
623 for (Index
= 0; ; Index
++) {
624 Status
= X509PopCertificate (CtxChain
, &SingleCert
, &CertSize
);
629 OldSize
= BufferSize
;
631 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
632 CertBuf
= malloc (BufferSize
);
634 if (CertBuf
== NULL
) {
638 if (OldBuf
!= NULL
) {
639 CopyMem (CertBuf
, OldBuf
, OldSize
);
644 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) CertSize
);
645 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
651 if (CertBuf
!= NULL
) {
653 // Update CertNumber.
657 *SignerChainCerts
= CertBuf
;
658 *ChainLength
= BufferSize
;
662 if (CtxUntrusted
!= NULL
) {
663 BufferSize
= sizeof (UINT8
);
666 for (Index
= 0; ; Index
++) {
667 Status
= X509PopCertificate (CtxUntrusted
, &SingleCert
, &CertSize
);
672 OldSize
= BufferSize
;
674 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
675 CertBuf
= malloc (BufferSize
);
677 if (CertBuf
== NULL
) {
681 if (OldBuf
!= NULL
) {
682 CopyMem (CertBuf
, OldBuf
, OldSize
);
687 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) CertSize
);
688 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
694 if (CertBuf
!= NULL
) {
696 // Update CertNumber.
700 *UnchainCerts
= CertBuf
;
701 *UnchainLength
= BufferSize
;
709 // Release Resources.
711 if (!Wrapped
&& (NewP7Data
!= NULL
)) {
718 sk_X509_free (Signers
);
720 if (CertCtx
!= NULL
) {
721 X509_STORE_CTX_cleanup (CertCtx
);
722 X509_STORE_CTX_free (CertCtx
);
725 if (SingleCert
!= NULL
) {
729 if (OldBuf
!= NULL
) {
733 if (!Status
&& (CertBuf
!= NULL
)) {
735 *SignerChainCerts
= NULL
;
736 *UnchainCerts
= NULL
;
743 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:
744 Cryptographic Message Syntax Standard". The input signed data could be wrapped
745 in a ContentInfo structure.
747 If P7Data, TrustedCert or InData is NULL, then return FALSE.
748 If P7Length, CertLength or DataLength overflow, then return FALSE.
750 Caution: This function may receive untrusted input.
751 UEFI Authenticated Variable is external input, so this function will do basic
752 check for PKCS#7 data structure.
754 @param[in] P7Data Pointer to the PKCS#7 message to verify.
755 @param[in] P7Length Length of the PKCS#7 message in bytes.
756 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
757 is used for certificate chain verification.
758 @param[in] CertLength Length of the trusted certificate in bytes.
759 @param[in] InData Pointer to the content to be verified.
760 @param[in] DataLength Length of InData in bytes.
762 @retval TRUE The specified PKCS#7 signed data is valid.
763 @retval FALSE Invalid PKCS#7 signed data.
769 IN CONST UINT8
*P7Data
,
771 IN CONST UINT8
*TrustedCert
,
773 IN CONST UINT8
*InData
,
781 X509_STORE
*CertStore
;
784 UINTN SignedDataSize
;
788 // Check input parameters.
790 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
791 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
801 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
803 if (EVP_add_digest (EVP_md5 ()) == 0) {
806 if (EVP_add_digest (EVP_sha1 ()) == 0) {
809 if (EVP_add_digest (EVP_sha256 ()) == 0) {
812 if (EVP_add_digest (EVP_sha384 ()) == 0) {
815 if (EVP_add_digest (EVP_sha512 ()) == 0) {
818 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
822 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
830 // Retrieve PKCS#7 Data (DER encoding)
832 if (SignedDataSize
> INT_MAX
) {
837 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
843 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
845 if (!PKCS7_type_is_signed (Pkcs7
)) {
850 // Read DER-encoded root certificate and Construct X509 Certificate
853 Cert
= d2i_X509 (NULL
, &Temp
, (long) CertLength
);
859 // Setup X509 Store for trusted certificate
861 CertStore
= X509_STORE_new ();
862 if (CertStore
== NULL
) {
865 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
870 // For generic PKCS#7 handling, InData may be NULL if the content is present
871 // in PKCS#7 structure. So ignore NULL checking here.
873 DataBio
= BIO_new (BIO_s_mem ());
874 if (DataBio
== NULL
) {
878 if (BIO_write (DataBio
, InData
, (int) DataLength
) <= 0) {
883 // Allow partial certificate chains, terminated by a non-self-signed but
884 // still trusted intermediate certificate. Also disable time checks.
886 X509_STORE_set_flags (CertStore
,
887 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
);
890 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
891 // doesn't support the extended key usage for Authenticode Code Signing.
892 // Bypass the certificate purpose checking by enabling any purposes setting.
894 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
897 // Verifies the PKCS#7 signedData structure
899 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
907 X509_STORE_free (CertStore
);
911 OPENSSL_free (SignedData
);
918 Extracts the attached content from a PKCS#7 signed data if existed. The input signed
919 data could be wrapped in a ContentInfo structure.
921 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,
922 then return FALSE. If the P7Data is not correctly formatted, then return FALSE.
924 Caution: This function may receive untrusted input. So this function will do
925 basic check for PKCS#7 data structure.
927 @param[in] P7Data Pointer to the PKCS#7 signed data to process.
928 @param[in] P7Length Length of the PKCS#7 signed data in bytes.
929 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.
930 It's caller's responsibility to free the buffer with FreePool().
931 @param[out] ContentSize The size of the extracted content in bytes.
933 @retval TRUE The P7Data was correctly formatted for processing.
934 @retval FALSE The P7Data was not correctly formatted for processing.
939 Pkcs7GetAttachedContent (
940 IN CONST UINT8
*P7Data
,
943 OUT UINTN
*ContentSize
949 UINTN SignedDataSize
;
952 ASN1_OCTET_STRING
*OctStr
;
955 // Check input parameter.
957 if ((P7Data
== NULL
) || (P7Length
> INT_MAX
) || (Content
== NULL
) || (ContentSize
== NULL
)) {
966 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
967 if (!Status
|| (SignedDataSize
> INT_MAX
)) {
974 // Decoding PKCS#7 SignedData
977 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
983 // The type of Pkcs7 must be signedData
985 if (!PKCS7_type_is_signed (Pkcs7
)) {
990 // Check for detached or attached content
992 if (PKCS7_get_detached (Pkcs7
)) {
994 // No Content supplied for PKCS7 detached signedData
1000 // Retrieve the attached content in PKCS7 signedData
1002 OctStr
= Pkcs7
->d
.sign
->contents
->d
.data
;
1003 if ((OctStr
->length
> 0) && (OctStr
->data
!= NULL
)) {
1004 *ContentSize
= OctStr
->length
;
1005 *Content
= AllocatePool (*ContentSize
);
1006 if (*Content
== NULL
) {
1010 CopyMem (*Content
, OctStr
->data
, *ContentSize
);
1017 // Release Resources
1022 OPENSSL_free (SignedData
);