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 Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message
115 Syntax Standard, version 1.5". This interface is only intended to be used for
116 application to perform PKCS#7 functionality validation.
118 @param[in] PrivateKey Pointer to the PEM-formatted private key data for
120 @param[in] PrivateKeySize Size of the PEM private key data in bytes.
121 @param[in] KeyPassword NULL-terminated passphrase used for encrypted PEM
123 @param[in] InData Pointer to the content to be signed.
124 @param[in] InDataSize Size of InData in bytes.
125 @param[in] SignCert Pointer to signer's DER-encoded certificate to sign with.
126 @param[in] OtherCerts Pointer to an optional additional set of certificates to
127 include in the PKCS#7 signedData (e.g. any intermediate
129 @param[out] SignedData Pointer to output PKCS#7 signedData.
130 @param[out] SignedDataSize Size of SignedData in bytes.
132 @retval TRUE PKCS#7 data signing succeeded.
133 @retval FALSE PKCS#7 data signing failed.
139 IN CONST UINT8
*PrivateKey
,
140 IN UINTN PrivateKeySize
,
141 IN CONST UINT8
*KeyPassword
,
145 IN UINT8
*OtherCerts OPTIONAL
,
146 OUT UINT8
**SignedData
,
147 OUT UINTN
*SignedDataSize
160 // Check input parameters.
162 if (PrivateKey
== NULL
|| KeyPassword
== NULL
|| InData
== NULL
||
163 SignCert
== NULL
|| SignedData
== NULL
|| SignedDataSize
== NULL
|| InDataSize
> INT_MAX
) {
174 // Retrieve RSA private key from PEM data.
176 Status
= RsaGetPrivateKeyFromPem (
179 (CONST CHAR8
*) KeyPassword
,
180 (VOID
**) &RsaContext
187 // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling
189 EVP_add_digest (EVP_md5());
190 EVP_add_digest (EVP_sha1());
191 EVP_add_digest (EVP_sha256());
192 RandomSeed (NULL
, 0);
195 // Construct OpenSSL EVP_PKEY for private key.
197 Key
= EVP_PKEY_new ();
202 Key
->save_type
= EVP_PKEY_RSA
;
203 Key
->type
= EVP_PKEY_type (EVP_PKEY_RSA
);
204 Key
->pkey
.rsa
= (RSA
*) RsaContext
;
207 // Convert the data to be signed to BIO format.
209 DataBio
= BIO_new (BIO_s_mem ());
210 BIO_write (DataBio
, InData
, (int) InDataSize
);
213 // Create the PKCS#7 signedData structure.
218 (STACK_OF(X509
) *) OtherCerts
,
220 PKCS7_BINARY
| PKCS7_NOATTR
| PKCS7_DETACHED
228 // Convert PKCS#7 signedData structure into DER-encoded buffer.
230 P7DataSize
= i2d_PKCS7 (Pkcs7
, NULL
);
231 if (P7DataSize
<= 19) {
236 P7Data
= malloc (P7DataSize
);
237 if (P7Data
== NULL
) {
243 P7DataSize
= i2d_PKCS7 (Pkcs7
, (unsigned char **) &Tmp
);
246 // Strip ContentInfo to content only for signeddata. The data be trimmed off
247 // is totally 19 bytes.
249 *SignedDataSize
= P7DataSize
- 19;
250 *SignedData
= malloc (*SignedDataSize
);
251 if (*SignedData
== NULL
) {
253 OPENSSL_free (P7Data
);
257 CopyMem (*SignedData
, P7Data
+ 19, *SignedDataSize
);
259 OPENSSL_free (P7Data
);
267 if (RsaContext
!= NULL
) {
268 RsaFree (RsaContext
);
270 Key
->pkey
.rsa
= NULL
;
278 if (DataBio
!= NULL
) {
290 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
291 a new structure to wrap P7Data.
293 Caution: This function may receive untrusted input.
294 UEFI Authenticated Variable is external input, so this function will do basic
295 check for PKCS#7 data structure.
297 @param[in] P7Data Pointer to the PKCS#7 message to verify.
298 @param[in] P7Length Length of the PKCS#7 message in bytes.
299 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
301 @param[out] WrapData If return status of this function is TRUE:
302 1) when WrapFlag is TRUE, pointer to P7Data.
303 2) when WrapFlag is FALSE, pointer to a new ContentInfo
304 structure. It's caller's responsibility to free this
306 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
308 @retval TRUE The operation is finished successfully.
309 @retval FALSE The operation is failed due to lack of resources.
314 IN CONST UINT8
*P7Data
,
316 OUT BOOLEAN
*WrapFlag
,
317 OUT UINT8
**WrapData
,
318 OUT UINTN
*WrapDataSize
325 // Check whether input P7Data is a wrapped ContentInfo structure or not.
328 if ((P7Data
[4] == 0x06) && (P7Data
[5] == 0x09)) {
329 if (CompareMem (P7Data
+ 6, mOidValue
, sizeof (mOidValue
)) == 0) {
330 if ((P7Data
[15] == 0xA0) && (P7Data
[16] == 0x82)) {
337 *WrapData
= (UINT8
*) P7Data
;
338 *WrapDataSize
= P7Length
;
341 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
343 *WrapDataSize
= P7Length
+ 19;
344 *WrapData
= malloc (*WrapDataSize
);
345 if (*WrapData
== NULL
) {
350 SignedData
= *WrapData
;
353 // Part1: 0x30, 0x82.
355 SignedData
[0] = 0x30;
356 SignedData
[1] = 0x82;
359 // Part2: Length1 = P7Length + 19 - 4, in big endian.
361 SignedData
[2] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) >> 8);
362 SignedData
[3] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) & 0xff);
365 // Part3: 0x06, 0x09.
367 SignedData
[4] = 0x06;
368 SignedData
[5] = 0x09;
371 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
373 CopyMem (SignedData
+ 6, mOidValue
, sizeof (mOidValue
));
376 // Part5: 0xA0, 0x82.
378 SignedData
[15] = 0xA0;
379 SignedData
[16] = 0x82;
382 // Part6: Length2 = P7Length, in big endian.
384 SignedData
[17] = (UINT8
) (((UINT16
) P7Length
) >> 8);
385 SignedData
[18] = (UINT8
) (((UINT16
) P7Length
) & 0xff);
390 CopyMem (SignedData
+ 19, P7Data
, P7Length
);
398 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
399 Cryptographic Message Syntax Standard". The input signed data could be wrapped
400 in a ContentInfo structure.
402 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
403 return FALSE. If P7Length overflow, then return FAlSE.
405 Caution: This function may receive untrusted input.
406 UEFI Authenticated Variable is external input, so this function will do basic
407 check for PKCS#7 data structure.
409 @param[in] P7Data Pointer to the PKCS#7 message to verify.
410 @param[in] P7Length Length of the PKCS#7 message in bytes.
411 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
412 It's caller's responsiblity to free the buffer.
413 @param[out] StackLength Length of signer's certificates in bytes.
414 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
415 It's caller's responsiblity to free the buffer.
416 @param[out] CertLength Length of the trusted certificate in bytes.
418 @retval TRUE The operation is finished successfully.
419 @retval FALSE Error occurs during the operation.
425 IN CONST UINT8
*P7Data
,
427 OUT UINT8
**CertStack
,
428 OUT UINTN
*StackLength
,
429 OUT UINT8
**TrustedCert
,
430 OUT UINTN
*CertLength
437 UINTN SignedDataSize
;
439 STACK_OF(X509
) *Stack
;
446 UINTN SingleCertSize
;
448 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
449 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
453 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
466 // Retrieve PKCS#7 Data (DER encoding)
468 if (SignedDataSize
> INT_MAX
) {
473 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
479 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
481 if (!PKCS7_type_is_signed (Pkcs7
)) {
485 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
491 // Convert CertStack to buffer in following format:
493 // UINT32 Cert1Length;
495 // UINT32 Cert2Length;
498 // UINT32 CertnLength;
501 BufferSize
= sizeof (UINT8
);
502 OldSize
= BufferSize
;
504 for (Index
= 0; ; Index
++) {
505 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
510 OldSize
= BufferSize
;
512 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
513 CertBuf
= malloc (BufferSize
);
515 if (CertBuf
== NULL
) {
519 if (OldBuf
!= NULL
) {
520 CopyMem (CertBuf
, OldBuf
, OldSize
);
525 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
526 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
532 if (CertBuf
!= NULL
) {
534 // Update CertNumber.
538 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
539 *TrustedCert
= malloc (*CertLength
);
540 if (*TrustedCert
== NULL
) {
544 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
545 *CertStack
= CertBuf
;
546 *StackLength
= BufferSize
;
563 sk_X509_pop_free(Stack
, X509_free
);
566 if (SingleCert
!= NULL
) {
570 if (!Status
&& (CertBuf
!= NULL
)) {
575 if (OldBuf
!= NULL
) {
583 Wrap function to use free() to free allocated memory for certificates.
585 @param[in] Certs Pointer to the certificates to be freed.
602 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
603 Cryptographic Message Syntax Standard". The input signed data could be wrapped
604 in a ContentInfo structure.
606 If P7Data, TrustedCert or InData is NULL, then return FALSE.
607 If P7Length, CertLength or DataLength overflow, then return FAlSE.
609 Caution: This function may receive untrusted input.
610 UEFI Authenticated Variable is external input, so this function will do basic
611 check for PKCS#7 data structure.
613 @param[in] P7Data Pointer to the PKCS#7 message to verify.
614 @param[in] P7Length Length of the PKCS#7 message in bytes.
615 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
616 is used for certificate chain verification.
617 @param[in] CertLength Length of the trusted certificate in bytes.
618 @param[in] InData Pointer to the content to be verified.
619 @param[in] DataLength Length of InData in bytes.
621 @retval TRUE The specified PKCS#7 signed data is valid.
622 @retval FALSE Invalid PKCS#7 signed data.
628 IN CONST UINT8
*P7Data
,
630 IN CONST UINT8
*TrustedCert
,
632 IN CONST UINT8
*InData
,
641 X509_STORE
*CertStore
;
644 UINTN SignedDataSize
;
648 // Check input parameters.
650 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
651 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
662 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
664 EVP_add_digest (EVP_md5());
665 EVP_add_digest (EVP_sha1());
666 EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
);
667 EVP_add_digest (EVP_sha256());
669 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
677 // Retrieve PKCS#7 Data (DER encoding)
679 if (SignedDataSize
> INT_MAX
) {
684 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
690 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
692 if (!PKCS7_type_is_signed (Pkcs7
)) {
697 // Read DER-encoded root certificate and Construct X509 Certificate
699 CertBio
= BIO_new (BIO_s_mem ());
700 BIO_write (CertBio
, TrustedCert
, (int)CertLength
);
701 if (CertBio
== NULL
) {
704 Cert
= d2i_X509_bio (CertBio
, NULL
);
710 // Setup X509 Store for trusted certificate
712 CertStore
= X509_STORE_new ();
713 if (CertStore
== NULL
) {
716 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
721 // Register customized X509 verification callback function to support
722 // trusted intermediate certificate anchor.
724 CertStore
->verify_cb
= X509VerifyCb
;
727 // For generic PKCS#7 handling, InData may be NULL if the content is present
728 // in PKCS#7 structure. So ignore NULL checking here.
730 DataBio
= BIO_new (BIO_s_mem ());
731 BIO_write (DataBio
, InData
, (int)DataLength
);
734 // Verifies the PKCS#7 signedData structure
736 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
745 X509_STORE_free (CertStore
);
749 OPENSSL_free (SignedData
);