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 - 2012, 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/pkcs7.h>
30 UINT8 mOidValue
[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
33 Verification callback function to override any existing callbacks in OpenSSL
34 for intermediate certificate supports.
36 @param[in] Status Original status before calling this callback.
37 @param[in] Context X509 store context.
39 @retval 1 Current X509 certificate is verified successfully.
40 @retval 0 Verification failed.
46 IN X509_STORE_CTX
*Context
55 Error
= (INTN
) X509_STORE_CTX_get_error (Context
);
58 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
59 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
60 // can not be found in X509_verify_cert of X509_vfy.c.
61 // In order to support intermediate certificate node, we override the
62 // errors if the certification is obtained from X509 store, i.e. it is
63 // a trusted ceritifcate node that is enrolled by user.
64 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
65 // are also ignored to enable such feature.
67 if ((Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
) ||
68 (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
)) {
69 Obj
= (X509_OBJECT
*) malloc (sizeof (X509_OBJECT
));
74 Obj
->type
= X509_LU_X509
;
75 Obj
->data
.x509
= Context
->current_cert
;
77 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE
);
79 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
83 // If any certificate in the chain is enrolled as trusted certificate,
84 // pass the certificate verification.
86 if (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
) {
87 Count
= (INTN
) sk_X509_num (Context
->chain
);
88 for (Index
= 0; Index
< Count
; Index
++) {
89 Obj
->data
.x509
= sk_X509_value (Context
->chain
, (int) Index
);
90 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
98 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE
);
101 if ((Error
== X509_V_ERR_CERT_UNTRUSTED
) ||
102 (Error
== X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
)) {
114 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
115 a new structure to wrap P7Data.
117 Caution: This function may receive untrusted input.
118 UEFI Authenticated Variable is external input, so this function will do basic
119 check for PKCS#7 data structure.
121 @param[in] P7Data Pointer to the PKCS#7 message to verify.
122 @param[in] P7Length Length of the PKCS#7 message in bytes.
123 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
125 @param[out] WrapData If return status of this function is TRUE:
126 1) when WrapFlag is TRUE, pointer to P7Data.
127 2) when WrapFlag is FALSE, pointer to a new ContentInfo
128 structure. It's caller's responsibility to free this
130 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
132 @retval TRUE The operation is finished successfully.
133 @retval FALSE The operation is failed due to lack of resources.
138 IN CONST UINT8
*P7Data
,
140 OUT BOOLEAN
*WrapFlag
,
141 OUT UINT8
**WrapData
,
142 OUT UINTN
*WrapDataSize
149 // Check whether input P7Data is a wrapped ContentInfo structure or not.
152 if ((P7Data
[4] == 0x06) && (P7Data
[5] == 0x09)) {
153 if (CompareMem (P7Data
+ 6, mOidValue
, sizeof (mOidValue
)) == 0) {
154 if ((P7Data
[15] == 0xA0) && (P7Data
[16] == 0x82)) {
161 *WrapData
= (UINT8
*) P7Data
;
162 *WrapDataSize
= P7Length
;
165 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
167 *WrapDataSize
= P7Length
+ 19;
168 *WrapData
= malloc (*WrapDataSize
);
169 if (*WrapData
== NULL
) {
174 SignedData
= *WrapData
;
177 // Part1: 0x30, 0x82.
179 SignedData
[0] = 0x30;
180 SignedData
[1] = 0x82;
183 // Part2: Length1 = P7Length + 19 - 4, in big endian.
185 SignedData
[2] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) >> 8);
186 SignedData
[3] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) & 0xff);
189 // Part3: 0x06, 0x09.
191 SignedData
[4] = 0x06;
192 SignedData
[5] = 0x09;
195 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
197 CopyMem (SignedData
+ 6, mOidValue
, sizeof (mOidValue
));
200 // Part5: 0xA0, 0x82.
202 SignedData
[15] = 0xA0;
203 SignedData
[16] = 0x82;
206 // Part6: Length2 = P7Length, in big endian.
208 SignedData
[17] = (UINT8
) (((UINT16
) P7Length
) >> 8);
209 SignedData
[18] = (UINT8
) (((UINT16
) P7Length
) & 0xff);
214 CopyMem (SignedData
+ 19, P7Data
, P7Length
);
222 Pop single certificate from STACK_OF(X509).
224 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
226 @param[in] X509Stack Pointer to a X509 stack object.
227 @param[out] Cert Pointer to a X509 certificate.
228 @param[out] CertSize Length of output X509 certificate in bytes.
230 @retval TRUE The X509 stack pop succeeded.
231 @retval FALSE The pop operation failed.
243 STACK_OF(X509
) *CertStack
;
251 if ((X509Stack
== NULL
) || (Cert
== NULL
) || (CertSize
== NULL
)) {
255 CertStack
= (STACK_OF(X509
) *) X509Stack
;
257 X509Cert
= sk_X509_pop (CertStack
);
259 if (X509Cert
== NULL
) {
265 CertBio
= BIO_new (BIO_s_mem ());
266 if (CertBio
== NULL
) {
270 Result
= i2d_X509_bio (CertBio
, X509Cert
);
275 Length
= ((BUF_MEM
*) CertBio
->ptr
)->length
;
280 Buffer
= malloc (Length
);
281 if (Buffer
== NULL
) {
285 Result
= BIO_read (CertBio
, Buffer
, Length
);
286 if (Result
!= Length
) {
299 if (!Status
&& (Buffer
!= NULL
)) {
307 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
308 Cryptographic Message Syntax Standard". The input signed data could be wrapped
309 in a ContentInfo structure.
311 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
312 return FALSE. If P7Length overflow, then return FAlSE.
314 Caution: This function may receive untrusted input.
315 UEFI Authenticated Variable is external input, so this function will do basic
316 check for PKCS#7 data structure.
318 @param[in] P7Data Pointer to the PKCS#7 message to verify.
319 @param[in] P7Length Length of the PKCS#7 message in bytes.
320 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
321 It's caller's responsiblity to free the buffer.
322 @param[out] StackLength Length of signer's certificates in bytes.
323 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
324 It's caller's responsiblity to free the buffer.
325 @param[out] CertLength Length of the trusted certificate in bytes.
327 @retval TRUE The operation is finished successfully.
328 @retval FALSE Error occurs during the operation.
334 IN CONST UINT8
*P7Data
,
336 OUT UINT8
**CertStack
,
337 OUT UINTN
*StackLength
,
338 OUT UINT8
**TrustedCert
,
339 OUT UINTN
*CertLength
346 UINTN SignedDataSize
;
348 STACK_OF(X509
) *Stack
;
355 UINTN SingleCertSize
;
357 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
358 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
362 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
375 // Retrieve PKCS#7 Data (DER encoding)
377 if (SignedDataSize
> INT_MAX
) {
382 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
388 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
390 if (!PKCS7_type_is_signed (Pkcs7
)) {
394 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
400 // Convert CertStack to buffer in following format:
402 // UINT32 Cert1Length;
404 // UINT32 Cert2Length;
407 // UINT32 CertnLength;
410 BufferSize
= sizeof (UINT8
);
411 OldSize
= BufferSize
;
413 for (Index
= 0; ; Index
++) {
414 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
419 OldSize
= BufferSize
;
421 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
422 CertBuf
= malloc (BufferSize
);
424 if (CertBuf
== NULL
) {
428 if (OldBuf
!= NULL
) {
429 CopyMem (CertBuf
, OldBuf
, OldSize
);
434 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
435 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
441 if (CertBuf
!= NULL
) {
443 // Update CertNumber.
447 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
448 *TrustedCert
= malloc (*CertLength
);
449 if (*TrustedCert
== NULL
) {
453 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
454 *CertStack
= CertBuf
;
455 *StackLength
= BufferSize
;
472 sk_X509_pop_free(Stack
, X509_free
);
475 if (SingleCert
!= NULL
) {
479 if (!Status
&& (CertBuf
!= NULL
)) {
484 if (OldBuf
!= NULL
) {
492 Wrap function to use free() to free allocated memory for certificates.
494 @param[in] Certs Pointer to the certificates to be freed.
511 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
512 Cryptographic Message Syntax Standard". The input signed data could be wrapped
513 in a ContentInfo structure.
515 If P7Data, TrustedCert or InData is NULL, then return FALSE.
516 If P7Length, CertLength or DataLength overflow, then return FAlSE.
518 Caution: This function may receive untrusted input.
519 UEFI Authenticated Variable is external input, so this function will do basic
520 check for PKCS#7 data structure.
522 @param[in] P7Data Pointer to the PKCS#7 message to verify.
523 @param[in] P7Length Length of the PKCS#7 message in bytes.
524 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
525 is used for certificate chain verification.
526 @param[in] CertLength Length of the trusted certificate in bytes.
527 @param[in] InData Pointer to the content to be verified.
528 @param[in] DataLength Length of InData in bytes.
530 @retval TRUE The specified PKCS#7 signed data is valid.
531 @retval FALSE Invalid PKCS#7 signed data.
537 IN CONST UINT8
*P7Data
,
539 IN CONST UINT8
*TrustedCert
,
541 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
) {
571 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
573 if (EVP_add_digest (EVP_md5 ()) == 0) {
576 if (EVP_add_digest (EVP_sha1 ()) == 0) {
579 if (EVP_add_digest (EVP_sha256 ()) == 0) {
582 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
587 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
595 // Retrieve PKCS#7 Data (DER encoding)
597 if (SignedDataSize
> INT_MAX
) {
602 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
608 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
610 if (!PKCS7_type_is_signed (Pkcs7
)) {
615 // Read DER-encoded root certificate and Construct X509 Certificate
617 CertBio
= BIO_new (BIO_s_mem ());
618 BIO_write (CertBio
, TrustedCert
, (int)CertLength
);
619 if (CertBio
== NULL
) {
622 Cert
= d2i_X509_bio (CertBio
, NULL
);
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 BIO_write (DataBio
, InData
, (int)DataLength
);
652 // Verifies the PKCS#7 signedData structure
654 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
663 X509_STORE_free (CertStore
);
667 OPENSSL_free (SignedData
);