Fix several issues in BaseCryptLib:
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptX509.c
1 /** @file\r
2   X.509 Certificate Handler Wrapper Implementation over OpenSSL.\r
3 \r
4 Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "InternalCryptLib.h"\r
16 #include <openssl/x509.h>\r
17 \r
18 \r
19 /**\r
20   Construct a X509 object from DER-encoded certificate data.\r
21 \r
22   If Cert is NULL, then return FALSE.\r
23   If SingleX509Cert is NULL, then return FALSE.\r
24 \r
25   @param[in]  Cert            Pointer to the DER-encoded certificate data.\r
26   @param[in]  CertSize        The size of certificate data in bytes.\r
27   @param[out] SingleX509Cert  The generated X509 object.\r
28 \r
29   @retval     TRUE            The X509 object generation succeeded.\r
30   @retval     FALSE           The operation failed.\r
31 \r
32 **/\r
33 BOOLEAN\r
34 EFIAPI\r
35 X509ConstructCertificate (\r
36   IN   CONST UINT8  *Cert,\r
37   IN   UINTN        CertSize,\r
38   OUT  UINT8        **SingleX509Cert\r
39   )\r
40 {\r
41   BIO      *CertBio;\r
42   X509     *X509Cert;\r
43   BOOLEAN  Status;\r
44 \r
45   //\r
46   // Check input parameters.\r
47   //\r
48   if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) {\r
49     return FALSE;\r
50   }\r
51 \r
52   Status = FALSE;\r
53 \r
54   //\r
55   // Read DER-encoded X509 Certificate and Construct X509 object.\r
56   //\r
57   CertBio = BIO_new (BIO_s_mem ());\r
58   BIO_write (CertBio, Cert, (int) CertSize);\r
59   if (CertBio == NULL) {\r
60     goto _Exit;\r
61   }\r
62   X509Cert = d2i_X509_bio (CertBio, NULL);\r
63   if (X509Cert == NULL) {\r
64     goto _Exit;\r
65   }\r
66 \r
67   *SingleX509Cert = (UINT8 *) X509Cert;\r
68   Status = TRUE;\r
69 \r
70 _Exit:\r
71   //\r
72   // Release Resources.\r
73   //\r
74   BIO_free (CertBio);\r
75 \r
76   return Status;\r
77 }\r
78 \r
79 /**\r
80   Construct a X509 stack object from a list of DER-encoded certificate data.\r
81 \r
82   If X509Stack is NULL, then return FALSE.\r
83 \r
84   @param[in, out]  X509Stack  On input, pointer to an existing X509 stack object.\r
85                               On output, pointer to the X509 stack object with new\r
86                               inserted X509 certificate.\r
87   @param           ...        A list of DER-encoded single certificate data followed\r
88                               by certificate size. A NULL terminates the list. The\r
89                               pairs are the arguments to X509ConstructCertificate().\r
90                                  \r
91   @retval     TRUE            The X509 stack construction succeeded.\r
92   @retval     FALSE           The construction operation failed.\r
93 \r
94 **/\r
95 BOOLEAN\r
96 EFIAPI\r
97 X509ConstructCertificateStack (\r
98   IN OUT  UINT8  **X509Stack,\r
99   ...  \r
100   )\r
101 {\r
102   UINT8           *Cert;\r
103   UINTN           CertSize;\r
104   X509            *X509Cert;\r
105   STACK_OF(X509)  *CertStack;\r
106   BOOLEAN         Status;\r
107   VA_LIST         Args;\r
108   UINTN           Index;\r
109 \r
110   //\r
111   // Check input parameters.\r
112   //\r
113   if (X509Stack == NULL) {\r
114     return FALSE;\r
115   }\r
116 \r
117   Status = FALSE;\r
118 \r
119   //\r
120   // Initialize X509 stack object.\r
121   //\r
122   CertStack = (STACK_OF(X509) *) (*X509Stack);\r
123   if (CertStack == NULL) {\r
124     CertStack = sk_X509_new_null ();\r
125     if (CertStack == NULL) {\r
126       return Status;\r
127     }\r
128   }\r
129 \r
130   VA_START (Args, X509Stack);\r
131 \r
132   for (Index = 0; ; Index++) {\r
133     //\r
134     // If Cert is NULL, then it is the end of the list.\r
135     //\r
136     Cert = VA_ARG (Args, UINT8 *);\r
137     if (Cert == NULL) {\r
138       break;\r
139     }\r
140 \r
141     CertSize = VA_ARG (Args, UINTN);\r
142 \r
143     //\r
144     // Construct X509 Object from the given DER-encoded certificate data.\r
145     //\r
146     Status = X509ConstructCertificate (\r
147                (CONST UINT8 *) Cert,\r
148                CertSize,\r
149                (UINT8 **) &X509Cert\r
150                );\r
151     if (!Status) {\r
152       X509_free (X509Cert);\r
153       break;\r
154     }\r
155 \r
156     //\r
157     // Insert the new X509 object into X509 stack object.\r
158     //\r
159     sk_X509_push (CertStack, X509Cert);\r
160   }\r
161 \r
162   VA_END (Args);\r
163 \r
164   if (!Status) {\r
165     sk_X509_pop_free (CertStack, X509_free);\r
166   } else {\r
167     *X509Stack = (UINT8 *) CertStack;\r
168   }\r
169 \r
170   return Status;\r
171 }\r
172 \r
173 /**\r
174   Release the specified X509 object.\r
175 \r
176   If X509Cert is NULL, then return FALSE.\r
177 \r
178   @param[in]  X509Cert  Pointer to the X509 object to be released.\r
179 \r
180 **/\r
181 VOID\r
182 EFIAPI\r
183 X509Free (\r
184   IN  VOID  *X509Cert\r
185   )\r
186\r
187   //\r
188   // Check input parameters.\r
189   //\r
190   if (X509Cert == NULL) {\r
191     return;\r
192   }\r
193   \r
194   //\r
195   // Free OpenSSL X509 object.\r
196   //\r
197   X509_free ((X509 *) X509Cert);\r
198 }\r
199 \r
200 /**\r
201   Release the specified X509 stack object.\r
202 \r
203   If X509Stack is NULL, then return FALSE.\r
204 \r
205   @param[in]  X509Stack  Pointer to the X509 stack object to be released.\r
206 \r
207 **/\r
208 VOID\r
209 EFIAPI\r
210 X509StackFree (\r
211   IN  VOID  *X509Stack\r
212   )\r
213 {\r
214   //\r
215   // Check input parameters.\r
216   //\r
217   if (X509Stack == NULL) {\r
218     return;\r
219   }\r
220   \r
221   //\r
222   // Free OpenSSL X509 stack object.\r
223   //\r
224   sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);\r
225 }\r
226 \r
227 /**\r
228   Pop single certificate from STACK_OF(X509).\r
229 \r
230   If X509Stack, Cert, or CertSize is NULL, then return FALSE.\r
231 \r
232   @param[in]  X509Stack       Pointer to a X509 stack object.\r
233   @param[out] Cert            Pointer to a X509 certificate.\r
234   @param[out] CertSize        Length of output X509 certificate in bytes.\r
235                                  \r
236   @retval     TRUE            The X509 stack pop succeeded.\r
237   @retval     FALSE           The pop operation failed.\r
238 \r
239 **/\r
240 BOOLEAN\r
241 X509PopCertificate (\r
242   IN  VOID  *X509Stack,\r
243   OUT UINT8 **Cert,\r
244   OUT UINTN *CertSize\r
245   )\r
246 {\r
247   BIO             *CertBio;\r
248   X509            *X509Cert;\r
249   STACK_OF(X509)  *CertStack;\r
250   BOOLEAN         Status;\r
251   int             Result;\r
252   int             Length;\r
253   VOID            *Buffer;\r
254 \r
255   Status = FALSE;\r
256 \r
257   if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {\r
258     return Status;\r
259   }\r
260 \r
261   CertStack = (STACK_OF(X509) *) X509Stack;\r
262 \r
263   X509Cert = sk_X509_pop (CertStack);\r
264 \r
265   if (X509Cert == NULL) {\r
266     return Status;\r
267   }\r
268 \r
269   Buffer = NULL;\r
270 \r
271   CertBio = BIO_new (BIO_s_mem ());\r
272   if (CertBio == NULL) {\r
273     return Status;\r
274   }\r
275 \r
276   Result = i2d_X509_bio (CertBio, X509Cert);\r
277   if (Result == 0) {\r
278     goto _Exit;\r
279   }\r
280 \r
281   Length = ((BUF_MEM *) CertBio->ptr)->length;\r
282   if (Length <= 0) {\r
283     goto _Exit;\r
284   }\r
285 \r
286   Buffer = malloc (Length);\r
287   if (Buffer == NULL) {\r
288     goto _Exit;\r
289   }\r
290 \r
291   Result = BIO_read (CertBio, Buffer, Length);\r
292   if (Result != Length) {\r
293     goto _Exit;\r
294   }\r
295 \r
296   *Cert     = Buffer;\r
297   *CertSize = Length;\r
298 \r
299   Status = TRUE;\r
300 \r
301 _Exit:\r
302 \r
303   BIO_free (CertBio);\r
304 \r
305   if (!Status && (Buffer != NULL)) {\r
306     free (Buffer);\r
307   }\r
308 \r
309   return Status;\r
310 }\r
311 \r
312 /**\r
313   Retrieve the subject bytes from one X.509 certificate.\r
314 \r
315   @param[in]      Cert         Pointer to the DER-encoded X509 certificate.\r
316   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
317   @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.\r
318   @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,\r
319                                and the size of buffer returned CertSubject on output.\r
320 \r
321   If Cert is NULL, then return FALSE.\r
322   If SubjectSize is NULL, then return FALSE.\r
323 \r
324   @retval  TRUE   The certificate subject retrieved successfully.\r
325   @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.\r
326                   The SubjectSize will be updated with the required size.\r
327 \r
328 **/\r
329 BOOLEAN\r
330 EFIAPI\r
331 X509GetSubjectName (\r
332   IN      CONST UINT8  *Cert,\r
333   IN      UINTN        CertSize,\r
334   OUT     UINT8        *CertSubject,\r
335   IN OUT  UINTN        *SubjectSize\r
336   )\r
337 {\r
338   BOOLEAN    Status;\r
339   X509       *X509Cert;\r
340   X509_NAME  *X509Name;\r
341 \r
342   //\r
343   // Check input parameters.\r
344   //\r
345   if (Cert == NULL || SubjectSize == NULL) {\r
346     return FALSE;\r
347   }\r
348 \r
349   X509Cert = NULL;\r
350 \r
351   //\r
352   // Read DER-encoded X509 Certificate and Construct X509 object.\r
353   //\r
354   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
355   if ((X509Cert == NULL) || (!Status)) {\r
356     Status = FALSE;\r
357     goto _Exit;\r
358   }\r
359 \r
360   Status = FALSE;\r
361 \r
362   //\r
363   // Retrieve subject name from certificate object.\r
364   //\r
365   X509Name = X509_get_subject_name (X509Cert);\r
366   if (X509Name == NULL) {\r
367     goto _Exit;\r
368   }\r
369 \r
370   if (*SubjectSize < (UINTN) X509Name->bytes->length) {\r
371     *SubjectSize = (UINTN) X509Name->bytes->length;\r
372     goto _Exit;\r
373   }\r
374   *SubjectSize = (UINTN) X509Name->bytes->length;\r
375   if (CertSubject != NULL) {\r
376     CopyMem (CertSubject, (UINT8 *) X509Name->bytes->data, *SubjectSize);\r
377     Status = TRUE;\r
378   }\r
379 \r
380 _Exit:\r
381   //\r
382   // Release Resources.\r
383   //\r
384   if (X509Cert != NULL) {\r
385     X509_free (X509Cert);\r
386   }\r
387 \r
388   return Status;\r
389 }\r
390 \r
391 /**\r
392   Retrieve the RSA Public Key from one DER-encoded X509 certificate.\r
393 \r
394   @param[in]  Cert         Pointer to the DER-encoded X509 certificate.\r
395   @param[in]  CertSize     Size of the X509 certificate in bytes.\r
396   @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved\r
397                            RSA public key component. Use RsaFree() function to free the\r
398                            resource.\r
399 \r
400   If Cert is NULL, then return FALSE.\r
401   If RsaContext is NULL, then return FALSE.\r
402 \r
403   @retval  TRUE   RSA Public Key was retrieved successfully.\r
404   @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.\r
405 \r
406 **/\r
407 BOOLEAN\r
408 EFIAPI\r
409 RsaGetPublicKeyFromX509 (\r
410   IN   CONST UINT8  *Cert,\r
411   IN   UINTN        CertSize,\r
412   OUT  VOID         **RsaContext\r
413   )\r
414 {\r
415   BOOLEAN   Status;\r
416   EVP_PKEY  *Pkey;\r
417   X509      *X509Cert;\r
418   \r
419   //\r
420   // Check input parameters.\r
421   //\r
422   if (Cert == NULL || RsaContext == NULL) {\r
423     return FALSE;\r
424   }\r
425 \r
426   Pkey     = NULL;\r
427   X509Cert = NULL;\r
428 \r
429   //\r
430   // Read DER-encoded X509 Certificate and Construct X509 object.\r
431   //\r
432   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
433   if ((X509Cert == NULL) || (!Status)) {\r
434     Status = FALSE;\r
435     goto _Exit;\r
436   }\r
437 \r
438   Status = FALSE;\r
439 \r
440   //\r
441   // Retrieve and check EVP_PKEY data from X509 Certificate.\r
442   //\r
443   Pkey = X509_get_pubkey (X509Cert);\r
444   if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {\r
445     goto _Exit;\r
446   }\r
447 \r
448   //\r
449   // Duplicate RSA Context from the retrieved EVP_PKEY.\r
450   //\r
451   if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {\r
452     Status = TRUE;\r
453   }\r
454 \r
455 _Exit:\r
456   //\r
457   // Release Resources.\r
458   //\r
459   if (X509Cert != NULL) {\r
460     X509_free (X509Cert);\r
461   }\r
462 \r
463   if (Pkey != NULL) {\r
464     EVP_PKEY_free (Pkey);\r
465   }  \r
466 \r
467   return Status;\r
468 }\r
469 \r
470 /**\r
471   Verify one X509 certificate was issued by the trusted CA.\r
472 \r
473   @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.\r
474   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
475   @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.\r
476   @param[in]      CACertSize   Size of the CA Certificate in bytes.\r
477 \r
478   If Cert is NULL, then return FALSE.\r
479   If CACert is NULL, then return FALSE.\r
480 \r
481   @retval  TRUE   The certificate was issued by the trusted CA.\r
482   @retval  FALSE  Invalid certificate or the certificate was not issued by the given\r
483                   trusted CA.\r
484 \r
485 **/\r
486 BOOLEAN\r
487 EFIAPI\r
488 X509VerifyCert (\r
489   IN  CONST UINT8  *Cert,\r
490   IN  UINTN        CertSize,\r
491   IN  CONST UINT8  *CACert,\r
492   IN  UINTN        CACertSize\r
493   )\r
494 {\r
495   BOOLEAN         Status;\r
496   X509            *X509Cert;\r
497   X509            *X509CACert;\r
498   X509_STORE      *CertStore;\r
499   X509_STORE_CTX  CertCtx;\r
500   \r
501   //\r
502   // Check input parameters.\r
503   //\r
504   if (Cert == NULL || CACert == NULL) {\r
505     return FALSE;\r
506   }\r
507 \r
508   Status     = FALSE;\r
509   X509Cert   = NULL;\r
510   X509CACert = NULL;\r
511   CertStore  = NULL;\r
512 \r
513   //\r
514   // Register & Initialize necessary digest algorithms for certificate verification.\r
515   //\r
516   if (EVP_add_digest (EVP_md5 ()) == 0) {\r
517     goto _Exit;\r
518   }\r
519   if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
520     goto _Exit;\r
521   }\r
522   if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
523     goto _Exit;\r
524   }\r
525 \r
526   //\r
527   // Read DER-encoded certificate to be verified and Construct X509 object.\r
528   //\r
529   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
530   if ((X509Cert == NULL) || (!Status)) {\r
531     Status = FALSE;\r
532     goto _Exit;\r
533   }\r
534 \r
535   //\r
536   // Read DER-encoded root certificate and Construct X509 object.\r
537   //\r
538   Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);\r
539   if ((X509CACert == NULL) || (!Status)) {\r
540     Status = FALSE;\r
541     goto _Exit;\r
542   }\r
543 \r
544   Status = FALSE;\r
545 \r
546   //\r
547   // Set up X509 Store for trusted certificate.\r
548   //\r
549   CertStore = X509_STORE_new ();\r
550   if (CertStore == NULL) {\r
551     goto _Exit;\r
552   }\r
553   if (!(X509_STORE_add_cert (CertStore, X509CACert))) {\r
554     goto _Exit;\r
555   }\r
556 \r
557   //\r
558   // Set up X509_STORE_CTX for the subsequent verification operation.\r
559   //\r
560   if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {\r
561     goto _Exit;\r
562   }\r
563 \r
564   //\r
565   // X509 Certificate Verification.\r
566   //\r
567   Status = (BOOLEAN) X509_verify_cert (&CertCtx);\r
568   X509_STORE_CTX_cleanup (&CertCtx);\r
569 \r
570 _Exit:\r
571   //\r
572   // Release Resources.\r
573   //\r
574   if (X509Cert != NULL) {\r
575     X509_free (X509Cert);\r
576   }\r
577 \r
578   if (X509CACert != NULL) {\r
579     X509_free (X509CACert);\r
580   }\r
581 \r
582   if (CertStore != NULL) {\r
583     X509_STORE_free (CertStore);\r
584   }\r
585   \r
586   return Status;\r
587 }\r