/src/strongswan/src/libstrongswan/plugins/openssl/openssl_pkcs7.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2012 Martin Willi |
3 | | * |
4 | | * Copyright (C) secunet Security Networks AG |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU General Public License as published by the |
8 | | * Free Software Foundation; either version 2 of the License, or (at your |
9 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, but |
12 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | | * for more details. |
15 | | */ |
16 | | |
17 | | #include <openssl/opensslv.h> |
18 | | #include <openssl/opensslconf.h> |
19 | | |
20 | | #if OPENSSL_VERSION_NUMBER >= 0x0090807fL |
21 | | #ifndef OPENSSL_NO_CMS |
22 | | |
23 | | #include "openssl_pkcs7.h" |
24 | | #include "openssl_util.h" |
25 | | |
26 | | #include <library.h> |
27 | | #include <utils/debug.h> |
28 | | #include <asn1/oid.h> |
29 | | #include <credentials/sets/mem_cred.h> |
30 | | |
31 | | #include <openssl/cms.h> |
32 | | |
33 | | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
34 | | #define X509_ATTRIBUTE_get0_object(attr) ({ (attr)->object; }) |
35 | | #endif |
36 | | |
37 | | typedef struct private_openssl_pkcs7_t private_openssl_pkcs7_t; |
38 | | |
39 | | /** |
40 | | * Private data of an openssl_pkcs7_t object. |
41 | | */ |
42 | | struct private_openssl_pkcs7_t { |
43 | | |
44 | | /** |
45 | | * Public pkcs7_t interface. |
46 | | */ |
47 | | pkcs7_t public; |
48 | | |
49 | | /** |
50 | | * Type of this container |
51 | | */ |
52 | | container_type_t type; |
53 | | |
54 | | /** |
55 | | * OpenSSL CMS structure |
56 | | */ |
57 | | CMS_ContentInfo *cms; |
58 | | }; |
59 | | |
60 | | /** |
61 | | * OpenSSL does not allow us to read the signature to verify it with our own |
62 | | * crypto API. We define the internal CMS_SignerInfo structure here to get it. |
63 | | */ |
64 | | struct CMS_SignerInfo_st { |
65 | | long version; |
66 | | void *sid; |
67 | | X509_ALGOR *digestAlgorithm; |
68 | | STACK_OF(X509_ATTRIBUTE) *signedAttrs; |
69 | | X509_ALGOR *signatureAlgorithm; |
70 | | ASN1_OCTET_STRING *signature; |
71 | | /* and more... */ |
72 | | }; |
73 | | |
74 | | /** |
75 | | * And we also need access to the wrappend CMS_KeyTransRecipientInfo to |
76 | | * read the encrypted key |
77 | | */ |
78 | | struct CMS_KeyTransRecipientInfo_st { |
79 | | long version; |
80 | | void *rid; |
81 | | X509_ALGOR *keyEncryptionAlgorithm; |
82 | | ASN1_OCTET_STRING *encryptedKey; |
83 | | }; |
84 | | |
85 | | struct CMS_RecipientInfo_st { |
86 | | int type; |
87 | | struct CMS_KeyTransRecipientInfo_st *ktri; |
88 | | /* and more in union... */ |
89 | | }; |
90 | | |
91 | | struct CMS_EncryptedContentInfo_st { |
92 | | ASN1_OBJECT *contentType; |
93 | | X509_ALGOR *contentEncryptionAlgorithm; |
94 | | ASN1_OCTET_STRING *encryptedContent; |
95 | | /* and more... */ |
96 | | }; |
97 | | |
98 | | struct CMS_EnvelopedData_st { |
99 | | long version; |
100 | | void *originatorInfo; |
101 | | STACK_OF(CMS_RecipientInfo) *recipientInfos; |
102 | | struct CMS_EncryptedContentInfo_st *encryptedContentInfo; |
103 | | /* and more... */ |
104 | | }; |
105 | | |
106 | | struct CMS_ContentInfo_st { |
107 | | ASN1_OBJECT *contentType; |
108 | | struct CMS_EnvelopedData_st *envelopedData; |
109 | | /* and more in union... */ |
110 | | }; |
111 | | |
112 | | /** |
113 | | * We can't include asn1.h, declare function prototypes directly |
114 | | */ |
115 | | chunk_t asn1_wrap(int, const char *mode, ...); |
116 | | int asn1_unwrap(chunk_t*, chunk_t*); |
117 | | |
118 | | /** |
119 | | * Enumerator over certificates |
120 | | */ |
121 | | typedef struct { |
122 | | /** implements enumerator_t */ |
123 | | enumerator_t public; |
124 | | /** Stack of X509 certificates */ |
125 | | STACK_OF(X509) *certs; |
126 | | /** current enumerator position in certificates */ |
127 | | int i; |
128 | | /** currently enumerating certificate_t */ |
129 | | certificate_t *cert; |
130 | | } cert_enumerator_t; |
131 | | |
132 | | METHOD(enumerator_t, cert_destroy, void, |
133 | | cert_enumerator_t *this) |
134 | 0 | { |
135 | 0 | DESTROY_IF(this->cert); |
136 | 0 | free(this); |
137 | 0 | } |
138 | | |
139 | | METHOD(enumerator_t, cert_enumerate, bool, |
140 | | cert_enumerator_t *this, va_list args) |
141 | 0 | { |
142 | 0 | certificate_t **out; |
143 | |
|
144 | 0 | VA_ARGS_VGET(args, out); |
145 | |
|
146 | 0 | if (!this->certs) |
147 | 0 | { |
148 | 0 | return FALSE; |
149 | 0 | } |
150 | 0 | while (this->i < sk_X509_num(this->certs)) |
151 | 0 | { |
152 | 0 | chunk_t encoding; |
153 | 0 | X509 *x509; |
154 | | |
155 | | /* clean up previous round */ |
156 | 0 | DESTROY_IF(this->cert); |
157 | 0 | this->cert = NULL; |
158 | |
|
159 | 0 | x509 = sk_X509_value(this->certs, this->i++); |
160 | 0 | encoding = openssl_i2chunk(X509, x509); |
161 | 0 | this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, |
162 | 0 | BUILD_BLOB_ASN1_DER, encoding, |
163 | 0 | BUILD_END); |
164 | 0 | free(encoding.ptr); |
165 | 0 | if (!this->cert) |
166 | 0 | { |
167 | 0 | continue; |
168 | 0 | } |
169 | 0 | *out = this->cert; |
170 | 0 | return TRUE; |
171 | 0 | } |
172 | 0 | return FALSE; |
173 | 0 | } |
174 | | |
175 | | METHOD(pkcs7_t, create_cert_enumerator, enumerator_t*, |
176 | | private_openssl_pkcs7_t *this) |
177 | 0 | { |
178 | 0 | cert_enumerator_t *enumerator; |
179 | |
|
180 | 0 | if (this->type == CONTAINER_PKCS7_SIGNED_DATA) |
181 | 0 | { |
182 | 0 | INIT(enumerator, |
183 | 0 | .public = { |
184 | 0 | .enumerate = enumerator_enumerate_default, |
185 | 0 | .venumerate = _cert_enumerate, |
186 | 0 | .destroy = _cert_destroy, |
187 | 0 | }, |
188 | 0 | .certs = CMS_get1_certs(this->cms), |
189 | 0 | ); |
190 | 0 | return &enumerator->public; |
191 | 0 | } |
192 | 0 | return enumerator_create_empty(); |
193 | 0 | } |
194 | | |
195 | | /** |
196 | | * Enumerator for signatures |
197 | | */ |
198 | | typedef struct { |
199 | | /** implements enumerator_t */ |
200 | | enumerator_t public; |
201 | | /** Stack of signerinfos */ |
202 | | STACK_OF(CMS_SignerInfo) *signers; |
203 | | /** current enumerator position in signers */ |
204 | | int i; |
205 | | /** currently enumerating auth config */ |
206 | | auth_cfg_t *auth; |
207 | | /** full CMS */ |
208 | | CMS_ContentInfo *cms; |
209 | | /** credential set containing wrapped certificates */ |
210 | | mem_cred_t *creds; |
211 | | } signature_enumerator_t; |
212 | | |
213 | | /** |
214 | | * Verify signerInfo signature |
215 | | */ |
216 | | static auth_cfg_t *verify_signature(CMS_SignerInfo *si, |
217 | | signature_params_t *sig_alg) |
218 | 0 | { |
219 | 0 | enumerator_t *enumerator; |
220 | 0 | public_key_t *key; |
221 | 0 | certificate_t *cert; |
222 | 0 | auth_cfg_t *auth, *found = NULL; |
223 | 0 | identification_t *issuer, *serial; |
224 | 0 | chunk_t attrs = chunk_empty, sig, attr; |
225 | 0 | X509_NAME *name; |
226 | 0 | ASN1_INTEGER *snr; |
227 | 0 | int i; |
228 | |
|
229 | 0 | if (CMS_SignerInfo_get0_signer_id(si, NULL, &name, &snr) != 1) |
230 | 0 | { |
231 | 0 | return NULL; |
232 | 0 | } |
233 | 0 | issuer = openssl_x509_name2id(name); |
234 | 0 | if (!issuer) |
235 | 0 | { |
236 | 0 | return NULL; |
237 | 0 | } |
238 | 0 | serial = identification_create_from_encoding( |
239 | 0 | ID_KEY_ID, openssl_asn1_str2chunk(snr)); |
240 | | |
241 | | /* reconstruct DER encoded attributes to verify signature */ |
242 | 0 | for (i = 0; i < CMS_signed_get_attr_count(si); i++) |
243 | 0 | { |
244 | 0 | attr = openssl_i2chunk(X509_ATTRIBUTE, CMS_signed_get_attr(si, i)); |
245 | 0 | attrs = chunk_cat("mm", attrs, attr); |
246 | 0 | } |
247 | | /* wrap in a ASN1_SET */ |
248 | 0 | attrs = asn1_wrap(0x31, "m", attrs); |
249 | | |
250 | | /* TODO: find a better way to access and verify the signature */ |
251 | 0 | sig = openssl_asn1_str2chunk(si->signature); |
252 | 0 | enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr, |
253 | 0 | key_type_from_signature_scheme(sig_alg->scheme), |
254 | 0 | serial, FALSE); |
255 | 0 | while (enumerator->enumerate(enumerator, &cert, &auth)) |
256 | 0 | { |
257 | 0 | if (issuer->equals(issuer, cert->get_issuer(cert))) |
258 | 0 | { |
259 | 0 | key = cert->get_public_key(cert); |
260 | 0 | if (key) |
261 | 0 | { |
262 | 0 | if (key->verify(key, sig_alg->scheme, sig_alg->params, |
263 | 0 | attrs, sig)) |
264 | 0 | { |
265 | 0 | found = auth->clone(auth); |
266 | 0 | key->destroy(key); |
267 | 0 | break; |
268 | 0 | } |
269 | 0 | key->destroy(key); |
270 | 0 | } |
271 | 0 | } |
272 | 0 | } |
273 | 0 | enumerator->destroy(enumerator); |
274 | 0 | issuer->destroy(issuer); |
275 | 0 | serial->destroy(serial); |
276 | 0 | free(attrs.ptr); |
277 | |
|
278 | 0 | return found; |
279 | 0 | } |
280 | | |
281 | | /** |
282 | | * Verify the message digest in the signerInfo attributes |
283 | | */ |
284 | | static bool verify_digest(CMS_ContentInfo *cms, CMS_SignerInfo *si, int hash_oid) |
285 | 0 | { |
286 | 0 | const ASN1_OCTET_STRING *os; |
287 | 0 | ASN1_OCTET_STRING **osp; |
288 | 0 | hash_algorithm_t hash_alg; |
289 | 0 | chunk_t digest, content, hash; |
290 | 0 | hasher_t *hasher; |
291 | |
|
292 | 0 | os = CMS_signed_get0_data_by_OBJ(si, |
293 | 0 | OBJ_nid2obj(NID_pkcs9_messageDigest), -3, V_ASN1_OCTET_STRING); |
294 | 0 | if (!os) |
295 | 0 | { |
296 | 0 | return FALSE; |
297 | 0 | } |
298 | 0 | digest = openssl_asn1_str2chunk(os); |
299 | 0 | osp = CMS_get0_content(cms); |
300 | 0 | if (!osp) |
301 | 0 | { |
302 | 0 | return FALSE; |
303 | 0 | } |
304 | 0 | content = openssl_asn1_str2chunk(*osp); |
305 | |
|
306 | 0 | hash_alg = hasher_algorithm_from_oid(hash_oid); |
307 | 0 | hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); |
308 | 0 | if (!hasher) |
309 | 0 | { |
310 | 0 | DBG1(DBG_LIB, "hash algorithm %N not supported", |
311 | 0 | hash_algorithm_names, hash_alg); |
312 | 0 | return FALSE; |
313 | 0 | } |
314 | 0 | if (!hasher->allocate_hash(hasher, content, &hash)) |
315 | 0 | { |
316 | 0 | hasher->destroy(hasher); |
317 | 0 | return FALSE; |
318 | 0 | } |
319 | 0 | hasher->destroy(hasher); |
320 | |
|
321 | 0 | if (!chunk_equals_const(digest, hash)) |
322 | 0 | { |
323 | 0 | free(hash.ptr); |
324 | 0 | DBG1(DBG_LIB, "invalid messageDigest"); |
325 | 0 | return FALSE; |
326 | 0 | } |
327 | 0 | free(hash.ptr); |
328 | 0 | return TRUE; |
329 | 0 | } |
330 | | |
331 | | METHOD(enumerator_t, signature_enumerate, bool, |
332 | | signature_enumerator_t *this, va_list args) |
333 | 0 | { |
334 | 0 | auth_cfg_t **out; |
335 | |
|
336 | 0 | VA_ARGS_VGET(args, out); |
337 | |
|
338 | 0 | if (!this->signers) |
339 | 0 | { |
340 | 0 | return FALSE; |
341 | 0 | } |
342 | 0 | while (this->i < sk_CMS_SignerInfo_num(this->signers)) |
343 | 0 | { |
344 | 0 | CMS_SignerInfo *si; |
345 | 0 | X509_ALGOR *digest, *sig; |
346 | 0 | signature_params_t sig_alg = {}; |
347 | 0 | chunk_t sig_scheme; |
348 | 0 | int hash_oid; |
349 | | |
350 | | /* clean up previous round */ |
351 | 0 | DESTROY_IF(this->auth); |
352 | 0 | this->auth = NULL; |
353 | |
|
354 | 0 | si = sk_CMS_SignerInfo_value(this->signers, this->i++); |
355 | |
|
356 | 0 | CMS_SignerInfo_get0_algs(si, NULL, NULL, &digest, &sig); |
357 | 0 | hash_oid = openssl_asn1_known_oid(digest->algorithm); |
358 | 0 | if (openssl_asn1_known_oid(sig->algorithm) == OID_RSA_ENCRYPTION) |
359 | 0 | { |
360 | | /* derive the signature scheme from the digest algorithm |
361 | | * for the classic PKCS#7 RSA mechanism */ |
362 | 0 | sig_alg.scheme = signature_scheme_from_oid(hash_oid); |
363 | 0 | } |
364 | 0 | else |
365 | 0 | { |
366 | 0 | sig_scheme = openssl_i2chunk(X509_ALGOR, sig); |
367 | 0 | if (!signature_params_parse(sig_scheme, 0, &sig_alg)) |
368 | 0 | { |
369 | 0 | free(sig_scheme.ptr); |
370 | 0 | continue; |
371 | 0 | } |
372 | 0 | free(sig_scheme.ptr); |
373 | 0 | } |
374 | 0 | this->auth = verify_signature(si, &sig_alg); |
375 | 0 | signature_params_clear(&sig_alg); |
376 | 0 | if (!this->auth) |
377 | 0 | { |
378 | 0 | DBG1(DBG_LIB, "unable to verify pkcs7 attributes signature"); |
379 | 0 | continue; |
380 | 0 | } |
381 | 0 | if (!verify_digest(this->cms, si, hash_oid)) |
382 | 0 | { |
383 | 0 | continue; |
384 | 0 | } |
385 | 0 | *out = this->auth; |
386 | 0 | return TRUE; |
387 | 0 | } |
388 | 0 | return FALSE; |
389 | 0 | } |
390 | | |
391 | | METHOD(enumerator_t, signature_destroy, void, |
392 | | signature_enumerator_t *this) |
393 | 0 | { |
394 | 0 | lib->credmgr->remove_local_set(lib->credmgr, &this->creds->set); |
395 | 0 | this->creds->destroy(this->creds); |
396 | 0 | DESTROY_IF(this->auth); |
397 | 0 | free(this); |
398 | 0 | } |
399 | | |
400 | | METHOD(container_t, create_signature_enumerator, enumerator_t*, |
401 | | private_openssl_pkcs7_t *this) |
402 | 0 | { |
403 | 0 | signature_enumerator_t *enumerator; |
404 | |
|
405 | 0 | if (this->type == CONTAINER_PKCS7_SIGNED_DATA) |
406 | 0 | { |
407 | 0 | enumerator_t *certs; |
408 | 0 | certificate_t *cert; |
409 | |
|
410 | 0 | INIT(enumerator, |
411 | 0 | .public = { |
412 | 0 | .enumerate = enumerator_enumerate_default, |
413 | 0 | .venumerate = _signature_enumerate, |
414 | 0 | .destroy = _signature_destroy, |
415 | 0 | }, |
416 | 0 | .cms = this->cms, |
417 | 0 | .signers = CMS_get0_SignerInfos(this->cms), |
418 | 0 | .creds = mem_cred_create(), |
419 | 0 | ); |
420 | | |
421 | | /* make available wrapped certs during signature checking */ |
422 | 0 | certs = create_cert_enumerator(this); |
423 | 0 | while (certs->enumerate(certs, &cert)) |
424 | 0 | { |
425 | 0 | enumerator->creds->add_cert(enumerator->creds, FALSE, |
426 | 0 | cert->get_ref(cert)); |
427 | 0 | } |
428 | 0 | certs->destroy(certs); |
429 | |
|
430 | 0 | lib->credmgr->add_local_set(lib->credmgr, &enumerator->creds->set, |
431 | 0 | FALSE); |
432 | |
|
433 | 0 | return &enumerator->public; |
434 | 0 | } |
435 | 0 | return enumerator_create_empty(); |
436 | 0 | } |
437 | | |
438 | | |
439 | | METHOD(container_t, get_type, container_type_t, |
440 | | private_openssl_pkcs7_t *this) |
441 | 0 | { |
442 | 0 | return this->type; |
443 | 0 | } |
444 | | |
445 | | METHOD(pkcs7_t, get_attribute, bool, |
446 | | private_openssl_pkcs7_t *this, int oid, |
447 | | enumerator_t *enumerator, chunk_t *value) |
448 | 0 | { |
449 | 0 | signature_enumerator_t *e; |
450 | 0 | CMS_SignerInfo *si; |
451 | 0 | X509_ATTRIBUTE *attr; |
452 | 0 | const ASN1_TYPE *type; |
453 | 0 | chunk_t chunk, wrapped; |
454 | 0 | int i; |
455 | |
|
456 | 0 | e = (signature_enumerator_t*)enumerator; |
457 | 0 | if (e->i <= 0) |
458 | 0 | { |
459 | 0 | return FALSE; |
460 | 0 | } |
461 | | |
462 | | /* "i" gets incremented after enumerate(), hence read from previous */ |
463 | 0 | si = sk_CMS_SignerInfo_value(e->signers, e->i - 1); |
464 | 0 | for (i = 0; i < CMS_signed_get_attr_count(si); i++) |
465 | 0 | { |
466 | 0 | attr = CMS_signed_get_attr(si, i); |
467 | 0 | if (X509_ATTRIBUTE_count(attr) == 1 && |
468 | 0 | openssl_asn1_known_oid(X509_ATTRIBUTE_get0_object(attr)) == oid) |
469 | 0 | { |
470 | | /* get first value in SET */ |
471 | 0 | type = X509_ATTRIBUTE_get0_type(attr, 0); |
472 | 0 | #if OPENSSL_VERSION_NUMBER < 0x30000000L |
473 | 0 | chunk = wrapped = openssl_i2chunk(ASN1_TYPE, (ASN1_TYPE*)type); |
474 | | #else |
475 | | chunk = wrapped = openssl_i2chunk(ASN1_TYPE, type); |
476 | | #endif |
477 | 0 | if (asn1_unwrap(&chunk, &chunk) != 0x100 /* ASN1_INVALID */) |
478 | 0 | { |
479 | 0 | *value = chunk_clone(chunk); |
480 | 0 | free(wrapped.ptr); |
481 | 0 | return TRUE; |
482 | 0 | } |
483 | 0 | free(wrapped.ptr); |
484 | 0 | } |
485 | 0 | } |
486 | 0 | return FALSE; |
487 | 0 | } |
488 | | |
489 | | /** |
490 | | * Find a private key for issuerAndSerialNumber |
491 | | */ |
492 | | static private_key_t *find_private(identification_t *issuer, |
493 | | identification_t *serial) |
494 | 0 | { |
495 | 0 | enumerator_t *enumerator; |
496 | 0 | certificate_t *cert; |
497 | 0 | public_key_t *public; |
498 | 0 | private_key_t *private = NULL; |
499 | 0 | identification_t *id; |
500 | 0 | chunk_t fp; |
501 | |
|
502 | 0 | enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, |
503 | 0 | CERT_X509, KEY_RSA, serial, FALSE); |
504 | 0 | while (enumerator->enumerate(enumerator, &cert)) |
505 | 0 | { |
506 | 0 | if (issuer->equals(issuer, cert->get_issuer(cert))) |
507 | 0 | { |
508 | 0 | public = cert->get_public_key(cert); |
509 | 0 | if (public) |
510 | 0 | { |
511 | 0 | if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &fp)) |
512 | 0 | { |
513 | 0 | id = identification_create_from_encoding(ID_KEY_ID, fp); |
514 | 0 | private = lib->credmgr->get_private(lib->credmgr, |
515 | 0 | KEY_ANY, id, NULL); |
516 | 0 | id->destroy(id); |
517 | 0 | } |
518 | 0 | public->destroy(public); |
519 | 0 | } |
520 | 0 | } |
521 | 0 | if (private) |
522 | 0 | { |
523 | 0 | break; |
524 | 0 | } |
525 | 0 | } |
526 | 0 | enumerator->destroy(enumerator); |
527 | 0 | return private; |
528 | 0 | } |
529 | | |
530 | | /** |
531 | | * Decrypt enveloped-data with a decrypted symmetric key |
532 | | */ |
533 | | static bool decrypt_symmetric(private_openssl_pkcs7_t *this, chunk_t key, |
534 | | chunk_t encrypted, chunk_t *plain) |
535 | 0 | { |
536 | 0 | encryption_algorithm_t encr; |
537 | 0 | X509_ALGOR *alg; |
538 | 0 | crypter_t *crypter; |
539 | 0 | chunk_t iv; |
540 | 0 | size_t key_size; |
541 | | |
542 | | /* read encryption algorithm from internal structures; TODO fixup */ |
543 | 0 | alg = this->cms->envelopedData->encryptedContentInfo-> |
544 | 0 | contentEncryptionAlgorithm; |
545 | 0 | encr = encryption_algorithm_from_oid(openssl_asn1_known_oid(alg->algorithm), |
546 | 0 | &key_size); |
547 | 0 | if (alg->parameter->type != V_ASN1_OCTET_STRING) |
548 | 0 | { |
549 | 0 | return FALSE; |
550 | 0 | } |
551 | 0 | iv = openssl_asn1_str2chunk(alg->parameter->value.octet_string); |
552 | |
|
553 | 0 | crypter = lib->crypto->create_crypter(lib->crypto, encr, key_size / 8); |
554 | 0 | if (!crypter) |
555 | 0 | { |
556 | 0 | DBG1(DBG_LIB, "crypter %N-%d not available", |
557 | 0 | encryption_algorithm_names, alg, key_size); |
558 | 0 | return FALSE; |
559 | 0 | } |
560 | 0 | if (key.len != crypter->get_key_size(crypter)) |
561 | 0 | { |
562 | 0 | DBG1(DBG_LIB, "symmetric key length is wrong"); |
563 | 0 | crypter->destroy(crypter); |
564 | 0 | return FALSE; |
565 | 0 | } |
566 | 0 | if (iv.len != crypter->get_iv_size(crypter)) |
567 | 0 | { |
568 | 0 | DBG1(DBG_LIB, "IV length is wrong"); |
569 | 0 | crypter->destroy(crypter); |
570 | 0 | return FALSE; |
571 | 0 | } |
572 | 0 | if (!crypter->set_key(crypter, key) || |
573 | 0 | !crypter->decrypt(crypter, encrypted, iv, plain)) |
574 | 0 | { |
575 | 0 | crypter->destroy(crypter); |
576 | 0 | return FALSE; |
577 | 0 | } |
578 | 0 | crypter->destroy(crypter); |
579 | 0 | return TRUE; |
580 | 0 | } |
581 | | |
582 | | /** |
583 | | * Remove enveloped-data PKCS#7 padding from plain data |
584 | | */ |
585 | | static bool remove_padding(chunk_t *data) |
586 | 0 | { |
587 | 0 | u_char *pos; |
588 | 0 | u_char pattern; |
589 | 0 | size_t padding; |
590 | |
|
591 | 0 | if (!data->len) |
592 | 0 | { |
593 | 0 | return FALSE; |
594 | 0 | } |
595 | 0 | pos = data->ptr + data->len - 1; |
596 | 0 | padding = pattern = *pos; |
597 | |
|
598 | 0 | if (padding > data->len) |
599 | 0 | { |
600 | 0 | DBG1(DBG_LIB, "padding greater than data length"); |
601 | 0 | return FALSE; |
602 | 0 | } |
603 | 0 | data->len -= padding; |
604 | |
|
605 | 0 | while (padding-- > 0) |
606 | 0 | { |
607 | 0 | if (*pos-- != pattern) |
608 | 0 | { |
609 | 0 | DBG1(DBG_LIB, "wrong padding pattern"); |
610 | 0 | return FALSE; |
611 | 0 | } |
612 | 0 | } |
613 | 0 | return TRUE; |
614 | 0 | } |
615 | | |
616 | | /** |
617 | | * Decrypt PKCS#7 enveloped-data |
618 | | */ |
619 | | static bool decrypt(private_openssl_pkcs7_t *this, |
620 | | chunk_t encrypted, chunk_t *plain) |
621 | 0 | { |
622 | 0 | STACK_OF(CMS_RecipientInfo) *ris; |
623 | 0 | CMS_RecipientInfo *ri; |
624 | 0 | chunk_t chunk, key = chunk_empty; |
625 | 0 | int i; |
626 | |
|
627 | 0 | ris = CMS_get0_RecipientInfos(this->cms); |
628 | 0 | for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) |
629 | 0 | { |
630 | 0 | ri = sk_CMS_RecipientInfo_value(ris, i); |
631 | 0 | if (CMS_RecipientInfo_type(ri) == CMS_RECIPINFO_TRANS) |
632 | 0 | { |
633 | 0 | identification_t *serial, *issuer; |
634 | 0 | private_key_t *private; |
635 | 0 | X509_ALGOR *alg; |
636 | 0 | X509_NAME *name; |
637 | 0 | ASN1_INTEGER *sn; |
638 | 0 | u_char zero = 0; |
639 | 0 | int oid; |
640 | |
|
641 | 0 | if (CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg) == 1 && |
642 | 0 | CMS_RecipientInfo_ktri_get0_signer_id(ri, NULL, &name, &sn) == 1) |
643 | 0 | { |
644 | 0 | oid = openssl_asn1_known_oid(alg->algorithm); |
645 | 0 | if (oid != OID_RSA_ENCRYPTION) |
646 | 0 | { |
647 | 0 | DBG1(DBG_LIB, "only RSA encryption supported in PKCS#7"); |
648 | 0 | continue; |
649 | 0 | } |
650 | 0 | issuer = openssl_x509_name2id(name); |
651 | 0 | if (!issuer) |
652 | 0 | { |
653 | 0 | continue; |
654 | 0 | } |
655 | 0 | chunk = openssl_asn1_str2chunk(sn); |
656 | 0 | if (chunk.len && chunk.ptr[0] & 0x80) |
657 | 0 | { /* if MSB is set, append a zero to make it non-negative */ |
658 | 0 | chunk = chunk_cata("cc", chunk_from_thing(zero), chunk); |
659 | 0 | } |
660 | 0 | serial = identification_create_from_encoding(ID_KEY_ID, chunk); |
661 | 0 | private = find_private(issuer, serial); |
662 | 0 | issuer->destroy(issuer); |
663 | 0 | serial->destroy(serial); |
664 | |
|
665 | 0 | if (private) |
666 | 0 | { |
667 | | /* get encryptedKey from internal structure; TODO fixup */ |
668 | 0 | chunk = openssl_asn1_str2chunk(ri->ktri->encryptedKey); |
669 | 0 | if (private->decrypt(private, ENCRYPT_RSA_PKCS1, NULL, |
670 | 0 | chunk, &key)) |
671 | 0 | { |
672 | 0 | private->destroy(private); |
673 | 0 | break; |
674 | 0 | } |
675 | 0 | private->destroy(private); |
676 | 0 | } |
677 | 0 | } |
678 | 0 | } |
679 | 0 | } |
680 | 0 | if (!key.len) |
681 | 0 | { |
682 | 0 | DBG1(DBG_LIB, "no private key found to decrypt PKCS#7"); |
683 | 0 | return FALSE; |
684 | 0 | } |
685 | 0 | if (!decrypt_symmetric(this, key, encrypted, plain)) |
686 | 0 | { |
687 | 0 | chunk_clear(&key); |
688 | 0 | return FALSE; |
689 | 0 | } |
690 | 0 | chunk_clear(&key); |
691 | 0 | if (!remove_padding(plain)) |
692 | 0 | { |
693 | 0 | free(plain->ptr); |
694 | 0 | return FALSE; |
695 | 0 | } |
696 | 0 | return TRUE; |
697 | 0 | } |
698 | | |
699 | | METHOD(container_t, get_data, bool, |
700 | | private_openssl_pkcs7_t *this, chunk_t *data) |
701 | 0 | { |
702 | 0 | ASN1_OCTET_STRING **os; |
703 | 0 | chunk_t chunk; |
704 | |
|
705 | 0 | os = CMS_get0_content(this->cms); |
706 | 0 | if (os) |
707 | 0 | { |
708 | 0 | chunk = openssl_asn1_str2chunk(*os); |
709 | 0 | switch (this->type) |
710 | 0 | { |
711 | 0 | case CONTAINER_PKCS7_DATA: |
712 | 0 | case CONTAINER_PKCS7_SIGNED_DATA: |
713 | 0 | *data = chunk_clone(chunk); |
714 | 0 | return TRUE; |
715 | 0 | case CONTAINER_PKCS7_ENVELOPED_DATA: |
716 | 0 | return decrypt(this, chunk, data); |
717 | 0 | default: |
718 | 0 | break; |
719 | 0 | } |
720 | 0 | } |
721 | 0 | return FALSE; |
722 | 0 | } |
723 | | |
724 | | METHOD(container_t, get_encoding, bool, |
725 | | private_openssl_pkcs7_t *this, chunk_t *data) |
726 | 0 | { |
727 | 0 | return FALSE; |
728 | 0 | } |
729 | | |
730 | | METHOD(container_t, destroy, void, |
731 | | private_openssl_pkcs7_t *this) |
732 | 0 | { |
733 | 0 | CMS_ContentInfo_free(this->cms); |
734 | 0 | free(this); |
735 | 0 | } |
736 | | |
737 | | /** |
738 | | * Generic constructor |
739 | | */ |
740 | | static private_openssl_pkcs7_t* create_empty() |
741 | 0 | { |
742 | 0 | private_openssl_pkcs7_t *this; |
743 | |
|
744 | 0 | INIT(this, |
745 | 0 | .public = { |
746 | 0 | .container = { |
747 | 0 | .get_type = _get_type, |
748 | 0 | .create_signature_enumerator = _create_signature_enumerator, |
749 | 0 | .get_data = _get_data, |
750 | 0 | .get_encoding = _get_encoding, |
751 | 0 | .destroy = _destroy, |
752 | 0 | }, |
753 | 0 | .get_attribute = _get_attribute, |
754 | 0 | .create_cert_enumerator = _create_cert_enumerator, |
755 | 0 | }, |
756 | 0 | ); |
757 | |
|
758 | 0 | return this; |
759 | 0 | } |
760 | | |
761 | | /** |
762 | | * Parse a PKCS#7 container |
763 | | */ |
764 | | static bool parse(private_openssl_pkcs7_t *this, chunk_t blob) |
765 | 0 | { |
766 | 0 | BIO *bio; |
767 | |
|
768 | 0 | bio = BIO_new_mem_buf(blob.ptr, blob.len); |
769 | 0 | this->cms = d2i_CMS_bio(bio, NULL); |
770 | 0 | BIO_free(bio); |
771 | |
|
772 | 0 | if (!this->cms) |
773 | 0 | { |
774 | 0 | return FALSE; |
775 | 0 | } |
776 | 0 | switch (openssl_asn1_known_oid((ASN1_OBJECT*)CMS_get0_type(this->cms))) |
777 | 0 | { |
778 | 0 | case OID_PKCS7_DATA: |
779 | 0 | this->type = CONTAINER_PKCS7_DATA; |
780 | 0 | break; |
781 | 0 | case OID_PKCS7_SIGNED_DATA: |
782 | 0 | this->type = CONTAINER_PKCS7_SIGNED_DATA; |
783 | 0 | break; |
784 | 0 | case OID_PKCS7_ENVELOPED_DATA: |
785 | 0 | this->type = CONTAINER_PKCS7_ENVELOPED_DATA; |
786 | 0 | break; |
787 | 0 | default: |
788 | 0 | return FALSE; |
789 | 0 | } |
790 | | |
791 | 0 | return TRUE; |
792 | 0 | } |
793 | | |
794 | | /** |
795 | | * See header |
796 | | */ |
797 | | pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args) |
798 | 0 | { |
799 | 0 | chunk_t blob = chunk_empty; |
800 | 0 | private_openssl_pkcs7_t *this; |
801 | |
|
802 | 0 | while (TRUE) |
803 | 0 | { |
804 | 0 | switch (va_arg(args, builder_part_t)) |
805 | 0 | { |
806 | 0 | case BUILD_BLOB_ASN1_DER: |
807 | 0 | blob = va_arg(args, chunk_t); |
808 | 0 | continue; |
809 | 0 | case BUILD_END: |
810 | 0 | break; |
811 | 0 | default: |
812 | 0 | return NULL; |
813 | 0 | } |
814 | 0 | break; |
815 | 0 | } |
816 | 0 | if (blob.len) |
817 | 0 | { |
818 | 0 | this = create_empty(); |
819 | 0 | if (parse(this, blob)) |
820 | 0 | { |
821 | 0 | return &this->public; |
822 | 0 | } |
823 | 0 | destroy(this); |
824 | 0 | } |
825 | 0 | return NULL; |
826 | 0 | } |
827 | | |
828 | | #endif /* OPENSSL_NO_CMS */ |
829 | | #endif /* OPENSSL_VERSION_NUMBER */ |