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 creation. | |
7 * | |
8 * $Id: p7create.c,v 1.11 2012/04/25 14:50:06 gerv%gerv.net Exp $ | |
9 */ | |
10 | |
11 #include "p7local.h" | |
12 | |
13 #include "cert.h" | |
14 #include "secasn1.h" | |
15 #include "secitem.h" | |
16 #include "secoid.h" | |
17 #include "pk11func.h" | |
18 #include "prtime.h" | |
19 #include "secerr.h" | |
20 #include "secder.h" | |
21 #include "secpkcs5.h" | |
22 | |
23 const int NSS_PBE_DEFAULT_ITERATION_COUNT = 2000; /* used in p12e.c too */ | |
24 | |
25 static SECStatus | |
26 sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp, | |
27 SECOidTag kind, PRBool detached) | |
28 { | |
29 void *thing; | |
30 int version; | |
31 SECItem *versionp; | |
32 SECStatus rv; | |
33 | |
34 PORT_Assert (cinfo != NULL && poolp != NULL); | |
35 if (cinfo == NULL || poolp == NULL) | |
36 return SECFailure; | |
37 | |
38 cinfo->contentTypeTag = SECOID_FindOIDByTag (kind); | |
39 PORT_Assert (cinfo->contentTypeTag | |
40 && cinfo->contentTypeTag->offset == kind); | |
41 | |
42 rv = SECITEM_CopyItem (poolp, &(cinfo->contentType), | |
43 &(cinfo->contentTypeTag->oid)); | |
44 if (rv != SECSuccess) | |
45 return rv; | |
46 | |
47 if (detached) | |
48 return SECSuccess; | |
49 | |
50 switch (kind) { | |
51 default: | |
52 case SEC_OID_PKCS7_DATA: | |
53 thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem)); | |
54 cinfo->content.data = (SECItem*)thing; | |
55 versionp = NULL; | |
56 version = -1; | |
57 break; | |
58 case SEC_OID_PKCS7_DIGESTED_DATA: | |
59 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData)); | |
60 cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing; | |
61 versionp = &(cinfo->content.digestedData->version); | |
62 version = SEC_PKCS7_DIGESTED_DATA_VERSION; | |
63 break; | |
64 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
65 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData)); | |
66 cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing; | |
67 versionp = &(cinfo->content.encryptedData->version); | |
68 version = SEC_PKCS7_ENCRYPTED_DATA_VERSION; | |
69 break; | |
70 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
71 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData)); | |
72 cinfo->content.envelopedData = | |
73 (SEC_PKCS7EnvelopedData*)thing; | |
74 versionp = &(cinfo->content.envelopedData->version); | |
75 version = SEC_PKCS7_ENVELOPED_DATA_VERSION; | |
76 break; | |
77 case SEC_OID_PKCS7_SIGNED_DATA: | |
78 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData)); | |
79 cinfo->content.signedData = | |
80 (SEC_PKCS7SignedData*)thing; | |
81 versionp = &(cinfo->content.signedData->version); | |
82 version = SEC_PKCS7_SIGNED_DATA_VERSION; | |
83 break; | |
84 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
85 thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData)); | |
86 cinfo->content.signedAndEnvelopedData = | |
87 (SEC_PKCS7SignedAndEnvelopedData*)thing; | |
88 versionp = &(cinfo->content.signedAndEnvelopedData->version); | |
89 version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION; | |
90 break; | |
91 } | |
92 | |
93 if (thing == NULL) | |
94 return SECFailure; | |
95 | |
96 if (versionp != NULL) { | |
97 SECItem *dummy; | |
98 | |
99 PORT_Assert (version >= 0); | |
100 dummy = SEC_ASN1EncodeInteger (poolp, versionp, version); | |
101 if (dummy == NULL) | |
102 return SECFailure; | |
103 PORT_Assert (dummy == versionp); | |
104 } | |
105 | |
106 return SECSuccess; | |
107 } | |
108 | |
109 | |
110 static SEC_PKCS7ContentInfo * | |
111 sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached, | |
112 SECKEYGetPasswordKey pwfn, void *pwfn_arg) | |
113 { | |
114 SEC_PKCS7ContentInfo *cinfo; | |
115 PRArenaPool *poolp; | |
116 SECStatus rv; | |
117 | |
118 poolp = PORT_NewArena (1024); /* XXX what is right value? */ | |
119 if (poolp == NULL) | |
120 return NULL; | |
121 | |
122 cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); | |
123 if (cinfo == NULL) { | |
124 PORT_FreeArena (poolp, PR_FALSE); | |
125 return NULL; | |
126 } | |
127 | |
128 cinfo->poolp = poolp; | |
129 cinfo->pwfn = pwfn; | |
130 cinfo->pwfn_arg = pwfn_arg; | |
131 cinfo->created = PR_TRUE; | |
132 cinfo->refCount = 1; | |
133 | |
134 rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached); | |
135 if (rv != SECSuccess) { | |
136 PORT_FreeArena (poolp, PR_FALSE); | |
137 return NULL; | |
138 } | |
139 | |
140 return cinfo; | |
141 } | |
142 | |
143 | |
144 /* | |
145 * Add a signer to a PKCS7 thing, verifying the signature cert first. | |
146 * Any error returns SECFailure. | |
147 * | |
148 * XXX Right now this only adds the *first* signer. It fails if you try | |
149 * to add a second one -- this needs to be fixed. | |
150 */ | |
151 static SECStatus | |
152 sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo, | |
153 CERTCertificate * cert, | |
154 SECCertUsage certusage, | |
155 CERTCertDBHandle * certdb, | |
156 SECOidTag digestalgtag, | |
157 SECItem * digestdata) | |
158 { | |
159 SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp; | |
160 SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp; | |
161 SECItem *digest, **digests, ***digestsp; | |
162 SECItem * dummy; | |
163 void * mark; | |
164 SECStatus rv; | |
165 SECOidTag kind; | |
166 | |
167 kind = SEC_PKCS7ContentType (cinfo); | |
168 switch (kind) { | |
169 case SEC_OID_PKCS7_SIGNED_DATA: | |
170 { | |
171 SEC_PKCS7SignedData *sdp; | |
172 | |
173 sdp = cinfo->content.signedData; | |
174 digestalgsp = &(sdp->digestAlgorithms); | |
175 digestsp = &(sdp->digests); | |
176 signerinfosp = &(sdp->signerInfos); | |
177 } | |
178 break; | |
179 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
180 { | |
181 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
182 | |
183 saedp = cinfo->content.signedAndEnvelopedData; | |
184 digestalgsp = &(saedp->digestAlgorithms); | |
185 digestsp = &(saedp->digests); | |
186 signerinfosp = &(saedp->signerInfos); | |
187 } | |
188 break; | |
189 default: | |
190 return SECFailure; /* XXX set an error? */ | |
191 } | |
192 | |
193 /* | |
194 * XXX I think that CERT_VerifyCert should do this if *it* is passed | |
195 * a NULL database. | |
196 */ | |
197 if (certdb == NULL) { | |
198 certdb = CERT_GetDefaultCertDB(); | |
199 if (certdb == NULL) | |
200 return SECFailure; /* XXX set an error? */ | |
201 } | |
202 | |
203 if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), | |
204 cinfo->pwfn_arg, NULL) != SECSuccess) | |
205 { | |
206 /* XXX Did CERT_VerifyCert set an error? */ | |
207 return SECFailure; | |
208 } | |
209 | |
210 /* | |
211 * XXX This is the check that we do not already have a signer. | |
212 * This is not what we really want -- we want to allow this | |
213 * and *add* the new signer. | |
214 */ | |
215 PORT_Assert (*signerinfosp == NULL | |
216 && *digestalgsp == NULL && *digestsp == NULL); | |
217 if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL) | |
218 return SECFailure; | |
219 | |
220 mark = PORT_ArenaMark (cinfo->poolp); | |
221 | |
222 signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, | |
223 sizeof(SEC_PKCS7SignerInfo)); | |
224 if (signerinfo == NULL) { | |
225 PORT_ArenaRelease (cinfo->poolp, mark); | |
226 return SECFailure; | |
227 } | |
228 | |
229 dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version, | |
230 SEC_PKCS7_SIGNER_INFO_VERSION); | |
231 if (dummy == NULL) { | |
232 PORT_ArenaRelease (cinfo->poolp, mark); | |
233 return SECFailure; | |
234 } | |
235 PORT_Assert (dummy == &signerinfo->version); | |
236 | |
237 signerinfo->cert = CERT_DupCertificate (cert); | |
238 if (signerinfo->cert == NULL) { | |
239 PORT_ArenaRelease (cinfo->poolp, mark); | |
240 return SECFailure; | |
241 } | |
242 | |
243 signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); | |
244 if (signerinfo->issuerAndSN == NULL) { | |
245 PORT_ArenaRelease (cinfo->poolp, mark); | |
246 return SECFailure; | |
247 } | |
248 | |
249 rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg, | |
250 digestalgtag, NULL); | |
251 if (rv != SECSuccess) { | |
252 PORT_ArenaRelease (cinfo->poolp, mark); | |
253 return SECFailure; | |
254 } | |
255 | |
256 /* | |
257 * Okay, now signerinfo is all set. We just need to put it and its | |
258 * companions (another copy of the digest algorithm, and the digest | |
259 * itself if given) into the main structure. | |
260 * | |
261 * XXX If we are handling more than one signer, the following code | |
262 * needs to look through the digest algorithms already specified | |
263 * and see if the same one is there already. If it is, it does not | |
264 * need to be added again. Also, if it is there *and* the digest | |
265 * is not null, then the digest given should match the digest already | |
266 * specified -- if not, that is an error. Finally, the new signerinfo | |
267 * should be *added* to the set already found. | |
268 */ | |
269 | |
270 signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp, | |
271 2 * sizeof(SEC_PKCS7SignerInfo *)); | |
272 if (signerinfos == NULL) { | |
273 PORT_ArenaRelease (cinfo->poolp, mark); | |
274 return SECFailure; | |
275 } | |
276 signerinfos[0] = signerinfo; | |
277 signerinfos[1] = NULL; | |
278 | |
279 digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID)); | |
280 digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *)); | |
281 if (digestalg == NULL || digestalgs == NULL) { | |
282 PORT_ArenaRelease (cinfo->poolp, mark); | |
283 return SECFailure; | |
284 } | |
285 rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL); | |
286 if (rv != SECSuccess) { | |
287 PORT_ArenaRelease (cinfo->poolp, mark); | |
288 return SECFailure; | |
289 } | |
290 digestalgs[0] = digestalg; | |
291 digestalgs[1] = NULL; | |
292 | |
293 if (digestdata != NULL) { | |
294 digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem)); | |
295 digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, | |
296 2 * sizeof(SECItem *)); | |
297 if (digest == NULL || digests == NULL) { | |
298 PORT_ArenaRelease (cinfo->poolp, mark); | |
299 return SECFailure; | |
300 } | |
301 rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata); | |
302 if (rv != SECSuccess) { | |
303 PORT_ArenaRelease (cinfo->poolp, mark); | |
304 return SECFailure; | |
305 } | |
306 digests[0] = digest; | |
307 digests[1] = NULL; | |
308 } else { | |
309 digests = NULL; | |
310 } | |
311 | |
312 *signerinfosp = signerinfos; | |
313 *digestalgsp = digestalgs; | |
314 *digestsp = digests; | |
315 | |
316 PORT_ArenaUnmark(cinfo->poolp, mark); | |
317 return SECSuccess; | |
318 } | |
319 | |
320 | |
321 /* | |
322 * Helper function for creating an empty signedData. | |
323 */ | |
324 static SEC_PKCS7ContentInfo * | |
325 sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg) | |
326 { | |
327 SEC_PKCS7ContentInfo *cinfo; | |
328 SEC_PKCS7SignedData *sigd; | |
329 SECStatus rv; | |
330 | |
331 cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE, | |
332 pwfn, pwfn_arg); | |
333 if (cinfo == NULL) | |
334 return NULL; | |
335 | |
336 sigd = cinfo->content.signedData; | |
337 PORT_Assert (sigd != NULL); | |
338 | |
339 /* | |
340 * XXX Might we want to allow content types other than data? | |
341 * If so, via what interface? | |
342 */ | |
343 rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp, | |
344 SEC_OID_PKCS7_DATA, PR_TRUE); | |
345 if (rv != SECSuccess) { | |
346 SEC_PKCS7DestroyContentInfo (cinfo); | |
347 return NULL; | |
348 } | |
349 | |
350 return cinfo; | |
351 } | |
352 | |
353 | |
354 /* | |
355 * Start a PKCS7 signing context. | |
356 * | |
357 * "cert" is the cert that will be used to sign the data. It will be | |
358 * checked for validity. | |
359 * | |
360 * "certusage" describes the signing usage (e.g. certUsageEmailSigner) | |
361 * XXX Maybe SECCertUsage should be split so that our caller just says | |
362 * "email" and *we* add the "signing" part -- otherwise our caller | |
363 * could be lying about the usage; we do not want to allow encryption | |
364 * certs for signing or vice versa. | |
365 * | |
366 * "certdb" is the cert database to use for verifying the cert. | |
367 * It can be NULL if a default database is available (like in the client). | |
368 * | |
369 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). | |
370 * | |
371 * "digest" is the actual digest of the data. It must be provided in | |
372 * the case of detached data or NULL if the content will be included. | |
373 * | |
374 * The return value can be passed to functions which add things to | |
375 * it like attributes, then eventually to SEC_PKCS7Encode() or to | |
376 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to | |
377 * SEC_PKCS7DestroyContentInfo(). | |
378 * | |
379 * An error results in a return value of NULL and an error set. | |
380 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) | |
381 */ | |
382 SEC_PKCS7ContentInfo * | |
383 SEC_PKCS7CreateSignedData (CERTCertificate *cert, | |
384 SECCertUsage certusage, | |
385 CERTCertDBHandle *certdb, | |
386 SECOidTag digestalg, | |
387 SECItem *digest, | |
388 SECKEYGetPasswordKey pwfn, void *pwfn_arg) | |
389 { | |
390 SEC_PKCS7ContentInfo *cinfo; | |
391 SECStatus rv; | |
392 | |
393 cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg); | |
394 if (cinfo == NULL) | |
395 return NULL; | |
396 | |
397 rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb, | |
398 digestalg, digest); | |
399 if (rv != SECSuccess) { | |
400 SEC_PKCS7DestroyContentInfo (cinfo); | |
401 return NULL; | |
402 } | |
403 | |
404 return cinfo; | |
405 } | |
406 | |
407 | |
408 static SEC_PKCS7Attribute * | |
409 sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag, | |
410 SECItem *value, PRBool encoded) | |
411 { | |
412 SEC_PKCS7Attribute *attr; | |
413 SECItem **values; | |
414 void *mark; | |
415 | |
416 PORT_Assert (poolp != NULL); | |
417 mark = PORT_ArenaMark (poolp); | |
418 | |
419 attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, | |
420 sizeof(SEC_PKCS7Attribute)); | |
421 if (attr == NULL) | |
422 goto loser; | |
423 | |
424 attr->typeTag = SECOID_FindOIDByTag (oidtag); | |
425 if (attr->typeTag == NULL) | |
426 goto loser; | |
427 | |
428 if (SECITEM_CopyItem (poolp, &(attr->type), | |
429 &(attr->typeTag->oid)) != SECSuccess) | |
430 goto loser; | |
431 | |
432 values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *)); | |
433 if (values == NULL) | |
434 goto loser; | |
435 | |
436 if (value != NULL) { | |
437 SECItem *copy; | |
438 | |
439 copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem)); | |
440 if (copy == NULL) | |
441 goto loser; | |
442 | |
443 if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess) | |
444 goto loser; | |
445 | |
446 value = copy; | |
447 } | |
448 | |
449 values[0] = value; | |
450 values[1] = NULL; | |
451 attr->values = values; | |
452 attr->encoded = encoded; | |
453 | |
454 PORT_ArenaUnmark (poolp, mark); | |
455 return attr; | |
456 | |
457 loser: | |
458 PORT_Assert (mark != NULL); | |
459 PORT_ArenaRelease (poolp, mark); | |
460 return NULL; | |
461 } | |
462 | |
463 | |
464 static SECStatus | |
465 sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo, | |
466 SEC_PKCS7Attribute ***attrsp, | |
467 SEC_PKCS7Attribute *attr) | |
468 { | |
469 SEC_PKCS7Attribute **attrs; | |
470 SECItem *ct_value; | |
471 void *mark; | |
472 | |
473 PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); | |
474 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) | |
475 return SECFailure; | |
476 | |
477 attrs = *attrsp; | |
478 if (attrs != NULL) { | |
479 int count; | |
480 | |
481 /* | |
482 * We already have some attributes, and just need to add this | |
483 * new one. | |
484 */ | |
485 | |
486 /* | |
487 * We should already have the *required* attributes, which were | |
488 * created/added at the same time the first attribute was added. | |
489 */ | |
490 PORT_Assert (sec_PKCS7FindAttribute (attrs, | |
491 SEC_OID_PKCS9_CONTENT_TYPE, | |
492 PR_FALSE) != NULL); | |
493 PORT_Assert (sec_PKCS7FindAttribute (attrs, | |
494 SEC_OID_PKCS9_MESSAGE_DIGEST, | |
495 PR_FALSE) != NULL); | |
496 | |
497 for (count = 0; attrs[count] != NULL; count++) | |
498 ; | |
499 attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs, | |
500 (count + 1) * sizeof(SEC_PKCS7Attribute *), | |
501 (count + 2) * sizeof(SEC_PKCS7Attribute *)); | |
502 if (attrs == NULL) | |
503 return SECFailure; | |
504 | |
505 attrs[count] = attr; | |
506 attrs[count+1] = NULL; | |
507 *attrsp = attrs; | |
508 | |
509 return SECSuccess; | |
510 } | |
511 | |
512 /* | |
513 * This is the first time an attribute is going in. | |
514 * We need to create and add the required attributes, and then | |
515 * we will also add in the one our caller gave us. | |
516 */ | |
517 | |
518 /* | |
519 * There are 2 required attributes, plus the one our caller wants | |
520 * to add, plus we always end with a NULL one. Thus, four slots. | |
521 */ | |
522 attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, | |
523 4 * sizeof(SEC_PKCS7Attribute *)); | |
524 if (attrs == NULL) | |
525 return SECFailure; | |
526 | |
527 mark = PORT_ArenaMark (cinfo->poolp); | |
528 | |
529 /* | |
530 * First required attribute is the content type of the data | |
531 * being signed. | |
532 */ | |
533 ct_value = &(cinfo->content.signedData->contentInfo.contentType); | |
534 attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp, | |
535 SEC_OID_PKCS9_CONTENT_TYPE, | |
536 ct_value, PR_FALSE); | |
537 /* | |
538 * Second required attribute is the message digest of the data | |
539 * being signed; we leave the value NULL for now (just create | |
540 * the place for it to go), and the encoder will fill it in later. | |
541 */ | |
542 attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp, | |
543 SEC_OID_PKCS9_MESSAGE_DIGEST, | |
544 NULL, PR_FALSE); | |
545 if (attrs[0] == NULL || attrs[1] == NULL) { | |
546 PORT_ArenaRelease (cinfo->poolp, mark); | |
547 return SECFailure; | |
548 } | |
549 | |
550 attrs[2] = attr; | |
551 attrs[3] = NULL; | |
552 *attrsp = attrs; | |
553 | |
554 PORT_ArenaUnmark (cinfo->poolp, mark); | |
555 return SECSuccess; | |
556 } | |
557 | |
558 | |
559 /* | |
560 * Add the signing time to the authenticated (i.e. signed) attributes | |
561 * of "cinfo". This is expected to be included in outgoing signed | |
562 * messages for email (S/MIME) but is likely useful in other situations. | |
563 * | |
564 * This should only be added once; a second call will either do | |
565 * nothing or replace an old signing time with a newer one. | |
566 * | |
567 * XXX This will probably just shove the current time into "cinfo" | |
568 * but it will not actually get signed until the entire item is | |
569 * processed for encoding. Is this (expected to be small) delay okay? | |
570 * | |
571 * "cinfo" should be of type signedData (the only kind of pkcs7 data | |
572 * that is allowed authenticated attributes); SECFailure will be returned | |
573 * if it is not. | |
574 */ | |
575 SECStatus | |
576 SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo) | |
577 { | |
578 SEC_PKCS7SignerInfo **signerinfos; | |
579 SEC_PKCS7Attribute *attr; | |
580 SECItem stime; | |
581 SECStatus rv; | |
582 int si; | |
583 | |
584 PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); | |
585 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) | |
586 return SECFailure; | |
587 | |
588 signerinfos = cinfo->content.signedData->signerInfos; | |
589 | |
590 /* There has to be a signer, or it makes no sense. */ | |
591 if (signerinfos == NULL || signerinfos[0] == NULL) | |
592 return SECFailure; | |
593 | |
594 rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now()); | |
595 if (rv != SECSuccess) | |
596 return rv; | |
597 | |
598 attr = sec_pkcs7_create_attribute (cinfo->poolp, | |
599 SEC_OID_PKCS9_SIGNING_TIME, | |
600 &stime, PR_FALSE); | |
601 SECITEM_FreeItem (&stime, PR_FALSE); | |
602 | |
603 if (attr == NULL) | |
604 return SECFailure; | |
605 | |
606 rv = SECSuccess; | |
607 for (si = 0; signerinfos[si] != NULL; si++) { | |
608 SEC_PKCS7Attribute *oattr; | |
609 | |
610 oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr, | |
611 SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE); | |
612 PORT_Assert (oattr == NULL); | |
613 if (oattr != NULL) | |
614 continue; /* XXX or would it be better to replace it? */ | |
615 | |
616 rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr), | |
617 attr); | |
618 if (rv != SECSuccess) | |
619 break; /* could try to continue, but may as well give up now */ | |
620 } | |
621 | |
622 return rv; | |
623 } | |
624 | |
625 | |
626 /* | |
627 * Add the specified attribute to the authenticated (i.e. signed) attributes | |
628 * of "cinfo" -- "oidtag" describes the attribute and "value" is the | |
629 * value to be associated with it. NOTE! "value" must already be encoded; | |
630 * no interpretation of "oidtag" is done. Also, it is assumed that this | |
631 * signedData has only one signer -- if we ever need to add attributes | |
632 * when there is more than one signature, we need a way to specify *which* | |
633 * signature should get the attribute. | |
634 * | |
635 * XXX Technically, a signed attribute can have multiple values; if/when | |
636 * we ever need to support an attribute which takes multiple values, we | |
637 * either need to change this interface or create an AddSignedAttributeValue | |
638 * which can be called subsequently, and would then append a value. | |
639 * | |
640 * "cinfo" should be of type signedData (the only kind of pkcs7 data | |
641 * that is allowed authenticated attributes); SECFailure will be returned | |
642 * if it is not. | |
643 */ | |
644 SECStatus | |
645 SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo, | |
646 SECOidTag oidtag, | |
647 SECItem *value) | |
648 { | |
649 SEC_PKCS7SignerInfo **signerinfos; | |
650 SEC_PKCS7Attribute *attr; | |
651 | |
652 PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); | |
653 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) | |
654 return SECFailure; | |
655 | |
656 signerinfos = cinfo->content.signedData->signerInfos; | |
657 | |
658 /* | |
659 * No signature or more than one means no deal. | |
660 */ | |
661 if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) | |
662 return SECFailure; | |
663 | |
664 attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE); | |
665 if (attr == NULL) | |
666 return SECFailure; | |
667 | |
668 return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr); | |
669 } | |
670 | |
671 | |
672 /* | |
673 * Mark that the signer certificates and their issuing chain should | |
674 * be included in the encoded data. This is expected to be used | |
675 * in outgoing signed messages for email (S/MIME). | |
676 * | |
677 * "certdb" is the cert database to use for finding the chain. | |
678 * It can be NULL, meaning use the default database. | |
679 * | |
680 * "cinfo" should be of type signedData or signedAndEnvelopedData; | |
681 * SECFailure will be returned if it is not. | |
682 */ | |
683 SECStatus | |
684 SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo, | |
685 CERTCertDBHandle *certdb) | |
686 { | |
687 SECOidTag kind; | |
688 SEC_PKCS7SignerInfo *signerinfo, **signerinfos; | |
689 | |
690 kind = SEC_PKCS7ContentType (cinfo); | |
691 switch (kind) { | |
692 case SEC_OID_PKCS7_SIGNED_DATA: | |
693 signerinfos = cinfo->content.signedData->signerInfos; | |
694 break; | |
695 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
696 signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; | |
697 break; | |
698 default: | |
699 return SECFailure; /* XXX set an error? */ | |
700 } | |
701 | |
702 if (signerinfos == NULL) /* no signer, no certs? */ | |
703 return SECFailure; /* XXX set an error? */ | |
704 | |
705 if (certdb == NULL) { | |
706 certdb = CERT_GetDefaultCertDB(); | |
707 if (certdb == NULL) { | |
708 PORT_SetError (SEC_ERROR_BAD_DATABASE); | |
709 return SECFailure; | |
710 } | |
711 } | |
712 | |
713 /* XXX Should it be an error if we find no signerinfo or no certs? */ | |
714 while ((signerinfo = *signerinfos++) != NULL) { | |
715 if (signerinfo->cert != NULL) | |
716 /* get the cert chain. don't send the root to avoid contamination | |
717 * of old clients with a new root that they don't trust | |
718 */ | |
719 signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert, | |
720 certUsageEmailSigner, | |
721 PR_FALSE); | |
722 } | |
723 | |
724 return SECSuccess; | |
725 } | |
726 | |
727 | |
728 /* | |
729 * Helper function to add a certificate chain for inclusion in the | |
730 * bag of certificates in a signedData. | |
731 */ | |
732 static SECStatus | |
733 sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo, | |
734 CERTCertificate *cert, | |
735 CERTCertDBHandle *certdb) | |
736 { | |
737 SECOidTag kind; | |
738 CERTCertificateList *certlist, **certlists, ***certlistsp; | |
739 int count; | |
740 | |
741 kind = SEC_PKCS7ContentType (cinfo); | |
742 switch (kind) { | |
743 case SEC_OID_PKCS7_SIGNED_DATA: | |
744 { | |
745 SEC_PKCS7SignedData *sdp; | |
746 | |
747 sdp = cinfo->content.signedData; | |
748 certlistsp = &(sdp->certLists); | |
749 } | |
750 break; | |
751 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
752 { | |
753 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
754 | |
755 saedp = cinfo->content.signedAndEnvelopedData; | |
756 certlistsp = &(saedp->certLists); | |
757 } | |
758 break; | |
759 default: | |
760 return SECFailure; /* XXX set an error? */ | |
761 } | |
762 | |
763 if (certdb == NULL) { | |
764 certdb = CERT_GetDefaultCertDB(); | |
765 if (certdb == NULL) { | |
766 PORT_SetError (SEC_ERROR_BAD_DATABASE); | |
767 return SECFailure; | |
768 } | |
769 } | |
770 | |
771 certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE); | |
772 if (certlist == NULL) | |
773 return SECFailure; | |
774 | |
775 certlists = *certlistsp; | |
776 if (certlists == NULL) { | |
777 count = 0; | |
778 certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp, | |
779 2 * sizeof(CERTCertificateList *)); | |
780 } else { | |
781 for (count = 0; certlists[count] != NULL; count++) | |
782 ; | |
783 PORT_Assert (count); /* should be at least one already */ | |
784 certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, | |
785 certlists, | |
786 (count + 1) * sizeof(CERTCertificateList *), | |
787 (count + 2) * sizeof(CERTCertificateList *)); | |
788 } | |
789 | |
790 if (certlists == NULL) { | |
791 CERT_DestroyCertificateList (certlist); | |
792 return SECFailure; | |
793 } | |
794 | |
795 certlists[count] = certlist; | |
796 certlists[count + 1] = NULL; | |
797 | |
798 *certlistsp = certlists; | |
799 | |
800 return SECSuccess; | |
801 } | |
802 | |
803 | |
804 /* | |
805 * Helper function to add a certificate for inclusion in the bag of | |
806 * certificates in a signedData. | |
807 */ | |
808 static SECStatus | |
809 sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo, | |
810 CERTCertificate *cert) | |
811 { | |
812 SECOidTag kind; | |
813 CERTCertificate **certs, ***certsp; | |
814 int count; | |
815 | |
816 kind = SEC_PKCS7ContentType (cinfo); | |
817 switch (kind) { | |
818 case SEC_OID_PKCS7_SIGNED_DATA: | |
819 { | |
820 SEC_PKCS7SignedData *sdp; | |
821 | |
822 sdp = cinfo->content.signedData; | |
823 certsp = &(sdp->certs); | |
824 } | |
825 break; | |
826 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
827 { | |
828 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
829 | |
830 saedp = cinfo->content.signedAndEnvelopedData; | |
831 certsp = &(saedp->certs); | |
832 } | |
833 break; | |
834 default: | |
835 return SECFailure; /* XXX set an error? */ | |
836 } | |
837 | |
838 cert = CERT_DupCertificate (cert); | |
839 if (cert == NULL) | |
840 return SECFailure; | |
841 | |
842 certs = *certsp; | |
843 if (certs == NULL) { | |
844 count = 0; | |
845 certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, | |
846 2 * sizeof(CERTCertificate *)); | |
847 } else { | |
848 for (count = 0; certs[count] != NULL; count++) | |
849 ; | |
850 PORT_Assert (count); /* should be at least one already */ | |
851 certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs, | |
852 (count + 1) * sizeof(CERTCertificate *), | |
853 (count + 2) * sizeof(CERTCertificate *)); | |
854 } | |
855 | |
856 if (certs == NULL) { | |
857 CERT_DestroyCertificate (cert); | |
858 return SECFailure; | |
859 } | |
860 | |
861 certs[count] = cert; | |
862 certs[count + 1] = NULL; | |
863 | |
864 *certsp = certs; | |
865 | |
866 return SECSuccess; | |
867 } | |
868 | |
869 | |
870 /* | |
871 * Create a PKCS7 certs-only container. | |
872 * | |
873 * "cert" is the (first) cert that will be included. | |
874 * | |
875 * "include_chain" specifies whether the entire chain for "cert" should | |
876 * be included. | |
877 * | |
878 * "certdb" is the cert database to use for finding the chain. | |
879 * It can be NULL in when "include_chain" is false, or when meaning | |
880 * use the default database. | |
881 * | |
882 * More certs and chains can be added via AddCertificate and AddCertChain. | |
883 * | |
884 * An error results in a return value of NULL and an error set. | |
885 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) | |
886 */ | |
887 SEC_PKCS7ContentInfo * | |
888 SEC_PKCS7CreateCertsOnly (CERTCertificate *cert, | |
889 PRBool include_chain, | |
890 CERTCertDBHandle *certdb) | |
891 { | |
892 SEC_PKCS7ContentInfo *cinfo; | |
893 SECStatus rv; | |
894 | |
895 cinfo = sec_pkcs7_create_signed_data (NULL, NULL); | |
896 if (cinfo == NULL) | |
897 return NULL; | |
898 | |
899 if (include_chain) | |
900 rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb); | |
901 else | |
902 rv = sec_pkcs7_add_certificate (cinfo, cert); | |
903 | |
904 if (rv != SECSuccess) { | |
905 SEC_PKCS7DestroyContentInfo (cinfo); | |
906 return NULL; | |
907 } | |
908 | |
909 return cinfo; | |
910 } | |
911 | |
912 | |
913 /* | |
914 * Add "cert" and its entire chain to the set of certs included in "cinfo". | |
915 * | |
916 * "certdb" is the cert database to use for finding the chain. | |
917 * It can be NULL, meaning use the default database. | |
918 * | |
919 * "cinfo" should be of type signedData or signedAndEnvelopedData; | |
920 * SECFailure will be returned if it is not. | |
921 */ | |
922 SECStatus | |
923 SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo, | |
924 CERTCertificate *cert, | |
925 CERTCertDBHandle *certdb) | |
926 { | |
927 SECOidTag kind; | |
928 | |
929 kind = SEC_PKCS7ContentType (cinfo); | |
930 if (kind != SEC_OID_PKCS7_SIGNED_DATA | |
931 && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) | |
932 return SECFailure; /* XXX set an error? */ | |
933 | |
934 return sec_pkcs7_add_cert_chain (cinfo, cert, certdb); | |
935 } | |
936 | |
937 | |
938 /* | |
939 * Add "cert" to the set of certs included in "cinfo". | |
940 * | |
941 * "cinfo" should be of type signedData or signedAndEnvelopedData; | |
942 * SECFailure will be returned if it is not. | |
943 */ | |
944 SECStatus | |
945 SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert) | |
946 { | |
947 SECOidTag kind; | |
948 | |
949 kind = SEC_PKCS7ContentType (cinfo); | |
950 if (kind != SEC_OID_PKCS7_SIGNED_DATA | |
951 && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) | |
952 return SECFailure; /* XXX set an error? */ | |
953 | |
954 return sec_pkcs7_add_certificate (cinfo, cert); | |
955 } | |
956 | |
957 | |
958 static SECStatus | |
959 sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo, | |
960 PRArenaPool *poolp, | |
961 SECOidTag kind, PRBool detached, | |
962 SECOidTag encalg, int keysize) | |
963 { | |
964 SECStatus rv; | |
965 | |
966 PORT_Assert (enccinfo != NULL && poolp != NULL); | |
967 if (enccinfo == NULL || poolp == NULL) | |
968 return SECFailure; | |
969 | |
970 /* | |
971 * XXX Some day we may want to allow for other kinds. That needs | |
972 * more work and modifications to the creation interface, etc. | |
973 * For now, allow but notice callers who pass in other kinds. | |
974 * They are responsible for creating the inner type and encoding, | |
975 * if it is other than DATA. | |
976 */ | |
977 PORT_Assert (kind == SEC_OID_PKCS7_DATA); | |
978 | |
979 enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind); | |
980 PORT_Assert (enccinfo->contentTypeTag | |
981 && enccinfo->contentTypeTag->offset == kind); | |
982 | |
983 rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType), | |
984 &(enccinfo->contentTypeTag->oid)); | |
985 if (rv != SECSuccess) | |
986 return rv; | |
987 | |
988 /* Save keysize and algorithm for later. */ | |
989 enccinfo->keysize = keysize; | |
990 enccinfo->encalg = encalg; | |
991 | |
992 return SECSuccess; | |
993 } | |
994 | |
995 | |
996 /* | |
997 * Add a recipient to a PKCS7 thing, verifying their cert first. | |
998 * Any error returns SECFailure. | |
999 */ | |
1000 static SECStatus | |
1001 sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo, | |
1002 CERTCertificate *cert, | |
1003 SECCertUsage certusage, | |
1004 CERTCertDBHandle *certdb) | |
1005 { | |
1006 SECOidTag kind; | |
1007 SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp; | |
1008 SECItem *dummy; | |
1009 void *mark; | |
1010 int count; | |
1011 | |
1012 kind = SEC_PKCS7ContentType (cinfo); | |
1013 switch (kind) { | |
1014 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1015 { | |
1016 SEC_PKCS7EnvelopedData *edp; | |
1017 | |
1018 edp = cinfo->content.envelopedData; | |
1019 recipientinfosp = &(edp->recipientInfos); | |
1020 } | |
1021 break; | |
1022 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
1023 { | |
1024 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
1025 | |
1026 saedp = cinfo->content.signedAndEnvelopedData; | |
1027 recipientinfosp = &(saedp->recipientInfos); | |
1028 } | |
1029 break; | |
1030 default: | |
1031 return SECFailure; /* XXX set an error? */ | |
1032 } | |
1033 | |
1034 /* | |
1035 * XXX I think that CERT_VerifyCert should do this if *it* is passed | |
1036 * a NULL database. | |
1037 */ | |
1038 if (certdb == NULL) { | |
1039 certdb = CERT_GetDefaultCertDB(); | |
1040 if (certdb == NULL) | |
1041 return SECFailure; /* XXX set an error? */ | |
1042 } | |
1043 | |
1044 if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), | |
1045 cinfo->pwfn_arg, NULL) != SECSuccess) | |
1046 { | |
1047 /* XXX Did CERT_VerifyCert set an error? */ | |
1048 return SECFailure; | |
1049 } | |
1050 | |
1051 mark = PORT_ArenaMark (cinfo->poolp); | |
1052 | |
1053 recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp, | |
1054 sizeof(SEC_PKCS7RecipientInfo)); | |
1055 if (recipientinfo == NULL) { | |
1056 PORT_ArenaRelease (cinfo->poolp, mark); | |
1057 return SECFailure; | |
1058 } | |
1059 | |
1060 dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version, | |
1061 SEC_PKCS7_RECIPIENT_INFO_VERSION); | |
1062 if (dummy == NULL) { | |
1063 PORT_ArenaRelease (cinfo->poolp, mark); | |
1064 return SECFailure; | |
1065 } | |
1066 PORT_Assert (dummy == &recipientinfo->version); | |
1067 | |
1068 recipientinfo->cert = CERT_DupCertificate (cert); | |
1069 if (recipientinfo->cert == NULL) { | |
1070 PORT_ArenaRelease (cinfo->poolp, mark); | |
1071 return SECFailure; | |
1072 } | |
1073 | |
1074 recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); | |
1075 if (recipientinfo->issuerAndSN == NULL) { | |
1076 PORT_ArenaRelease (cinfo->poolp, mark); | |
1077 return SECFailure; | |
1078 } | |
1079 | |
1080 /* | |
1081 * Okay, now recipientinfo is all set. We just need to put it into | |
1082 * the main structure. | |
1083 * | |
1084 * If this is the first recipient, allocate a new recipientinfos array; | |
1085 * otherwise, reallocate the array, making room for the new entry. | |
1086 */ | |
1087 recipientinfos = *recipientinfosp; | |
1088 if (recipientinfos == NULL) { | |
1089 count = 0; | |
1090 recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc ( | |
1091 cinfo->poolp, | |
1092 2 * sizeof(SEC_PKCS7RecipientInfo *)); | |
1093 } else { | |
1094 for (count = 0; recipientinfos[count] != NULL; count++) | |
1095 ; | |
1096 PORT_Assert (count); /* should be at least one already */ | |
1097 recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow ( | |
1098 cinfo->poolp, recipientinfos, | |
1099 (count + 1) * sizeof(SEC_PKCS7RecipientInfo *), | |
1100 (count + 2) * sizeof(SEC_PKCS7RecipientInfo *)); | |
1101 } | |
1102 | |
1103 if (recipientinfos == NULL) { | |
1104 PORT_ArenaRelease (cinfo->poolp, mark); | |
1105 return SECFailure; | |
1106 } | |
1107 | |
1108 recipientinfos[count] = recipientinfo; | |
1109 recipientinfos[count + 1] = NULL; | |
1110 | |
1111 *recipientinfosp = recipientinfos; | |
1112 | |
1113 PORT_ArenaUnmark (cinfo->poolp, mark); | |
1114 return SECSuccess; | |
1115 } | |
1116 | |
1117 | |
1118 /* | |
1119 * Start a PKCS7 enveloping context. | |
1120 * | |
1121 * "cert" is the cert for the recipient. It will be checked for validity. | |
1122 * | |
1123 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) | |
1124 * XXX Maybe SECCertUsage should be split so that our caller just says | |
1125 * "email" and *we* add the "recipient" part -- otherwise our caller | |
1126 * could be lying about the usage; we do not want to allow encryption | |
1127 * certs for signing or vice versa. | |
1128 * | |
1129 * "certdb" is the cert database to use for verifying the cert. | |
1130 * It can be NULL if a default database is available (like in the client). | |
1131 * | |
1132 * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2). | |
1133 * | |
1134 * "keysize" specifies the bulk encryption key size, in bits. | |
1135 * | |
1136 * The return value can be passed to functions which add things to | |
1137 * it like more recipients, then eventually to SEC_PKCS7Encode() or to | |
1138 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to | |
1139 * SEC_PKCS7DestroyContentInfo(). | |
1140 * | |
1141 * An error results in a return value of NULL and an error set. | |
1142 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) | |
1143 */ | |
1144 extern SEC_PKCS7ContentInfo * | |
1145 SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert, | |
1146 SECCertUsage certusage, | |
1147 CERTCertDBHandle *certdb, | |
1148 SECOidTag encalg, | |
1149 int keysize, | |
1150 SECKEYGetPasswordKey pwfn, void *pwfn_arg) | |
1151 { | |
1152 SEC_PKCS7ContentInfo *cinfo; | |
1153 SEC_PKCS7EnvelopedData *envd; | |
1154 SECStatus rv; | |
1155 | |
1156 cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA, | |
1157 PR_FALSE, pwfn, pwfn_arg); | |
1158 if (cinfo == NULL) | |
1159 return NULL; | |
1160 | |
1161 rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); | |
1162 if (rv != SECSuccess) { | |
1163 SEC_PKCS7DestroyContentInfo (cinfo); | |
1164 return NULL; | |
1165 } | |
1166 | |
1167 envd = cinfo->content.envelopedData; | |
1168 PORT_Assert (envd != NULL); | |
1169 | |
1170 /* | |
1171 * XXX Might we want to allow content types other than data? | |
1172 * If so, via what interface? | |
1173 */ | |
1174 rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo), | |
1175 cinfo->poolp, | |
1176 SEC_OID_PKCS7_DATA, PR_FALSE, | |
1177 encalg, keysize); | |
1178 if (rv != SECSuccess) { | |
1179 SEC_PKCS7DestroyContentInfo (cinfo); | |
1180 return NULL; | |
1181 } | |
1182 | |
1183 /* XXX Anything more to do here? */ | |
1184 | |
1185 return cinfo; | |
1186 } | |
1187 | |
1188 | |
1189 /* | |
1190 * Add another recipient to an encrypted message. | |
1191 * | |
1192 * "cinfo" should be of type envelopedData or signedAndEnvelopedData; | |
1193 * SECFailure will be returned if it is not. | |
1194 * | |
1195 * "cert" is the cert for the recipient. It will be checked for validity. | |
1196 * | |
1197 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) | |
1198 * XXX Maybe SECCertUsage should be split so that our caller just says | |
1199 * "email" and *we* add the "recipient" part -- otherwise our caller | |
1200 * could be lying about the usage; we do not want to allow encryption | |
1201 * certs for signing or vice versa. | |
1202 * | |
1203 * "certdb" is the cert database to use for verifying the cert. | |
1204 * It can be NULL if a default database is available (like in the client). | |
1205 */ | |
1206 SECStatus | |
1207 SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo, | |
1208 CERTCertificate *cert, | |
1209 SECCertUsage certusage, | |
1210 CERTCertDBHandle *certdb) | |
1211 { | |
1212 return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); | |
1213 } | |
1214 | |
1215 | |
1216 /* | |
1217 * Create an empty PKCS7 data content info. | |
1218 * | |
1219 * An error results in a return value of NULL and an error set. | |
1220 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) | |
1221 */ | |
1222 SEC_PKCS7ContentInfo * | |
1223 SEC_PKCS7CreateData (void) | |
1224 { | |
1225 return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE, | |
1226 NULL, NULL); | |
1227 } | |
1228 | |
1229 | |
1230 /* | |
1231 * Create an empty PKCS7 encrypted content info. | |
1232 * | |
1233 * "algorithm" specifies the bulk encryption algorithm to use. | |
1234 * | |
1235 * An error results in a return value of NULL and an error set. | |
1236 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) | |
1237 */ | |
1238 SEC_PKCS7ContentInfo * | |
1239 SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize, | |
1240 SECKEYGetPasswordKey pwfn, void *pwfn_arg) | |
1241 { | |
1242 SEC_PKCS7ContentInfo *cinfo; | |
1243 SECAlgorithmID *algid; | |
1244 SEC_PKCS7EncryptedData *enc_data; | |
1245 SECStatus rv; | |
1246 | |
1247 cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, | |
1248 PR_FALSE, pwfn, pwfn_arg); | |
1249 if (cinfo == NULL) | |
1250 return NULL; | |
1251 | |
1252 enc_data = cinfo->content.encryptedData; | |
1253 algid = &(enc_data->encContentInfo.contentEncAlg); | |
1254 | |
1255 if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { | |
1256 rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL); | |
1257 } else { | |
1258 /* Assume password-based-encryption. | |
1259 * Note: we can't generate pkcs5v2 from this interface. | |
1260 * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting | |
1261 * non-PBE oids and assuming that they are pkcs5v2 oids, but | |
1262 * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular | |
1263 * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData | |
1264 * to create pkcs5v2 PBEs */ | |
1265 SECAlgorithmID *pbe_algid; | |
1266 pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, | |
1267 NSS_PBE_DEFAULT_ITERATION_COUNT, | |
1268 NULL); | |
1269 if (pbe_algid == NULL) { | |
1270 rv = SECFailure; | |
1271 } else { | |
1272 rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid); | |
1273 SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); | |
1274 } | |
1275 } | |
1276 | |
1277 if (rv != SECSuccess) { | |
1278 SEC_PKCS7DestroyContentInfo (cinfo); | |
1279 return NULL; | |
1280 } | |
1281 | |
1282 rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo), | |
1283 cinfo->poolp, | |
1284 SEC_OID_PKCS7_DATA, PR_FALSE, | |
1285 algorithm, keysize); | |
1286 if (rv != SECSuccess) { | |
1287 SEC_PKCS7DestroyContentInfo (cinfo); | |
1288 return NULL; | |
1289 } | |
1290 | |
1291 return cinfo; | |
1292 } | |
1293 | |
OLD | NEW |