f90600473a0db474166522f974d86d951a081b77
[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   Status   = FALSE;\r
350   X509Cert = NULL;\r
351 \r
352   //\r
353   // Read DER-encoded X509 Certificate and Construct X509 object.\r
354   //\r
355   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
356   if ((X509Cert == NULL) || (!Status)) {\r
357     goto _Exit;\r
358   }\r
359 \r
360   //\r
361   // Retrieve subject name from certificate object.\r
362   //\r
363   X509Name = X509_get_subject_name (X509Cert);\r
364   if (*SubjectSize < (UINTN) X509Name->bytes->length) {\r
365     *SubjectSize = (UINTN) X509Name->bytes->length;\r
366     goto _Exit;\r
367   }\r
368   *SubjectSize = (UINTN) X509Name->bytes->length;\r
369   if (CertSubject != NULL) {\r
370     CopyMem (CertSubject, (UINT8 *) X509Name->bytes->data, *SubjectSize);\r
371     Status = TRUE;\r
372   }\r
373 \r
374 _Exit:\r
375   //\r
376   // Release Resources.\r
377   //\r
378   X509_free (X509Cert);\r
379 \r
380   return Status;\r
381 }\r
382 \r
383 /**\r
384   Retrieve the RSA Public Key from one DER-encoded X509 certificate.\r
385 \r
386   @param[in]  Cert         Pointer to the DER-encoded X509 certificate.\r
387   @param[in]  CertSize     Size of the X509 certificate in bytes.\r
388   @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved\r
389                            RSA public key component. Use RsaFree() function to free the\r
390                            resource.\r
391 \r
392   If Cert is NULL, then return FALSE.\r
393   If RsaContext is NULL, then return FALSE.\r
394 \r
395   @retval  TRUE   RSA Public Key was retrieved successfully.\r
396   @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.\r
397 \r
398 **/\r
399 BOOLEAN\r
400 EFIAPI\r
401 RsaGetPublicKeyFromX509 (\r
402   IN   CONST UINT8  *Cert,\r
403   IN   UINTN        CertSize,\r
404   OUT  VOID         **RsaContext\r
405   )\r
406 {\r
407   BOOLEAN   Status;\r
408   EVP_PKEY  *Pkey;\r
409   X509      *X509Cert;\r
410   \r
411   //\r
412   // Check input parameters.\r
413   //\r
414   if (Cert == NULL || RsaContext == NULL) {\r
415     return FALSE;\r
416   }\r
417 \r
418   Status   = FALSE;\r
419   Pkey     = NULL;\r
420   X509Cert = NULL;\r
421 \r
422   //\r
423   // Read DER-encoded X509 Certificate and Construct X509 object.\r
424   //\r
425   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
426   if ((X509Cert == NULL) || (!Status)) {\r
427     goto _Exit;\r
428   }\r
429 \r
430   //\r
431   // Retrieve and check EVP_PKEY data from X509 Certificate.\r
432   //\r
433   Pkey = X509_get_pubkey (X509Cert);\r
434   if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {\r
435     goto _Exit;\r
436   }\r
437 \r
438   //\r
439   // Duplicate RSA Context from the retrieved EVP_PKEY.\r
440   //\r
441   if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {\r
442     Status = TRUE;\r
443   }\r
444 \r
445 _Exit:\r
446   //\r
447   // Release Resources.\r
448   //\r
449   X509_free (X509Cert);\r
450   EVP_PKEY_free (Pkey);\r
451 \r
452   return Status;\r
453 }\r
454 \r
455 /**\r
456   Verify one X509 certificate was issued by the trusted CA.\r
457 \r
458   @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.\r
459   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
460   @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.\r
461   @param[in]      CACertSize   Size of the CA Certificate in bytes.\r
462 \r
463   If Cert is NULL, then return FALSE.\r
464   If CACert is NULL, then return FALSE.\r
465 \r
466   @retval  TRUE   The certificate was issued by the trusted CA.\r
467   @retval  FALSE  Invalid certificate or the certificate was not issued by the given\r
468                   trusted CA.\r
469 \r
470 **/\r
471 BOOLEAN\r
472 EFIAPI\r
473 X509VerifyCert (\r
474   IN  CONST UINT8  *Cert,\r
475   IN  UINTN        CertSize,\r
476   IN  CONST UINT8  *CACert,\r
477   IN  UINTN        CACertSize\r
478   )\r
479 {\r
480   BOOLEAN         Status;\r
481   X509            *X509Cert;\r
482   X509            *X509CACert;\r
483   X509_STORE      *CertStore;\r
484   X509_STORE_CTX  CertCtx;\r
485   \r
486   //\r
487   // Check input parameters.\r
488   //\r
489   if (Cert == NULL || CACert == NULL) {\r
490     return FALSE;\r
491   }\r
492 \r
493   Status     = FALSE;\r
494   X509Cert   = NULL;\r
495   X509CACert = NULL;\r
496   CertStore  = NULL;\r
497 \r
498   //\r
499   // Register & Initialize necessary digest algorithms for certificate verification.\r
500   //\r
501   EVP_add_digest (EVP_md5());\r
502   EVP_add_digest (EVP_sha1());\r
503   EVP_add_digest (EVP_sha256());\r
504 \r
505   //\r
506   // Read DER-encoded certificate to be verified and Construct X509 object.\r
507   //\r
508   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
509   if ((X509Cert == NULL) || (!Status)) {\r
510     goto _Exit;\r
511   }\r
512 \r
513   //\r
514   // Read DER-encoded root certificate and Construct X509 object.\r
515   //\r
516   Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);\r
517   if ((X509CACert == NULL) || (!Status)) {\r
518     goto _Exit;\r
519   }\r
520 \r
521   //\r
522   // Set up X509 Store for trusted certificate.\r
523   //\r
524   CertStore = X509_STORE_new ();\r
525   if (CertStore == NULL) {\r
526     goto _Exit;\r
527   }\r
528   if (!(X509_STORE_add_cert (CertStore, X509CACert))) {\r
529     goto _Exit;\r
530   }\r
531 \r
532   //\r
533   // Set up X509_STORE_CTX for the subsequent verification operation.\r
534   //\r
535   if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {\r
536     goto _Exit;\r
537   }\r
538 \r
539   //\r
540   // X509 Certificate Verification.\r
541   //\r
542   Status = (BOOLEAN) X509_verify_cert (&CertCtx);\r
543   X509_STORE_CTX_cleanup (&CertCtx);\r
544 \r
545 _Exit:\r
546   //\r
547   // Release Resources.\r
548   //\r
549   X509_free (X509Cert);\r
550   X509_free (X509CACert);\r
551   X509_STORE_free (CertStore);\r
552 \r
553   return Status;\r
554 }\r