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