7d275977c59724c87149291c673fe7ba4828f705
[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 - 2017, 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 #include <openssl/rsa.h>\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   X509         *X509Cert;\r
42   CONST UINT8  *Temp;\r
43 \r
44   //\r
45   // Check input parameters.\r
46   //\r
47   if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) {\r
48     return FALSE;\r
49   }\r
50 \r
51   //\r
52   // Read DER-encoded X509 Certificate and Construct X509 object.\r
53   //\r
54   Temp     = Cert;\r
55   X509Cert = d2i_X509 (NULL, &Temp, (long) CertSize);\r
56   if (X509Cert == NULL) {\r
57     return FALSE;\r
58   }\r
59 \r
60   *SingleX509Cert = (UINT8 *) X509Cert;\r
61 \r
62   return TRUE;\r
63 }\r
64 \r
65 /**\r
66   Construct a X509 stack object from a list of DER-encoded certificate data.\r
67 \r
68   If X509Stack is NULL, then return FALSE.\r
69 \r
70   @param[in, out]  X509Stack  On input, pointer to an existing or NULL X509 stack object.\r
71                               On output, pointer to the X509 stack object with new\r
72                               inserted X509 certificate.\r
73   @param           ...        A list of DER-encoded single certificate data followed\r
74                               by certificate size. A NULL terminates the list. The\r
75                               pairs are the arguments to X509ConstructCertificate().\r
76 \r
77   @retval     TRUE            The X509 stack construction succeeded.\r
78   @retval     FALSE           The construction operation failed.\r
79 \r
80 **/\r
81 BOOLEAN\r
82 EFIAPI\r
83 X509ConstructCertificateStack (\r
84   IN OUT  UINT8  **X509Stack,\r
85   ...\r
86   )\r
87 {\r
88   UINT8           *Cert;\r
89   UINTN           CertSize;\r
90   X509            *X509Cert;\r
91   STACK_OF(X509)  *CertStack;\r
92   BOOLEAN         Status;\r
93   VA_LIST         Args;\r
94   UINTN           Index;\r
95 \r
96   //\r
97   // Check input parameters.\r
98   //\r
99   if (X509Stack == NULL) {\r
100     return FALSE;\r
101   }\r
102 \r
103   Status = FALSE;\r
104 \r
105   //\r
106   // Initialize X509 stack object.\r
107   //\r
108   CertStack = (STACK_OF(X509) *) (*X509Stack);\r
109   if (CertStack == NULL) {\r
110     CertStack = sk_X509_new_null ();\r
111     if (CertStack == NULL) {\r
112       return Status;\r
113     }\r
114   }\r
115 \r
116   VA_START (Args, X509Stack);\r
117 \r
118   for (Index = 0; ; Index++) {\r
119     //\r
120     // If Cert is NULL, then it is the end of the list.\r
121     //\r
122     Cert = VA_ARG (Args, UINT8 *);\r
123     if (Cert == NULL) {\r
124       break;\r
125     }\r
126 \r
127     CertSize = VA_ARG (Args, UINTN);\r
128     if (CertSize == 0) {\r
129       break;\r
130     }\r
131 \r
132     //\r
133     // Construct X509 Object from the given DER-encoded certificate data.\r
134     //\r
135     X509Cert = NULL;\r
136     Status = X509ConstructCertificate (\r
137                (CONST UINT8 *) Cert,\r
138                CertSize,\r
139                (UINT8 **) &X509Cert\r
140                );\r
141     if (!Status) {\r
142       if (X509Cert != NULL) {\r
143         X509_free (X509Cert);\r
144       }\r
145       break;\r
146     }\r
147 \r
148     //\r
149     // Insert the new X509 object into X509 stack object.\r
150     //\r
151     sk_X509_push (CertStack, X509Cert);\r
152   }\r
153 \r
154   VA_END (Args);\r
155 \r
156   if (!Status) {\r
157     sk_X509_pop_free (CertStack, X509_free);\r
158   } else {\r
159     *X509Stack = (UINT8 *) CertStack;\r
160   }\r
161 \r
162   return Status;\r
163 }\r
164 \r
165 /**\r
166   Release the specified X509 object.\r
167 \r
168   If X509Cert is NULL, then return FALSE.\r
169 \r
170   @param[in]  X509Cert  Pointer to the X509 object to be released.\r
171 \r
172 **/\r
173 VOID\r
174 EFIAPI\r
175 X509Free (\r
176   IN  VOID  *X509Cert\r
177   )\r
178 {\r
179   //\r
180   // Check input parameters.\r
181   //\r
182   if (X509Cert == NULL) {\r
183     return;\r
184   }\r
185 \r
186   //\r
187   // Free OpenSSL X509 object.\r
188   //\r
189   X509_free ((X509 *) X509Cert);\r
190 }\r
191 \r
192 /**\r
193   Release the specified X509 stack object.\r
194 \r
195   If X509Stack is NULL, then return FALSE.\r
196 \r
197   @param[in]  X509Stack  Pointer to the X509 stack object to be released.\r
198 \r
199 **/\r
200 VOID\r
201 EFIAPI\r
202 X509StackFree (\r
203   IN  VOID  *X509Stack\r
204   )\r
205 {\r
206   //\r
207   // Check input parameters.\r
208   //\r
209   if (X509Stack == NULL) {\r
210     return;\r
211   }\r
212 \r
213   //\r
214   // Free OpenSSL X509 stack object.\r
215   //\r
216   sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);\r
217 }\r
218 \r
219 /**\r
220   Retrieve the subject bytes from one X.509 certificate.\r
221 \r
222   @param[in]      Cert         Pointer to the DER-encoded X509 certificate.\r
223   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
224   @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.\r
225   @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,\r
226                                and the size of buffer returned CertSubject on output.\r
227 \r
228   If Cert is NULL, then return FALSE.\r
229   If SubjectSize is NULL, then return FALSE.\r
230 \r
231   @retval  TRUE   The certificate subject retrieved successfully.\r
232   @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.\r
233                   The SubjectSize will be updated with the required size.\r
234 \r
235 **/\r
236 BOOLEAN\r
237 EFIAPI\r
238 X509GetSubjectName (\r
239   IN      CONST UINT8  *Cert,\r
240   IN      UINTN        CertSize,\r
241   OUT     UINT8        *CertSubject,\r
242   IN OUT  UINTN        *SubjectSize\r
243   )\r
244 {\r
245   BOOLEAN    Status;\r
246   X509       *X509Cert;\r
247   X509_NAME  *X509Name;\r
248   UINTN      X509NameSize;\r
249 \r
250   //\r
251   // Check input parameters.\r
252   //\r
253   if (Cert == NULL || SubjectSize == NULL) {\r
254     return FALSE;\r
255   }\r
256 \r
257   X509Cert = NULL;\r
258 \r
259   //\r
260   // Read DER-encoded X509 Certificate and Construct X509 object.\r
261   //\r
262   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
263   if ((X509Cert == NULL) || (!Status)) {\r
264     Status = FALSE;\r
265     goto _Exit;\r
266   }\r
267 \r
268   Status = FALSE;\r
269 \r
270   //\r
271   // Retrieve subject name from certificate object.\r
272   //\r
273   X509Name = X509_get_subject_name (X509Cert);\r
274   if (X509Name == NULL) {\r
275     goto _Exit;\r
276   }\r
277 \r
278   X509NameSize = i2d_X509_NAME(X509Name, NULL);\r
279   if (*SubjectSize < X509NameSize) {\r
280     *SubjectSize = X509NameSize;\r
281     goto _Exit;\r
282   }\r
283   *SubjectSize = X509NameSize;\r
284   if (CertSubject != NULL) {\r
285     i2d_X509_NAME(X509Name, &CertSubject);\r
286     Status = TRUE;\r
287   }\r
288 \r
289 _Exit:\r
290   //\r
291   // Release Resources.\r
292   //\r
293   if (X509Cert != NULL) {\r
294     X509_free (X509Cert);\r
295   }\r
296 \r
297   return Status;\r
298 }\r
299 \r
300 /**\r
301   Retrieve the RSA Public Key from one DER-encoded X509 certificate.\r
302 \r
303   @param[in]  Cert         Pointer to the DER-encoded X509 certificate.\r
304   @param[in]  CertSize     Size of the X509 certificate in bytes.\r
305   @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved\r
306                            RSA public key component. Use RsaFree() function to free the\r
307                            resource.\r
308 \r
309   If Cert is NULL, then return FALSE.\r
310   If RsaContext is NULL, then return FALSE.\r
311 \r
312   @retval  TRUE   RSA Public Key was retrieved successfully.\r
313   @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.\r
314 \r
315 **/\r
316 BOOLEAN\r
317 EFIAPI\r
318 RsaGetPublicKeyFromX509 (\r
319   IN   CONST UINT8  *Cert,\r
320   IN   UINTN        CertSize,\r
321   OUT  VOID         **RsaContext\r
322   )\r
323 {\r
324   BOOLEAN   Status;\r
325   EVP_PKEY  *Pkey;\r
326   X509      *X509Cert;\r
327 \r
328   //\r
329   // Check input parameters.\r
330   //\r
331   if (Cert == NULL || RsaContext == NULL) {\r
332     return FALSE;\r
333   }\r
334 \r
335   Pkey     = NULL;\r
336   X509Cert = NULL;\r
337 \r
338   //\r
339   // Read DER-encoded X509 Certificate and Construct X509 object.\r
340   //\r
341   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
342   if ((X509Cert == NULL) || (!Status)) {\r
343     Status = FALSE;\r
344     goto _Exit;\r
345   }\r
346 \r
347   Status = FALSE;\r
348 \r
349   //\r
350   // Retrieve and check EVP_PKEY data from X509 Certificate.\r
351   //\r
352   Pkey = X509_get_pubkey (X509Cert);\r
353   if ((Pkey == NULL) || (EVP_PKEY_id (Pkey) != EVP_PKEY_RSA)) {\r
354     goto _Exit;\r
355   }\r
356 \r
357   //\r
358   // Duplicate RSA Context from the retrieved EVP_PKEY.\r
359   //\r
360   if ((*RsaContext = RSAPublicKey_dup (EVP_PKEY_get0_RSA (Pkey))) != NULL) {\r
361     Status = TRUE;\r
362   }\r
363 \r
364 _Exit:\r
365   //\r
366   // Release Resources.\r
367   //\r
368   if (X509Cert != NULL) {\r
369     X509_free (X509Cert);\r
370   }\r
371 \r
372   if (Pkey != NULL) {\r
373     EVP_PKEY_free (Pkey);\r
374   }\r
375 \r
376   return Status;\r
377 }\r
378 \r
379 /**\r
380   Verify one X509 certificate was issued by the trusted CA.\r
381 \r
382   @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.\r
383   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
384   @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.\r
385   @param[in]      CACertSize   Size of the CA Certificate in bytes.\r
386 \r
387   If Cert is NULL, then return FALSE.\r
388   If CACert is NULL, then return FALSE.\r
389 \r
390   @retval  TRUE   The certificate was issued by the trusted CA.\r
391   @retval  FALSE  Invalid certificate or the certificate was not issued by the given\r
392                   trusted CA.\r
393 \r
394 **/\r
395 BOOLEAN\r
396 EFIAPI\r
397 X509VerifyCert (\r
398   IN  CONST UINT8  *Cert,\r
399   IN  UINTN        CertSize,\r
400   IN  CONST UINT8  *CACert,\r
401   IN  UINTN        CACertSize\r
402   )\r
403 {\r
404   BOOLEAN         Status;\r
405   X509            *X509Cert;\r
406   X509            *X509CACert;\r
407   X509_STORE      *CertStore;\r
408   X509_STORE_CTX  *CertCtx;\r
409 \r
410   //\r
411   // Check input parameters.\r
412   //\r
413   if (Cert == NULL || CACert == NULL) {\r
414     return FALSE;\r
415   }\r
416 \r
417   Status     = FALSE;\r
418   X509Cert   = NULL;\r
419   X509CACert = NULL;\r
420   CertStore  = NULL;\r
421   CertCtx    = NULL;\r
422 \r
423   //\r
424   // Register & Initialize necessary digest algorithms for certificate verification.\r
425   //\r
426   if (EVP_add_digest (EVP_md5 ()) == 0) {\r
427     goto _Exit;\r
428   }\r
429   if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
430     goto _Exit;\r
431   }\r
432   if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
433     goto _Exit;\r
434   }\r
435 \r
436   //\r
437   // Read DER-encoded certificate to be verified and Construct X509 object.\r
438   //\r
439   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
440   if ((X509Cert == NULL) || (!Status)) {\r
441     Status = FALSE;\r
442     goto _Exit;\r
443   }\r
444 \r
445   //\r
446   // Read DER-encoded root certificate and Construct X509 object.\r
447   //\r
448   Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);\r
449   if ((X509CACert == NULL) || (!Status)) {\r
450     Status = FALSE;\r
451     goto _Exit;\r
452   }\r
453 \r
454   Status = FALSE;\r
455 \r
456   //\r
457   // Set up X509 Store for trusted certificate.\r
458   //\r
459   CertStore = X509_STORE_new ();\r
460   if (CertStore == NULL) {\r
461     goto _Exit;\r
462   }\r
463   if (!(X509_STORE_add_cert (CertStore, X509CACert))) {\r
464     goto _Exit;\r
465   }\r
466 \r
467   //\r
468   // Allow partial certificate chains, terminated by a non-self-signed but\r
469   // still trusted intermediate certificate. Also disable time checks.\r
470   //\r
471   X509_STORE_set_flags (CertStore,\r
472                         X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);\r
473 \r
474   //\r
475   // Set up X509_STORE_CTX for the subsequent verification operation.\r
476   //\r
477   CertCtx = X509_STORE_CTX_new ();\r
478   if (CertCtx == NULL) {\r
479     goto _Exit;\r
480   }\r
481   if (!X509_STORE_CTX_init (CertCtx, CertStore, X509Cert, NULL)) {\r
482     goto _Exit;\r
483   }\r
484 \r
485   //\r
486   // X509 Certificate Verification.\r
487   //\r
488   Status = (BOOLEAN) X509_verify_cert (CertCtx);\r
489   X509_STORE_CTX_cleanup (CertCtx);\r
490 \r
491 _Exit:\r
492   //\r
493   // Release Resources.\r
494   //\r
495   if (X509Cert != NULL) {\r
496     X509_free (X509Cert);\r
497   }\r
498 \r
499   if (X509CACert != NULL) {\r
500     X509_free (X509CACert);\r
501   }\r
502 \r
503   if (CertStore != NULL) {\r
504     X509_STORE_free (CertStore);\r
505   }\r
506 \r
507   X509_STORE_CTX_free (CertCtx);\r
508 \r
509   return Status;\r
510 }\r
511 \r
512 /**\r
513   Retrieve the TBSCertificate from one given X.509 certificate.\r
514 \r
515   @param[in]      Cert         Pointer to the given DER-encoded X509 certificate.\r
516   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
517   @param[out]     TBSCert      DER-Encoded To-Be-Signed certificate.\r
518   @param[out]     TBSCertSize  Size of the TBS certificate in bytes.\r
519 \r
520   If Cert is NULL, then return FALSE.\r
521   If TBSCert is NULL, then return FALSE.\r
522   If TBSCertSize is NULL, then return FALSE.\r
523 \r
524   @retval  TRUE   The TBSCertificate was retrieved successfully.\r
525   @retval  FALSE  Invalid X.509 certificate.\r
526 \r
527 **/\r
528 BOOLEAN\r
529 EFIAPI\r
530 X509GetTBSCert (\r
531   IN  CONST UINT8  *Cert,\r
532   IN  UINTN        CertSize,\r
533   OUT UINT8        **TBSCert,\r
534   OUT UINTN        *TBSCertSize\r
535   )\r
536 {\r
537   CONST UINT8  *Temp;\r
538   INTN         Asn1Tag;\r
539   INTN         ObjClass;\r
540   UINTN        Length;\r
541 \r
542   //\r
543   // Check input parameters.\r
544   //\r
545   if ((Cert == NULL) || (TBSCert == NULL) ||\r
546       (TBSCertSize == NULL) || (CertSize > INT_MAX)) {\r
547     return FALSE;\r
548   }\r
549 \r
550   //\r
551   // An X.509 Certificate is: (defined in RFC3280)\r
552   //   Certificate  ::=  SEQUENCE  {\r
553   //     tbsCertificate       TBSCertificate,\r
554   //     signatureAlgorithm   AlgorithmIdentifier,\r
555   //     signature            BIT STRING }\r
556   //\r
557   // and\r
558   //\r
559   //  TBSCertificate  ::=  SEQUENCE  {\r
560   //    version         [0]  Version DEFAULT v1,\r
561   //    ...\r
562   //    }\r
563   //\r
564   // So we can just ASN1-parse the x.509 DER-encoded data. If we strip\r
565   // the first SEQUENCE, the second SEQUENCE is the TBSCertificate.\r
566   //\r
567   Temp = Cert;\r
568   ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize);\r
569 \r
570   if (Asn1Tag != V_ASN1_SEQUENCE) {\r
571     return FALSE;\r
572   }\r
573 \r
574   *TBSCert = (UINT8 *)Temp;\r
575 \r
576   ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)Length);\r
577   //\r
578   // Verify the parsed TBSCertificate is one correct SEQUENCE data.\r
579   //\r
580   if (Asn1Tag != V_ASN1_SEQUENCE) {\r
581     return FALSE;\r
582   }\r
583 \r
584   *TBSCertSize = Length + (Temp - *TBSCert);\r
585 \r
586   return TRUE;\r
587 }\r