Add new interfaces to support PKCS7#7 signed data and authenticode signature. Update...
[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 - 2011, 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 ASSERT().\r
23   If SingleX509Cert is NULL, then ASSERT().\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   // ASSERT if Cert is NULL or SingleX509Cert is NULL.\r
47   //\r
48   ASSERT (Cert != NULL);\r
49   ASSERT (SingleX509Cert != NULL);\r
50 \r
51   Status = FALSE;\r
52 \r
53   //\r
54   // Read DER-encoded X509 Certificate and Construct X509 object.\r
55   //\r
56   CertBio = BIO_new (BIO_s_mem ());\r
57   BIO_write (CertBio, Cert, (int) CertSize);\r
58   if (CertBio == NULL) {\r
59     goto _Exit;\r
60   }\r
61   X509Cert = d2i_X509_bio (CertBio, NULL);\r
62   if (X509Cert == NULL) {\r
63     goto _Exit;\r
64   }\r
65 \r
66   *SingleX509Cert = (UINT8 *) X509Cert;\r
67   Status = TRUE;\r
68 \r
69 _Exit:\r
70   //\r
71   // Release Resources.\r
72   //\r
73   BIO_free (CertBio);\r
74 \r
75   return Status;\r
76 }\r
77 \r
78 /**\r
79   Construct a X509 stack object from a list of DER-encoded certificate data.\r
80 \r
81   If X509Stack is NULL, then ASSERT().\r
82 \r
83   @param[in, out]  X509Stack  On input, pointer to an existing X509 stack object.\r
84                               On output, pointer to the X509 stack object with new\r
85                               inserted X509 certificate.\r
86   @param           ...        A list of DER-encoded single certificate data followed\r
87                               by certificate size. A NULL terminates the list. The\r
88                               pairs are the arguments to X509ConstructCertificate().\r
89                                  \r
90   @retval     TRUE            The X509 stack construction succeeded.\r
91   @retval     FALSE           The construction operation failed.\r
92 \r
93 **/\r
94 BOOLEAN\r
95 EFIAPI\r
96 X509ConstructCertificateStack (\r
97   IN OUT  UINT8  **X509Stack,\r
98   ...  \r
99   )\r
100 {\r
101   UINT8           *Cert;\r
102   UINTN           CertSize;\r
103   X509            *X509Cert;\r
104   STACK_OF(X509)  *CertStack;\r
105   BOOLEAN         Status;\r
106   VA_LIST         Args;\r
107   UINTN           Index;\r
108 \r
109   //\r
110   // ASSERT if input X509Stack is NULL.\r
111   //\r
112   ASSERT (X509Stack != NULL);\r
113 \r
114   Status = FALSE;\r
115 \r
116   //\r
117   // Initialize X509 stack object.\r
118   //\r
119   CertStack = (STACK_OF(X509) *) (*X509Stack);\r
120   if (CertStack == NULL) {\r
121     CertStack = sk_X509_new_null ();\r
122     if (CertStack == NULL) {\r
123       return Status;\r
124     }\r
125   }\r
126 \r
127   VA_START (Args, X509Stack);\r
128 \r
129   for (Index = 0; ; Index++) {\r
130     //\r
131     // If Cert is NULL, then it is the end of the list.\r
132     //\r
133     Cert = VA_ARG (Args, UINT8 *);\r
134     if (Cert == NULL) {\r
135       break;\r
136     }\r
137 \r
138     CertSize = VA_ARG (Args, UINTN);\r
139 \r
140     //\r
141     // Construct X509 Object from the given DER-encoded certificate data.\r
142     //\r
143     Status = X509ConstructCertificate (\r
144                (CONST UINT8 *) Cert,\r
145                CertSize,\r
146                (UINT8 **) &X509Cert\r
147                );\r
148     if (!Status) {\r
149       X509_free (X509Cert);\r
150       break;\r
151     }\r
152 \r
153     //\r
154     // Insert the new X509 object into X509 stack object.\r
155     //\r
156     sk_X509_push (CertStack, X509Cert);\r
157   }\r
158 \r
159   VA_END (Args);\r
160 \r
161   if (!Status) {\r
162     sk_X509_pop_free (CertStack, X509_free);\r
163   } else {\r
164     *X509Stack = (UINT8 *) CertStack;\r
165   }\r
166 \r
167   return Status;\r
168 }\r
169 \r
170 /**\r
171   Release the specified X509 object.\r
172 \r
173   If X509Cert is NULL, then ASSERT().\r
174 \r
175   @param[in]  X509Cert  Pointer to the X509 object to be released.\r
176 \r
177 **/\r
178 VOID\r
179 EFIAPI\r
180 X509Free (\r
181   IN  VOID  *X509Cert\r
182   )\r
183 {\r
184   ASSERT (X509Cert != NULL);\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 ASSERT().\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   ASSERT (X509Stack != NULL);\r
207 \r
208   //\r
209   // Free OpenSSL X509 stack object.\r
210   //\r
211   sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);\r
212 }\r
213 \r
214 /**\r
215   Retrieve the subject bytes from one X.509 certificate.\r
216 \r
217   @param[in]      Cert         Pointer to the DER-encoded X509 certificate.\r
218   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
219   @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.\r
220   @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,\r
221                                and the size of buffer returned CertSubject on output.\r
222 \r
223   If Cert is NULL, then ASSERT().\r
224   If SubjectSize is NULL, then ASSERT().\r
225 \r
226   @retval  TRUE   The certificate subject retrieved successfully.\r
227   @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.\r
228                   The SubjectSize will be updated with the required size.\r
229 \r
230 **/\r
231 BOOLEAN\r
232 EFIAPI\r
233 X509GetSubjectName (\r
234   IN      CONST UINT8  *Cert,\r
235   IN      UINTN        CertSize,\r
236   OUT     UINT8        *CertSubject,\r
237   IN OUT  UINTN        *SubjectSize\r
238   )\r
239 {\r
240   BOOLEAN    Status;\r
241   X509       *X509Cert;\r
242   X509_NAME  *X509Name;\r
243 \r
244   //\r
245   // ASSERT if Cert is NULL or SubjectSize is NULL.\r
246   //\r
247   ASSERT (Cert        != NULL);\r
248   ASSERT (SubjectSize != NULL);\r
249 \r
250   Status   = FALSE;\r
251   X509Cert = NULL;\r
252 \r
253   //\r
254   // Read DER-encoded X509 Certificate and Construct X509 object.\r
255   //\r
256   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
257   if ((X509Cert == NULL) || (!Status)) {\r
258     goto _Exit;\r
259   }\r
260 \r
261   //\r
262   // Retrieve subject name from certificate object.\r
263   //\r
264   X509Name = X509_get_subject_name (X509Cert);\r
265   if (*SubjectSize < (UINTN) X509Name->bytes->length) {\r
266     *SubjectSize = (UINTN) X509Name->bytes->length;\r
267     goto _Exit;\r
268   }\r
269   *SubjectSize = (UINTN) X509Name->bytes->length;\r
270   if (CertSubject != NULL) {\r
271     CopyMem (CertSubject, (UINT8 *)X509Name->bytes->data, *SubjectSize);\r
272     Status = TRUE;\r
273   }\r
274 \r
275 _Exit:\r
276   //\r
277   // Release Resources.\r
278   //\r
279   X509_free (X509Cert);\r
280 \r
281   return Status;\r
282 }\r
283 \r
284 /**\r
285   Retrieve the RSA Public Key from one DER-encoded X509 certificate.\r
286 \r
287   @param[in]  Cert         Pointer to the DER-encoded X509 certificate.\r
288   @param[in]  CertSize     Size of the X509 certificate in bytes.\r
289   @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved\r
290                            RSA public key component. Use RsaFree() function to free the\r
291                            resource.\r
292 \r
293   If Cert is NULL, then ASSERT().\r
294   If RsaContext is NULL, then ASSERT().\r
295 \r
296   @retval  TRUE   RSA Public Key was retrieved successfully.\r
297   @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.\r
298 \r
299 **/\r
300 BOOLEAN\r
301 EFIAPI\r
302 RsaGetPublicKeyFromX509 (\r
303   IN   CONST UINT8  *Cert,\r
304   IN   UINTN        CertSize,\r
305   OUT  VOID         **RsaContext\r
306   )\r
307 {\r
308   BOOLEAN   Status;\r
309   EVP_PKEY  *Pkey;\r
310   X509      *X509Cert;\r
311 \r
312   //\r
313   // ASSERT if Cert is NULL or RsaContext is NULL.\r
314   //\r
315   ASSERT (Cert       != NULL);\r
316   ASSERT (RsaContext != NULL);\r
317 \r
318   Status   = FALSE;\r
319   Pkey     = NULL;\r
320   X509Cert = NULL;\r
321 \r
322   //\r
323   // Read DER-encoded X509 Certificate and Construct X509 object.\r
324   //\r
325   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
326   if ((X509Cert == NULL) || (!Status)) {\r
327     goto _Exit;\r
328   }\r
329 \r
330   //\r
331   // Retrieve and check EVP_PKEY data from X509 Certificate.\r
332   //\r
333   Pkey = X509_get_pubkey (X509Cert);\r
334   if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {\r
335     goto _Exit;\r
336   }\r
337 \r
338   //\r
339   // Duplicate RSA Context from the retrieved EVP_PKEY.\r
340   //\r
341   if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {\r
342     Status = TRUE;\r
343   }\r
344 \r
345 _Exit:\r
346   //\r
347   // Release Resources.\r
348   //\r
349   X509_free (X509Cert);\r
350   EVP_PKEY_free (Pkey);\r
351 \r
352   return Status;\r
353 }\r
354 \r
355 /**\r
356   Verify one X509 certificate was issued by the trusted CA.\r
357 \r
358   @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.\r
359   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
360   @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.\r
361   @param[in]      CACertSize   Size of the CA Certificate in bytes.\r
362 \r
363   If Cert is NULL, then ASSERT().\r
364   If CACert is NULL, then ASSERT().\r
365 \r
366   @retval  TRUE   The certificate was issued by the trusted CA.\r
367   @retval  FALSE  Invalid certificate or the certificate was not issued by the given\r
368                   trusted CA.\r
369 \r
370 **/\r
371 BOOLEAN\r
372 EFIAPI\r
373 X509VerifyCert (\r
374   IN  CONST UINT8  *Cert,\r
375   IN  UINTN        CertSize,\r
376   IN  CONST UINT8  *CACert,\r
377   IN  UINTN        CACertSize\r
378   )\r
379 {\r
380   BOOLEAN         Status;\r
381   X509            *X509Cert;\r
382   X509            *X509CACert;\r
383   X509_STORE      *CertStore;\r
384   X509_STORE_CTX  CertCtx;\r
385 \r
386   //\r
387   // ASSERT if Cert is NULL or CACert is NULL.\r
388   //\r
389   ASSERT (Cert   != NULL);\r
390   ASSERT (CACert != NULL);\r
391 \r
392   Status     = FALSE;\r
393   X509Cert   = NULL;\r
394   X509CACert = NULL;\r
395   CertStore  = NULL;\r
396 \r
397   //\r
398   // Register & Initialize necessary digest algorithms for certificate verification.\r
399   //\r
400   EVP_add_digest (EVP_md5());\r
401   EVP_add_digest (EVP_sha1());\r
402   EVP_add_digest (EVP_sha256());\r
403 \r
404   //\r
405   // Read DER-encoded certificate to be verified and Construct X509 object.\r
406   //\r
407   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
408   if ((X509Cert == NULL) || (!Status)) {\r
409     goto _Exit;\r
410   }\r
411 \r
412   //\r
413   // Read DER-encoded root certificate and Construct X509 object.\r
414   //\r
415   Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);\r
416   if ((X509CACert == NULL) || (!Status)) {\r
417     goto _Exit;\r
418   }\r
419 \r
420   //\r
421   // Set up X509 Store for trusted certificate.\r
422   //\r
423   CertStore = X509_STORE_new ();\r
424   if (CertStore == NULL) {\r
425     goto _Exit;\r
426   }\r
427   if (!(X509_STORE_add_cert (CertStore, X509CACert))) {\r
428     goto _Exit;\r
429   }\r
430 \r
431   //\r
432   // Set up X509_STORE_CTX for the subsequent verification operation.\r
433   //\r
434   if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {\r
435     goto _Exit;\r
436   }\r
437 \r
438   //\r
439   // X509 Certificate Verification.\r
440   //\r
441   Status = (BOOLEAN) X509_verify_cert (&CertCtx);\r
442 \r
443 _Exit:\r
444   //\r
445   // Release Resources.\r
446   //\r
447   X509_free (X509Cert);\r
448   X509_free (X509CACert);\r
449   X509_STORE_free (CertStore);\r
450   X509_STORE_CTX_cleanup (&CertCtx);\r
451 \r
452   return Status;\r
453 }\r