Add new interfaces to support PKCS7#7 signed data and authenticode signature. Update...
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7.c
1 /** @file\r
2   PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.\r
3 \r
4 Copyright (c) 2009 - 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 \r
17 #include <openssl/objects.h>\r
18 #include <openssl/x509.h>\r
19 #include <openssl/pkcs7.h>\r
20 \r
21 \r
22 /**\r
23   Verification callback function to override any existing callbacks in OpenSSL\r
24   for intermediate certificate supports.\r
25 \r
26   @param[in]  Status   Original status before calling this callback.\r
27   @param[in]  Context  X509 store context.\r
28 \r
29   @retval     1        Current X509 certificate is verified successfully.\r
30   @retval     0        Verification failed.\r
31 \r
32 **/\r
33 STATIC int X509VerifyCb (int Status, X509_STORE_CTX *Context)\r
34 {\r
35   X509_OBJECT  *Obj;\r
36   int          Error;\r
37   int          Index;\r
38   int          Count;\r
39 \r
40   Obj   = NULL;\r
41   Error = X509_STORE_CTX_get_error (Context);\r
42 \r
43   //\r
44   // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_\r
45   // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer\r
46   // can not be found in X509_verify_cert of X509_vfy.c.\r
47   // In order to support intermediate certificate node, we override the\r
48   // errors if the certification is obtained from X509 store, i.e. it is\r
49   // a trusted ceritifcate node that is enrolled by user.\r
50   // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE\r
51   // are also ignored to enable such feature.\r
52   //\r
53   if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||\r
54       (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {\r
55     Obj = (X509_OBJECT *) OPENSSL_malloc (sizeof (X509_OBJECT));\r
56     if (Obj == NULL) {\r
57       return 0;\r
58     }\r
59 \r
60     Obj->type      = X509_LU_X509;\r
61     Obj->data.x509 = Context->current_cert;\r
62 \r
63     CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);\r
64 \r
65     if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
66       Status = 1;\r
67     } else {\r
68       //\r
69       // If any certificate in the chain is enrolled as trusted certificate,\r
70       // pass the certificate verification.\r
71       //\r
72       if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {\r
73         Count = sk_X509_num (Context->chain);\r
74         for (Index = 0; Index < Count; Index++) {\r
75           Obj->data.x509 = sk_X509_value (Context->chain, Index);\r
76           if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
77             Status = 1;\r
78             break;\r
79           }\r
80         }\r
81       }\r
82     }\r
83 \r
84     CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);\r
85   }\r
86 \r
87   if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||\r
88       (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {\r
89     Status = 1;\r
90   }\r
91 \r
92   if (Obj != NULL) {\r
93     OPENSSL_free (Obj);\r
94   }\r
95 \r
96   return Status;\r
97 }\r
98 \r
99 /**\r
100   Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message\r
101   Syntax Standard, version 1.5". This interface is only intended to be used for\r
102   application to perform PKCS#7 functionality validation.\r
103 \r
104   @param[in]  PrivateKey       Pointer to the PEM-formatted private key data for\r
105                                data signing.\r
106   @param[in]  PrivateKeySize   Size of the PEM private key data in bytes.\r
107   @param[in]  KeyPassword      NULL-terminated passphrase used for encrypted PEM\r
108                                key data.\r
109   @param[in]  InData           Pointer to the content to be signed.\r
110   @param[in]  InDataSize       Size of InData in bytes.\r
111   @param[in]  SignCert         Pointer to signer's DER-encoded certificate to sign with.\r
112   @param[in]  OtherCerts       Pointer to an optional additional set of certificates to\r
113                                include in the PKCS#7 signedData (e.g. any intermediate\r
114                                CAs in the chain).\r
115   @param[out] SignedData       Pointer to output PKCS#7 signedData.\r
116   @param[out] SignedDataSize   Size of SignedData in bytes.\r
117 \r
118   @retval     TRUE             PKCS#7 data signing succeeded.\r
119   @retval     FALSE            PKCS#7 data signing failed.\r
120 \r
121 **/\r
122 BOOLEAN\r
123 EFIAPI\r
124 Pkcs7Sign (\r
125   IN   CONST UINT8  *PrivateKey,\r
126   IN   UINTN        PrivateKeySize,\r
127   IN   CONST UINT8  *KeyPassword,\r
128   IN   UINT8        *InData,\r
129   IN   UINTN        InDataSize,\r
130   IN   UINT8        *SignCert,\r
131   IN   UINT8        *OtherCerts      OPTIONAL,\r
132   OUT  UINT8        **SignedData,\r
133   OUT  UINTN        *SignedDataSize\r
134   )\r
135 {\r
136   BOOLEAN   Status;\r
137   EVP_PKEY  *Key;\r
138   BIO       *DataBio;\r
139   PKCS7     *Pkcs7;\r
140   UINT8     *RsaContext;\r
141   UINT8     *P7Data;\r
142 \r
143   //\r
144   // Check input parameters.\r
145   //\r
146   if ((PrivateKey == NULL) || (KeyPassword == NULL) || (InData == NULL)) {\r
147     return FALSE;\r
148   }\r
149   \r
150   if ((SignCert == NULL) || (SignedData == NULL) || (SignedDataSize == NULL)) {\r
151     return FALSE;\r
152   }\r
153 \r
154   RsaContext = NULL;\r
155   Key        = NULL;\r
156   Pkcs7      = NULL;\r
157   DataBio    = NULL;\r
158   Status     = FALSE;\r
159 \r
160   //\r
161   // Retrieve RSA private key from PEM data.\r
162   //\r
163   Status = RsaGetPrivateKeyFromPem (\r
164              PrivateKey,\r
165              PrivateKeySize,\r
166              (CONST CHAR8 *) KeyPassword,\r
167              (VOID **) &RsaContext\r
168              );\r
169   if (!Status) {\r
170     return Status;\r
171   }\r
172 \r
173   //\r
174   // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling\r
175   //\r
176   EVP_add_digest (EVP_md5());\r
177   EVP_add_digest (EVP_sha1());\r
178   EVP_add_digest (EVP_sha256());\r
179   RandomSeed (NULL, 0);\r
180 \r
181   //\r
182   // Construct OpenSSL EVP_PKEY for private key.\r
183   //\r
184   Key = EVP_PKEY_new ();\r
185   if (Key == NULL) {\r
186     goto _Exit;\r
187   }\r
188   Key->save_type = EVP_PKEY_RSA;\r
189   Key->type      = EVP_PKEY_type (EVP_PKEY_RSA);\r
190   Key->pkey.rsa  = (RSA *) RsaContext;\r
191 \r
192   //\r
193   // Convert the data to be signed to BIO format. \r
194   //\r
195   DataBio = BIO_new (BIO_s_mem ());\r
196   BIO_write (DataBio, InData, (int) InDataSize);\r
197 \r
198   //\r
199   // Create the PKCS#7 signedData structure.\r
200   //\r
201   Pkcs7 = PKCS7_sign (\r
202             (X509 *) SignCert,\r
203             Key,\r
204             (STACK_OF(X509) *) OtherCerts,\r
205             DataBio,\r
206             PKCS7_BINARY\r
207             );\r
208   if (Pkcs7 == NULL) {\r
209     goto _Exit;\r
210   }\r
211 \r
212   //\r
213   // Convert PKCS#7 signedData structure into DER-encoded buffer.\r
214   //\r
215   *SignedDataSize = i2d_PKCS7 (Pkcs7, NULL);\r
216   if (*SignedDataSize == 0) {\r
217     goto _Exit;\r
218   }\r
219   *SignedData     = OPENSSL_malloc (*SignedDataSize);\r
220   P7Data          = *SignedData;\r
221   *SignedDataSize = i2d_PKCS7 (Pkcs7, (unsigned char **) &P7Data);\r
222 \r
223   Status = TRUE;\r
224 \r
225 _Exit:\r
226   //\r
227   // Release Resources\r
228   //\r
229   if (RsaContext != NULL) {\r
230     RsaFree (RsaContext);\r
231     if (Key != NULL) {\r
232       Key->pkey.rsa = NULL;\r
233     }\r
234   }\r
235 \r
236   if (Key != NULL) {\r
237     EVP_PKEY_free (Key);\r
238   }\r
239 \r
240   if (DataBio != NULL) {\r
241     BIO_free (DataBio);\r
242   }\r
243 \r
244   if (Pkcs7 != NULL) {\r
245     PKCS7_free (Pkcs7);\r
246   }\r
247 \r
248   return Status;\r
249 }\r
250 \r
251 /**\r
252   Verifies the validility of a PKCS#7 signed data as described in "PKCS #7: Cryptographic\r
253   Message Syntax Standard".\r
254 \r
255   If P7Data is NULL, then ASSERT().\r
256 \r
257   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.\r
258   @param[in]  P7Length     Length of the PKCS#7 message in bytes.\r
259   @param[in]  TrustedCert  Pointer to a trusted/root certificate encoded in DER, which\r
260                            is used for certificate chain verification.\r
261   @param[in]  CertLength   Length of the trusted certificate in bytes.\r
262   @param[in]  InData       Pointer to the content to be verified.\r
263   @param[in]  DataLength   Length of InData in bytes.\r
264 \r
265   @retval  TRUE  The specified PKCS#7 signed data is valid.\r
266   @retval  FALSE Invalid PKCS#7 signed data.\r
267 \r
268 **/\r
269 BOOLEAN\r
270 EFIAPI\r
271 Pkcs7Verify (\r
272   IN  CONST UINT8  *P7Data,\r
273   IN  UINTN        P7Length,\r
274   IN  CONST UINT8  *TrustedCert,\r
275   IN  UINTN        CertLength,\r
276   IN  CONST UINT8  *InData,\r
277   IN  UINTN        DataLength\r
278   )\r
279 {\r
280   PKCS7       *Pkcs7;\r
281   BIO         *CertBio;\r
282   BIO         *DataBio;\r
283   BOOLEAN     Status;\r
284   X509        *Cert;\r
285   X509_STORE  *CertStore;\r
286 \r
287   //\r
288   // ASSERT if P7Data is NULL\r
289   //\r
290   ASSERT (P7Data != NULL);\r
291 \r
292   Status    = FALSE;\r
293   Pkcs7     = NULL;\r
294   CertBio   = NULL;\r
295   DataBio   = NULL;\r
296   Cert      = NULL;\r
297   CertStore = NULL;\r
298 \r
299   //\r
300   // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
301   //\r
302   EVP_add_digest (EVP_md5());\r
303   EVP_add_digest (EVP_sha1());\r
304   EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA);\r
305   EVP_add_digest (EVP_sha256());\r
306 \r
307   //\r
308   // Retrieve PKCS#7 Data (DER encoding)\r
309   //\r
310   Pkcs7 = d2i_PKCS7 (NULL, &P7Data, (int)P7Length);\r
311   if (Pkcs7 == NULL) {\r
312     goto _Exit;\r
313   }\r
314 \r
315   //\r
316   // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
317   //\r
318   if (!PKCS7_type_is_signed (Pkcs7)) {\r
319     goto _Exit;\r
320   }\r
321 \r
322   //\r
323   // Read DER-encoded root certificate and Construct X509 Certificate\r
324   //\r
325   CertBio = BIO_new (BIO_s_mem ());\r
326   BIO_write (CertBio, TrustedCert, (int)CertLength);\r
327   if (CertBio == NULL) {\r
328     goto _Exit;\r
329   }\r
330   Cert = d2i_X509_bio (CertBio, NULL);\r
331   if (Cert == NULL) {\r
332     goto _Exit;\r
333   }\r
334 \r
335   //\r
336   // Setup X509 Store for trusted certificate\r
337   //\r
338   CertStore = X509_STORE_new ();\r
339   if (CertStore == NULL) {\r
340     goto _Exit;\r
341   }\r
342   if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
343     goto _Exit;\r
344   }\r
345 \r
346   //\r
347   // Register customized X509 verification callback function to support\r
348   // trusted intermediate certificate anchor.\r
349   //\r
350   CertStore->verify_cb = X509VerifyCb;\r
351 \r
352   //\r
353   // For generic PKCS#7 handling, InData may be NULL if the content is present\r
354   // in PKCS#7 structure. So ignore NULL checking here.\r
355   //\r
356   DataBio = BIO_new (BIO_s_mem ());\r
357   BIO_write (DataBio, InData, (int)DataLength);\r
358 \r
359   //\r
360   // Verifies the PKCS#7 signedData structure\r
361   //\r
362   Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
363 \r
364 _Exit:\r
365   //\r
366   // Release Resources\r
367   //\r
368   BIO_free (DataBio);\r
369   BIO_free (CertBio);\r
370   X509_free (Cert);\r
371   X509_STORE_free (CertStore);\r
372   PKCS7_free (Pkcs7);\r
373 \r
374   return Status;\r
375 }\r