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