]> git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
CryptoPkg Updates to support RFC3161 timestamp signature verification.
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7Verify.c
1 /** @file
2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
3
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.
9
10 WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated
11 Variable and will do basic check for data structure.
12
13 Copyright (c) 2009 - 2014, 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
18
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.
21
22 **/
23
24 #include "InternalCryptLib.h"
25
26 #include <openssl/objects.h>
27 #include <openssl/x509.h>
28 #include <openssl/x509v3.h>
29 #include <openssl/pkcs7.h>
30
31 UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
32
33 /**
34 Verification callback function to override any existing callbacks in OpenSSL
35 for intermediate certificate supports.
36
37 @param[in] Status Original status before calling this callback.
38 @param[in] Context X509 store context.
39
40 @retval 1 Current X509 certificate is verified successfully.
41 @retval 0 Verification failed.
42
43 **/
44 int
45 X509VerifyCb (
46 IN int Status,
47 IN X509_STORE_CTX *Context
48 )
49 {
50 X509_OBJECT *Obj;
51 INTN Error;
52 INTN Index;
53 INTN Count;
54
55 Obj = NULL;
56 Error = (INTN) X509_STORE_CTX_get_error (Context);
57
58 //
59 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
60 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
61 // can not be found in X509_verify_cert of X509_vfy.c.
62 // In order to support intermediate certificate node, we override the
63 // errors if the certification is obtained from X509 store, i.e. it is
64 // a trusted ceritifcate node that is enrolled by user.
65 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
66 // are also ignored to enable such feature.
67 //
68 if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||
69 (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {
70 Obj = (X509_OBJECT *) malloc (sizeof (X509_OBJECT));
71 if (Obj == NULL) {
72 return 0;
73 }
74
75 Obj->type = X509_LU_X509;
76 Obj->data.x509 = Context->current_cert;
77
78 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);
79
80 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {
81 Status = 1;
82 } else {
83 //
84 // If any certificate in the chain is enrolled as trusted certificate,
85 // pass the certificate verification.
86 //
87 if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
88 Count = (INTN) sk_X509_num (Context->chain);
89 for (Index = 0; Index < Count; Index++) {
90 Obj->data.x509 = sk_X509_value (Context->chain, (int) Index);
91 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {
92 Status = 1;
93 break;
94 }
95 }
96 }
97 }
98
99 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);
100 }
101
102 if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||
103 (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {
104 Status = 1;
105 }
106
107 if (Obj != NULL) {
108 OPENSSL_free (Obj);
109 }
110
111 return Status;
112 }
113
114 /**
115 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
116 a new structure to wrap P7Data.
117
118 Caution: This function may receive untrusted input.
119 UEFI Authenticated Variable is external input, so this function will do basic
120 check for PKCS#7 data structure.
121
122 @param[in] P7Data Pointer to the PKCS#7 message to verify.
123 @param[in] P7Length Length of the PKCS#7 message in bytes.
124 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
125 return FALSE.
126 @param[out] WrapData If return status of this function is TRUE:
127 1) when WrapFlag is TRUE, pointer to P7Data.
128 2) when WrapFlag is FALSE, pointer to a new ContentInfo
129 structure. It's caller's responsibility to free this
130 buffer.
131 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
132
133 @retval TRUE The operation is finished successfully.
134 @retval FALSE The operation is failed due to lack of resources.
135
136 **/
137 BOOLEAN
138 WrapPkcs7Data (
139 IN CONST UINT8 *P7Data,
140 IN UINTN P7Length,
141 OUT BOOLEAN *WrapFlag,
142 OUT UINT8 **WrapData,
143 OUT UINTN *WrapDataSize
144 )
145 {
146 BOOLEAN Wrapped;
147 UINT8 *SignedData;
148
149 //
150 // Check whether input P7Data is a wrapped ContentInfo structure or not.
151 //
152 Wrapped = FALSE;
153 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {
154 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {
155 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {
156 Wrapped = TRUE;
157 }
158 }
159 }
160
161 if (Wrapped) {
162 *WrapData = (UINT8 *) P7Data;
163 *WrapDataSize = P7Length;
164 } else {
165 //
166 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
167 //
168 *WrapDataSize = P7Length + 19;
169 *WrapData = malloc (*WrapDataSize);
170 if (*WrapData == NULL) {
171 *WrapFlag = Wrapped;
172 return FALSE;
173 }
174
175 SignedData = *WrapData;
176
177 //
178 // Part1: 0x30, 0x82.
179 //
180 SignedData[0] = 0x30;
181 SignedData[1] = 0x82;
182
183 //
184 // Part2: Length1 = P7Length + 19 - 4, in big endian.
185 //
186 SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);
187 SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);
188
189 //
190 // Part3: 0x06, 0x09.
191 //
192 SignedData[4] = 0x06;
193 SignedData[5] = 0x09;
194
195 //
196 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
197 //
198 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));
199
200 //
201 // Part5: 0xA0, 0x82.
202 //
203 SignedData[15] = 0xA0;
204 SignedData[16] = 0x82;
205
206 //
207 // Part6: Length2 = P7Length, in big endian.
208 //
209 SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);
210 SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);
211
212 //
213 // Part7: P7Data.
214 //
215 CopyMem (SignedData + 19, P7Data, P7Length);
216 }
217
218 *WrapFlag = Wrapped;
219 return TRUE;
220 }
221
222 /**
223 Pop single certificate from STACK_OF(X509).
224
225 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
226
227 @param[in] X509Stack Pointer to a X509 stack object.
228 @param[out] Cert Pointer to a X509 certificate.
229 @param[out] CertSize Length of output X509 certificate in bytes.
230
231 @retval TRUE The X509 stack pop succeeded.
232 @retval FALSE The pop operation failed.
233
234 **/
235 BOOLEAN
236 X509PopCertificate (
237 IN VOID *X509Stack,
238 OUT UINT8 **Cert,
239 OUT UINTN *CertSize
240 )
241 {
242 BIO *CertBio;
243 X509 *X509Cert;
244 STACK_OF(X509) *CertStack;
245 BOOLEAN Status;
246 INT32 Result;
247 INT32 Length;
248 VOID *Buffer;
249
250 Status = FALSE;
251
252 if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {
253 return Status;
254 }
255
256 CertStack = (STACK_OF(X509) *) X509Stack;
257
258 X509Cert = sk_X509_pop (CertStack);
259
260 if (X509Cert == NULL) {
261 return Status;
262 }
263
264 Buffer = NULL;
265
266 CertBio = BIO_new (BIO_s_mem ());
267 if (CertBio == NULL) {
268 return Status;
269 }
270
271 Result = i2d_X509_bio (CertBio, X509Cert);
272 if (Result == 0) {
273 goto _Exit;
274 }
275
276 Length = ((BUF_MEM *) CertBio->ptr)->length;
277 if (Length <= 0) {
278 goto _Exit;
279 }
280
281 Buffer = malloc (Length);
282 if (Buffer == NULL) {
283 goto _Exit;
284 }
285
286 Result = BIO_read (CertBio, Buffer, Length);
287 if (Result != Length) {
288 goto _Exit;
289 }
290
291 *Cert = Buffer;
292 *CertSize = Length;
293
294 Status = TRUE;
295
296 _Exit:
297
298 BIO_free (CertBio);
299
300 if (!Status && (Buffer != NULL)) {
301 free (Buffer);
302 }
303
304 return Status;
305 }
306
307 /**
308 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
309 Cryptographic Message Syntax Standard". The input signed data could be wrapped
310 in a ContentInfo structure.
311
312 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
313 return FALSE. If P7Length overflow, then return FAlSE.
314
315 Caution: This function may receive untrusted input.
316 UEFI Authenticated Variable is external input, so this function will do basic
317 check for PKCS#7 data structure.
318
319 @param[in] P7Data Pointer to the PKCS#7 message to verify.
320 @param[in] P7Length Length of the PKCS#7 message in bytes.
321 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
322 It's caller's responsiblity to free the buffer.
323 @param[out] StackLength Length of signer's certificates in bytes.
324 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
325 It's caller's responsiblity to free the buffer.
326 @param[out] CertLength Length of the trusted certificate in bytes.
327
328 @retval TRUE The operation is finished successfully.
329 @retval FALSE Error occurs during the operation.
330
331 **/
332 BOOLEAN
333 EFIAPI
334 Pkcs7GetSigners (
335 IN CONST UINT8 *P7Data,
336 IN UINTN P7Length,
337 OUT UINT8 **CertStack,
338 OUT UINTN *StackLength,
339 OUT UINT8 **TrustedCert,
340 OUT UINTN *CertLength
341 )
342 {
343 PKCS7 *Pkcs7;
344 BOOLEAN Status;
345 UINT8 *SignedData;
346 UINT8 *Temp;
347 UINTN SignedDataSize;
348 BOOLEAN Wrapped;
349 STACK_OF(X509) *Stack;
350 UINT8 Index;
351 UINT8 *CertBuf;
352 UINT8 *OldBuf;
353 UINTN BufferSize;
354 UINTN OldSize;
355 UINT8 *SingleCert;
356 UINTN SingleCertSize;
357
358 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
359 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {
360 return FALSE;
361 }
362
363 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
364 if (!Status) {
365 return Status;
366 }
367
368 Status = FALSE;
369 Pkcs7 = NULL;
370 Stack = NULL;
371 CertBuf = NULL;
372 OldBuf = NULL;
373 SingleCert = NULL;
374
375 //
376 // Retrieve PKCS#7 Data (DER encoding)
377 //
378 if (SignedDataSize > INT_MAX) {
379 goto _Exit;
380 }
381
382 Temp = SignedData;
383 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
384 if (Pkcs7 == NULL) {
385 goto _Exit;
386 }
387
388 //
389 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
390 //
391 if (!PKCS7_type_is_signed (Pkcs7)) {
392 goto _Exit;
393 }
394
395 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);
396 if (Stack == NULL) {
397 goto _Exit;
398 }
399
400 //
401 // Convert CertStack to buffer in following format:
402 // UINT8 CertNumber;
403 // UINT32 Cert1Length;
404 // UINT8 Cert1[];
405 // UINT32 Cert2Length;
406 // UINT8 Cert2[];
407 // ...
408 // UINT32 CertnLength;
409 // UINT8 Certn[];
410 //
411 BufferSize = sizeof (UINT8);
412 OldSize = BufferSize;
413
414 for (Index = 0; ; Index++) {
415 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);
416 if (!Status) {
417 break;
418 }
419
420 OldSize = BufferSize;
421 OldBuf = CertBuf;
422 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);
423 CertBuf = malloc (BufferSize);
424
425 if (CertBuf == NULL) {
426 goto _Exit;
427 }
428
429 if (OldBuf != NULL) {
430 CopyMem (CertBuf, OldBuf, OldSize);
431 free (OldBuf);
432 OldBuf = NULL;
433 }
434
435 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);
436 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);
437
438 free (SingleCert);
439 SingleCert = NULL;
440 }
441
442 if (CertBuf != NULL) {
443 //
444 // Update CertNumber.
445 //
446 CertBuf[0] = Index;
447
448 *CertLength = BufferSize - OldSize - sizeof (UINT32);
449 *TrustedCert = malloc (*CertLength);
450 if (*TrustedCert == NULL) {
451 goto _Exit;
452 }
453
454 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
455 *CertStack = CertBuf;
456 *StackLength = BufferSize;
457 Status = TRUE;
458 }
459
460 _Exit:
461 //
462 // Release Resources
463 //
464 if (!Wrapped) {
465 free (SignedData);
466 }
467
468 if (Pkcs7 != NULL) {
469 PKCS7_free (Pkcs7);
470 }
471
472 if (Stack != NULL) {
473 sk_X509_pop_free(Stack, X509_free);
474 }
475
476 if (SingleCert != NULL) {
477 free (SingleCert);
478 }
479
480 if (!Status && (CertBuf != NULL)) {
481 free (CertBuf);
482 *CertStack = NULL;
483 }
484
485 if (OldBuf != NULL) {
486 free (OldBuf);
487 }
488
489 return Status;
490 }
491
492 /**
493 Wrap function to use free() to free allocated memory for certificates.
494
495 @param[in] Certs Pointer to the certificates to be freed.
496
497 **/
498 VOID
499 EFIAPI
500 Pkcs7FreeSigners (
501 IN UINT8 *Certs
502 )
503 {
504 if (Certs == NULL) {
505 return;
506 }
507
508 free (Certs);
509 }
510
511 /**
512 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
513 Cryptographic Message Syntax Standard". The input signed data could be wrapped
514 in a ContentInfo structure.
515
516 If P7Data, TrustedCert or InData is NULL, then return FALSE.
517 If P7Length, CertLength or DataLength overflow, then return FAlSE.
518
519 Caution: This function may receive untrusted input.
520 UEFI Authenticated Variable is external input, so this function will do basic
521 check for PKCS#7 data structure.
522
523 @param[in] P7Data Pointer to the PKCS#7 message to verify.
524 @param[in] P7Length Length of the PKCS#7 message in bytes.
525 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
526 is used for certificate chain verification.
527 @param[in] CertLength Length of the trusted certificate in bytes.
528 @param[in] InData Pointer to the content to be verified.
529 @param[in] DataLength Length of InData in bytes.
530
531 @retval TRUE The specified PKCS#7 signed data is valid.
532 @retval FALSE Invalid PKCS#7 signed data.
533
534 **/
535 BOOLEAN
536 EFIAPI
537 Pkcs7Verify (
538 IN CONST UINT8 *P7Data,
539 IN UINTN P7Length,
540 IN CONST UINT8 *TrustedCert,
541 IN UINTN CertLength,
542 IN CONST UINT8 *InData,
543 IN UINTN DataLength
544 )
545 {
546 PKCS7 *Pkcs7;
547 BIO *DataBio;
548 BOOLEAN Status;
549 X509 *Cert;
550 X509_STORE *CertStore;
551 UINT8 *SignedData;
552 UINT8 *Temp;
553 UINTN SignedDataSize;
554 BOOLEAN Wrapped;
555
556 //
557 // Check input parameters.
558 //
559 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||
560 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {
561 return FALSE;
562 }
563
564 Pkcs7 = NULL;
565 DataBio = NULL;
566 Cert = NULL;
567 CertStore = NULL;
568
569 //
570 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
571 //
572 if (EVP_add_digest (EVP_md5 ()) == 0) {
573 return FALSE;
574 }
575 if (EVP_add_digest (EVP_sha1 ()) == 0) {
576 return FALSE;
577 }
578 if (EVP_add_digest (EVP_sha256 ()) == 0) {
579 return FALSE;
580 }
581 if (EVP_add_digest (EVP_sha384 ()) == 0) {
582 return FALSE;
583 }
584 if (EVP_add_digest (EVP_sha512 ()) == 0) {
585 return FALSE;
586 }
587 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {
588 return FALSE;
589 }
590
591 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
592 if (!Status) {
593 return Status;
594 }
595
596 Status = FALSE;
597
598 //
599 // Retrieve PKCS#7 Data (DER encoding)
600 //
601 if (SignedDataSize > INT_MAX) {
602 goto _Exit;
603 }
604
605 Temp = SignedData;
606 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
607 if (Pkcs7 == NULL) {
608 goto _Exit;
609 }
610
611 //
612 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
613 //
614 if (!PKCS7_type_is_signed (Pkcs7)) {
615 goto _Exit;
616 }
617
618 //
619 // Read DER-encoded root certificate and Construct X509 Certificate
620 //
621 Cert = d2i_X509 (NULL, &TrustedCert, (long) CertLength);
622 if (Cert == NULL) {
623 goto _Exit;
624 }
625
626 //
627 // Setup X509 Store for trusted certificate
628 //
629 CertStore = X509_STORE_new ();
630 if (CertStore == NULL) {
631 goto _Exit;
632 }
633 if (!(X509_STORE_add_cert (CertStore, Cert))) {
634 goto _Exit;
635 }
636
637 //
638 // Register customized X509 verification callback function to support
639 // trusted intermediate certificate anchor.
640 //
641 CertStore->verify_cb = X509VerifyCb;
642
643 //
644 // For generic PKCS#7 handling, InData may be NULL if the content is present
645 // in PKCS#7 structure. So ignore NULL checking here.
646 //
647 DataBio = BIO_new (BIO_s_mem ());
648 if (DataBio == NULL) {
649 goto _Exit;
650 }
651
652 if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {
653 goto _Exit;
654 }
655
656 //
657 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
658 // doesn't support the extended key usage for Authenticode Code Signing.
659 // Bypass the certificate purpose checking by enabling any purposes setting.
660 //
661 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);
662
663 //
664 // Verifies the PKCS#7 signedData structure
665 //
666 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);
667
668 _Exit:
669 //
670 // Release Resources
671 //
672 BIO_free (DataBio);
673 X509_free (Cert);
674 X509_STORE_free (CertStore);
675 PKCS7_free (Pkcs7);
676
677 if (!Wrapped) {
678 OPENSSL_free (SignedData);
679 }
680
681 return Status;
682 }