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