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