OLD | NEW |
| (Empty) |
1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
4 | |
5 /* | |
6 * PKCS7 decoding, verification. | |
7 * | |
8 * $Id: p7decode.c,v 1.31 2012/12/12 19:25:36 wtc%google.com Exp $ | |
9 */ | |
10 | |
11 #include "p7local.h" | |
12 | |
13 #include "cert.h" | |
14 /* XXX do not want to have to include */ | |
15 #include "certdb.h" /* certdb.h -- the trust stuff needed by */ | |
16 /* the add certificate code needs to get */ | |
17 /* rewritten/abstracted and then this */ | |
18 /* include should be removed! */ | |
19 /*#include "cdbhdl.h" */ | |
20 #include "cryptohi.h" | |
21 #include "key.h" | |
22 #include "secasn1.h" | |
23 #include "secitem.h" | |
24 #include "secoid.h" | |
25 #include "pk11func.h" | |
26 #include "prtime.h" | |
27 #include "secerr.h" | |
28 #include "sechash.h" /* for HASH_GetHashObject() */ | |
29 #include "secder.h" | |
30 #include "secpkcs5.h" | |
31 | |
32 struct sec_pkcs7_decoder_worker { | |
33 int depth; | |
34 int digcnt; | |
35 void **digcxs; | |
36 const SECHashObject **digobjs; | |
37 sec_PKCS7CipherObject *decryptobj; | |
38 PRBool saw_contents; | |
39 }; | |
40 | |
41 struct SEC_PKCS7DecoderContextStr { | |
42 SEC_ASN1DecoderContext *dcx; | |
43 SEC_PKCS7ContentInfo *cinfo; | |
44 SEC_PKCS7DecoderContentCallback cb; | |
45 void *cb_arg; | |
46 SECKEYGetPasswordKey pwfn; | |
47 void *pwfn_arg; | |
48 struct sec_pkcs7_decoder_worker worker; | |
49 PRArenaPool *tmp_poolp; | |
50 int error; | |
51 SEC_PKCS7GetDecryptKeyCallback dkcb; | |
52 void *dkcb_arg; | |
53 SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb; | |
54 }; | |
55 | |
56 /* | |
57 * Handle one worker, decrypting and digesting the data as necessary. | |
58 * | |
59 * XXX If/when we support nested contents, this probably needs to be | |
60 * revised somewhat to get passed the content-info (which unfortunately | |
61 * can be two different types depending on whether it is encrypted or not) | |
62 * corresponding to the given worker. | |
63 */ | |
64 static void | |
65 sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx, | |
66 struct sec_pkcs7_decoder_worker *worker, | |
67 const unsigned char *data, unsigned long len, | |
68 PRBool final) | |
69 { | |
70 unsigned char *buf = NULL; | |
71 SECStatus rv; | |
72 int i; | |
73 | |
74 /* | |
75 * We should really have data to process, or we should be trying | |
76 * to finish/flush the last block. (This is an overly paranoid | |
77 * check since all callers are in this file and simple inspection | |
78 * proves they do it right. But it could find a bug in future | |
79 * modifications/development, that is why it is here.) | |
80 */ | |
81 PORT_Assert ((data != NULL && len) || final); | |
82 | |
83 /* | |
84 * Decrypt this chunk. | |
85 * | |
86 * XXX If we get an error, we do not want to do the digest or callback, | |
87 * but we want to keep decoding. Or maybe we want to stop decoding | |
88 * altogether if there is a callback, because obviously we are not | |
89 * sending the data back and they want to know that. | |
90 */ | |
91 if (worker->decryptobj != NULL) { | |
92 /* XXX the following lengths should all be longs? */ | |
93 unsigned int inlen; /* length of data being decrypted */ | |
94 unsigned int outlen; /* length of decrypted data */ | |
95 unsigned int buflen; /* length available for decrypted data */ | |
96 SECItem *plain; | |
97 | |
98 inlen = len; | |
99 buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final); | |
100 if (buflen == 0) { | |
101 if (inlen == 0) /* no input and no output */ | |
102 return; | |
103 /* | |
104 * No output is expected, but the input data may be buffered | |
105 * so we still have to call Decrypt. | |
106 */ | |
107 rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0, | |
108 data, inlen, final); | |
109 if (rv != SECSuccess) { | |
110 p7dcx->error = PORT_GetError(); | |
111 return; /* XXX indicate error? */ | |
112 } | |
113 return; | |
114 } | |
115 | |
116 if (p7dcx->cb != NULL) { | |
117 buf = (unsigned char *) PORT_Alloc (buflen); | |
118 plain = NULL; | |
119 } else { | |
120 unsigned long oldlen; | |
121 | |
122 /* | |
123 * XXX This assumes one level of content only. | |
124 * See comment above about nested content types. | |
125 * XXX Also, it should work for signedAndEnvelopedData, too! | |
126 */ | |
127 plain = &(p7dcx->cinfo-> | |
128 content.envelopedData->encContentInfo.plainContent); | |
129 | |
130 oldlen = plain->len; | |
131 if (oldlen == 0) { | |
132 buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, | |
133 buflen); | |
134 } else { | |
135 buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, | |
136 plain->data, | |
137 oldlen, oldlen + buflen); | |
138 if (buf != NULL) | |
139 buf += oldlen; | |
140 } | |
141 plain->data = buf; | |
142 } | |
143 if (buf == NULL) { | |
144 p7dcx->error = SEC_ERROR_NO_MEMORY; | |
145 return; /* XXX indicate error? */ | |
146 } | |
147 rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen, | |
148 data, inlen, final); | |
149 if (rv != SECSuccess) { | |
150 p7dcx->error = PORT_GetError(); | |
151 return; /* XXX indicate error? */ | |
152 } | |
153 if (plain != NULL) { | |
154 PORT_Assert (final || outlen == buflen); | |
155 plain->len += outlen; | |
156 } | |
157 data = buf; | |
158 len = outlen; | |
159 } | |
160 | |
161 /* | |
162 * Update the running digests. | |
163 */ | |
164 if (len) { | |
165 for (i = 0; i < worker->digcnt; i++) { | |
166 (* worker->digobjs[i]->update) (worker->digcxs[i], data, len); | |
167 } | |
168 } | |
169 | |
170 /* | |
171 * Pass back the contents bytes, and free the temporary buffer. | |
172 */ | |
173 if (p7dcx->cb != NULL) { | |
174 if (len) | |
175 (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len); | |
176 if (worker->decryptobj != NULL) { | |
177 PORT_Assert (buf != NULL); | |
178 PORT_Free (buf); | |
179 } | |
180 } | |
181 } | |
182 | |
183 static void | |
184 sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len, | |
185 int depth, SEC_ASN1EncodingPart data_kind) | |
186 { | |
187 SEC_PKCS7DecoderContext *p7dcx; | |
188 struct sec_pkcs7_decoder_worker *worker; | |
189 | |
190 /* | |
191 * Since we do not handle any nested contents, the only bytes we | |
192 * are really interested in are the actual contents bytes (not | |
193 * the identifier, length, or end-of-contents bytes). If we were | |
194 * handling nested types we would probably need to do something | |
195 * smarter based on depth and data_kind. | |
196 */ | |
197 if (data_kind != SEC_ASN1_Contents) | |
198 return; | |
199 | |
200 /* | |
201 * The ASN.1 decoder should not even call us with a length of 0. | |
202 * Just being paranoid. | |
203 */ | |
204 PORT_Assert (len); | |
205 if (len == 0) | |
206 return; | |
207 | |
208 p7dcx = (SEC_PKCS7DecoderContext*)arg; | |
209 | |
210 /* | |
211 * Handling nested contents would mean that there is a chain | |
212 * of workers -- one per each level of content. The following | |
213 * would start with the first worker and loop over them. | |
214 */ | |
215 worker = &(p7dcx->worker); | |
216 | |
217 worker->saw_contents = PR_TRUE; | |
218 | |
219 sec_pkcs7_decoder_work_data (p7dcx, worker, | |
220 (const unsigned char *) data, len, PR_FALSE); | |
221 } | |
222 | |
223 | |
224 /* | |
225 * Create digest contexts for each algorithm in "digestalgs". | |
226 * No algorithms is not an error, we just do not do anything. | |
227 * An error (like trouble allocating memory), marks the error | |
228 * in "p7dcx" and returns SECFailure, which means that our caller | |
229 * should just give up altogether. | |
230 */ | |
231 static SECStatus | |
232 sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth, | |
233 SECAlgorithmID **digestalgs) | |
234 { | |
235 int i, digcnt; | |
236 | |
237 if (digestalgs == NULL) | |
238 return SECSuccess; | |
239 | |
240 /* | |
241 * Count the algorithms. | |
242 */ | |
243 digcnt = 0; | |
244 while (digestalgs[digcnt] != NULL) | |
245 digcnt++; | |
246 | |
247 /* | |
248 * No algorithms means no work to do. | |
249 * Just act as if there were no algorithms specified. | |
250 */ | |
251 if (digcnt == 0) | |
252 return SECSuccess; | |
253 | |
254 p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp, | |
255 digcnt * sizeof (void *)); | |
256 p7dcx->worker.digobjs = (const SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_p
oolp, | |
257 digcnt * sizeof (SECHashObject *)); | |
258 if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) { | |
259 p7dcx->error = SEC_ERROR_NO_MEMORY; | |
260 return SECFailure; | |
261 } | |
262 | |
263 p7dcx->worker.depth = depth; | |
264 p7dcx->worker.digcnt = 0; | |
265 | |
266 /* | |
267 * Create a digest context for each algorithm. | |
268 */ | |
269 for (i = 0; i < digcnt; i++) { | |
270 SECAlgorithmID * algid = digestalgs[i]; | |
271 SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm)); | |
272 const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag); | |
273 void *digcx; | |
274 | |
275 /* | |
276 * Skip any algorithm we do not even recognize; obviously, | |
277 * this could be a problem, but if it is critical then the | |
278 * result will just be that the signature does not verify. | |
279 * We do not necessarily want to error out here, because | |
280 * the particular algorithm may not actually be important, | |
281 * but we cannot know that until later. | |
282 */ | |
283 if (digobj == NULL) { | |
284 p7dcx->worker.digcnt--; | |
285 continue; | |
286 } | |
287 | |
288 digcx = (* digobj->create)(); | |
289 if (digcx != NULL) { | |
290 (* digobj->begin) (digcx); | |
291 p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj; | |
292 p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx; | |
293 p7dcx->worker.digcnt++; | |
294 } | |
295 } | |
296 | |
297 if (p7dcx->worker.digcnt != 0) | |
298 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, | |
299 sec_pkcs7_decoder_filter, | |
300 p7dcx, | |
301 (PRBool)(p7dcx->cb != NULL)); | |
302 return SECSuccess; | |
303 } | |
304 | |
305 | |
306 /* | |
307 * Close out all of the digest contexts, storing the results in "digestsp". | |
308 */ | |
309 static SECStatus | |
310 sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx, | |
311 PRArenaPool *poolp, | |
312 SECItem ***digestsp) | |
313 { | |
314 struct sec_pkcs7_decoder_worker *worker; | |
315 const SECHashObject *digobj; | |
316 void *digcx; | |
317 SECItem **digests, *digest; | |
318 int i; | |
319 void *mark; | |
320 | |
321 /* | |
322 * XXX Handling nested contents would mean that there is a chain | |
323 * of workers -- one per each level of content. The following | |
324 * would want to find the last worker in the chain. | |
325 */ | |
326 worker = &(p7dcx->worker); | |
327 | |
328 /* | |
329 * If no digests, then we have nothing to do. | |
330 */ | |
331 if (worker->digcnt == 0) | |
332 return SECSuccess; | |
333 | |
334 /* | |
335 * No matter what happens after this, we want to stop filtering. | |
336 * XXX If we handle nested contents, we only want to stop filtering | |
337 * if we are finishing off the *last* worker. | |
338 */ | |
339 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
340 | |
341 /* | |
342 * If we ended up with no contents, just destroy each | |
343 * digest context -- they are meaningless and potentially | |
344 * confusing, because their presence would imply some content | |
345 * was digested. | |
346 */ | |
347 if (! worker->saw_contents) { | |
348 for (i = 0; i < worker->digcnt; i++) { | |
349 digcx = worker->digcxs[i]; | |
350 digobj = worker->digobjs[i]; | |
351 (* digobj->destroy) (digcx, PR_TRUE); | |
352 } | |
353 return SECSuccess; | |
354 } | |
355 | |
356 mark = PORT_ArenaMark (poolp); | |
357 | |
358 /* | |
359 * Close out each digest context, saving digest away. | |
360 */ | |
361 digests = | |
362 (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *)); | |
363 digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem)); | |
364 if (digests == NULL || digest == NULL) { | |
365 p7dcx->error = PORT_GetError(); | |
366 PORT_ArenaRelease (poolp, mark); | |
367 return SECFailure; | |
368 } | |
369 | |
370 for (i = 0; i < worker->digcnt; i++, digest++) { | |
371 digcx = worker->digcxs[i]; | |
372 digobj = worker->digobjs[i]; | |
373 | |
374 digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length); | |
375 if (digest->data == NULL) { | |
376 p7dcx->error = PORT_GetError(); | |
377 PORT_ArenaRelease (poolp, mark); | |
378 return SECFailure; | |
379 } | |
380 | |
381 digest->len = digobj->length; | |
382 (* digobj->end) (digcx, digest->data, &(digest->len), digest->len); | |
383 (* digobj->destroy) (digcx, PR_TRUE); | |
384 | |
385 digests[i] = digest; | |
386 } | |
387 digests[i] = NULL; | |
388 *digestsp = digests; | |
389 | |
390 PORT_ArenaUnmark (poolp, mark); | |
391 return SECSuccess; | |
392 } | |
393 | |
394 /* | |
395 * XXX Need comment explaining following helper function (which is used | |
396 * by sec_pkcs7_decoder_start_decrypt). | |
397 */ | |
398 | |
399 static PK11SymKey * | |
400 sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx, | |
401 SEC_PKCS7RecipientInfo **recipientinfos, | |
402 SEC_PKCS7EncryptedContentInfo *enccinfo) | |
403 { | |
404 SEC_PKCS7RecipientInfo *ri; | |
405 CERTCertificate *cert = NULL; | |
406 SECKEYPrivateKey *privkey = NULL; | |
407 PK11SymKey *bulkkey = NULL; | |
408 SECOidTag keyalgtag, bulkalgtag, encalgtag; | |
409 PK11SlotInfo *slot = NULL; | |
410 | |
411 if (recipientinfos == NULL || recipientinfos[0] == NULL) { | |
412 p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; | |
413 goto no_key_found; | |
414 } | |
415 | |
416 cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri, | |
417 &privkey, p7dcx->pwfn_arg); | |
418 if (cert == NULL) { | |
419 p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; | |
420 goto no_key_found; | |
421 } | |
422 | |
423 ri->cert = cert; /* so we can find it later */ | |
424 PORT_Assert(privkey != NULL); | |
425 | |
426 keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); | |
427 encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg)); | |
428 if (keyalgtag != encalgtag) { | |
429 p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH; | |
430 goto no_key_found; | |
431 } | |
432 bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg)); | |
433 | |
434 switch (encalgtag) { | |
435 case SEC_OID_PKCS1_RSA_ENCRYPTION: | |
436 bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey, | |
437 PK11_AlgtagToMechanism (bulkalgtag), | |
438 CKA_DECRYPT, 0); | |
439 if (bulkkey == NULL) { | |
440 p7dcx->error = PORT_GetError(); | |
441 PORT_SetError(0); | |
442 goto no_key_found; | |
443 } | |
444 break; | |
445 default: | |
446 p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG; | |
447 break; | |
448 } | |
449 | |
450 no_key_found: | |
451 if (privkey != NULL) | |
452 SECKEY_DestroyPrivateKey (privkey); | |
453 if (slot != NULL) | |
454 PK11_FreeSlot(slot); | |
455 | |
456 return bulkkey; | |
457 } | |
458 | |
459 /* | |
460 * XXX The following comment is old -- the function used to only handle | |
461 * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData | |
462 * as well (and it had all of the code of the helper function above | |
463 * built into it), though the comment was left as is. Fix it... | |
464 * | |
465 * We are just about to decode the content of an EnvelopedData. | |
466 * Set up a decryption context so we can decrypt as we go. | |
467 * Presumably we are one of the recipients listed in "recipientinfos". | |
468 * (XXX And if we are not, or if we have trouble, what should we do? | |
469 * It would be nice to let the decoding still work. Maybe it should | |
470 * be an error if there is a content callback, but not an error otherwise?) | |
471 * The encryption key and related information can be found in "enccinfo". | |
472 */ | |
473 static SECStatus | |
474 sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth, | |
475 SEC_PKCS7RecipientInfo **recipientinfos, | |
476 SEC_PKCS7EncryptedContentInfo *enccinfo, | |
477 PK11SymKey **copy_key_for_signature) | |
478 { | |
479 PK11SymKey *bulkkey = NULL; | |
480 sec_PKCS7CipherObject *decryptobj; | |
481 | |
482 /* | |
483 * If a callback is supplied to retrieve the encryption key, | |
484 * for instance, for Encrypted Content infos, then retrieve | |
485 * the bulkkey from the callback. Otherwise, assume that | |
486 * we are processing Enveloped or SignedAndEnveloped data | |
487 * content infos. | |
488 * | |
489 * XXX Put an assert here? | |
490 */ | |
491 if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) { | |
492 if (p7dcx->dkcb != NULL) { | |
493 bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, | |
494 &(enccinfo->contentEncAlg)); | |
495 } | |
496 enccinfo->keysize = 0; | |
497 } else { | |
498 bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, | |
499 enccinfo); | |
500 if (bulkkey == NULL) goto no_decryption; | |
501 enccinfo->keysize = PK11_GetKeyStrength(bulkkey, | |
502 &(enccinfo->contentEncAlg)); | |
503 | |
504 } | |
505 | |
506 /* | |
507 * XXX I think following should set error in p7dcx and clear set error | |
508 * (as used to be done here, or as is done in get_receipient_key above. | |
509 */ | |
510 if(bulkkey == NULL) { | |
511 goto no_decryption; | |
512 } | |
513 | |
514 /* | |
515 * We want to make sure decryption is allowed. This is done via | |
516 * a callback specified in SEC_PKCS7DecoderStart(). | |
517 */ | |
518 if (p7dcx->decrypt_allowed_cb) { | |
519 if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), | |
520 bulkkey) == PR_FALSE) { | |
521 p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; | |
522 goto no_decryption; | |
523 } | |
524 } else { | |
525 p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; | |
526 goto no_decryption; | |
527 } | |
528 | |
529 /* | |
530 * When decrypting a signedAndEnvelopedData, the signature also has | |
531 * to be decrypted with the bulk encryption key; to avoid having to | |
532 * get it all over again later (and do another potentially expensive | |
533 * RSA operation), copy it for later signature verification to use. | |
534 */ | |
535 if (copy_key_for_signature != NULL) | |
536 *copy_key_for_signature = PK11_ReferenceSymKey (bulkkey); | |
537 | |
538 /* | |
539 * Now we have the bulk encryption key (in bulkkey) and the | |
540 * the algorithm (in enccinfo->contentEncAlg). Using those, | |
541 * create a decryption context. | |
542 */ | |
543 decryptobj = sec_PKCS7CreateDecryptObject (bulkkey, | |
544 &(enccinfo->contentEncAlg)); | |
545 | |
546 /* | |
547 * We are done with (this) bulkkey now. | |
548 */ | |
549 PK11_FreeSymKey (bulkkey); | |
550 | |
551 if (decryptobj == NULL) { | |
552 p7dcx->error = PORT_GetError(); | |
553 PORT_SetError(0); | |
554 goto no_decryption; | |
555 } | |
556 | |
557 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, | |
558 sec_pkcs7_decoder_filter, | |
559 p7dcx, | |
560 (PRBool)(p7dcx->cb != NULL)); | |
561 | |
562 p7dcx->worker.depth = depth; | |
563 p7dcx->worker.decryptobj = decryptobj; | |
564 | |
565 return SECSuccess; | |
566 | |
567 no_decryption: | |
568 /* | |
569 * For some reason (error set already, if appropriate), we cannot | |
570 * decrypt the content. I am not sure what exactly is the right | |
571 * thing to do here; in some cases we want to just stop, and in | |
572 * others we want to let the decoding finish even though we cannot | |
573 * decrypt the content. My current thinking is that if the caller | |
574 * set up a content callback, then they are really interested in | |
575 * getting (decrypted) content, and if they cannot they will want | |
576 * to know about it. However, if no callback was specified, then | |
577 * maybe it is not important that the decryption failed. | |
578 */ | |
579 if (p7dcx->cb != NULL) | |
580 return SECFailure; | |
581 else | |
582 return SECSuccess; /* Let the decoding continue. */ | |
583 } | |
584 | |
585 | |
586 static SECStatus | |
587 sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx, | |
588 PRArenaPool *poolp, | |
589 SEC_PKCS7EncryptedContentInfo *enccinfo) | |
590 { | |
591 struct sec_pkcs7_decoder_worker *worker; | |
592 | |
593 /* | |
594 * XXX Handling nested contents would mean that there is a chain | |
595 * of workers -- one per each level of content. The following | |
596 * would want to find the last worker in the chain. | |
597 */ | |
598 worker = &(p7dcx->worker); | |
599 | |
600 /* | |
601 * If no decryption context, then we have nothing to do. | |
602 */ | |
603 if (worker->decryptobj == NULL) | |
604 return SECSuccess; | |
605 | |
606 /* | |
607 * No matter what happens after this, we want to stop filtering. | |
608 * XXX If we handle nested contents, we only want to stop filtering | |
609 * if we are finishing off the *last* worker. | |
610 */ | |
611 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
612 | |
613 /* | |
614 * Handle the last block. | |
615 */ | |
616 sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE); | |
617 | |
618 /* | |
619 * All done, destroy it. | |
620 */ | |
621 sec_PKCS7DestroyDecryptObject (worker->decryptobj); | |
622 worker->decryptobj = NULL; | |
623 | |
624 return SECSuccess; | |
625 } | |
626 | |
627 | |
628 static void | |
629 sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth) | |
630 { | |
631 SEC_PKCS7DecoderContext *p7dcx; | |
632 SEC_PKCS7ContentInfo *cinfo; | |
633 SEC_PKCS7SignedData *sigd; | |
634 SEC_PKCS7EnvelopedData *envd; | |
635 SEC_PKCS7SignedAndEnvelopedData *saed; | |
636 SEC_PKCS7EncryptedData *encd; | |
637 SEC_PKCS7DigestedData *digd; | |
638 PRBool after; | |
639 SECStatus rv; | |
640 | |
641 /* | |
642 * Just to make the code easier to read, create an "after" variable | |
643 * that is equivalent to "not before". | |
644 * (This used to be just the statement "after = !before", but that | |
645 * causes a warning on the mac; to avoid that, we do it the long way.) | |
646 */ | |
647 if (before) | |
648 after = PR_FALSE; | |
649 else | |
650 after = PR_TRUE; | |
651 | |
652 p7dcx = (SEC_PKCS7DecoderContext*)arg; | |
653 cinfo = p7dcx->cinfo; | |
654 | |
655 if (cinfo->contentTypeTag == NULL) { | |
656 if (after && dest == &(cinfo->contentType)) | |
657 cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType)); | |
658 return; | |
659 } | |
660 | |
661 switch (cinfo->contentTypeTag->offset) { | |
662 case SEC_OID_PKCS7_SIGNED_DATA: | |
663 sigd = cinfo->content.signedData; | |
664 if (sigd == NULL) | |
665 break; | |
666 | |
667 if (sigd->contentInfo.contentTypeTag == NULL) { | |
668 if (after && dest == &(sigd->contentInfo.contentType)) | |
669 sigd->contentInfo.contentTypeTag = | |
670 SECOID_FindOID(&(sigd->contentInfo.contentType)); | |
671 break; | |
672 } | |
673 | |
674 /* | |
675 * We only set up a filtering digest if the content is | |
676 * plain DATA; anything else needs more work because a | |
677 * second pass is required to produce a DER encoding from | |
678 * an input that can be BER encoded. (This is a requirement | |
679 * of PKCS7 that is unfortunate, but there you have it.) | |
680 * | |
681 * XXX Also, since we stop here if this is not DATA, the | |
682 * inner content is not getting processed at all. Someday | |
683 * we may want to fix that. | |
684 */ | |
685 if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) { | |
686 /* XXX Set an error in p7dcx->error */ | |
687 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
688 break; | |
689 } | |
690 | |
691 /* | |
692 * Just before the content, we want to set up a digest context | |
693 * for each digest algorithm listed, and start a filter which | |
694 * will run all of the contents bytes through that digest. | |
695 */ | |
696 if (before && dest == &(sigd->contentInfo.content)) { | |
697 rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, | |
698 sigd->digestAlgorithms); | |
699 if (rv != SECSuccess) | |
700 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
701 | |
702 break; | |
703 } | |
704 | |
705 /* | |
706 * XXX To handle nested types, here is where we would want | |
707 * to check for inner boundaries that need handling. | |
708 */ | |
709 | |
710 /* | |
711 * Are we done? | |
712 */ | |
713 if (after && dest == &(sigd->contentInfo.content)) { | |
714 /* | |
715 * Close out the digest contexts. We ignore any error | |
716 * because we are stopping anyway; the error status left | |
717 * behind in p7dcx will be seen by outer functions. | |
718 */ | |
719 (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, | |
720 &(sigd->digests)); | |
721 | |
722 /* | |
723 * XXX To handle nested contents, we would need to remove | |
724 * the worker from the chain (and free it). | |
725 */ | |
726 | |
727 /* | |
728 * Stop notify. | |
729 */ | |
730 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
731 } | |
732 break; | |
733 | |
734 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
735 envd = cinfo->content.envelopedData; | |
736 if (envd == NULL) | |
737 break; | |
738 | |
739 if (envd->encContentInfo.contentTypeTag == NULL) { | |
740 if (after && dest == &(envd->encContentInfo.contentType)) | |
741 envd->encContentInfo.contentTypeTag = | |
742 SECOID_FindOID(&(envd->encContentInfo.contentType)); | |
743 break; | |
744 } | |
745 | |
746 /* | |
747 * Just before the content, we want to set up a decryption | |
748 * context, and start a filter which will run all of the | |
749 * contents bytes through it to determine the plain content. | |
750 */ | |
751 if (before && dest == &(envd->encContentInfo.encContent)) { | |
752 rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, | |
753 envd->recipientInfos, | |
754 &(envd->encContentInfo), | |
755 NULL); | |
756 if (rv != SECSuccess) | |
757 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
758 | |
759 break; | |
760 } | |
761 | |
762 /* | |
763 * Are we done? | |
764 */ | |
765 if (after && dest == &(envd->encContentInfo.encContent)) { | |
766 /* | |
767 * Close out the decryption context. We ignore any error | |
768 * because we are stopping anyway; the error status left | |
769 * behind in p7dcx will be seen by outer functions. | |
770 */ | |
771 (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, | |
772 &(envd->encContentInfo)); | |
773 | |
774 /* | |
775 * XXX To handle nested contents, we would need to remove | |
776 * the worker from the chain (and free it). | |
777 */ | |
778 | |
779 /* | |
780 * Stop notify. | |
781 */ | |
782 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
783 } | |
784 break; | |
785 | |
786 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
787 saed = cinfo->content.signedAndEnvelopedData; | |
788 if (saed == NULL) | |
789 break; | |
790 | |
791 if (saed->encContentInfo.contentTypeTag == NULL) { | |
792 if (after && dest == &(saed->encContentInfo.contentType)) | |
793 saed->encContentInfo.contentTypeTag = | |
794 SECOID_FindOID(&(saed->encContentInfo.contentType)); | |
795 break; | |
796 } | |
797 | |
798 /* | |
799 * Just before the content, we want to set up a decryption | |
800 * context *and* digest contexts, and start a filter which | |
801 * will run all of the contents bytes through both. | |
802 */ | |
803 if (before && dest == &(saed->encContentInfo.encContent)) { | |
804 rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, | |
805 saed->recipientInfos, | |
806 &(saed->encContentInfo), | |
807 &(saed->sigKey)); | |
808 if (rv == SECSuccess) | |
809 rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, | |
810 saed->digestAlgorithms); | |
811 if (rv != SECSuccess) | |
812 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
813 | |
814 break; | |
815 } | |
816 | |
817 /* | |
818 * Are we done? | |
819 */ | |
820 if (after && dest == &(saed->encContentInfo.encContent)) { | |
821 /* | |
822 * Close out the decryption and digests contexts. | |
823 * We ignore any errors because we are stopping anyway; | |
824 * the error status left behind in p7dcx will be seen by | |
825 * outer functions. | |
826 * | |
827 * Note that the decrypt stuff must be called first; | |
828 * it may have a last buffer to do which in turn has | |
829 * to be added to the digest. | |
830 */ | |
831 (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, | |
832 &(saed->encContentInfo)); | |
833 (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, | |
834 &(saed->digests)); | |
835 | |
836 /* | |
837 * XXX To handle nested contents, we would need to remove | |
838 * the worker from the chain (and free it). | |
839 */ | |
840 | |
841 /* | |
842 * Stop notify. | |
843 */ | |
844 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
845 } | |
846 break; | |
847 | |
848 case SEC_OID_PKCS7_DIGESTED_DATA: | |
849 digd = cinfo->content.digestedData; | |
850 | |
851 /* | |
852 * XXX Want to do the digest or not? Maybe future enhancement... | |
853 */ | |
854 if (before && dest == &(digd->contentInfo.content.data)) { | |
855 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter, | |
856 p7dcx, | |
857 (PRBool)(p7dcx->cb != NULL)); | |
858 break; | |
859 } | |
860 | |
861 /* | |
862 * Are we done? | |
863 */ | |
864 if (after && dest == &(digd->contentInfo.content.data)) { | |
865 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
866 } | |
867 break; | |
868 | |
869 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
870 encd = cinfo->content.encryptedData; | |
871 | |
872 /* | |
873 * XXX If the decryption key callback is set, we want to start | |
874 * the decryption. If the callback is not set, we will treat the | |
875 * content as plain data, since we do not have the key. | |
876 * | |
877 * Is this the proper thing to do? | |
878 */ | |
879 if (before && dest == &(encd->encContentInfo.encContent)) { | |
880 /* | |
881 * Start the encryption process if the decryption key callback | |
882 * is present. Otherwise, treat the content like plain data. | |
883 */ | |
884 rv = SECSuccess; | |
885 if (p7dcx->dkcb != NULL) { | |
886 rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL, | |
887 &(encd->encContentInfo), | |
888 NULL); | |
889 } | |
890 | |
891 if (rv != SECSuccess) | |
892 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
893 | |
894 break; | |
895 } | |
896 | |
897 /* | |
898 * Are we done? | |
899 */ | |
900 if (after && dest == &(encd->encContentInfo.encContent)) { | |
901 /* | |
902 * Close out the decryption context. We ignore any error | |
903 * because we are stopping anyway; the error status left | |
904 * behind in p7dcx will be seen by outer functions. | |
905 */ | |
906 (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, | |
907 &(encd->encContentInfo)); | |
908 | |
909 /* | |
910 * Stop notify. | |
911 */ | |
912 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
913 } | |
914 break; | |
915 | |
916 case SEC_OID_PKCS7_DATA: | |
917 /* | |
918 * If a output callback has been specified, we want to set the filter | |
919 * to call the callback. This is taken care of in | |
920 * sec_pkcs7_decoder_start_decrypt() or | |
921 * sec_pkcs7_decoder_start_digests() for the other content types. | |
922 */ | |
923 | |
924 if (before && dest == &(cinfo->content.data)) { | |
925 | |
926 /* | |
927 * Set the filter proc up. | |
928 */ | |
929 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, | |
930 sec_pkcs7_decoder_filter, | |
931 p7dcx, | |
932 (PRBool)(p7dcx->cb != NULL)); | |
933 break; | |
934 } | |
935 | |
936 if (after && dest == &(cinfo->content.data)) { | |
937 /* | |
938 * Time to clean up after ourself, stop the Notify and Filter | |
939 * procedures. | |
940 */ | |
941 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
942 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
943 } | |
944 break; | |
945 | |
946 default: | |
947 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
948 break; | |
949 } | |
950 } | |
951 | |
952 | |
953 SEC_PKCS7DecoderContext * | |
954 SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg, | |
955 SECKEYGetPasswordKey pwfn, void *pwfn_arg, | |
956 SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, | |
957 void *decrypt_key_cb_arg, | |
958 SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) | |
959 { | |
960 SEC_PKCS7DecoderContext *p7dcx; | |
961 SEC_ASN1DecoderContext *dcx; | |
962 SEC_PKCS7ContentInfo *cinfo; | |
963 PRArenaPool *poolp; | |
964 | |
965 poolp = PORT_NewArena (1024); /* XXX what is right value? */ | |
966 if (poolp == NULL) | |
967 return NULL; | |
968 | |
969 cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); | |
970 if (cinfo == NULL) { | |
971 PORT_FreeArena (poolp, PR_FALSE); | |
972 return NULL; | |
973 } | |
974 | |
975 cinfo->poolp = poolp; | |
976 cinfo->pwfn = pwfn; | |
977 cinfo->pwfn_arg = pwfn_arg; | |
978 cinfo->created = PR_FALSE; | |
979 cinfo->refCount = 1; | |
980 | |
981 p7dcx = | |
982 (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext)); | |
983 if (p7dcx == NULL) { | |
984 PORT_FreeArena (poolp, PR_FALSE); | |
985 return NULL; | |
986 } | |
987 | |
988 p7dcx->tmp_poolp = PORT_NewArena (1024); /* XXX what is right value? */ | |
989 if (p7dcx->tmp_poolp == NULL) { | |
990 PORT_Free (p7dcx); | |
991 PORT_FreeArena (poolp, PR_FALSE); | |
992 return NULL; | |
993 } | |
994 | |
995 dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate); | |
996 if (dcx == NULL) { | |
997 PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); | |
998 PORT_Free (p7dcx); | |
999 PORT_FreeArena (poolp, PR_FALSE); | |
1000 return NULL; | |
1001 } | |
1002 | |
1003 SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx); | |
1004 | |
1005 p7dcx->dcx = dcx; | |
1006 p7dcx->cinfo = cinfo; | |
1007 p7dcx->cb = cb; | |
1008 p7dcx->cb_arg = cb_arg; | |
1009 p7dcx->pwfn = pwfn; | |
1010 p7dcx->pwfn_arg = pwfn_arg; | |
1011 p7dcx->dkcb = decrypt_key_cb; | |
1012 p7dcx->dkcb_arg = decrypt_key_cb_arg; | |
1013 p7dcx->decrypt_allowed_cb = decrypt_allowed_cb; | |
1014 | |
1015 return p7dcx; | |
1016 } | |
1017 | |
1018 | |
1019 /* | |
1020 * Do the next chunk of PKCS7 decoding. If there is a problem, set | |
1021 * an error and return a failure status. Note that in the case of | |
1022 * an error, this routine is still prepared to be called again and | |
1023 * again in case that is the easiest route for our caller to take. | |
1024 * We simply detect it and do not do anything except keep setting | |
1025 * that error in case our caller has not noticed it yet... | |
1026 */ | |
1027 SECStatus | |
1028 SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx, | |
1029 const char *buf, unsigned long len) | |
1030 { | |
1031 if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { | |
1032 PORT_Assert (p7dcx->error == 0); | |
1033 if (p7dcx->error == 0) { | |
1034 if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) { | |
1035 p7dcx->error = PORT_GetError(); | |
1036 PORT_Assert (p7dcx->error); | |
1037 if (p7dcx->error == 0) | |
1038 p7dcx->error = -1; | |
1039 } | |
1040 } | |
1041 } | |
1042 | |
1043 if (p7dcx->error) { | |
1044 if (p7dcx->dcx != NULL) { | |
1045 (void) SEC_ASN1DecoderFinish (p7dcx->dcx); | |
1046 p7dcx->dcx = NULL; | |
1047 } | |
1048 if (p7dcx->cinfo != NULL) { | |
1049 SEC_PKCS7DestroyContentInfo (p7dcx->cinfo); | |
1050 p7dcx->cinfo = NULL; | |
1051 } | |
1052 PORT_SetError (p7dcx->error); | |
1053 return SECFailure; | |
1054 } | |
1055 | |
1056 return SECSuccess; | |
1057 } | |
1058 | |
1059 | |
1060 SEC_PKCS7ContentInfo * | |
1061 SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx) | |
1062 { | |
1063 SEC_PKCS7ContentInfo *cinfo; | |
1064 | |
1065 cinfo = p7dcx->cinfo; | |
1066 if (p7dcx->dcx != NULL) { | |
1067 if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) { | |
1068 SEC_PKCS7DestroyContentInfo (cinfo); | |
1069 cinfo = NULL; | |
1070 } | |
1071 } | |
1072 /* free any NSS data structures */ | |
1073 if (p7dcx->worker.decryptobj) { | |
1074 sec_PKCS7DestroyDecryptObject (p7dcx->worker.decryptobj); | |
1075 } | |
1076 PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); | |
1077 PORT_Free (p7dcx); | |
1078 return cinfo; | |
1079 } | |
1080 | |
1081 | |
1082 SEC_PKCS7ContentInfo * | |
1083 SEC_PKCS7DecodeItem(SECItem *p7item, | |
1084 SEC_PKCS7DecoderContentCallback cb, void *cb_arg, | |
1085 SECKEYGetPasswordKey pwfn, void *pwfn_arg, | |
1086 SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, | |
1087 void *decrypt_key_cb_arg, | |
1088 SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) | |
1089 { | |
1090 SEC_PKCS7DecoderContext *p7dcx; | |
1091 | |
1092 p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, | |
1093 decrypt_key_cb_arg, decrypt_allowed_cb); | |
1094 if (!p7dcx) { | |
1095 /* error code is set */ | |
1096 return NULL; | |
1097 } | |
1098 (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len); | |
1099 return SEC_PKCS7DecoderFinish(p7dcx); | |
1100 } | |
1101 | |
1102 /* | |
1103 * Abort the ASN.1 stream. Used by pkcs 12 | |
1104 */ | |
1105 void | |
1106 SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error) | |
1107 { | |
1108 PORT_Assert(p7dcx); | |
1109 SEC_ASN1DecoderAbort(p7dcx->dcx, error); | |
1110 } | |
1111 | |
1112 | |
1113 /* | |
1114 * If the thing contains any certs or crls return true; false otherwise. | |
1115 */ | |
1116 PRBool | |
1117 SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo) | |
1118 { | |
1119 SECOidTag kind; | |
1120 SECItem **certs; | |
1121 CERTSignedCrl **crls; | |
1122 | |
1123 kind = SEC_PKCS7ContentType (cinfo); | |
1124 switch (kind) { | |
1125 default: | |
1126 case SEC_OID_PKCS7_DATA: | |
1127 case SEC_OID_PKCS7_DIGESTED_DATA: | |
1128 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1129 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1130 return PR_FALSE; | |
1131 case SEC_OID_PKCS7_SIGNED_DATA: | |
1132 certs = cinfo->content.signedData->rawCerts; | |
1133 crls = cinfo->content.signedData->crls; | |
1134 break; | |
1135 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
1136 certs = cinfo->content.signedAndEnvelopedData->rawCerts; | |
1137 crls = cinfo->content.signedAndEnvelopedData->crls; | |
1138 break; | |
1139 } | |
1140 | |
1141 /* | |
1142 * I know this could be collapsed, but I was in a mood to be explicit. | |
1143 */ | |
1144 if (certs != NULL && certs[0] != NULL) | |
1145 return PR_TRUE; | |
1146 else if (crls != NULL && crls[0] != NULL) | |
1147 return PR_TRUE; | |
1148 else | |
1149 return PR_FALSE; | |
1150 } | |
1151 | |
1152 /* return the content length...could use GetContent, however we | |
1153 * need the encrypted content length | |
1154 */ | |
1155 PRBool | |
1156 SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen) | |
1157 { | |
1158 SECItem *item = NULL; | |
1159 | |
1160 if(cinfo == NULL) { | |
1161 return PR_TRUE; | |
1162 } | |
1163 | |
1164 switch(SEC_PKCS7ContentType(cinfo)) | |
1165 { | |
1166 case SEC_OID_PKCS7_DATA: | |
1167 item = cinfo->content.data; | |
1168 break; | |
1169 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1170 item = &cinfo->content.encryptedData->encContentInfo.encContent; | |
1171 break; | |
1172 default: | |
1173 /* add other types */ | |
1174 return PR_FALSE; | |
1175 } | |
1176 | |
1177 if(!item) { | |
1178 return PR_TRUE; | |
1179 } else if(item->len <= minLen) { | |
1180 return PR_TRUE; | |
1181 } | |
1182 | |
1183 return PR_FALSE; | |
1184 } | |
1185 | |
1186 | |
1187 PRBool | |
1188 SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo) | |
1189 { | |
1190 SECOidTag kind; | |
1191 | |
1192 kind = SEC_PKCS7ContentType (cinfo); | |
1193 switch (kind) { | |
1194 default: | |
1195 case SEC_OID_PKCS7_DATA: | |
1196 case SEC_OID_PKCS7_DIGESTED_DATA: | |
1197 case SEC_OID_PKCS7_SIGNED_DATA: | |
1198 return PR_FALSE; | |
1199 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1200 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1201 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
1202 return PR_TRUE; | |
1203 } | |
1204 } | |
1205 | |
1206 | |
1207 /* | |
1208 * If the PKCS7 content has a signature (not just *could* have a signature) | |
1209 * return true; false otherwise. This can/should be called before calling | |
1210 * VerifySignature, which will always indicate failure if no signature is | |
1211 * present, but that does not mean there even was a signature! | |
1212 * Note that the content itself can be empty (detached content was sent | |
1213 * another way); it is the presence of the signature that matters. | |
1214 */ | |
1215 PRBool | |
1216 SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo) | |
1217 { | |
1218 SECOidTag kind; | |
1219 SEC_PKCS7SignerInfo **signerinfos; | |
1220 | |
1221 kind = SEC_PKCS7ContentType (cinfo); | |
1222 switch (kind) { | |
1223 default: | |
1224 case SEC_OID_PKCS7_DATA: | |
1225 case SEC_OID_PKCS7_DIGESTED_DATA: | |
1226 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1227 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1228 return PR_FALSE; | |
1229 case SEC_OID_PKCS7_SIGNED_DATA: | |
1230 signerinfos = cinfo->content.signedData->signerInfos; | |
1231 break; | |
1232 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
1233 signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; | |
1234 break; | |
1235 } | |
1236 | |
1237 /* | |
1238 * I know this could be collapsed; but I kind of think it will get | |
1239 * more complicated before I am finished, so... | |
1240 */ | |
1241 if (signerinfos != NULL && signerinfos[0] != NULL) | |
1242 return PR_TRUE; | |
1243 else | |
1244 return PR_FALSE; | |
1245 } | |
1246 | |
1247 | |
1248 /* | |
1249 * SEC_PKCS7ContentVerifySignature | |
1250 * Look at a PKCS7 contentInfo and check if the signature is good. | |
1251 * The digest was either calculated earlier (and is stored in the | |
1252 * contentInfo itself) or is passed in via "detached_digest". | |
1253 * | |
1254 * The verification checks that the signing cert is valid and trusted | |
1255 * for the purpose specified by "certusage". | |
1256 * | |
1257 * In addition, if "keepcerts" is true, add any new certificates found | |
1258 * into our local database. | |
1259 * | |
1260 * XXX Each place which returns PR_FALSE should be sure to have a good | |
1261 * error set for inspection by the caller. Alternatively, we could create | |
1262 * an enumeration of success and each type of failure and return that | |
1263 * instead of a boolean. For now, the default in a bad situation is to | |
1264 * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE. But this should be | |
1265 * reviewed; better (more specific) errors should be possible (to distinguish | |
1266 * a signature failure from a badly-formed pkcs7 signedData, for example). | |
1267 * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE, | |
1268 * but that has a less helpful error string associated with it right now; | |
1269 * if/when that changes, review and change these as needed. | |
1270 * | |
1271 * XXX This is broken wrt signedAndEnvelopedData. In that case, the | |
1272 * message digest is doubly encrypted -- first encrypted with the signer | |
1273 * private key but then again encrypted with the bulk encryption key used | |
1274 * to encrypt the content. So before we can pass the digest to VerifyDigest, | |
1275 * we need to decrypt it with the bulk encryption key. Also, in this case, | |
1276 * there should be NO authenticatedAttributes (signerinfo->authAttr should | |
1277 * be NULL). | |
1278 */ | |
1279 static PRBool | |
1280 sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo, | |
1281 SECCertUsage certusage, | |
1282 const SECItem *detached_digest, | |
1283 HASH_HashType digest_type, | |
1284 PRBool keepcerts) | |
1285 { | |
1286 SECAlgorithmID **digestalgs, *bulkid; | |
1287 const SECItem *digest; | |
1288 SECItem **digests; | |
1289 SECItem **rawcerts; | |
1290 CERTSignedCrl **crls; | |
1291 SEC_PKCS7SignerInfo **signerinfos, *signerinfo; | |
1292 CERTCertificate *cert, **certs; | |
1293 PRBool goodsig; | |
1294 CERTCertDBHandle *certdb, *defaultdb; | |
1295 SECOidTag encTag,digestTag; | |
1296 HASH_HashType found_type; | |
1297 int i, certcount; | |
1298 SECKEYPublicKey *publickey; | |
1299 SECItem *content_type; | |
1300 PK11SymKey *sigkey; | |
1301 SECItem *encoded_stime; | |
1302 int64 stime; | |
1303 SECStatus rv; | |
1304 | |
1305 /* | |
1306 * Everything needed in order to "goto done" safely. | |
1307 */ | |
1308 goodsig = PR_FALSE; | |
1309 certcount = 0; | |
1310 cert = NULL; | |
1311 certs = NULL; | |
1312 certdb = NULL; | |
1313 defaultdb = CERT_GetDefaultCertDB(); | |
1314 publickey = NULL; | |
1315 | |
1316 if (! SEC_PKCS7ContentIsSigned(cinfo)) { | |
1317 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1318 goto done; | |
1319 } | |
1320 | |
1321 PORT_Assert (cinfo->contentTypeTag != NULL); | |
1322 | |
1323 switch (cinfo->contentTypeTag->offset) { | |
1324 default: | |
1325 case SEC_OID_PKCS7_DATA: | |
1326 case SEC_OID_PKCS7_DIGESTED_DATA: | |
1327 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1328 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1329 /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */ | |
1330 PORT_Assert (0); | |
1331 case SEC_OID_PKCS7_SIGNED_DATA: | |
1332 { | |
1333 SEC_PKCS7SignedData *sdp; | |
1334 | |
1335 sdp = cinfo->content.signedData; | |
1336 digestalgs = sdp->digestAlgorithms; | |
1337 digests = sdp->digests; | |
1338 rawcerts = sdp->rawCerts; | |
1339 crls = sdp->crls; | |
1340 signerinfos = sdp->signerInfos; | |
1341 content_type = &(sdp->contentInfo.contentType); | |
1342 sigkey = NULL; | |
1343 bulkid = NULL; | |
1344 } | |
1345 break; | |
1346 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
1347 { | |
1348 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
1349 | |
1350 saedp = cinfo->content.signedAndEnvelopedData; | |
1351 digestalgs = saedp->digestAlgorithms; | |
1352 digests = saedp->digests; | |
1353 rawcerts = saedp->rawCerts; | |
1354 crls = saedp->crls; | |
1355 signerinfos = saedp->signerInfos; | |
1356 content_type = &(saedp->encContentInfo.contentType); | |
1357 sigkey = saedp->sigKey; | |
1358 bulkid = &(saedp->encContentInfo.contentEncAlg); | |
1359 } | |
1360 break; | |
1361 } | |
1362 | |
1363 if ((signerinfos == NULL) || (signerinfos[0] == NULL)) { | |
1364 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1365 goto done; | |
1366 } | |
1367 | |
1368 /* | |
1369 * XXX Need to handle multiple signatures; checking them is easy, | |
1370 * but what should be the semantics here (like, return value)? | |
1371 */ | |
1372 if (signerinfos[1] != NULL) { | |
1373 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1374 goto done; | |
1375 } | |
1376 | |
1377 signerinfo = signerinfos[0]; | |
1378 | |
1379 /* | |
1380 * XXX I would like to just pass the issuerAndSN, along with the rawcerts | |
1381 * and crls, to some function that did all of this certificate stuff | |
1382 * (open/close the database if necessary, verifying the certs, etc.) | |
1383 * and gave me back a cert pointer if all was good. | |
1384 */ | |
1385 certdb = defaultdb; | |
1386 if (certdb == NULL) { | |
1387 goto done; | |
1388 } | |
1389 | |
1390 certcount = 0; | |
1391 if (rawcerts != NULL) { | |
1392 for (; rawcerts[certcount] != NULL; certcount++) { | |
1393 /* just counting */ | |
1394 } | |
1395 } | |
1396 | |
1397 /* | |
1398 * Note that the result of this is that each cert in "certs" | |
1399 * needs to be destroyed. | |
1400 */ | |
1401 rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs, | |
1402 keepcerts, PR_FALSE, NULL); | |
1403 if ( rv != SECSuccess ) { | |
1404 goto done; | |
1405 } | |
1406 | |
1407 /* | |
1408 * This cert will also need to be freed, but since we save it | |
1409 * in signerinfo for later, we do not want to destroy it when | |
1410 * we leave this function -- we let the clean-up of the entire | |
1411 * cinfo structure later do the destroy of this cert. | |
1412 */ | |
1413 cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN); | |
1414 if (cert == NULL) { | |
1415 goto done; | |
1416 } | |
1417 | |
1418 signerinfo->cert = cert; | |
1419 | |
1420 /* | |
1421 * Get and convert the signing time; if available, it will be used | |
1422 * both on the cert verification and for importing the sender | |
1423 * email profile. | |
1424 */ | |
1425 encoded_stime = SEC_PKCS7GetSigningTime (cinfo); | |
1426 if (encoded_stime != NULL) { | |
1427 if (DER_DecodeTimeChoice (&stime, encoded_stime) != SECSuccess) | |
1428 encoded_stime = NULL; /* conversion failed, so pretend none */ | |
1429 } | |
1430 | |
1431 /* | |
1432 * XXX This uses the signing time, if available. Additionally, we | |
1433 * might want to, if there is no signing time, get the message time | |
1434 * from the mail header itself, and use that. That would require | |
1435 * a change to our interface though, and for S/MIME callers to pass | |
1436 * in a time (and for non-S/MIME callers to pass in nothing, or | |
1437 * maybe make them pass in the current time, always?). | |
1438 */ | |
1439 if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, | |
1440 encoded_stime != NULL ? stime : PR_Now(), | |
1441 cinfo->pwfn_arg, NULL) != SECSuccess) | |
1442 { | |
1443 /* | |
1444 * XXX Give the user an option to check the signature anyway? | |
1445 * If we want to do this, need to give a way to leave and display | |
1446 * some dialog and get the answer and come back through (or do | |
1447 * the rest of what we do below elsewhere, maybe by putting it | |
1448 * in a function that we call below and could call from a dialog | |
1449 * finish handler). | |
1450 */ | |
1451 goto savecert; | |
1452 } | |
1453 | |
1454 publickey = CERT_ExtractPublicKey (cert); | |
1455 if (publickey == NULL) | |
1456 goto done; | |
1457 | |
1458 /* | |
1459 * XXX No! If digests is empty, see if we can create it now by | |
1460 * digesting the contents. This is necessary if we want to allow | |
1461 * somebody to do a simple decode (without filtering, etc.) and | |
1462 * then later call us here to do the verification. | |
1463 * OR, we can just specify that the interface to this routine | |
1464 * *requires* that the digest(s) be done before calling and either | |
1465 * stashed in the struct itself or passed in explicitly (as would | |
1466 * be done for detached contents). | |
1467 */ | |
1468 if ((digests == NULL || digests[0] == NULL) | |
1469 && (detached_digest == NULL || detached_digest->data == NULL)) | |
1470 goto done; | |
1471 | |
1472 /* | |
1473 * Find and confirm digest algorithm. | |
1474 */ | |
1475 digestTag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm)); | |
1476 | |
1477 /* make sure we understand the digest type first */ | |
1478 found_type = HASH_GetHashTypeByOidTag(digestTag); | |
1479 if ((digestTag == SEC_OID_UNKNOWN) || (found_type == HASH_AlgNULL)) { | |
1480 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1481 goto done; | |
1482 } | |
1483 | |
1484 if (detached_digest != NULL) { | |
1485 unsigned int hashLen = HASH_ResultLen(found_type); | |
1486 | |
1487 if (digest_type != found_type || | |
1488 detached_digest->len != hashLen) { | |
1489 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1490 goto done; | |
1491 } | |
1492 digest = detached_digest; | |
1493 } else { | |
1494 PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL); | |
1495 if (digestalgs == NULL || digestalgs[0] == NULL) { | |
1496 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1497 goto done; | |
1498 } | |
1499 | |
1500 /* | |
1501 * pick digest matching signerinfo->digestAlg from digests | |
1502 */ | |
1503 for (i = 0; digestalgs[i] != NULL; i++) { | |
1504 if (SECOID_FindOIDTag(&(digestalgs[i]->algorithm)) == digestTag) | |
1505 break; | |
1506 } | |
1507 if (digestalgs[i] == NULL) { | |
1508 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1509 goto done; | |
1510 } | |
1511 | |
1512 digest = digests[i]; | |
1513 } | |
1514 | |
1515 encTag = SECOID_FindOIDTag(&(signerinfo->digestEncAlg.algorithm)); | |
1516 if (encTag == SEC_OID_UNKNOWN) { | |
1517 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1518 goto done; | |
1519 } | |
1520 | |
1521 #ifndef NSS_ECC_MORE_THAN_SUITE_B | |
1522 if (encTag == SEC_OID_ANSIX962_EC_PUBLIC_KEY) { | |
1523 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1524 goto done; | |
1525 } | |
1526 #endif | |
1527 | |
1528 | |
1529 if (signerinfo->authAttr != NULL) { | |
1530 SEC_PKCS7Attribute *attr; | |
1531 SECItem *value; | |
1532 SECItem encoded_attrs; | |
1533 | |
1534 /* | |
1535 * We have a sigkey only for signedAndEnvelopedData, which is | |
1536 * not supposed to have any authenticated attributes. | |
1537 */ | |
1538 if (sigkey != NULL) { | |
1539 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1540 goto done; | |
1541 } | |
1542 | |
1543 /* | |
1544 * PKCS #7 says that if there are any authenticated attributes, | |
1545 * then there must be one for content type which matches the | |
1546 * content type of the content being signed, and there must | |
1547 * be one for message digest which matches our message digest. | |
1548 * So check these things first. | |
1549 * XXX Might be nice to have a compare-attribute-value function | |
1550 * which could collapse the following nicely. | |
1551 */ | |
1552 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
1553 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); | |
1554 value = sec_PKCS7AttributeValue (attr); | |
1555 if (value == NULL || value->len != content_type->len) { | |
1556 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1557 goto done; | |
1558 } | |
1559 if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) { | |
1560 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1561 goto done; | |
1562 } | |
1563 | |
1564 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
1565 SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); | |
1566 value = sec_PKCS7AttributeValue (attr); | |
1567 if (value == NULL || value->len != digest->len) { | |
1568 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1569 goto done; | |
1570 } | |
1571 if (PORT_Memcmp (value->data, digest->data, value->len) != 0) { | |
1572 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1573 goto done; | |
1574 } | |
1575 | |
1576 /* | |
1577 * Okay, we met the constraints of the basic attributes. | |
1578 * Now check the signature, which is based on a digest of | |
1579 * the DER-encoded authenticated attributes. So, first we | |
1580 * encode and then we digest/verify. | |
1581 */ | |
1582 encoded_attrs.data = NULL; | |
1583 encoded_attrs.len = 0; | |
1584 if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, | |
1585 &(signerinfo->authAttr)) == NULL) | |
1586 goto done; | |
1587 | |
1588 if (encoded_attrs.data == NULL || encoded_attrs.len == 0) { | |
1589 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1590 goto done; | |
1591 } | |
1592 | |
1593 | |
1594 goodsig = (PRBool)(VFY_VerifyDataDirect(encoded_attrs.data, | |
1595 encoded_attrs.len, | |
1596 publickey, &(signerinfo->encDigest), | |
1597 encTag, digestTag, NULL, | |
1598 cinfo->pwfn_arg) == SECSuccess); | |
1599 PORT_Free (encoded_attrs.data); | |
1600 } else { | |
1601 SECItem *sig; | |
1602 SECItem holder; | |
1603 SECStatus rv; | |
1604 | |
1605 /* | |
1606 * No authenticated attributes. | |
1607 * The signature is based on the plain message digest. | |
1608 */ | |
1609 | |
1610 sig = &(signerinfo->encDigest); | |
1611 if (sig->len == 0) { /* bad signature */ | |
1612 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1613 goto done; | |
1614 } | |
1615 | |
1616 if (sigkey != NULL) { | |
1617 sec_PKCS7CipherObject *decryptobj; | |
1618 unsigned int buflen; | |
1619 | |
1620 /* | |
1621 * For signedAndEnvelopedData, we first must decrypt the encrypted | |
1622 * digest with the bulk encryption key. The result is the normal | |
1623 * encrypted digest (aka the signature). | |
1624 */ | |
1625 decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid); | |
1626 if (decryptobj == NULL) | |
1627 goto done; | |
1628 | |
1629 buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE); | |
1630 PORT_Assert (buflen); | |
1631 if (buflen == 0) { /* something is wrong */ | |
1632 sec_PKCS7DestroyDecryptObject (decryptobj); | |
1633 goto done; | |
1634 } | |
1635 | |
1636 holder.data = (unsigned char*)PORT_Alloc (buflen); | |
1637 if (holder.data == NULL) { | |
1638 sec_PKCS7DestroyDecryptObject (decryptobj); | |
1639 goto done; | |
1640 } | |
1641 | |
1642 rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen, | |
1643 sig->data, sig->len, PR_TRUE); | |
1644 sec_PKCS7DestroyDecryptObject (decryptobj); | |
1645 if (rv != SECSuccess) { | |
1646 goto done; | |
1647 } | |
1648 | |
1649 sig = &holder; | |
1650 } | |
1651 | |
1652 goodsig = (PRBool)(VFY_VerifyDigestDirect(digest, publickey, sig, | |
1653 encTag, digestTag, cinfo->pwfn_arg) | |
1654 == SECSuccess); | |
1655 | |
1656 if (sigkey != NULL) { | |
1657 PORT_Assert (sig == &holder); | |
1658 PORT_ZFree (holder.data, holder.len); | |
1659 } | |
1660 } | |
1661 | |
1662 if (! goodsig) { | |
1663 /* | |
1664 * XXX Change the generic error into our specific one, because | |
1665 * in that case we get a better explanation out of the Security | |
1666 * Advisor. This is really a bug in our error strings (the | |
1667 * "generic" error has a lousy/wrong message associated with it | |
1668 * which assumes the signature verification was done for the | |
1669 * purposes of checking the issuer signature on a certificate) | |
1670 * but this is at least an easy workaround and/or in the | |
1671 * Security Advisor, which specifically checks for the error | |
1672 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation | |
1673 * in that case but does not similarly check for | |
1674 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would | |
1675 * probably say the wrong thing in the case that it *was* the | |
1676 * certificate signature check that failed during the cert | |
1677 * verification done above. Our error handling is really a mess. | |
1678 */ | |
1679 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) | |
1680 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
1681 } | |
1682 | |
1683 savecert: | |
1684 /* | |
1685 * Only save the smime profile if we are checking an email message and | |
1686 * the cert has an email address in it. | |
1687 */ | |
1688 if ( cert->emailAddr && cert->emailAddr[0] && | |
1689 ( ( certusage == certUsageEmailSigner ) || | |
1690 ( certusage == certUsageEmailRecipient ) ) ) { | |
1691 SECItem *profile = NULL; | |
1692 int save_error; | |
1693 | |
1694 /* | |
1695 * Remember the current error set because we do not care about | |
1696 * anything set by the functions we are about to call. | |
1697 */ | |
1698 save_error = PORT_GetError(); | |
1699 | |
1700 if (goodsig && (signerinfo->authAttr != NULL)) { | |
1701 /* | |
1702 * If the signature is good, then we can save the S/MIME profile, | |
1703 * if we have one. | |
1704 */ | |
1705 SEC_PKCS7Attribute *attr; | |
1706 | |
1707 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
1708 SEC_OID_PKCS9_SMIME_CAPABILITIES, | |
1709 PR_TRUE); | |
1710 profile = sec_PKCS7AttributeValue (attr); | |
1711 } | |
1712 | |
1713 rv = CERT_SaveSMimeProfile (cert, profile, encoded_stime); | |
1714 | |
1715 /* | |
1716 * Restore the saved error in case the calls above set a new | |
1717 * one that we do not actually care about. | |
1718 */ | |
1719 PORT_SetError (save_error); | |
1720 | |
1721 /* | |
1722 * XXX Failure is not indicated anywhere -- the signature | |
1723 * verification itself is unaffected by whether or not the | |
1724 * profile was successfully saved. | |
1725 */ | |
1726 } | |
1727 | |
1728 | |
1729 done: | |
1730 | |
1731 /* | |
1732 * See comment above about why we do not want to destroy cert | |
1733 * itself here. | |
1734 */ | |
1735 | |
1736 if (certs != NULL) | |
1737 CERT_DestroyCertArray (certs, certcount); | |
1738 | |
1739 if (publickey != NULL) | |
1740 SECKEY_DestroyPublicKey (publickey); | |
1741 | |
1742 return goodsig; | |
1743 } | |
1744 | |
1745 /* | |
1746 * SEC_PKCS7VerifySignature | |
1747 * Look at a PKCS7 contentInfo and check if the signature is good. | |
1748 * The verification checks that the signing cert is valid and trusted | |
1749 * for the purpose specified by "certusage". | |
1750 * | |
1751 * In addition, if "keepcerts" is true, add any new certificates found | |
1752 * into our local database. | |
1753 */ | |
1754 PRBool | |
1755 SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo, | |
1756 SECCertUsage certusage, | |
1757 PRBool keepcerts) | |
1758 { | |
1759 return sec_pkcs7_verify_signature (cinfo, certusage, | |
1760 NULL, HASH_AlgNULL, keepcerts); | |
1761 } | |
1762 | |
1763 /* | |
1764 * SEC_PKCS7VerifyDetachedSignature | |
1765 * Look at a PKCS7 contentInfo and check if the signature matches | |
1766 * a passed-in digest (calculated, supposedly, from detached contents). | |
1767 * The verification checks that the signing cert is valid and trusted | |
1768 * for the purpose specified by "certusage". | |
1769 * | |
1770 * In addition, if "keepcerts" is true, add any new certificates found | |
1771 * into our local database. | |
1772 */ | |
1773 PRBool | |
1774 SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo, | |
1775 SECCertUsage certusage, | |
1776 const SECItem *detached_digest, | |
1777 HASH_HashType digest_type, | |
1778 PRBool keepcerts) | |
1779 { | |
1780 return sec_pkcs7_verify_signature (cinfo, certusage, | |
1781 detached_digest, digest_type, | |
1782 keepcerts); | |
1783 } | |
1784 | |
1785 | |
1786 /* | |
1787 * Return the asked-for portion of the name of the signer of a PKCS7 | |
1788 * signed object. | |
1789 * | |
1790 * Returns a pointer to allocated memory, which must be freed. | |
1791 * A NULL return value is an error. | |
1792 */ | |
1793 | |
1794 #define sec_common_name 1 | |
1795 #define sec_email_address 2 | |
1796 | |
1797 static char * | |
1798 sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector) | |
1799 { | |
1800 SECOidTag kind; | |
1801 SEC_PKCS7SignerInfo **signerinfos; | |
1802 CERTCertificate *signercert; | |
1803 char *container; | |
1804 | |
1805 kind = SEC_PKCS7ContentType (cinfo); | |
1806 switch (kind) { | |
1807 default: | |
1808 case SEC_OID_PKCS7_DATA: | |
1809 case SEC_OID_PKCS7_DIGESTED_DATA: | |
1810 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1811 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1812 PORT_Assert (0); | |
1813 return NULL; | |
1814 case SEC_OID_PKCS7_SIGNED_DATA: | |
1815 { | |
1816 SEC_PKCS7SignedData *sdp; | |
1817 | |
1818 sdp = cinfo->content.signedData; | |
1819 signerinfos = sdp->signerInfos; | |
1820 } | |
1821 break; | |
1822 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
1823 { | |
1824 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
1825 | |
1826 saedp = cinfo->content.signedAndEnvelopedData; | |
1827 signerinfos = saedp->signerInfos; | |
1828 } | |
1829 break; | |
1830 } | |
1831 | |
1832 if (signerinfos == NULL || signerinfos[0] == NULL) | |
1833 return NULL; | |
1834 | |
1835 signercert = signerinfos[0]->cert; | |
1836 | |
1837 /* | |
1838 * No cert there; see if we can find one by calling verify ourselves. | |
1839 */ | |
1840 if (signercert == NULL) { | |
1841 /* | |
1842 * The cert usage does not matter in this case, because we do not | |
1843 * actually care about the verification itself, but we have to pick | |
1844 * some valid usage to pass in. | |
1845 */ | |
1846 (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner, | |
1847 NULL, HASH_AlgNULL, PR_FALSE); | |
1848 signercert = signerinfos[0]->cert; | |
1849 if (signercert == NULL) | |
1850 return NULL; | |
1851 } | |
1852 | |
1853 switch (selector) { | |
1854 case sec_common_name: | |
1855 container = CERT_GetCommonName (&signercert->subject); | |
1856 break; | |
1857 case sec_email_address: | |
1858 if(signercert->emailAddr && signercert->emailAddr[0]) { | |
1859 container = PORT_Strdup(signercert->emailAddr); | |
1860 } else { | |
1861 container = NULL; | |
1862 } | |
1863 break; | |
1864 default: | |
1865 PORT_Assert (0); | |
1866 container = NULL; | |
1867 break; | |
1868 } | |
1869 | |
1870 return container; | |
1871 } | |
1872 | |
1873 char * | |
1874 SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo) | |
1875 { | |
1876 return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name); | |
1877 } | |
1878 | |
1879 char * | |
1880 SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo) | |
1881 { | |
1882 return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address); | |
1883 } | |
1884 | |
1885 | |
1886 /* | |
1887 * Return the signing time, in UTCTime format, of a PKCS7 contentInfo. | |
1888 */ | |
1889 SECItem * | |
1890 SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo) | |
1891 { | |
1892 SEC_PKCS7SignerInfo **signerinfos; | |
1893 SEC_PKCS7Attribute *attr; | |
1894 | |
1895 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) | |
1896 return NULL; | |
1897 | |
1898 signerinfos = cinfo->content.signedData->signerInfos; | |
1899 | |
1900 /* | |
1901 * No signature, or more than one, means no deal. | |
1902 */ | |
1903 if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) | |
1904 return NULL; | |
1905 | |
1906 attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr, | |
1907 SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); | |
1908 return sec_PKCS7AttributeValue (attr); | |
1909 } | |
OLD | NEW |