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