]> git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7.c
Add two new interfaces Pkcs7GetSigners and Pkcs7FreeSigners to BaseCryptLib.
[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 - 2012, 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 *) 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 if (PrivateKey == NULL || KeyPassword == NULL || InData == NULL ||
154 SignCert == NULL || SignedData == NULL || SignedDataSize == NULL || InDataSize > INT_MAX) {
155 return FALSE;
156 }
157
158 RsaContext = NULL;
159 Key = NULL;
160 Pkcs7 = NULL;
161 DataBio = NULL;
162 Status = FALSE;
163
164 //
165 // Retrieve RSA private key from PEM data.
166 //
167 Status = RsaGetPrivateKeyFromPem (
168 PrivateKey,
169 PrivateKeySize,
170 (CONST CHAR8 *) KeyPassword,
171 (VOID **) &RsaContext
172 );
173 if (!Status) {
174 return Status;
175 }
176
177 //
178 // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling
179 //
180 EVP_add_digest (EVP_md5());
181 EVP_add_digest (EVP_sha1());
182 EVP_add_digest (EVP_sha256());
183 RandomSeed (NULL, 0);
184
185 //
186 // Construct OpenSSL EVP_PKEY for private key.
187 //
188 Key = EVP_PKEY_new ();
189 if (Key == NULL) {
190 Status = FALSE;
191 goto _Exit;
192 }
193 Key->save_type = EVP_PKEY_RSA;
194 Key->type = EVP_PKEY_type (EVP_PKEY_RSA);
195 Key->pkey.rsa = (RSA *) RsaContext;
196
197 //
198 // Convert the data to be signed to BIO format.
199 //
200 DataBio = BIO_new (BIO_s_mem ());
201 BIO_write (DataBio, InData, (int) InDataSize);
202
203 //
204 // Create the PKCS#7 signedData structure.
205 //
206 Pkcs7 = PKCS7_sign (
207 (X509 *) SignCert,
208 Key,
209 (STACK_OF(X509) *) OtherCerts,
210 DataBio,
211 PKCS7_BINARY | PKCS7_NOATTR | PKCS7_DETACHED
212 );
213 if (Pkcs7 == NULL) {
214 Status = FALSE;
215 goto _Exit;
216 }
217
218 //
219 // Convert PKCS#7 signedData structure into DER-encoded buffer.
220 //
221 P7DataSize = i2d_PKCS7 (Pkcs7, NULL);
222 if (P7DataSize <= 19) {
223 Status = FALSE;
224 goto _Exit;
225 }
226
227 P7Data = malloc (P7DataSize);
228 if (P7Data == NULL) {
229 Status = FALSE;
230 goto _Exit;
231 }
232
233 Tmp = P7Data;
234 P7DataSize = i2d_PKCS7 (Pkcs7, (unsigned char **) &Tmp);
235
236 //
237 // Strip ContentInfo to content only for signeddata. The data be trimmed off
238 // is totally 19 bytes.
239 //
240 *SignedDataSize = P7DataSize - 19;
241 *SignedData = malloc (*SignedDataSize);
242 if (*SignedData == NULL) {
243 Status = FALSE;
244 OPENSSL_free (P7Data);
245 goto _Exit;
246 }
247
248 CopyMem (*SignedData, P7Data + 19, *SignedDataSize);
249
250 OPENSSL_free (P7Data);
251
252 Status = TRUE;
253
254 _Exit:
255 //
256 // Release Resources
257 //
258 if (RsaContext != NULL) {
259 RsaFree (RsaContext);
260 if (Key != NULL) {
261 Key->pkey.rsa = NULL;
262 }
263 }
264
265 if (Key != NULL) {
266 EVP_PKEY_free (Key);
267 }
268
269 if (DataBio != NULL) {
270 BIO_free (DataBio);
271 }
272
273 if (Pkcs7 != NULL) {
274 PKCS7_free (Pkcs7);
275 }
276
277 return Status;
278 }
279
280 /**
281 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
282 a new structure to wrap P7Data.
283
284 @param[in] P7Data Pointer to the PKCS#7 message to verify.
285 @param[in] P7Length Length of the PKCS#7 message in bytes.
286 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
287 return FALSE.
288 @param[out] WrapData If return status of this function is TRUE:
289 1) when WrapFlag is TRUE, pointer to P7Data.
290 2) when WrapFlag is FALSE, pointer to a new ContentInfo
291 structure. It's caller's responsibility to free this
292 buffer.
293 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
294
295 @retval TRUE The operation is finished successfully.
296 @retval FALSE The operation is failed due to lack of resources.
297
298 **/
299 BOOLEAN
300 WrapPkcs7Data (
301 IN CONST UINT8 *P7Data,
302 IN UINTN P7Length,
303 OUT BOOLEAN *WrapFlag,
304 OUT UINT8 **WrapData,
305 OUT UINTN *WrapDataSize
306 )
307 {
308 BOOLEAN Wrapped;
309 UINT8 *SignedData;
310
311 //
312 // Check whether input P7Data is a wrapped ContentInfo structure or not.
313 //
314 Wrapped = FALSE;
315 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {
316 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {
317 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {
318 Wrapped = TRUE;
319 }
320 }
321 }
322
323 if (Wrapped) {
324 *WrapData = (UINT8 *) P7Data;
325 *WrapDataSize = P7Length;
326 } else {
327 //
328 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
329 //
330 *WrapDataSize = P7Length + 19;
331 *WrapData = malloc (*WrapDataSize);
332 if (*WrapData == NULL) {
333 *WrapFlag = Wrapped;
334 return FALSE;
335 }
336
337 SignedData = *WrapData;
338
339 //
340 // Part1: 0x30, 0x82.
341 //
342 SignedData[0] = 0x30;
343 SignedData[1] = 0x82;
344
345 //
346 // Part2: Length1 = P7Length + 19 - 4, in big endian.
347 //
348 SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);
349 SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);
350
351 //
352 // Part3: 0x06, 0x09.
353 //
354 SignedData[4] = 0x06;
355 SignedData[5] = 0x09;
356
357 //
358 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
359 //
360 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));
361
362 //
363 // Part5: 0xA0, 0x82.
364 //
365 SignedData[15] = 0xA0;
366 SignedData[16] = 0x82;
367
368 //
369 // Part6: Length2 = P7Length, in big endian.
370 //
371 SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);
372 SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);
373
374 //
375 // Part7: P7Data.
376 //
377 CopyMem (SignedData + 19, P7Data, P7Length);
378 }
379
380 *WrapFlag = Wrapped;
381 return TRUE;
382 }
383
384 /**
385 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
386 Cryptographic Message Syntax Standard". The input signed data could be wrapped
387 in a ContentInfo structure.
388
389 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
390 return FALSE. If P7Length overflow, then return FAlSE.
391
392 @param[in] P7Data Pointer to the PKCS#7 message to verify.
393 @param[in] P7Length Length of the PKCS#7 message in bytes.
394 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
395 It's caller's responsiblity to free the buffer.
396 @param[out] StackLength Length of signer's certificates in bytes.
397 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
398 It's caller's responsiblity to free the buffer.
399 @param[out] CertLength Length of the trusted certificate in bytes.
400
401 @retval TRUE The operation is finished successfully.
402 @retval FALSE Error occurs during the operation.
403
404 **/
405 BOOLEAN
406 EFIAPI
407 Pkcs7GetSigners (
408 IN CONST UINT8 *P7Data,
409 IN UINTN P7Length,
410 OUT UINT8 **CertStack,
411 OUT UINTN *StackLength,
412 OUT UINT8 **TrustedCert,
413 OUT UINTN *CertLength
414 )
415 {
416 PKCS7 *Pkcs7;
417 BOOLEAN Status;
418 UINT8 *SignedData;
419 UINT8 *Temp;
420 UINTN SignedDataSize;
421 BOOLEAN Wrapped;
422 STACK_OF(X509) *Stack;
423 UINT8 Index;
424 UINT8 *CertBuf;
425 UINT8 *OldBuf;
426 UINTN BufferSize;
427 UINTN OldSize;
428 UINT8 *SingleCert;
429 UINTN SingleCertSize;
430
431 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
432 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {
433 return FALSE;
434 }
435
436 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
437 if (!Status) {
438 return Status;
439 }
440
441 Status = FALSE;
442 Pkcs7 = NULL;
443 Stack = NULL;
444 CertBuf = NULL;
445 OldBuf = NULL;
446 SingleCert = NULL;
447
448 //
449 // Retrieve PKCS#7 Data (DER encoding)
450 //
451 if (SignedDataSize > INT_MAX) {
452 goto _Exit;
453 }
454
455 Temp = SignedData;
456 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
457 if (Pkcs7 == NULL) {
458 goto _Exit;
459 }
460
461 //
462 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
463 //
464 if (!PKCS7_type_is_signed (Pkcs7)) {
465 goto _Exit;
466 }
467
468 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);
469 if (Stack == NULL) {
470 goto _Exit;
471 }
472
473 //
474 // Convert CertStack to buffer in following format:
475 // UINT8 CertNumber;
476 // UINT32 Cert1Length;
477 // UINT8 Cert1[];
478 // UINT32 Cert2Length;
479 // UINT8 Cert2[];
480 // ...
481 // UINT32 CertnLength;
482 // UINT8 Certn[];
483 //
484 BufferSize = sizeof (UINT8);
485 OldSize = BufferSize;
486
487 for (Index = 0; ; Index++) {
488 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);
489 if (!Status) {
490 break;
491 }
492
493 OldSize = BufferSize;
494 OldBuf = CertBuf;
495 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);
496 CertBuf = malloc (BufferSize);
497
498 if (CertBuf == NULL) {
499 goto _Exit;
500 }
501
502 if (OldBuf != NULL) {
503 CopyMem (CertBuf, OldBuf, OldSize);
504 free (OldBuf);
505 OldBuf = NULL;
506 }
507
508 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);
509 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);
510
511 free (SingleCert);
512 SingleCert = NULL;
513 }
514
515 if (CertBuf != NULL) {
516 //
517 // Update CertNumber.
518 //
519 CertBuf[0] = Index;
520
521 *CertLength = BufferSize - OldSize - sizeof (UINT32);
522 *TrustedCert = malloc (*CertLength);
523 if (*TrustedCert == NULL) {
524 goto _Exit;
525 }
526
527 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
528 *CertStack = CertBuf;
529 *StackLength = BufferSize;
530 Status = TRUE;
531 }
532
533 _Exit:
534 //
535 // Release Resources
536 //
537 if (!Wrapped) {
538 free (SignedData);
539 }
540
541 if (Pkcs7 != NULL) {
542 PKCS7_free (Pkcs7);
543 }
544
545 if (Stack != NULL) {
546 sk_X509_pop_free(Stack, X509_free);
547 }
548
549 if (SingleCert != NULL) {
550 free (SingleCert);
551 }
552
553 if (!Status && (CertBuf != NULL)) {
554 free (CertBuf);
555 *CertStack = NULL;
556 }
557
558 if (OldBuf != NULL) {
559 free (OldBuf);
560 }
561
562 return Status;
563 }
564
565 /**
566 Wrap function to use free() to free allocated memory for certificates.
567
568 @param[in] Certs Pointer to the certificates to be freed.
569
570 **/
571 VOID
572 EFIAPI
573 Pkcs7FreeSigners (
574 IN UINT8 *Certs
575 )
576 {
577 if (Certs == NULL) {
578 return;
579 }
580
581 free (Certs);
582 }
583
584 /**
585 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
586 Cryptographic Message Syntax Standard". The input signed data could be wrapped
587 in a ContentInfo structure.
588
589 If P7Data, TrustedCert or InData is NULL, then return FALSE.
590 If P7Length, CertLength or DataLength overflow, then return FAlSE.
591
592 @param[in] P7Data Pointer to the PKCS#7 message to verify.
593 @param[in] P7Length Length of the PKCS#7 message in bytes.
594 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
595 is used for certificate chain verification.
596 @param[in] CertLength Length of the trusted certificate in bytes.
597 @param[in] InData Pointer to the content to be verified.
598 @param[in] DataLength Length of InData in bytes.
599
600 @retval TRUE The specified PKCS#7 signed data is valid.
601 @retval FALSE Invalid PKCS#7 signed data.
602
603 **/
604 BOOLEAN
605 EFIAPI
606 Pkcs7Verify (
607 IN CONST UINT8 *P7Data,
608 IN UINTN P7Length,
609 IN CONST UINT8 *TrustedCert,
610 IN UINTN CertLength,
611 IN CONST UINT8 *InData,
612 IN UINTN DataLength
613 )
614 {
615 PKCS7 *Pkcs7;
616 BIO *CertBio;
617 BIO *DataBio;
618 BOOLEAN Status;
619 X509 *Cert;
620 X509_STORE *CertStore;
621 UINT8 *SignedData;
622 UINT8 *Temp;
623 UINTN SignedDataSize;
624 BOOLEAN Wrapped;
625
626 //
627 // Check input parameters.
628 //
629 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||
630 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {
631 return FALSE;
632 }
633
634 Pkcs7 = NULL;
635 CertBio = NULL;
636 DataBio = NULL;
637 Cert = NULL;
638 CertStore = NULL;
639
640 //
641 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
642 //
643 EVP_add_digest (EVP_md5());
644 EVP_add_digest (EVP_sha1());
645 EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA);
646 EVP_add_digest (EVP_sha256());
647
648 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
649 if (!Status) {
650 return Status;
651 }
652
653 //
654 // Retrieve PKCS#7 Data (DER encoding)
655 //
656 if (SignedDataSize > INT_MAX) {
657 goto _Exit;
658 }
659
660 Temp = SignedData;
661 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
662 if (Pkcs7 == NULL) {
663 goto _Exit;
664 }
665
666 //
667 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
668 //
669 if (!PKCS7_type_is_signed (Pkcs7)) {
670 goto _Exit;
671 }
672
673 //
674 // Read DER-encoded root certificate and Construct X509 Certificate
675 //
676 CertBio = BIO_new (BIO_s_mem ());
677 BIO_write (CertBio, TrustedCert, (int)CertLength);
678 if (CertBio == NULL) {
679 goto _Exit;
680 }
681 Cert = d2i_X509_bio (CertBio, NULL);
682 if (Cert == NULL) {
683 goto _Exit;
684 }
685
686 //
687 // Setup X509 Store for trusted certificate
688 //
689 CertStore = X509_STORE_new ();
690 if (CertStore == NULL) {
691 goto _Exit;
692 }
693 if (!(X509_STORE_add_cert (CertStore, Cert))) {
694 goto _Exit;
695 }
696
697 //
698 // Register customized X509 verification callback function to support
699 // trusted intermediate certificate anchor.
700 //
701 CertStore->verify_cb = X509VerifyCb;
702
703 //
704 // For generic PKCS#7 handling, InData may be NULL if the content is present
705 // in PKCS#7 structure. So ignore NULL checking here.
706 //
707 DataBio = BIO_new (BIO_s_mem ());
708 BIO_write (DataBio, InData, (int)DataLength);
709
710 //
711 // Verifies the PKCS#7 signedData structure
712 //
713 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);
714
715 _Exit:
716 //
717 // Release Resources
718 //
719 BIO_free (DataBio);
720 BIO_free (CertBio);
721 X509_free (Cert);
722 X509_STORE_free (CertStore);
723 PKCS7_free (Pkcs7);
724
725 if (!Wrapped) {
726 OPENSSL_free (SignedData);
727 }
728
729 return Status;
730 }