1 /** @file
2   PKCS#7 SignedData Sign Wrapper Implementation over OpenSSL.
3 
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "InternalCryptLib.h"
10 
11 #include <openssl/objects.h>
12 #include <openssl/x509.h>
13 #include <openssl/pkcs7.h>
14 
15 /**
16   Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message
17   Syntax Standard, version 1.5". This interface is only intended to be used for
18   application to perform PKCS#7 functionality validation.
19 
20   @param[in]  PrivateKey       Pointer to the PEM-formatted private key data for
21                                data signing.
22   @param[in]  PrivateKeySize   Size of the PEM private key data in bytes.
23   @param[in]  KeyPassword      NULL-terminated passphrase used for encrypted PEM
24                                key data.
25   @param[in]  InData           Pointer to the content to be signed.
26   @param[in]  InDataSize       Size of InData in bytes.
27   @param[in]  SignCert         Pointer to signer's DER-encoded certificate to sign with.
28   @param[in]  OtherCerts       Pointer to an optional additional set of certificates to
29                                include in the PKCS#7 signedData (e.g. any intermediate
30                                CAs in the chain).
31   @param[out] SignedData       Pointer to output PKCS#7 signedData. It's caller's
32                                responsibility to free the buffer with FreePool().
33   @param[out] SignedDataSize   Size of SignedData in bytes.
34 
35   @retval     TRUE             PKCS#7 data signing succeeded.
36   @retval     FALSE            PKCS#7 data signing failed.
37 
38 **/
39 BOOLEAN
40 EFIAPI
Pkcs7Sign(IN CONST UINT8 * PrivateKey,IN UINTN PrivateKeySize,IN CONST UINT8 * KeyPassword,IN UINT8 * InData,IN UINTN InDataSize,IN UINT8 * SignCert,IN UINT8 * OtherCerts OPTIONAL,OUT UINT8 ** SignedData,OUT UINTN * SignedDataSize)41 Pkcs7Sign (
42   IN   CONST UINT8  *PrivateKey,
43   IN   UINTN        PrivateKeySize,
44   IN   CONST UINT8  *KeyPassword,
45   IN   UINT8        *InData,
46   IN   UINTN        InDataSize,
47   IN   UINT8        *SignCert,
48   IN   UINT8        *OtherCerts      OPTIONAL,
49   OUT  UINT8        **SignedData,
50   OUT  UINTN        *SignedDataSize
51   )
52 {
53   BOOLEAN   Status;
54   EVP_PKEY  *Key;
55   BIO       *DataBio;
56   PKCS7     *Pkcs7;
57   UINT8     *RsaContext;
58   UINT8     *P7Data;
59   UINTN     P7DataSize;
60   UINT8     *Tmp;
61 
62   //
63   // Check input parameters.
64   //
65   if (PrivateKey == NULL || KeyPassword == NULL || InData == NULL ||
66     SignCert == NULL || SignedData == NULL || SignedDataSize == NULL || InDataSize > INT_MAX) {
67     return FALSE;
68   }
69 
70   RsaContext = NULL;
71   Key        = NULL;
72   Pkcs7      = NULL;
73   DataBio    = NULL;
74   Status     = FALSE;
75 
76   //
77   // Retrieve RSA private key from PEM data.
78   //
79   Status = RsaGetPrivateKeyFromPem (
80              PrivateKey,
81              PrivateKeySize,
82              (CONST CHAR8 *) KeyPassword,
83              (VOID **) &RsaContext
84              );
85   if (!Status) {
86     return Status;
87   }
88 
89   Status = FALSE;
90 
91   //
92   // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling
93   //
94   if (EVP_add_digest (EVP_md5 ()) == 0) {
95     goto _Exit;
96   }
97   if (EVP_add_digest (EVP_sha1 ()) == 0) {
98     goto _Exit;
99   }
100   if (EVP_add_digest (EVP_sha256 ()) == 0) {
101     goto _Exit;
102   }
103 
104   RandomSeed (NULL, 0);
105 
106   //
107   // Construct OpenSSL EVP_PKEY for private key.
108   //
109   Key = EVP_PKEY_new ();
110   if (Key == NULL) {
111     goto _Exit;
112   }
113   if (EVP_PKEY_assign_RSA (Key, (RSA *) RsaContext) == 0) {
114     goto _Exit;
115   }
116 
117   //
118   // Convert the data to be signed to BIO format.
119   //
120   DataBio = BIO_new (BIO_s_mem ());
121   if (DataBio == NULL) {
122     goto _Exit;
123   }
124 
125   if (BIO_write (DataBio, InData, (int) InDataSize) <= 0) {
126     goto _Exit;
127   }
128 
129   //
130   // Create the PKCS#7 signedData structure.
131   //
132   Pkcs7 = PKCS7_sign (
133             (X509 *) SignCert,
134             Key,
135             (STACK_OF(X509) *) OtherCerts,
136             DataBio,
137             PKCS7_BINARY | PKCS7_NOATTR | PKCS7_DETACHED
138             );
139   if (Pkcs7 == NULL) {
140     goto _Exit;
141   }
142 
143   //
144   // Convert PKCS#7 signedData structure into DER-encoded buffer.
145   //
146   P7DataSize = i2d_PKCS7 (Pkcs7, NULL);
147   if (P7DataSize <= 19) {
148     goto _Exit;
149   }
150 
151   P7Data     = malloc (P7DataSize);
152   if (P7Data == NULL) {
153     goto _Exit;
154   }
155 
156   Tmp        = P7Data;
157   P7DataSize = i2d_PKCS7 (Pkcs7, (unsigned char **) &Tmp);
158   ASSERT (P7DataSize > 19);
159 
160   //
161   // Strip ContentInfo to content only for signeddata. The data be trimmed off
162   // is totally 19 bytes.
163   //
164   *SignedDataSize = P7DataSize - 19;
165   *SignedData     = AllocatePool (*SignedDataSize);
166   if (*SignedData == NULL) {
167     OPENSSL_free (P7Data);
168     goto _Exit;
169   }
170 
171   CopyMem (*SignedData, P7Data + 19, *SignedDataSize);
172 
173   OPENSSL_free (P7Data);
174 
175   Status = TRUE;
176 
177 _Exit:
178   //
179   // Release Resources
180   //
181   if (Key != NULL) {
182     EVP_PKEY_free (Key);
183   }
184 
185   if (DataBio != NULL) {
186     BIO_free (DataBio);
187   }
188 
189   if (Pkcs7 != NULL) {
190     PKCS7_free (Pkcs7);
191   }
192 
193   return Status;
194 }
195