]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7.c
Add new interfaces to support PKCS7#7 signed data and authenticode signature. Update...
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7.c
CommitLineData
97f98500
HT
1/** @file\r
2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.\r
3\r
b7d320f8 4Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
97f98500
HT
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
a8c44645 15#include "InternalCryptLib.h"\r
97f98500 16\r
97f98500
HT
17#include <openssl/objects.h>\r
18#include <openssl/x509.h>\r
19#include <openssl/pkcs7.h>\r
20\r
21\r
b7d320f8 22/**\r
23 Verification callback function to override any existing callbacks in OpenSSL\r
24 for intermediate certificate supports.\r
25\r
26 @param[in] Status Original status before calling this callback.\r
27 @param[in] Context X509 store context.\r
28\r
29 @retval 1 Current X509 certificate is verified successfully.\r
30 @retval 0 Verification failed.\r
31\r
32**/\r
33STATIC int X509VerifyCb (int Status, X509_STORE_CTX *Context)\r
34{\r
35 X509_OBJECT *Obj;\r
36 int Error;\r
37 int Index;\r
38 int Count;\r
39\r
40 Obj = NULL;\r
41 Error = X509_STORE_CTX_get_error (Context);\r
42\r
43 //\r
44 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_\r
45 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer\r
46 // can not be found in X509_verify_cert of X509_vfy.c.\r
47 // In order to support intermediate certificate node, we override the\r
48 // errors if the certification is obtained from X509 store, i.e. it is\r
49 // a trusted ceritifcate node that is enrolled by user.\r
50 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE\r
51 // are also ignored to enable such feature.\r
52 //\r
53 if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||\r
54 (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {\r
55 Obj = (X509_OBJECT *) OPENSSL_malloc (sizeof (X509_OBJECT));\r
56 if (Obj == NULL) {\r
57 return 0;\r
58 }\r
59\r
60 Obj->type = X509_LU_X509;\r
61 Obj->data.x509 = Context->current_cert;\r
62\r
63 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);\r
64\r
65 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
66 Status = 1;\r
67 } else {\r
68 //\r
69 // If any certificate in the chain is enrolled as trusted certificate,\r
70 // pass the certificate verification.\r
71 //\r
72 if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {\r
73 Count = sk_X509_num (Context->chain);\r
74 for (Index = 0; Index < Count; Index++) {\r
75 Obj->data.x509 = sk_X509_value (Context->chain, Index);\r
76 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
77 Status = 1;\r
78 break;\r
79 }\r
80 }\r
81 }\r
82 }\r
83\r
84 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);\r
85 }\r
86\r
87 if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||\r
88 (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {\r
89 Status = 1;\r
90 }\r
91\r
92 if (Obj != NULL) {\r
93 OPENSSL_free (Obj);\r
94 }\r
95\r
96 return Status;\r
97}\r
98\r
99/**\r
100 Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message\r
101 Syntax Standard, version 1.5". This interface is only intended to be used for\r
102 application to perform PKCS#7 functionality validation.\r
103\r
104 @param[in] PrivateKey Pointer to the PEM-formatted private key data for\r
105 data signing.\r
106 @param[in] PrivateKeySize Size of the PEM private key data in bytes.\r
107 @param[in] KeyPassword NULL-terminated passphrase used for encrypted PEM\r
108 key data.\r
109 @param[in] InData Pointer to the content to be signed.\r
110 @param[in] InDataSize Size of InData in bytes.\r
111 @param[in] SignCert Pointer to signer's DER-encoded certificate to sign with.\r
112 @param[in] OtherCerts Pointer to an optional additional set of certificates to\r
113 include in the PKCS#7 signedData (e.g. any intermediate\r
114 CAs in the chain).\r
115 @param[out] SignedData Pointer to output PKCS#7 signedData.\r
116 @param[out] SignedDataSize Size of SignedData in bytes.\r
117\r
118 @retval TRUE PKCS#7 data signing succeeded.\r
119 @retval FALSE PKCS#7 data signing failed.\r
120\r
121**/\r
122BOOLEAN\r
123EFIAPI\r
124Pkcs7Sign (\r
125 IN CONST UINT8 *PrivateKey,\r
126 IN UINTN PrivateKeySize,\r
127 IN CONST UINT8 *KeyPassword,\r
128 IN UINT8 *InData,\r
129 IN UINTN InDataSize,\r
130 IN UINT8 *SignCert,\r
131 IN UINT8 *OtherCerts OPTIONAL,\r
132 OUT UINT8 **SignedData,\r
133 OUT UINTN *SignedDataSize\r
134 )\r
135{\r
136 BOOLEAN Status;\r
137 EVP_PKEY *Key;\r
138 BIO *DataBio;\r
139 PKCS7 *Pkcs7;\r
140 UINT8 *RsaContext;\r
141 UINT8 *P7Data;\r
142\r
143 //\r
144 // Check input parameters.\r
145 //\r
146 if ((PrivateKey == NULL) || (KeyPassword == NULL) || (InData == NULL)) {\r
147 return FALSE;\r
148 }\r
149 \r
150 if ((SignCert == NULL) || (SignedData == NULL) || (SignedDataSize == NULL)) {\r
151 return FALSE;\r
152 }\r
153\r
154 RsaContext = NULL;\r
155 Key = NULL;\r
156 Pkcs7 = NULL;\r
157 DataBio = NULL;\r
158 Status = FALSE;\r
159\r
160 //\r
161 // Retrieve RSA private key from PEM data.\r
162 //\r
163 Status = RsaGetPrivateKeyFromPem (\r
164 PrivateKey,\r
165 PrivateKeySize,\r
166 (CONST CHAR8 *) KeyPassword,\r
167 (VOID **) &RsaContext\r
168 );\r
169 if (!Status) {\r
170 return Status;\r
171 }\r
172\r
173 //\r
174 // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling\r
175 //\r
176 EVP_add_digest (EVP_md5());\r
177 EVP_add_digest (EVP_sha1());\r
178 EVP_add_digest (EVP_sha256());\r
179 RandomSeed (NULL, 0);\r
180\r
181 //\r
182 // Construct OpenSSL EVP_PKEY for private key.\r
183 //\r
184 Key = EVP_PKEY_new ();\r
185 if (Key == NULL) {\r
186 goto _Exit;\r
187 }\r
188 Key->save_type = EVP_PKEY_RSA;\r
189 Key->type = EVP_PKEY_type (EVP_PKEY_RSA);\r
190 Key->pkey.rsa = (RSA *) RsaContext;\r
191\r
192 //\r
193 // Convert the data to be signed to BIO format. \r
194 //\r
195 DataBio = BIO_new (BIO_s_mem ());\r
196 BIO_write (DataBio, InData, (int) InDataSize);\r
197\r
198 //\r
199 // Create the PKCS#7 signedData structure.\r
200 //\r
201 Pkcs7 = PKCS7_sign (\r
202 (X509 *) SignCert,\r
203 Key,\r
204 (STACK_OF(X509) *) OtherCerts,\r
205 DataBio,\r
206 PKCS7_BINARY\r
207 );\r
208 if (Pkcs7 == NULL) {\r
209 goto _Exit;\r
210 }\r
211\r
212 //\r
213 // Convert PKCS#7 signedData structure into DER-encoded buffer.\r
214 //\r
215 *SignedDataSize = i2d_PKCS7 (Pkcs7, NULL);\r
216 if (*SignedDataSize == 0) {\r
217 goto _Exit;\r
218 }\r
219 *SignedData = OPENSSL_malloc (*SignedDataSize);\r
220 P7Data = *SignedData;\r
221 *SignedDataSize = i2d_PKCS7 (Pkcs7, (unsigned char **) &P7Data);\r
222\r
223 Status = TRUE;\r
224\r
225_Exit:\r
226 //\r
227 // Release Resources\r
228 //\r
229 if (RsaContext != NULL) {\r
230 RsaFree (RsaContext);\r
231 if (Key != NULL) {\r
232 Key->pkey.rsa = NULL;\r
233 }\r
234 }\r
235\r
236 if (Key != NULL) {\r
237 EVP_PKEY_free (Key);\r
238 }\r
239\r
240 if (DataBio != NULL) {\r
241 BIO_free (DataBio);\r
242 }\r
243\r
244 if (Pkcs7 != NULL) {\r
245 PKCS7_free (Pkcs7);\r
246 }\r
247\r
248 return Status;\r
249}\r
250\r
97f98500
HT
251/**\r
252 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7: Cryptographic\r
253 Message Syntax Standard".\r
254\r
255 If P7Data is NULL, then ASSERT().\r
256\r
257 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
258 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
259 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
260 is used for certificate chain verification.\r
261 @param[in] CertLength Length of the trusted certificate in bytes.\r
262 @param[in] InData Pointer to the content to be verified.\r
263 @param[in] DataLength Length of InData in bytes.\r
264\r
a8c44645 265 @retval TRUE The specified PKCS#7 signed data is valid.\r
266 @retval FALSE Invalid PKCS#7 signed data.\r
97f98500
HT
267\r
268**/\r
269BOOLEAN\r
270EFIAPI\r
271Pkcs7Verify (\r
272 IN CONST UINT8 *P7Data,\r
273 IN UINTN P7Length,\r
274 IN CONST UINT8 *TrustedCert,\r
275 IN UINTN CertLength,\r
276 IN CONST UINT8 *InData,\r
277 IN UINTN DataLength\r
278 )\r
279{\r
280 PKCS7 *Pkcs7;\r
97f98500
HT
281 BIO *CertBio;\r
282 BIO *DataBio;\r
283 BOOLEAN Status;\r
284 X509 *Cert;\r
285 X509_STORE *CertStore;\r
286\r
287 //\r
288 // ASSERT if P7Data is NULL\r
289 //\r
290 ASSERT (P7Data != NULL);\r
291\r
292 Status = FALSE;\r
293 Pkcs7 = NULL;\r
294 CertBio = NULL;\r
295 DataBio = NULL;\r
296 Cert = NULL;\r
297 CertStore = NULL;\r
298\r
299 //\r
300 // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
301 //\r
302 EVP_add_digest (EVP_md5());\r
303 EVP_add_digest (EVP_sha1());\r
b7d320f8 304 EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA);\r
97f98500
HT
305 EVP_add_digest (EVP_sha256());\r
306\r
307 //\r
308 // Retrieve PKCS#7 Data (DER encoding)\r
309 //\r
310 Pkcs7 = d2i_PKCS7 (NULL, &P7Data, (int)P7Length);\r
311 if (Pkcs7 == NULL) {\r
312 goto _Exit;\r
313 }\r
314\r
315 //\r
316 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
317 //\r
318 if (!PKCS7_type_is_signed (Pkcs7)) {\r
319 goto _Exit;\r
320 }\r
321\r
97f98500
HT
322 //\r
323 // Read DER-encoded root certificate and Construct X509 Certificate\r
324 //\r
325 CertBio = BIO_new (BIO_s_mem ());\r
326 BIO_write (CertBio, TrustedCert, (int)CertLength);\r
327 if (CertBio == NULL) {\r
328 goto _Exit;\r
329 }\r
330 Cert = d2i_X509_bio (CertBio, NULL);\r
331 if (Cert == NULL) {\r
332 goto _Exit;\r
333 }\r
334\r
335 //\r
336 // Setup X509 Store for trusted certificate\r
337 //\r
338 CertStore = X509_STORE_new ();\r
339 if (CertStore == NULL) {\r
340 goto _Exit;\r
341 }\r
342 if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
343 goto _Exit;\r
344 }\r
345\r
b7d320f8 346 //\r
347 // Register customized X509 verification callback function to support\r
348 // trusted intermediate certificate anchor.\r
349 //\r
350 CertStore->verify_cb = X509VerifyCb;\r
351\r
97f98500
HT
352 //\r
353 // For generic PKCS#7 handling, InData may be NULL if the content is present\r
354 // in PKCS#7 structure. So ignore NULL checking here.\r
355 //\r
356 DataBio = BIO_new (BIO_s_mem ());\r
357 BIO_write (DataBio, InData, (int)DataLength);\r
358\r
359 //\r
360 // Verifies the PKCS#7 signedData structure\r
361 //\r
b7d320f8 362 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
97f98500
HT
363\r
364_Exit:\r
365 //\r
366 // Release Resources\r
367 //\r
368 BIO_free (DataBio);\r
369 BIO_free (CertBio);\r
370 X509_free (Cert);\r
371 X509_STORE_free (CertStore);\r
372 PKCS7_free (Pkcs7);\r
373\r
374 return Status;\r
375}\r