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 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
223 Cryptographic Message Syntax Standard". The input signed data could be wrapped
224 in a ContentInfo structure.
226 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
227 return FALSE. If P7Length overflow, then return FAlSE.
229 Caution: This function may receive untrusted input.
230 UEFI Authenticated Variable is external input, so this function will do basic
231 check for PKCS#7 data structure.
233 @param[in] P7Data Pointer to the PKCS#7 message to verify.
234 @param[in] P7Length Length of the PKCS#7 message in bytes.
235 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
236 It's caller's responsiblity to free the buffer.
237 @param[out] StackLength Length of signer's certificates in bytes.
238 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
239 It's caller's responsiblity to free the buffer.
240 @param[out] CertLength Length of the trusted certificate in bytes.
242 @retval TRUE The operation is finished successfully.
243 @retval FALSE Error occurs during the operation.
249 IN CONST UINT8
*P7Data
,
251 OUT UINT8
**CertStack
,
252 OUT UINTN
*StackLength
,
253 OUT UINT8
**TrustedCert
,
254 OUT UINTN
*CertLength
261 UINTN SignedDataSize
;
263 STACK_OF(X509
) *Stack
;
270 UINTN SingleCertSize
;
272 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
273 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
277 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
290 // Retrieve PKCS#7 Data (DER encoding)
292 if (SignedDataSize
> INT_MAX
) {
297 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
303 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
305 if (!PKCS7_type_is_signed (Pkcs7
)) {
309 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
315 // Convert CertStack to buffer in following format:
317 // UINT32 Cert1Length;
319 // UINT32 Cert2Length;
322 // UINT32 CertnLength;
325 BufferSize
= sizeof (UINT8
);
326 OldSize
= BufferSize
;
328 for (Index
= 0; ; Index
++) {
329 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
334 OldSize
= BufferSize
;
336 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
337 CertBuf
= malloc (BufferSize
);
339 if (CertBuf
== NULL
) {
343 if (OldBuf
!= NULL
) {
344 CopyMem (CertBuf
, OldBuf
, OldSize
);
349 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
350 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
356 if (CertBuf
!= NULL
) {
358 // Update CertNumber.
362 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
363 *TrustedCert
= malloc (*CertLength
);
364 if (*TrustedCert
== NULL
) {
368 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
369 *CertStack
= CertBuf
;
370 *StackLength
= BufferSize
;
387 sk_X509_pop_free(Stack
, X509_free
);
390 if (SingleCert
!= NULL
) {
394 if (!Status
&& (CertBuf
!= NULL
)) {
399 if (OldBuf
!= NULL
) {
407 Wrap function to use free() to free allocated memory for certificates.
409 @param[in] Certs Pointer to the certificates to be freed.
426 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
427 Cryptographic Message Syntax Standard". The input signed data could be wrapped
428 in a ContentInfo structure.
430 If P7Data, TrustedCert or InData is NULL, then return FALSE.
431 If P7Length, CertLength or DataLength overflow, then return FAlSE.
433 Caution: This function may receive untrusted input.
434 UEFI Authenticated Variable is external input, so this function will do basic
435 check for PKCS#7 data structure.
437 @param[in] P7Data Pointer to the PKCS#7 message to verify.
438 @param[in] P7Length Length of the PKCS#7 message in bytes.
439 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
440 is used for certificate chain verification.
441 @param[in] CertLength Length of the trusted certificate in bytes.
442 @param[in] InData Pointer to the content to be verified.
443 @param[in] DataLength Length of InData in bytes.
445 @retval TRUE The specified PKCS#7 signed data is valid.
446 @retval FALSE Invalid PKCS#7 signed data.
452 IN CONST UINT8
*P7Data
,
454 IN CONST UINT8
*TrustedCert
,
456 IN CONST UINT8
*InData
,
465 X509_STORE
*CertStore
;
468 UINTN SignedDataSize
;
472 // Check input parameters.
474 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
475 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
486 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
488 if (EVP_add_digest (EVP_md5 ()) == 0) {
491 if (EVP_add_digest (EVP_sha1 ()) == 0) {
494 if (EVP_add_digest (EVP_sha256 ()) == 0) {
497 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
) == 0) {
502 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
510 // Retrieve PKCS#7 Data (DER encoding)
512 if (SignedDataSize
> INT_MAX
) {
517 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
523 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
525 if (!PKCS7_type_is_signed (Pkcs7
)) {
530 // Read DER-encoded root certificate and Construct X509 Certificate
532 CertBio
= BIO_new (BIO_s_mem ());
533 BIO_write (CertBio
, TrustedCert
, (int)CertLength
);
534 if (CertBio
== NULL
) {
537 Cert
= d2i_X509_bio (CertBio
, NULL
);
543 // Setup X509 Store for trusted certificate
545 CertStore
= X509_STORE_new ();
546 if (CertStore
== NULL
) {
549 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
554 // Register customized X509 verification callback function to support
555 // trusted intermediate certificate anchor.
557 CertStore
->verify_cb
= X509VerifyCb
;
560 // For generic PKCS#7 handling, InData may be NULL if the content is present
561 // in PKCS#7 structure. So ignore NULL checking here.
563 DataBio
= BIO_new (BIO_s_mem ());
564 BIO_write (DataBio
, InData
, (int)DataLength
);
567 // Verifies the PKCS#7 signedData structure
569 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
578 X509_STORE_free (CertStore
);
582 OPENSSL_free (SignedData
);