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