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 - 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
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 Verification callback function to override any existing callbacks in OpenSSL
35 for intermediate certificate supports.
37 @param[in] Status Original status before calling this callback.
38 @param[in] Context X509 store context.
40 @retval 1 Current X509 certificate is verified successfully.
41 @retval 0 Verification failed.
47 IN X509_STORE_CTX
*Context
56 Error
= (INTN
) X509_STORE_CTX_get_error (Context
);
59 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
60 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
61 // can not be found in X509_verify_cert of X509_vfy.c.
62 // In order to support intermediate certificate node, we override the
63 // errors if the certification is obtained from X509 store, i.e. it is
64 // a trusted ceritifcate node that is enrolled by user.
65 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
66 // are also ignored to enable such feature.
68 if ((Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
) ||
69 (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
)) {
70 Obj
= (X509_OBJECT
*) malloc (sizeof (X509_OBJECT
));
75 Obj
->type
= X509_LU_X509
;
76 Obj
->data
.x509
= Context
->current_cert
;
78 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE
);
80 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
84 // If any certificate in the chain is enrolled as trusted certificate,
85 // pass the certificate verification.
87 if (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
) {
88 Count
= (INTN
) sk_X509_num (Context
->chain
);
89 for (Index
= 0; Index
< Count
; Index
++) {
90 Obj
->data
.x509
= sk_X509_value (Context
->chain
, (int) Index
);
91 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
99 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE
);
102 if ((Error
== X509_V_ERR_CERT_UNTRUSTED
) ||
103 (Error
== X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
)) {
115 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
116 a new structure to wrap P7Data.
118 Caution: This function may receive untrusted input.
119 UEFI Authenticated Variable is external input, so this function will do basic
120 check for PKCS#7 data structure.
122 @param[in] P7Data Pointer to the PKCS#7 message to verify.
123 @param[in] P7Length Length of the PKCS#7 message in bytes.
124 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
126 @param[out] WrapData If return status of this function is TRUE:
127 1) when WrapFlag is TRUE, pointer to P7Data.
128 2) when WrapFlag is FALSE, pointer to a new ContentInfo
129 structure. It's caller's responsibility to free this
131 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
133 @retval TRUE The operation is finished successfully.
134 @retval FALSE The operation is failed due to lack of resources.
139 IN CONST UINT8
*P7Data
,
141 OUT BOOLEAN
*WrapFlag
,
142 OUT UINT8
**WrapData
,
143 OUT UINTN
*WrapDataSize
150 // Check whether input P7Data is a wrapped ContentInfo structure or not.
153 if ((P7Data
[4] == 0x06) && (P7Data
[5] == 0x09)) {
154 if (CompareMem (P7Data
+ 6, mOidValue
, sizeof (mOidValue
)) == 0) {
155 if ((P7Data
[15] == 0xA0) && (P7Data
[16] == 0x82)) {
162 *WrapData
= (UINT8
*) P7Data
;
163 *WrapDataSize
= P7Length
;
166 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
168 *WrapDataSize
= P7Length
+ 19;
169 *WrapData
= malloc (*WrapDataSize
);
170 if (*WrapData
== NULL
) {
175 SignedData
= *WrapData
;
178 // Part1: 0x30, 0x82.
180 SignedData
[0] = 0x30;
181 SignedData
[1] = 0x82;
184 // Part2: Length1 = P7Length + 19 - 4, in big endian.
186 SignedData
[2] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) >> 8);
187 SignedData
[3] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) & 0xff);
190 // Part3: 0x06, 0x09.
192 SignedData
[4] = 0x06;
193 SignedData
[5] = 0x09;
196 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
198 CopyMem (SignedData
+ 6, mOidValue
, sizeof (mOidValue
));
201 // Part5: 0xA0, 0x82.
203 SignedData
[15] = 0xA0;
204 SignedData
[16] = 0x82;
207 // Part6: Length2 = P7Length, in big endian.
209 SignedData
[17] = (UINT8
) (((UINT16
) P7Length
) >> 8);
210 SignedData
[18] = (UINT8
) (((UINT16
) P7Length
) & 0xff);
215 CopyMem (SignedData
+ 19, P7Data
, P7Length
);
223 Pop single certificate from STACK_OF(X509).
225 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
227 @param[in] X509Stack Pointer to a X509 stack object.
228 @param[out] Cert Pointer to a X509 certificate.
229 @param[out] CertSize Length of output X509 certificate in bytes.
231 @retval TRUE The X509 stack pop succeeded.
232 @retval FALSE The pop operation failed.
244 STACK_OF(X509
) *CertStack
;
252 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
256 CertStack
= (STACK_OF(X509
) *) X509Stack
;
258 X509Cert
= sk_X509_pop (CertStack
);
260 if (X509Cert
== NULL
) {
266 CertBio
= BIO_new (BIO_s_mem ());
267 if (CertBio
== NULL
) {
271 Result
= i2d_X509_bio (CertBio
, X509Cert
);
276 Length
= (INT32
)(((BUF_MEM
*) CertBio
->ptr
)->length
);
281 Buffer
= malloc (Length
);
282 if (Buffer
== NULL
) {
286 Result
= BIO_read (CertBio
, Buffer
, Length
);
287 if (Result
!= Length
) {
300 if (!Status
&& (Buffer
!= NULL
)) {
308 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
309 Cryptographic Message Syntax Standard". The input signed data could be wrapped
310 in a ContentInfo structure.
312 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
313 return FALSE. If P7Length overflow, then return FAlSE.
315 Caution: This function may receive untrusted input.
316 UEFI Authenticated Variable is external input, so this function will do basic
317 check for PKCS#7 data structure.
319 @param[in] P7Data Pointer to the PKCS#7 message to verify.
320 @param[in] P7Length Length of the PKCS#7 message in bytes.
321 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
322 It's caller's responsiblity to free the buffer.
323 @param[out] StackLength Length of signer's certificates in bytes.
324 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
325 It's caller's responsiblity to free the buffer.
326 @param[out] CertLength Length of the trusted certificate in bytes.
328 @retval TRUE The operation is finished successfully.
329 @retval FALSE Error occurs during the operation.
335 IN CONST UINT8
*P7Data
,
337 OUT UINT8
**CertStack
,
338 OUT UINTN
*StackLength
,
339 OUT UINT8
**TrustedCert
,
340 OUT UINTN
*CertLength
347 UINTN SignedDataSize
;
349 STACK_OF(X509
) *Stack
;
356 UINTN SingleCertSize
;
358 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
359 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
363 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
376 // Retrieve PKCS#7 Data (DER encoding)
378 if (SignedDataSize
> INT_MAX
) {
383 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
389 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
391 if (!PKCS7_type_is_signed (Pkcs7
)) {
395 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
401 // Convert CertStack to buffer in following format:
403 // UINT32 Cert1Length;
405 // UINT32 Cert2Length;
408 // UINT32 CertnLength;
411 BufferSize
= sizeof (UINT8
);
412 OldSize
= BufferSize
;
414 for (Index
= 0; ; Index
++) {
415 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
420 OldSize
= BufferSize
;
422 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
423 CertBuf
= malloc (BufferSize
);
425 if (CertBuf
== NULL
) {
429 if (OldBuf
!= NULL
) {
430 CopyMem (CertBuf
, OldBuf
, OldSize
);
435 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
436 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
442 if (CertBuf
!= NULL
) {
444 // Update CertNumber.
448 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
449 *TrustedCert
= malloc (*CertLength
);
450 if (*TrustedCert
== NULL
) {
454 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
455 *CertStack
= CertBuf
;
456 *StackLength
= BufferSize
;
473 sk_X509_pop_free(Stack
, X509_free
);
476 if (SingleCert
!= NULL
) {
480 if (!Status
&& (CertBuf
!= NULL
)) {
485 if (OldBuf
!= NULL
) {
493 Wrap function to use free() to free allocated memory for certificates.
495 @param[in] Certs Pointer to the certificates to be freed.
512 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
513 Cryptographic Message Syntax Standard". The input signed data could be wrapped
514 in a ContentInfo structure.
516 If P7Data, TrustedCert or InData is NULL, then return FALSE.
517 If P7Length, CertLength or DataLength overflow, then return FAlSE.
519 Caution: This function may receive untrusted input.
520 UEFI Authenticated Variable is external input, so this function will do basic
521 check for PKCS#7 data structure.
523 @param[in] P7Data Pointer to the PKCS#7 message to verify.
524 @param[in] P7Length Length of the PKCS#7 message in bytes.
525 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
526 is used for certificate chain verification.
527 @param[in] CertLength Length of the trusted certificate in bytes.
528 @param[in] InData Pointer to the content to be verified.
529 @param[in] DataLength Length of InData in bytes.
531 @retval TRUE The specified PKCS#7 signed data is valid.
532 @retval FALSE Invalid PKCS#7 signed data.
538 IN CONST UINT8
*P7Data
,
540 IN CONST UINT8
*TrustedCert
,
542 IN CONST UINT8
*InData
,
550 X509_STORE
*CertStore
;
553 UINTN SignedDataSize
;
557 // Check input parameters.
559 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
560 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
570 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
572 if (EVP_add_digest (EVP_md5 ()) == 0) {
575 if (EVP_add_digest (EVP_sha1 ()) == 0) {
578 if (EVP_add_digest (EVP_sha256 ()) == 0) {
581 if (EVP_add_digest (EVP_sha384 ()) == 0) {
584 if (EVP_add_digest (EVP_sha512 ()) == 0) {
587 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
591 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
599 // Retrieve PKCS#7 Data (DER encoding)
601 if (SignedDataSize
> INT_MAX
) {
606 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
612 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
614 if (!PKCS7_type_is_signed (Pkcs7
)) {
619 // Read DER-encoded root certificate and Construct X509 Certificate
622 Cert
= d2i_X509 (NULL
, &Temp
, (long) CertLength
);
628 // Setup X509 Store for trusted certificate
630 CertStore
= X509_STORE_new ();
631 if (CertStore
== NULL
) {
634 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
639 // Register customized X509 verification callback function to support
640 // trusted intermediate certificate anchor.
642 CertStore
->verify_cb
= X509VerifyCb
;
645 // For generic PKCS#7 handling, InData may be NULL if the content is present
646 // in PKCS#7 structure. So ignore NULL checking here.
648 DataBio
= BIO_new (BIO_s_mem ());
649 if (DataBio
== NULL
) {
653 if (BIO_write (DataBio
, InData
, (int) DataLength
) <= 0) {
658 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
659 // doesn't support the extended key usage for Authenticode Code Signing.
660 // Bypass the certificate purpose checking by enabling any purposes setting.
662 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
665 // Verifies the PKCS#7 signedData structure
667 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
675 X509_STORE_free (CertStore
);
679 OPENSSL_free (SignedData
);
686 Extracts the attached content from a PKCS#7 signed data if existed. The input signed
687 data could be wrapped in a ContentInfo structure.
689 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,
690 then return FAlSE. If the P7Data is not correctly formatted, then return FALSE.
692 Caution: This function may receive untrusted input. So this function will do
693 basic check for PKCS#7 data structure.
695 @param[in] P7Data Pointer to the PKCS#7 signed data to process.
696 @param[in] P7Length Length of the PKCS#7 signed data in bytes.
697 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.
698 It's caller's responsiblity to free the buffer.
699 @param[out] ContentSize The size of the extracted content in bytes.
701 @retval TRUE The P7Data was correctly formatted for processing.
702 @retval FALSE The P7Data was not correctly formatted for processing.
707 Pkcs7GetAttachedContent (
708 IN CONST UINT8
*P7Data
,
711 OUT UINTN
*ContentSize
717 UINTN SignedDataSize
;
720 ASN1_OCTET_STRING
*OctStr
;
728 // Check input parameter.
730 if ((P7Data
== NULL
) || (P7Length
> INT_MAX
) || (Content
== NULL
) || (ContentSize
== NULL
)) {
734 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
735 if (!Status
|| (SignedDataSize
> INT_MAX
)) {
742 // Decoding PKCS#7 SignedData
745 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
751 // The type of Pkcs7 must be signedData
753 if (!PKCS7_type_is_signed (Pkcs7
)) {
758 // Check for detached or attached content
760 if (PKCS7_get_detached (Pkcs7
)) {
762 // No Content supplied for PKCS7 detached signedData
768 // Retrieve the attached content in PKCS7 signedData
770 OctStr
= Pkcs7
->d
.sign
->contents
->d
.data
;
771 if ((OctStr
->length
> 0) && (OctStr
->data
!= NULL
)) {
772 *ContentSize
= OctStr
->length
;
773 *Content
= malloc (*ContentSize
);
774 CopyMem (*Content
, OctStr
->data
, *ContentSize
);
786 OPENSSL_free (SignedData
);