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