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 encoding. | |
7 */ | |
8 | |
9 #include "p7local.h" | |
10 | |
11 #include "cert.h" | |
12 #include "cryptohi.h" | |
13 #include "keyhi.h" | |
14 #include "secasn1.h" | |
15 #include "secoid.h" | |
16 #include "secitem.h" | |
17 #include "pk11func.h" | |
18 #include "secerr.h" | |
19 #include "sechash.h" /* for HASH_GetHashObject() */ | |
20 | |
21 struct sec_pkcs7_encoder_output { | |
22 SEC_PKCS7EncoderOutputCallback outputfn; | |
23 void *outputarg; | |
24 }; | |
25 | |
26 struct SEC_PKCS7EncoderContextStr { | |
27 SEC_ASN1EncoderContext *ecx; | |
28 SEC_PKCS7ContentInfo *cinfo; | |
29 struct sec_pkcs7_encoder_output output; | |
30 sec_PKCS7CipherObject *encryptobj; | |
31 const SECHashObject *digestobj; | |
32 void *digestcx; | |
33 }; | |
34 | |
35 | |
36 /* | |
37 * The little output function that the ASN.1 encoder calls to hand | |
38 * us bytes which we in turn hand back to our caller (via the callback | |
39 * they gave us). | |
40 */ | |
41 static void | |
42 sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len, | |
43 int depth, SEC_ASN1EncodingPart data_kind) | |
44 { | |
45 struct sec_pkcs7_encoder_output *output; | |
46 | |
47 output = (struct sec_pkcs7_encoder_output*)arg; | |
48 output->outputfn (output->outputarg, buf, len); | |
49 } | |
50 | |
51 static sec_PKCS7CipherObject * | |
52 sec_pkcs7_encoder_start_encrypt (SEC_PKCS7ContentInfo *cinfo, | |
53 PK11SymKey *orig_bulkkey) | |
54 { | |
55 SECOidTag kind; | |
56 sec_PKCS7CipherObject *encryptobj; | |
57 SEC_PKCS7RecipientInfo **recipientinfos, *ri; | |
58 SEC_PKCS7EncryptedContentInfo *enccinfo; | |
59 SECKEYPublicKey *publickey = NULL; | |
60 SECKEYPrivateKey *ourPrivKey = NULL; | |
61 PK11SymKey *bulkkey; | |
62 void *mark; | |
63 int i; | |
64 PLArenaPool *arena = NULL; | |
65 | |
66 kind = SEC_PKCS7ContentType (cinfo); | |
67 switch (kind) { | |
68 default: | |
69 case SEC_OID_PKCS7_DATA: | |
70 case SEC_OID_PKCS7_DIGESTED_DATA: | |
71 case SEC_OID_PKCS7_SIGNED_DATA: | |
72 recipientinfos = NULL; | |
73 enccinfo = NULL; | |
74 break; | |
75 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
76 { | |
77 SEC_PKCS7EncryptedData *encdp; | |
78 | |
79 /* To do EncryptedData we *must* be given a bulk key. */ | |
80 PORT_Assert (orig_bulkkey != NULL); | |
81 if (orig_bulkkey == NULL) { | |
82 /* XXX error? */ | |
83 return NULL; | |
84 } | |
85 | |
86 encdp = cinfo->content.encryptedData; | |
87 recipientinfos = NULL; | |
88 enccinfo = &(encdp->encContentInfo); | |
89 } | |
90 break; | |
91 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
92 { | |
93 SEC_PKCS7EnvelopedData *envdp; | |
94 | |
95 envdp = cinfo->content.envelopedData; | |
96 recipientinfos = envdp->recipientInfos; | |
97 enccinfo = &(envdp->encContentInfo); | |
98 } | |
99 break; | |
100 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
101 { | |
102 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
103 | |
104 saedp = cinfo->content.signedAndEnvelopedData; | |
105 recipientinfos = saedp->recipientInfos; | |
106 enccinfo = &(saedp->encContentInfo); | |
107 } | |
108 break; | |
109 } | |
110 | |
111 if (enccinfo == NULL) | |
112 return NULL; | |
113 | |
114 bulkkey = orig_bulkkey; | |
115 if (bulkkey == NULL) { | |
116 CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg); | |
117 PK11SlotInfo *slot; | |
118 | |
119 | |
120 slot = PK11_GetBestSlot(type,cinfo->pwfn_arg); | |
121 if (slot == NULL) { | |
122 return NULL; | |
123 } | |
124 bulkkey = PK11_KeyGen(slot,type,NULL, enccinfo->keysize/8, | |
125 cinfo->pwfn_arg); | |
126 PK11_FreeSlot(slot); | |
127 if (bulkkey == NULL) { | |
128 return NULL; | |
129 } | |
130 } | |
131 | |
132 encryptobj = NULL; | |
133 mark = PORT_ArenaMark (cinfo->poolp); | |
134 | |
135 /* | |
136 * Encrypt the bulk key with the public key of each recipient. | |
137 */ | |
138 for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) { | |
139 CERTCertificate *cert; | |
140 SECOidTag certalgtag, encalgtag; | |
141 SECStatus rv; | |
142 int data_len; | |
143 SECItem *params = NULL; | |
144 | |
145 cert = ri->cert; | |
146 PORT_Assert (cert != NULL); | |
147 if (cert == NULL) | |
148 continue; | |
149 | |
150 /* | |
151 * XXX Want an interface that takes a cert and some data and | |
152 * fills in an algorithmID and encrypts the data with the public | |
153 * key from the cert. Or, give me two interfaces -- one which | |
154 * gets the algorithm tag from a cert (I should not have to go | |
155 * down into the subjectPublicKeyInfo myself) and another which | |
156 * takes a public key and algorithm tag and data and encrypts | |
157 * the data. Or something like that. The point is that all | |
158 * of the following hardwired RSA stuff should be done elsewhere. | |
159 */ | |
160 | |
161 certalgtag=SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm
)); | |
162 | |
163 switch (certalgtag) { | |
164 case SEC_OID_PKCS1_RSA_ENCRYPTION: | |
165 encalgtag = certalgtag; | |
166 publickey = CERT_ExtractPublicKey (cert); | |
167 if (publickey == NULL) goto loser; | |
168 | |
169 data_len = SECKEY_PublicKeyStrength(publickey); | |
170 ri->encKey.data = | |
171 (unsigned char*)PORT_ArenaAlloc(cinfo->poolp ,data_len); | |
172 ri->encKey.len = data_len; | |
173 if (ri->encKey.data == NULL) goto loser; | |
174 | |
175 rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag),publickey
, | |
176 bulkkey,&ri->encKey); | |
177 | |
178 SECKEY_DestroyPublicKey(publickey); | |
179 publickey = NULL; | |
180 if (rv != SECSuccess) goto loser; | |
181 params = NULL; /* paranoia */ | |
182 break; | |
183 default: | |
184 PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); | |
185 goto loser; | |
186 } | |
187 | |
188 rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag, | |
189 params); | |
190 if (rv != SECSuccess) | |
191 goto loser; | |
192 if (arena) PORT_FreeArena(arena,PR_FALSE); | |
193 arena = NULL; | |
194 } | |
195 | |
196 encryptobj = sec_PKCS7CreateEncryptObject (cinfo->poolp, bulkkey, | |
197 enccinfo->encalg, | |
198 &(enccinfo->contentEncAlg)); | |
199 if (encryptobj != NULL) { | |
200 PORT_ArenaUnmark (cinfo->poolp, mark); | |
201 mark = NULL; /* good one; do not want to release */ | |
202 } | |
203 /* fallthru */ | |
204 | |
205 loser: | |
206 if (arena) { | |
207 PORT_FreeArena(arena, PR_FALSE); | |
208 } | |
209 if (publickey) { | |
210 SECKEY_DestroyPublicKey(publickey); | |
211 } | |
212 if (ourPrivKey) { | |
213 SECKEY_DestroyPrivateKey(ourPrivKey); | |
214 } | |
215 if (mark != NULL) { | |
216 PORT_ArenaRelease (cinfo->poolp, mark); | |
217 } | |
218 if (orig_bulkkey == NULL) { | |
219 if (bulkkey) PK11_FreeSymKey(bulkkey); | |
220 } | |
221 | |
222 return encryptobj; | |
223 } | |
224 | |
225 | |
226 static void | |
227 sec_pkcs7_encoder_notify (void *arg, PRBool before, void *dest, int depth) | |
228 { | |
229 SEC_PKCS7EncoderContext *p7ecx; | |
230 SEC_PKCS7ContentInfo *cinfo; | |
231 SECOidTag kind; | |
232 PRBool before_content; | |
233 | |
234 /* | |
235 * We want to notice just before the content field. After fields are | |
236 * not interesting to us. | |
237 */ | |
238 if (!before) | |
239 return; | |
240 | |
241 p7ecx = (SEC_PKCS7EncoderContext*)arg; | |
242 cinfo = p7ecx->cinfo; | |
243 | |
244 before_content = PR_FALSE; | |
245 | |
246 /* | |
247 * Watch for the content field, at which point we want to instruct | |
248 * the ASN.1 encoder to start taking bytes from the buffer. | |
249 * | |
250 * XXX The following assumes the inner content type is data; | |
251 * if/when we want to handle fully nested types, this will have | |
252 * to recurse until reaching the innermost data content. | |
253 */ | |
254 kind = SEC_PKCS7ContentType (cinfo); | |
255 switch (kind) { | |
256 default: | |
257 case SEC_OID_PKCS7_DATA: | |
258 if (dest == &(cinfo->content.data)) | |
259 before_content = PR_TRUE; | |
260 break; | |
261 | |
262 case SEC_OID_PKCS7_DIGESTED_DATA: | |
263 { | |
264 SEC_PKCS7DigestedData *digd; | |
265 | |
266 digd = cinfo->content.digestedData; | |
267 if (digd == NULL) | |
268 break; | |
269 | |
270 if (dest == &(digd->contentInfo.content)) | |
271 before_content = PR_TRUE; | |
272 } | |
273 break; | |
274 | |
275 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
276 { | |
277 SEC_PKCS7EncryptedData *encd; | |
278 | |
279 encd = cinfo->content.encryptedData; | |
280 if (encd == NULL) | |
281 break; | |
282 | |
283 if (dest == &(encd->encContentInfo.encContent)) | |
284 before_content = PR_TRUE; | |
285 } | |
286 break; | |
287 | |
288 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
289 { | |
290 SEC_PKCS7EnvelopedData *envd; | |
291 | |
292 envd = cinfo->content.envelopedData; | |
293 if (envd == NULL) | |
294 break; | |
295 | |
296 if (dest == &(envd->encContentInfo.encContent)) | |
297 before_content = PR_TRUE; | |
298 } | |
299 break; | |
300 | |
301 case SEC_OID_PKCS7_SIGNED_DATA: | |
302 { | |
303 SEC_PKCS7SignedData *sigd; | |
304 | |
305 sigd = cinfo->content.signedData; | |
306 if (sigd == NULL) | |
307 break; | |
308 | |
309 if (dest == &(sigd->contentInfo.content)) | |
310 before_content = PR_TRUE; | |
311 } | |
312 break; | |
313 | |
314 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
315 { | |
316 SEC_PKCS7SignedAndEnvelopedData *saed; | |
317 | |
318 saed = cinfo->content.signedAndEnvelopedData; | |
319 if (saed == NULL) | |
320 break; | |
321 | |
322 if (dest == &(saed->encContentInfo.encContent)) | |
323 before_content = PR_TRUE; | |
324 } | |
325 break; | |
326 } | |
327 | |
328 if (before_content) { | |
329 /* | |
330 * This will cause the next SEC_ASN1EncoderUpdate to take the | |
331 * contents bytes from the passed-in buffer. | |
332 */ | |
333 SEC_ASN1EncoderSetTakeFromBuf (p7ecx->ecx); | |
334 /* | |
335 * And that is all we needed this notify function for. | |
336 */ | |
337 SEC_ASN1EncoderClearNotifyProc (p7ecx->ecx); | |
338 } | |
339 } | |
340 | |
341 | |
342 static SEC_PKCS7EncoderContext * | |
343 sec_pkcs7_encoder_start_contexts (SEC_PKCS7ContentInfo *cinfo, | |
344 PK11SymKey *bulkkey) | |
345 { | |
346 SEC_PKCS7EncoderContext *p7ecx; | |
347 SECOidTag kind; | |
348 PRBool encrypt; | |
349 SECItem **digests; | |
350 SECAlgorithmID *digestalg, **digestalgs; | |
351 | |
352 p7ecx = | |
353 (SEC_PKCS7EncoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7EncoderContext)); | |
354 if (p7ecx == NULL) | |
355 return NULL; | |
356 | |
357 digests = NULL; | |
358 digestalg = NULL; | |
359 digestalgs = NULL; | |
360 encrypt = PR_FALSE; | |
361 | |
362 kind = SEC_PKCS7ContentType (cinfo); | |
363 switch (kind) { | |
364 default: | |
365 case SEC_OID_PKCS7_DATA: | |
366 break; | |
367 case SEC_OID_PKCS7_DIGESTED_DATA: | |
368 digestalg = &(cinfo->content.digestedData->digestAlg); | |
369 break; | |
370 case SEC_OID_PKCS7_SIGNED_DATA: | |
371 digests = cinfo->content.signedData->digests; | |
372 digestalgs = cinfo->content.signedData->digestAlgorithms; | |
373 break; | |
374 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
375 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
376 encrypt = PR_TRUE; | |
377 break; | |
378 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
379 digests = cinfo->content.signedAndEnvelopedData->digests; | |
380 digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms; | |
381 encrypt = PR_TRUE; | |
382 break; | |
383 } | |
384 | |
385 if (encrypt) { | |
386 p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt (cinfo, bulkkey); | |
387 if (p7ecx->encryptobj == NULL) { | |
388 PORT_Free (p7ecx); | |
389 return NULL; | |
390 } | |
391 } | |
392 | |
393 if (digestalgs != NULL) { | |
394 if (digests != NULL) { | |
395 /* digests already created (probably for detached data) */ | |
396 digestalg = NULL; | |
397 } else { | |
398 /* | |
399 * XXX Some day we should handle multiple digests; for now, | |
400 * assume only one will be done. | |
401 */ | |
402 PORT_Assert (digestalgs[0] != NULL && digestalgs[1] == NULL); | |
403 digestalg = digestalgs[0]; | |
404 } | |
405 } | |
406 | |
407 if (digestalg != NULL) { | |
408 SECOidTag oidTag = SECOID_FindOIDTag(&(digestalg->algorithm)); | |
409 | |
410 p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag); | |
411 if (p7ecx->digestobj != NULL) { | |
412 p7ecx->digestcx = (* p7ecx->digestobj->create) (); | |
413 if (p7ecx->digestcx == NULL) | |
414 p7ecx->digestobj = NULL; | |
415 else | |
416 (* p7ecx->digestobj->begin) (p7ecx->digestcx); | |
417 } | |
418 if (p7ecx->digestobj == NULL) { | |
419 if (p7ecx->encryptobj != NULL) | |
420 sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj); | |
421 PORT_Free (p7ecx); | |
422 return NULL; | |
423 } | |
424 } | |
425 | |
426 p7ecx->cinfo = cinfo; | |
427 return p7ecx; | |
428 } | |
429 | |
430 | |
431 SEC_PKCS7EncoderContext * | |
432 SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo, | |
433 SEC_PKCS7EncoderOutputCallback outputfn, | |
434 void *outputarg, | |
435 PK11SymKey *bulkkey) | |
436 { | |
437 SEC_PKCS7EncoderContext *p7ecx; | |
438 SECStatus rv; | |
439 | |
440 p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); | |
441 if (p7ecx == NULL) | |
442 return NULL; | |
443 | |
444 p7ecx->output.outputfn = outputfn; | |
445 p7ecx->output.outputarg = outputarg; | |
446 | |
447 /* | |
448 * Initialize the BER encoder. | |
449 */ | |
450 p7ecx->ecx = SEC_ASN1EncoderStart (cinfo, sec_PKCS7ContentInfoTemplate, | |
451 sec_pkcs7_encoder_out, &(p7ecx->output)); | |
452 if (p7ecx->ecx == NULL) { | |
453 PORT_Free (p7ecx); | |
454 return NULL; | |
455 } | |
456 | |
457 /* | |
458 * Indicate that we are streaming. We will be streaming until we | |
459 * get past the contents bytes. | |
460 */ | |
461 SEC_ASN1EncoderSetStreaming (p7ecx->ecx); | |
462 | |
463 /* | |
464 * The notify function will watch for the contents field. | |
465 */ | |
466 SEC_ASN1EncoderSetNotifyProc (p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx); | |
467 | |
468 /* | |
469 * This will encode everything up to the content bytes. (The notify | |
470 * function will then cause the encoding to stop there.) Then our | |
471 * caller can start passing contents bytes to our Update, which we | |
472 * will pass along. | |
473 */ | |
474 rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0); | |
475 if (rv != SECSuccess) { | |
476 PORT_Free (p7ecx); | |
477 return NULL; | |
478 } | |
479 | |
480 return p7ecx; | |
481 } | |
482 | |
483 | |
484 /* | |
485 * XXX If/when we support nested contents, this needs to be revised. | |
486 */ | |
487 static SECStatus | |
488 sec_pkcs7_encoder_work_data (SEC_PKCS7EncoderContext *p7ecx, SECItem *dest, | |
489 const unsigned char *data, unsigned long len, | |
490 PRBool final) | |
491 { | |
492 unsigned char *buf = NULL; | |
493 SECStatus rv; | |
494 | |
495 | |
496 rv = SECSuccess; /* may as well be optimistic */ | |
497 | |
498 /* | |
499 * We should really have data to process, or we should be trying | |
500 * to finish/flush the last block. (This is an overly paranoid | |
501 * check since all callers are in this file and simple inspection | |
502 * proves they do it right. But it could find a bug in future | |
503 * modifications/development, that is why it is here.) | |
504 */ | |
505 PORT_Assert ((data != NULL && len) || final); | |
506 | |
507 /* | |
508 * Update the running digest. | |
509 * XXX This needs modification if/when we handle multiple digests. | |
510 */ | |
511 if (len && p7ecx->digestobj != NULL) { | |
512 (* p7ecx->digestobj->update) (p7ecx->digestcx, data, len); | |
513 } | |
514 | |
515 /* | |
516 * Encrypt this chunk. | |
517 */ | |
518 if (p7ecx->encryptobj != NULL) { | |
519 /* XXX the following lengths should all be longs? */ | |
520 unsigned int inlen; /* length of data being encrypted */ | |
521 unsigned int outlen; /* length of encrypted data */ | |
522 unsigned int buflen; /* length available for encrypted data */ | |
523 | |
524 inlen = len; | |
525 buflen = sec_PKCS7EncryptLength (p7ecx->encryptobj, inlen, final); | |
526 if (buflen == 0) { | |
527 /* | |
528 * No output is expected, but the input data may be buffered | |
529 * so we still have to call Encrypt. | |
530 */ | |
531 rv = sec_PKCS7Encrypt (p7ecx->encryptobj, NULL, NULL, 0, | |
532 data, inlen, final); | |
533 if (final) { | |
534 len = 0; | |
535 goto done; | |
536 } | |
537 return rv; | |
538 } | |
539 | |
540 if (dest != NULL) | |
541 buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen); | |
542 else | |
543 buf = (unsigned char*)PORT_Alloc (buflen); | |
544 | |
545 if (buf == NULL) { | |
546 rv = SECFailure; | |
547 } else { | |
548 rv = sec_PKCS7Encrypt (p7ecx->encryptobj, buf, &outlen, buflen, | |
549 data, inlen, final); | |
550 data = buf; | |
551 len = outlen; | |
552 } | |
553 if (rv != SECSuccess) { | |
554 if (final) | |
555 goto done; | |
556 return rv; | |
557 } | |
558 } | |
559 | |
560 if (p7ecx->ecx != NULL) { | |
561 /* | |
562 * Encode the contents bytes. | |
563 */ | |
564 if(len) { | |
565 rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, (const char *)data, len); | |
566 } | |
567 } | |
568 | |
569 done: | |
570 if (p7ecx->encryptobj != NULL) { | |
571 if (final) | |
572 sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj); | |
573 if (dest != NULL) { | |
574 dest->data = buf; | |
575 dest->len = len; | |
576 } else if (buf != NULL) { | |
577 PORT_Free (buf); | |
578 } | |
579 } | |
580 | |
581 if (final && p7ecx->digestobj != NULL) { | |
582 SECItem *digest, **digests, ***digestsp; | |
583 unsigned char *digdata; | |
584 SECOidTag kind; | |
585 | |
586 kind = SEC_PKCS7ContentType (p7ecx->cinfo); | |
587 switch (kind) { | |
588 default: | |
589 PORT_Assert (0); | |
590 return SECFailure; | |
591 case SEC_OID_PKCS7_DIGESTED_DATA: | |
592 digest = &(p7ecx->cinfo->content.digestedData->digest); | |
593 digestsp = NULL; | |
594 break; | |
595 case SEC_OID_PKCS7_SIGNED_DATA: | |
596 digest = NULL; | |
597 digestsp = &(p7ecx->cinfo->content.signedData->digests); | |
598 break; | |
599 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
600 digest = NULL; | |
601 digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests); | |
602 break; | |
603 } | |
604 | |
605 digdata = (unsigned char*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, | |
606 p7ecx->digestobj->length); | |
607 if (digdata == NULL) | |
608 return SECFailure; | |
609 | |
610 if (digestsp != NULL) { | |
611 PORT_Assert (digest == NULL); | |
612 | |
613 digest = (SECItem*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, | |
614 sizeof(SECItem)); | |
615 digests = (SECItem**)PORT_ArenaAlloc (p7ecx->cinfo->poolp, | |
616 2 * sizeof(SECItem *)); | |
617 if (digests == NULL || digest == NULL) | |
618 return SECFailure; | |
619 | |
620 digests[0] = digest; | |
621 digests[1] = NULL; | |
622 | |
623 *digestsp = digests; | |
624 } | |
625 | |
626 PORT_Assert (digest != NULL); | |
627 | |
628 digest->data = digdata; | |
629 digest->len = p7ecx->digestobj->length; | |
630 | |
631 (* p7ecx->digestobj->end) (p7ecx->digestcx, digest->data, | |
632 &(digest->len), digest->len); | |
633 (* p7ecx->digestobj->destroy) (p7ecx->digestcx, PR_TRUE); | |
634 } | |
635 | |
636 return rv; | |
637 } | |
638 | |
639 | |
640 SECStatus | |
641 SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx, | |
642 const char *data, unsigned long len) | |
643 { | |
644 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ | |
645 return sec_pkcs7_encoder_work_data (p7ecx, NULL, | |
646 (const unsigned char *)data, len, | |
647 PR_FALSE); | |
648 } | |
649 | |
650 static SECStatus | |
651 sec_pkcs7_encoder_sig_and_certs (SEC_PKCS7ContentInfo *cinfo, | |
652 SECKEYGetPasswordKey pwfn, void *pwfnarg) | |
653 { | |
654 SECOidTag kind; | |
655 CERTCertificate **certs; | |
656 CERTCertificateList **certlists; | |
657 SECAlgorithmID **digestalgs; | |
658 SECItem **digests; | |
659 SEC_PKCS7SignerInfo *signerinfo, **signerinfos; | |
660 SECItem **rawcerts, ***rawcertsp; | |
661 PLArenaPool *poolp; | |
662 int certcount; | |
663 int ci, cli, rci, si; | |
664 | |
665 kind = SEC_PKCS7ContentType (cinfo); | |
666 switch (kind) { | |
667 default: | |
668 case SEC_OID_PKCS7_DATA: | |
669 case SEC_OID_PKCS7_DIGESTED_DATA: | |
670 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
671 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
672 certs = NULL; | |
673 certlists = NULL; | |
674 digestalgs = NULL; | |
675 digests = NULL; | |
676 signerinfos = NULL; | |
677 rawcertsp = NULL; | |
678 break; | |
679 case SEC_OID_PKCS7_SIGNED_DATA: | |
680 { | |
681 SEC_PKCS7SignedData *sdp; | |
682 | |
683 sdp = cinfo->content.signedData; | |
684 certs = sdp->certs; | |
685 certlists = sdp->certLists; | |
686 digestalgs = sdp->digestAlgorithms; | |
687 digests = sdp->digests; | |
688 signerinfos = sdp->signerInfos; | |
689 rawcertsp = &(sdp->rawCerts); | |
690 } | |
691 break; | |
692 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
693 { | |
694 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
695 | |
696 saedp = cinfo->content.signedAndEnvelopedData; | |
697 certs = saedp->certs; | |
698 certlists = saedp->certLists; | |
699 digestalgs = saedp->digestAlgorithms; | |
700 digests = saedp->digests; | |
701 signerinfos = saedp->signerInfos; | |
702 rawcertsp = &(saedp->rawCerts); | |
703 } | |
704 break; | |
705 } | |
706 | |
707 if (certs == NULL && certlists == NULL && signerinfos == NULL) | |
708 return SECSuccess; /* nothing for us to do! */ | |
709 | |
710 poolp = cinfo->poolp; | |
711 certcount = 0; | |
712 | |
713 if (signerinfos != NULL) { | |
714 SECOidTag digestalgtag; | |
715 int di; | |
716 SECStatus rv; | |
717 CERTCertificate *cert; | |
718 SECKEYPrivateKey *privkey; | |
719 SECItem signature; | |
720 SECOidTag signalgtag; | |
721 | |
722 PORT_Assert (digestalgs != NULL && digests != NULL); | |
723 | |
724 /* | |
725 * If one fails, we bail right then. If we want to continue and | |
726 * try to do subsequent signatures, this loop, and the departures | |
727 * from it, will need to be reworked. | |
728 */ | |
729 for (si = 0; signerinfos[si] != NULL; si++) { | |
730 | |
731 signerinfo = signerinfos[si]; | |
732 | |
733 /* find right digest */ | |
734 digestalgtag = SECOID_GetAlgorithmTag (&(signerinfo->digestAlg)); | |
735 for (di = 0; digestalgs[di] != NULL; di++) { | |
736 /* XXX Should I be comparing more than the tag? */ | |
737 if (digestalgtag == SECOID_GetAlgorithmTag (digestalgs[di])) | |
738 break; | |
739 } | |
740 if (digestalgs[di] == NULL) { | |
741 /* XXX oops; do what? set an error? */ | |
742 return SECFailure; | |
743 } | |
744 PORT_Assert (digests[di] != NULL); | |
745 | |
746 cert = signerinfo->cert; | |
747 privkey = PK11_FindKeyByAnyCert (cert, pwfnarg); | |
748 if (privkey == NULL) | |
749 return SECFailure; | |
750 | |
751 /* | |
752 * XXX I think there should be a cert-level interface for this, | |
753 * so that I do not have to know about subjectPublicKeyInfo... | |
754 */ | |
755 signalgtag = SECOID_GetAlgorithmTag (&(cert->subjectPublicKeyInfo.al
gorithm)); | |
756 | |
757 if (signerinfo->authAttr != NULL) { | |
758 SEC_PKCS7Attribute *attr; | |
759 SECItem encoded_attrs; | |
760 SECItem *dummy; | |
761 SECOidTag algid; | |
762 | |
763 /* | |
764 * First, find and fill in the message digest attribute. | |
765 */ | |
766 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
767 SEC_OID_PKCS9_MESSAGE_DIGEST, | |
768 PR_TRUE); | |
769 PORT_Assert (attr != NULL); | |
770 if (attr == NULL) { | |
771 SECKEY_DestroyPrivateKey (privkey); | |
772 return SECFailure; | |
773 } | |
774 | |
775 /* | |
776 * XXX The second half of the following assertion prevents | |
777 * the encoder from being called twice on the same content. | |
778 * Either just remove the second half the assertion, or | |
779 * change the code to check if the value already there is | |
780 * the same as digests[di], whichever seems more right. | |
781 */ | |
782 PORT_Assert (attr->values != NULL && attr->values[0] == NULL); | |
783 attr->values[0] = digests[di]; | |
784 | |
785 /* | |
786 * Before encoding, reorder the attributes so that when they | |
787 * are encoded, they will be conforming DER, which is required | |
788 * to have a specific order and that is what must be used for | |
789 * the hash/signature. We do this here, rather than building | |
790 * it into EncodeAttributes, because we do not want to do | |
791 * such reordering on incoming messages (which also uses | |
792 * EncodeAttributes) or our old signatures (and other "broken" | |
793 * implementations) will not verify. So, we want to guarantee | |
794 * that we send out good DER encodings of attributes, but not | |
795 * to expect to receive them. | |
796 */ | |
797 rv = sec_PKCS7ReorderAttributes (signerinfo->authAttr); | |
798 if (rv != SECSuccess) { | |
799 SECKEY_DestroyPrivateKey (privkey); | |
800 return SECFailure; | |
801 } | |
802 | |
803 encoded_attrs.data = NULL; | |
804 encoded_attrs.len = 0; | |
805 dummy = sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, | |
806 &(signerinfo->authAttr)); | |
807 if (dummy == NULL) { | |
808 SECKEY_DestroyPrivateKey (privkey); | |
809 return SECFailure; | |
810 } | |
811 | |
812 algid = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, | |
813 digestalgtag); | |
814 if (algid == SEC_OID_UNKNOWN) { | |
815 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); | |
816 SECKEY_DestroyPrivateKey (privkey); | |
817 return SECFailure; | |
818 } | |
819 rv = SEC_SignData (&signature, | |
820 encoded_attrs.data, encoded_attrs.len, | |
821 privkey, | |
822 algid); | |
823 SECITEM_FreeItem (&encoded_attrs, PR_FALSE); | |
824 } else { | |
825 rv = SGN_Digest (privkey, digestalgtag, &signature, | |
826 digests[di]); | |
827 } | |
828 | |
829 SECKEY_DestroyPrivateKey (privkey); | |
830 | |
831 if (rv != SECSuccess) | |
832 return rv; | |
833 | |
834 rv = SECITEM_CopyItem (poolp, &(signerinfo->encDigest), &signature); | |
835 if (rv != SECSuccess) | |
836 return rv; | |
837 | |
838 SECITEM_FreeItem (&signature, PR_FALSE); | |
839 | |
840 rv = SECOID_SetAlgorithmID (poolp, &(signerinfo->digestEncAlg), | |
841 signalgtag, NULL); | |
842 if (rv != SECSuccess) | |
843 return SECFailure; | |
844 | |
845 /* | |
846 * Count the cert chain for this signer. | |
847 */ | |
848 if (signerinfo->certList != NULL) | |
849 certcount += signerinfo->certList->len; | |
850 } | |
851 } | |
852 | |
853 if (certs != NULL) { | |
854 for (ci = 0; certs[ci] != NULL; ci++) | |
855 certcount++; | |
856 } | |
857 | |
858 if (certlists != NULL) { | |
859 for (cli = 0; certlists[cli] != NULL; cli++) | |
860 certcount += certlists[cli]->len; | |
861 } | |
862 | |
863 if (certcount == 0) | |
864 return SECSuccess; /* signing done; no certs */ | |
865 | |
866 /* | |
867 * Combine all of the certs and cert chains into rawcerts. | |
868 * Note: certcount is an upper bound; we may not need that many slots | |
869 * but we will allocate anyway to avoid having to do another pass. | |
870 * (The temporary space saving is not worth it.) | |
871 */ | |
872 rawcerts = (SECItem**)PORT_ArenaAlloc (poolp, | |
873 (certcount + 1) * sizeof(SECItem *)); | |
874 if (rawcerts == NULL) | |
875 return SECFailure; | |
876 | |
877 /* | |
878 * XXX Want to check for duplicates and not add *any* cert that is | |
879 * already in the set. This will be more important when we start | |
880 * dealing with larger sets of certs, dual-key certs (signing and | |
881 * encryption), etc. For the time being we can slide by... | |
882 */ | |
883 rci = 0; | |
884 if (signerinfos != NULL) { | |
885 for (si = 0; signerinfos[si] != NULL; si++) { | |
886 signerinfo = signerinfos[si]; | |
887 for (ci = 0; ci < signerinfo->certList->len; ci++) | |
888 rawcerts[rci++] = &(signerinfo->certList->certs[ci]); | |
889 } | |
890 | |
891 } | |
892 | |
893 if (certs != NULL) { | |
894 for (ci = 0; certs[ci] != NULL; ci++) | |
895 rawcerts[rci++] = &(certs[ci]->derCert); | |
896 } | |
897 | |
898 if (certlists != NULL) { | |
899 for (cli = 0; certlists[cli] != NULL; cli++) { | |
900 for (ci = 0; ci < certlists[cli]->len; ci++) | |
901 rawcerts[rci++] = &(certlists[cli]->certs[ci]); | |
902 } | |
903 } | |
904 | |
905 rawcerts[rci] = NULL; | |
906 *rawcertsp = rawcerts; | |
907 | |
908 return SECSuccess; | |
909 } | |
910 | |
911 | |
912 SECStatus | |
913 SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx, | |
914 SECKEYGetPasswordKey pwfn, void *pwfnarg) | |
915 { | |
916 SECStatus rv; | |
917 | |
918 /* | |
919 * Flush out any remaining data. | |
920 */ | |
921 rv = sec_pkcs7_encoder_work_data (p7ecx, NULL, NULL, 0, PR_TRUE); | |
922 | |
923 /* | |
924 * Turn off streaming stuff. | |
925 */ | |
926 SEC_ASN1EncoderClearTakeFromBuf (p7ecx->ecx); | |
927 SEC_ASN1EncoderClearStreaming (p7ecx->ecx); | |
928 | |
929 if (rv != SECSuccess) | |
930 goto loser; | |
931 | |
932 rv = sec_pkcs7_encoder_sig_and_certs (p7ecx->cinfo, pwfn, pwfnarg); | |
933 if (rv != SECSuccess) | |
934 goto loser; | |
935 | |
936 rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0); | |
937 | |
938 loser: | |
939 SEC_ASN1EncoderFinish (p7ecx->ecx); | |
940 PORT_Free (p7ecx); | |
941 return rv; | |
942 } | |
943 | |
944 /* | |
945 * Abort the ASN.1 stream. Used by pkcs 12 | |
946 */ | |
947 void | |
948 SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error) | |
949 { | |
950 PORT_Assert(p7ecx); | |
951 SEC_ASN1EncoderAbort(p7ecx->ecx, error); | |
952 } | |
953 | |
954 /* | |
955 * After this routine is called, the entire PKCS7 contentInfo is ready | |
956 * to be encoded. This is used internally, but can also be called from | |
957 * elsewhere for those who want to be able to just have pointers to | |
958 * the ASN1 template for pkcs7 contentInfo built into their own encodings. | |
959 */ | |
960 SECStatus | |
961 SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo, | |
962 PK11SymKey *bulkkey, | |
963 SECKEYGetPasswordKey pwfn, | |
964 void *pwfnarg) | |
965 { | |
966 SEC_PKCS7EncoderContext *p7ecx; | |
967 SECItem *content, *enc_content; | |
968 SECStatus rv; | |
969 | |
970 p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); | |
971 if (p7ecx == NULL) | |
972 return SECFailure; | |
973 | |
974 content = SEC_PKCS7GetContent (cinfo); | |
975 | |
976 if (p7ecx->encryptobj != NULL) { | |
977 SECOidTag kind; | |
978 SEC_PKCS7EncryptedContentInfo *enccinfo; | |
979 | |
980 kind = SEC_PKCS7ContentType (p7ecx->cinfo); | |
981 switch (kind) { | |
982 default: | |
983 PORT_Assert (0); | |
984 rv = SECFailure; | |
985 goto loser; | |
986 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
987 enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo); | |
988 break; | |
989 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
990 enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo); | |
991 break; | |
992 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
993 enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encConten
tInfo); | |
994 break; | |
995 } | |
996 enc_content = &(enccinfo->encContent); | |
997 } else { | |
998 enc_content = NULL; | |
999 } | |
1000 | |
1001 if (content != NULL && content->data != NULL && content->len) { | |
1002 rv = sec_pkcs7_encoder_work_data (p7ecx, enc_content, | |
1003 content->data, content->len, PR_TRUE); | |
1004 if (rv != SECSuccess) | |
1005 goto loser; | |
1006 } | |
1007 | |
1008 rv = sec_pkcs7_encoder_sig_and_certs (cinfo, pwfn, pwfnarg); | |
1009 | |
1010 loser: | |
1011 PORT_Free (p7ecx); | |
1012 return rv; | |
1013 } | |
1014 | |
1015 | |
1016 /* | |
1017 * Encode a PKCS7 object, in one shot. All necessary components | |
1018 * of the object must already be specified. Either the data has | |
1019 * already been included (via SetContent), or the data is detached, | |
1020 * or there is no data at all (certs-only). | |
1021 * | |
1022 * "cinfo" specifies the object to be encoded. | |
1023 * | |
1024 * "outputfn" is where the encoded bytes will be passed. | |
1025 * | |
1026 * "outputarg" is an opaque argument to the above callback. | |
1027 * | |
1028 * "bulkkey" specifies the bulk encryption key to use. This argument | |
1029 * can be NULL if no encryption is being done, or if the bulk key should | |
1030 * be generated internally (usually the case for EnvelopedData but never | |
1031 * for EncryptedData, which *must* provide a bulk encryption key). | |
1032 * | |
1033 * "pwfn" is a callback for getting the password which protects the | |
1034 * private key of the signer. This argument can be NULL if it is known | |
1035 * that no signing is going to be done. | |
1036 * | |
1037 * "pwfnarg" is an opaque argument to the above callback. | |
1038 */ | |
1039 SECStatus | |
1040 SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo, | |
1041 SEC_PKCS7EncoderOutputCallback outputfn, | |
1042 void *outputarg, | |
1043 PK11SymKey *bulkkey, | |
1044 SECKEYGetPasswordKey pwfn, | |
1045 void *pwfnarg) | |
1046 { | |
1047 SECStatus rv; | |
1048 | |
1049 rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); | |
1050 if (rv == SECSuccess) { | |
1051 struct sec_pkcs7_encoder_output outputcx; | |
1052 | |
1053 outputcx.outputfn = outputfn; | |
1054 outputcx.outputarg = outputarg; | |
1055 | |
1056 rv = SEC_ASN1Encode (cinfo, sec_PKCS7ContentInfoTemplate, | |
1057 sec_pkcs7_encoder_out, &outputcx); | |
1058 } | |
1059 | |
1060 return rv; | |
1061 } | |
1062 | |
1063 | |
1064 /* | |
1065 * Encode a PKCS7 object, in one shot. All necessary components | |
1066 * of the object must already be specified. Either the data has | |
1067 * already been included (via SetContent), or the data is detached, | |
1068 * or there is no data at all (certs-only). The output, rather than | |
1069 * being passed to an output function as is done above, is all put | |
1070 * into a SECItem. | |
1071 * | |
1072 * "pool" specifies a pool from which to allocate the result. | |
1073 * It can be NULL, in which case memory is allocated generically. | |
1074 * | |
1075 * "dest" specifies a SECItem in which to put the result data. | |
1076 * It can be NULL, in which case the entire item is allocated, too. | |
1077 * | |
1078 * "cinfo" specifies the object to be encoded. | |
1079 * | |
1080 * "bulkkey" specifies the bulk encryption key to use. This argument | |
1081 * can be NULL if no encryption is being done, or if the bulk key should | |
1082 * be generated internally (usually the case for EnvelopedData but never | |
1083 * for EncryptedData, which *must* provide a bulk encryption key). | |
1084 * | |
1085 * "pwfn" is a callback for getting the password which protects the | |
1086 * private key of the signer. This argument can be NULL if it is known | |
1087 * that no signing is going to be done. | |
1088 * | |
1089 * "pwfnarg" is an opaque argument to the above callback. | |
1090 */ | |
1091 SECItem * | |
1092 SEC_PKCS7EncodeItem (PLArenaPool *pool, | |
1093 SECItem *dest, | |
1094 SEC_PKCS7ContentInfo *cinfo, | |
1095 PK11SymKey *bulkkey, | |
1096 SECKEYGetPasswordKey pwfn, | |
1097 void *pwfnarg) | |
1098 { | |
1099 SECStatus rv; | |
1100 | |
1101 rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); | |
1102 if (rv != SECSuccess) | |
1103 return NULL; | |
1104 | |
1105 return SEC_ASN1EncodeItem (pool, dest, cinfo, sec_PKCS7ContentInfoTemplate); | |
1106 } | |
1107 | |
OLD | NEW |