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 - 2016, 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
;
171 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
175 CertStack
= (STACK_OF(X509
) *) X509Stack
;
177 X509Cert
= sk_X509_pop (CertStack
);
179 if (X509Cert
== NULL
) {
185 CertBio
= BIO_new (BIO_s_mem ());
186 if (CertBio
== NULL
) {
190 Result
= i2d_X509_bio (CertBio
, X509Cert
);
195 Length
= (INT32
)(((BUF_MEM
*) CertBio
->ptr
)->length
);
200 Buffer
= malloc (Length
);
201 if (Buffer
== NULL
) {
205 Result
= BIO_read (CertBio
, Buffer
, Length
);
206 if (Result
!= Length
) {
219 if (!Status
&& (Buffer
!= NULL
)) {
227 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
228 Cryptographic Message Syntax Standard". The input signed data could be wrapped
229 in a ContentInfo structure.
231 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
232 return FALSE. If P7Length overflow, then return FAlSE.
234 Caution: This function may receive untrusted input.
235 UEFI Authenticated Variable is external input, so this function will do basic
236 check for PKCS#7 data structure.
238 @param[in] P7Data Pointer to the PKCS#7 message to verify.
239 @param[in] P7Length Length of the PKCS#7 message in bytes.
240 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
241 It's caller's responsiblity to free the buffer.
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 responsiblity to free the buffer.
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
;
268 STACK_OF(X509
) *Stack
;
275 UINTN SingleCertSize
;
277 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
278 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
282 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
295 // Retrieve PKCS#7 Data (DER encoding)
297 if (SignedDataSize
> INT_MAX
) {
302 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
308 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
310 if (!PKCS7_type_is_signed (Pkcs7
)) {
314 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
320 // Convert CertStack to buffer in following format:
322 // UINT32 Cert1Length;
324 // UINT32 Cert2Length;
327 // UINT32 CertnLength;
330 BufferSize
= sizeof (UINT8
);
331 OldSize
= BufferSize
;
333 for (Index
= 0; ; Index
++) {
334 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
339 OldSize
= BufferSize
;
341 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
342 CertBuf
= malloc (BufferSize
);
344 if (CertBuf
== NULL
) {
348 if (OldBuf
!= NULL
) {
349 CopyMem (CertBuf
, OldBuf
, OldSize
);
354 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
355 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
361 if (CertBuf
!= NULL
) {
363 // Update CertNumber.
367 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
368 *TrustedCert
= malloc (*CertLength
);
369 if (*TrustedCert
== NULL
) {
373 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
374 *CertStack
= CertBuf
;
375 *StackLength
= BufferSize
;
392 sk_X509_pop_free(Stack
, X509_free
);
395 if (SingleCert
!= NULL
) {
399 if (!Status
&& (CertBuf
!= NULL
)) {
404 if (OldBuf
!= NULL
) {
412 Wrap function to use free() to free allocated memory for certificates.
414 @param[in] Certs Pointer to the certificates to be freed.
431 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
432 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
433 unchained to the signer's certificates.
434 The input signed data could be wrapped in a ContentInfo structure.
436 @param[in] P7Data Pointer to the PKCS#7 message.
437 @param[in] P7Length Length of the PKCS#7 message in bytes.
438 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's
439 certificate. It's caller's responsiblity to free the buffer.
440 @param[out] ChainLength Length of the chained certificates list buffer in bytes.
441 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's
442 responsiblity to free the buffer.
443 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.
445 @retval TRUE The operation is finished successfully.
446 @retval FALSE Error occurs during the operation.
451 Pkcs7GetCertificatesList (
452 IN CONST UINT8
*P7Data
,
454 OUT UINT8
**SignerChainCerts
,
455 OUT UINTN
*ChainLength
,
456 OUT UINT8
**UnchainCerts
,
457 OUT UINTN
*UnchainLength
466 X509_STORE_CTX CertCtx
;
467 STACK_OF(X509
) *Signers
;
492 ZeroMem (&CertCtx
, sizeof (CertCtx
));
495 // Parameter Checking
497 if ((P7Data
== NULL
) || (SignerChainCerts
== NULL
) || (ChainLength
== NULL
) ||
498 (UnchainCerts
== NULL
) || (UnchainLength
== NULL
) || (P7Length
> INT_MAX
)) {
502 *SignerChainCerts
= NULL
;
504 *UnchainCerts
= NULL
;
508 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.
510 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &NewP7Data
, &NewP7Length
);
511 if (!Status
|| (NewP7Length
> INT_MAX
)) {
516 // Decodes PKCS#7 SignedData
518 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &NewP7Data
, (int) NewP7Length
);
519 if ((Pkcs7
== NULL
) || (!PKCS7_type_is_signed (Pkcs7
))) {
524 // Obtains Signer's Certificate from PKCS#7 data
525 // NOTE: Only one signer case will be handled in this function, which means SignerInfos
526 // should include only one signer's certificate.
528 Signers
= PKCS7_get0_signers (Pkcs7
, NULL
, PKCS7_BINARY
);
529 if ((Signers
== NULL
) || (sk_X509_num (Signers
) != 1)) {
532 Signer
= sk_X509_value (Signers
, 0);
534 if (!X509_STORE_CTX_init (&CertCtx
, NULL
, Signer
, Pkcs7
->d
.sign
->cert
)) {
538 // Initialize Chained & Untrusted stack
540 if (CertCtx
.chain
== NULL
) {
541 if (((CertCtx
.chain
= sk_X509_new_null ()) == NULL
) ||
542 (!sk_X509_push (CertCtx
.chain
, CertCtx
.cert
))) {
546 (VOID
)sk_X509_delete_ptr (CertCtx
.untrusted
, Signer
);
549 // Build certificates stack chained from Signer's certificate.
554 // Self-Issue checking
556 if (CertCtx
.check_issued (&CertCtx
, Cert
, Cert
)) {
561 // Found the issuer of the current certificate
563 if (CertCtx
.untrusted
!= NULL
) {
565 for (Index
= 0; Index
< sk_X509_num (CertCtx
.untrusted
); Index
++) {
566 TempCert
= sk_X509_value (CertCtx
.untrusted
, Index
);
567 if (CertCtx
.check_issued (&CertCtx
, Cert
, TempCert
)) {
572 if (Issuer
!= NULL
) {
573 if (!sk_X509_push (CertCtx
.chain
, Issuer
)) {
576 (VOID
)sk_X509_delete_ptr (CertCtx
.untrusted
, Issuer
);
587 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:
589 // UINT32 Cert1Length;
591 // UINT32 Cert2Length;
594 // UINT32 CertnLength;
598 if (CertCtx
.chain
!= NULL
) {
599 BufferSize
= sizeof (UINT8
);
600 OldSize
= BufferSize
;
603 for (Index
= 0; ; Index
++) {
604 Status
= X509PopCertificate (CertCtx
.chain
, &SingleCert
, &CertSize
);
609 OldSize
= BufferSize
;
611 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
612 CertBuf
= malloc (BufferSize
);
614 if (CertBuf
== NULL
) {
618 if (OldBuf
!= NULL
) {
619 CopyMem (CertBuf
, OldBuf
, OldSize
);
624 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) CertSize
);
625 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
631 if (CertBuf
!= NULL
) {
633 // Update CertNumber.
637 *SignerChainCerts
= CertBuf
;
638 *ChainLength
= BufferSize
;
642 if (CertCtx
.untrusted
!= NULL
) {
643 BufferSize
= sizeof (UINT8
);
644 OldSize
= BufferSize
;
647 for (Index
= 0; ; Index
++) {
648 Status
= X509PopCertificate (CertCtx
.untrusted
, &SingleCert
, &CertSize
);
653 OldSize
= BufferSize
;
655 BufferSize
= OldSize
+ CertSize
+ sizeof (UINT32
);
656 CertBuf
= malloc (BufferSize
);
658 if (CertBuf
== NULL
) {
662 if (OldBuf
!= NULL
) {
663 CopyMem (CertBuf
, OldBuf
, OldSize
);
668 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) CertSize
);
669 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, CertSize
);
675 if (CertBuf
!= NULL
) {
677 // Update CertNumber.
681 *UnchainCerts
= CertBuf
;
682 *UnchainLength
= BufferSize
;
690 // Release Resources.
692 if (!Wrapped
&& (NewP7Data
!= NULL
)) {
699 sk_X509_free (Signers
);
701 X509_STORE_CTX_cleanup (&CertCtx
);
703 if (SingleCert
!= NULL
) {
707 if (OldBuf
!= NULL
) {
711 if (!Status
&& (CertBuf
!= NULL
)) {
713 *SignerChainCerts
= NULL
;
714 *UnchainCerts
= NULL
;
721 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
722 Cryptographic Message Syntax Standard". The input signed data could be wrapped
723 in a ContentInfo structure.
725 If P7Data, TrustedCert or InData is NULL, then return FALSE.
726 If P7Length, CertLength or DataLength overflow, then return FAlSE.
728 Caution: This function may receive untrusted input.
729 UEFI Authenticated Variable is external input, so this function will do basic
730 check for PKCS#7 data structure.
732 @param[in] P7Data Pointer to the PKCS#7 message to verify.
733 @param[in] P7Length Length of the PKCS#7 message in bytes.
734 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
735 is used for certificate chain verification.
736 @param[in] CertLength Length of the trusted certificate in bytes.
737 @param[in] InData Pointer to the content to be verified.
738 @param[in] DataLength Length of InData in bytes.
740 @retval TRUE The specified PKCS#7 signed data is valid.
741 @retval FALSE Invalid PKCS#7 signed data.
747 IN CONST UINT8
*P7Data
,
749 IN CONST UINT8
*TrustedCert
,
751 IN CONST UINT8
*InData
,
759 X509_STORE
*CertStore
;
762 UINTN SignedDataSize
;
766 // Check input parameters.
768 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
769 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
779 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
781 if (EVP_add_digest (EVP_md5 ()) == 0) {
784 if (EVP_add_digest (EVP_sha1 ()) == 0) {
787 if (EVP_add_digest (EVP_sha256 ()) == 0) {
790 if (EVP_add_digest (EVP_sha384 ()) == 0) {
793 if (EVP_add_digest (EVP_sha512 ()) == 0) {
796 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
800 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
808 // Retrieve PKCS#7 Data (DER encoding)
810 if (SignedDataSize
> INT_MAX
) {
815 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
821 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
823 if (!PKCS7_type_is_signed (Pkcs7
)) {
828 // Read DER-encoded root certificate and Construct X509 Certificate
831 Cert
= d2i_X509 (NULL
, &Temp
, (long) CertLength
);
837 // Setup X509 Store for trusted certificate
839 CertStore
= X509_STORE_new ();
840 if (CertStore
== NULL
) {
843 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
848 // For generic PKCS#7 handling, InData may be NULL if the content is present
849 // in PKCS#7 structure. So ignore NULL checking here.
851 DataBio
= BIO_new (BIO_s_mem ());
852 if (DataBio
== NULL
) {
856 if (BIO_write (DataBio
, InData
, (int) DataLength
) <= 0) {
861 // Allow partial certificate chains, terminated by a non-self-signed but
862 // still trusted intermediate certificate. Also disable time checks.
864 X509_STORE_set_flags (CertStore
,
865 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
);
868 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
869 // doesn't support the extended key usage for Authenticode Code Signing.
870 // Bypass the certificate purpose checking by enabling any purposes setting.
872 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
875 // Verifies the PKCS#7 signedData structure
877 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
885 X509_STORE_free (CertStore
);
889 OPENSSL_free (SignedData
);
896 Extracts the attached content from a PKCS#7 signed data if existed. The input signed
897 data could be wrapped in a ContentInfo structure.
899 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,
900 then return FAlSE. If the P7Data is not correctly formatted, then return FALSE.
902 Caution: This function may receive untrusted input. So this function will do
903 basic check for PKCS#7 data structure.
905 @param[in] P7Data Pointer to the PKCS#7 signed data to process.
906 @param[in] P7Length Length of the PKCS#7 signed data in bytes.
907 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.
908 It's caller's responsiblity to free the buffer.
909 @param[out] ContentSize The size of the extracted content in bytes.
911 @retval TRUE The P7Data was correctly formatted for processing.
912 @retval FALSE The P7Data was not correctly formatted for processing.
917 Pkcs7GetAttachedContent (
918 IN CONST UINT8
*P7Data
,
921 OUT UINTN
*ContentSize
927 UINTN SignedDataSize
;
930 ASN1_OCTET_STRING
*OctStr
;
933 // Check input parameter.
935 if ((P7Data
== NULL
) || (P7Length
> INT_MAX
) || (Content
== NULL
) || (ContentSize
== NULL
)) {
944 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
945 if (!Status
|| (SignedDataSize
> INT_MAX
)) {
952 // Decoding PKCS#7 SignedData
955 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)SignedDataSize
);
961 // The type of Pkcs7 must be signedData
963 if (!PKCS7_type_is_signed (Pkcs7
)) {
968 // Check for detached or attached content
970 if (PKCS7_get_detached (Pkcs7
)) {
972 // No Content supplied for PKCS7 detached signedData
978 // Retrieve the attached content in PKCS7 signedData
980 OctStr
= Pkcs7
->d
.sign
->contents
->d
.data
;
981 if ((OctStr
->length
> 0) && (OctStr
->data
!= NULL
)) {
982 *ContentSize
= OctStr
->length
;
983 *Content
= malloc (*ContentSize
);
984 if (*Content
== NULL
) {
988 CopyMem (*Content
, OctStr
->data
, *ContentSize
);
1000 OPENSSL_free (SignedData
);