Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / security / nss / lib / pkcs7 / p7create.c
blob474bc2e9a329c5b200e792e860c7544e560d6cf8
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
38 * PKCS7 creation.
40 * $Id: p7create.c,v 1.9 2008/02/03 06:08:48 nelson%bolyard.com Exp $
43 #include "p7local.h"
45 #include "cert.h"
46 #include "secasn1.h"
47 #include "secitem.h"
48 #include "secoid.h"
49 #include "pk11func.h"
50 #include "prtime.h"
51 #include "secerr.h"
52 #include "secder.h"
53 #include "secpkcs5.h"
55 static SECStatus
56 sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp,
57 SECOidTag kind, PRBool detached)
59 void *thing;
60 int version;
61 SECItem *versionp;
62 SECStatus rv;
64 PORT_Assert (cinfo != NULL && poolp != NULL);
65 if (cinfo == NULL || poolp == NULL)
66 return SECFailure;
68 cinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
69 PORT_Assert (cinfo->contentTypeTag
70 && cinfo->contentTypeTag->offset == kind);
72 rv = SECITEM_CopyItem (poolp, &(cinfo->contentType),
73 &(cinfo->contentTypeTag->oid));
74 if (rv != SECSuccess)
75 return rv;
77 if (detached)
78 return SECSuccess;
80 switch (kind) {
81 default:
82 case SEC_OID_PKCS7_DATA:
83 thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem));
84 cinfo->content.data = (SECItem*)thing;
85 versionp = NULL;
86 version = -1;
87 break;
88 case SEC_OID_PKCS7_DIGESTED_DATA:
89 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData));
90 cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing;
91 versionp = &(cinfo->content.digestedData->version);
92 version = SEC_PKCS7_DIGESTED_DATA_VERSION;
93 break;
94 case SEC_OID_PKCS7_ENCRYPTED_DATA:
95 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData));
96 cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing;
97 versionp = &(cinfo->content.encryptedData->version);
98 version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
99 break;
100 case SEC_OID_PKCS7_ENVELOPED_DATA:
101 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData));
102 cinfo->content.envelopedData =
103 (SEC_PKCS7EnvelopedData*)thing;
104 versionp = &(cinfo->content.envelopedData->version);
105 version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
106 break;
107 case SEC_OID_PKCS7_SIGNED_DATA:
108 thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData));
109 cinfo->content.signedData =
110 (SEC_PKCS7SignedData*)thing;
111 versionp = &(cinfo->content.signedData->version);
112 version = SEC_PKCS7_SIGNED_DATA_VERSION;
113 break;
114 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
115 thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData));
116 cinfo->content.signedAndEnvelopedData =
117 (SEC_PKCS7SignedAndEnvelopedData*)thing;
118 versionp = &(cinfo->content.signedAndEnvelopedData->version);
119 version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
120 break;
123 if (thing == NULL)
124 return SECFailure;
126 if (versionp != NULL) {
127 SECItem *dummy;
129 PORT_Assert (version >= 0);
130 dummy = SEC_ASN1EncodeInteger (poolp, versionp, version);
131 if (dummy == NULL)
132 return SECFailure;
133 PORT_Assert (dummy == versionp);
136 return SECSuccess;
140 static SEC_PKCS7ContentInfo *
141 sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached,
142 SECKEYGetPasswordKey pwfn, void *pwfn_arg)
144 SEC_PKCS7ContentInfo *cinfo;
145 PRArenaPool *poolp;
146 SECStatus rv;
148 poolp = PORT_NewArena (1024); /* XXX what is right value? */
149 if (poolp == NULL)
150 return NULL;
152 cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
153 if (cinfo == NULL) {
154 PORT_FreeArena (poolp, PR_FALSE);
155 return NULL;
158 cinfo->poolp = poolp;
159 cinfo->pwfn = pwfn;
160 cinfo->pwfn_arg = pwfn_arg;
161 cinfo->created = PR_TRUE;
162 cinfo->refCount = 1;
164 rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached);
165 if (rv != SECSuccess) {
166 PORT_FreeArena (poolp, PR_FALSE);
167 return NULL;
170 return cinfo;
175 * Add a signer to a PKCS7 thing, verifying the signature cert first.
176 * Any error returns SECFailure.
178 * XXX Right now this only adds the *first* signer. It fails if you try
179 * to add a second one -- this needs to be fixed.
181 static SECStatus
182 sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo,
183 CERTCertificate * cert,
184 SECCertUsage certusage,
185 CERTCertDBHandle * certdb,
186 SECOidTag digestalgtag,
187 SECItem * digestdata)
189 SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
190 SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp;
191 SECItem *digest, **digests, ***digestsp;
192 SECItem * dummy;
193 void * mark;
194 SECStatus rv;
195 SECOidTag kind;
197 kind = SEC_PKCS7ContentType (cinfo);
198 switch (kind) {
199 case SEC_OID_PKCS7_SIGNED_DATA:
201 SEC_PKCS7SignedData *sdp;
203 sdp = cinfo->content.signedData;
204 digestalgsp = &(sdp->digestAlgorithms);
205 digestsp = &(sdp->digests);
206 signerinfosp = &(sdp->signerInfos);
208 break;
209 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
211 SEC_PKCS7SignedAndEnvelopedData *saedp;
213 saedp = cinfo->content.signedAndEnvelopedData;
214 digestalgsp = &(saedp->digestAlgorithms);
215 digestsp = &(saedp->digests);
216 signerinfosp = &(saedp->signerInfos);
218 break;
219 default:
220 return SECFailure; /* XXX set an error? */
224 * XXX I think that CERT_VerifyCert should do this if *it* is passed
225 * a NULL database.
227 if (certdb == NULL) {
228 certdb = CERT_GetDefaultCertDB();
229 if (certdb == NULL)
230 return SECFailure; /* XXX set an error? */
233 if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
234 cinfo->pwfn_arg, NULL) != SECSuccess)
236 /* XXX Did CERT_VerifyCert set an error? */
237 return SECFailure;
241 * XXX This is the check that we do not already have a signer.
242 * This is not what we really want -- we want to allow this
243 * and *add* the new signer.
245 PORT_Assert (*signerinfosp == NULL
246 && *digestalgsp == NULL && *digestsp == NULL);
247 if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
248 return SECFailure;
250 mark = PORT_ArenaMark (cinfo->poolp);
252 signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp,
253 sizeof(SEC_PKCS7SignerInfo));
254 if (signerinfo == NULL) {
255 PORT_ArenaRelease (cinfo->poolp, mark);
256 return SECFailure;
259 dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version,
260 SEC_PKCS7_SIGNER_INFO_VERSION);
261 if (dummy == NULL) {
262 PORT_ArenaRelease (cinfo->poolp, mark);
263 return SECFailure;
265 PORT_Assert (dummy == &signerinfo->version);
267 signerinfo->cert = CERT_DupCertificate (cert);
268 if (signerinfo->cert == NULL) {
269 PORT_ArenaRelease (cinfo->poolp, mark);
270 return SECFailure;
273 signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
274 if (signerinfo->issuerAndSN == NULL) {
275 PORT_ArenaRelease (cinfo->poolp, mark);
276 return SECFailure;
279 rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg,
280 digestalgtag, NULL);
281 if (rv != SECSuccess) {
282 PORT_ArenaRelease (cinfo->poolp, mark);
283 return SECFailure;
287 * Okay, now signerinfo is all set. We just need to put it and its
288 * companions (another copy of the digest algorithm, and the digest
289 * itself if given) into the main structure.
291 * XXX If we are handling more than one signer, the following code
292 * needs to look through the digest algorithms already specified
293 * and see if the same one is there already. If it is, it does not
294 * need to be added again. Also, if it is there *and* the digest
295 * is not null, then the digest given should match the digest already
296 * specified -- if not, that is an error. Finally, the new signerinfo
297 * should be *added* to the set already found.
300 signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp,
301 2 * sizeof(SEC_PKCS7SignerInfo *));
302 if (signerinfos == NULL) {
303 PORT_ArenaRelease (cinfo->poolp, mark);
304 return SECFailure;
306 signerinfos[0] = signerinfo;
307 signerinfos[1] = NULL;
309 digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID));
310 digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
311 if (digestalg == NULL || digestalgs == NULL) {
312 PORT_ArenaRelease (cinfo->poolp, mark);
313 return SECFailure;
315 rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL);
316 if (rv != SECSuccess) {
317 PORT_ArenaRelease (cinfo->poolp, mark);
318 return SECFailure;
320 digestalgs[0] = digestalg;
321 digestalgs[1] = NULL;
323 if (digestdata != NULL) {
324 digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem));
325 digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp,
326 2 * sizeof(SECItem *));
327 if (digest == NULL || digests == NULL) {
328 PORT_ArenaRelease (cinfo->poolp, mark);
329 return SECFailure;
331 rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata);
332 if (rv != SECSuccess) {
333 PORT_ArenaRelease (cinfo->poolp, mark);
334 return SECFailure;
336 digests[0] = digest;
337 digests[1] = NULL;
338 } else {
339 digests = NULL;
342 *signerinfosp = signerinfos;
343 *digestalgsp = digestalgs;
344 *digestsp = digests;
346 PORT_ArenaUnmark(cinfo->poolp, mark);
347 return SECSuccess;
352 * Helper function for creating an empty signedData.
354 static SEC_PKCS7ContentInfo *
355 sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg)
357 SEC_PKCS7ContentInfo *cinfo;
358 SEC_PKCS7SignedData *sigd;
359 SECStatus rv;
361 cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
362 pwfn, pwfn_arg);
363 if (cinfo == NULL)
364 return NULL;
366 sigd = cinfo->content.signedData;
367 PORT_Assert (sigd != NULL);
370 * XXX Might we want to allow content types other than data?
371 * If so, via what interface?
373 rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp,
374 SEC_OID_PKCS7_DATA, PR_TRUE);
375 if (rv != SECSuccess) {
376 SEC_PKCS7DestroyContentInfo (cinfo);
377 return NULL;
380 return cinfo;
385 * Start a PKCS7 signing context.
387 * "cert" is the cert that will be used to sign the data. It will be
388 * checked for validity.
390 * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
391 * XXX Maybe SECCertUsage should be split so that our caller just says
392 * "email" and *we* add the "signing" part -- otherwise our caller
393 * could be lying about the usage; we do not want to allow encryption
394 * certs for signing or vice versa.
396 * "certdb" is the cert database to use for verifying the cert.
397 * It can be NULL if a default database is available (like in the client).
399 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
401 * "digest" is the actual digest of the data. It must be provided in
402 * the case of detached data or NULL if the content will be included.
404 * The return value can be passed to functions which add things to
405 * it like attributes, then eventually to SEC_PKCS7Encode() or to
406 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
407 * SEC_PKCS7DestroyContentInfo().
409 * An error results in a return value of NULL and an error set.
410 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
412 SEC_PKCS7ContentInfo *
413 SEC_PKCS7CreateSignedData (CERTCertificate *cert,
414 SECCertUsage certusage,
415 CERTCertDBHandle *certdb,
416 SECOidTag digestalg,
417 SECItem *digest,
418 SECKEYGetPasswordKey pwfn, void *pwfn_arg)
420 SEC_PKCS7ContentInfo *cinfo;
421 SECStatus rv;
423 cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg);
424 if (cinfo == NULL)
425 return NULL;
427 rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb,
428 digestalg, digest);
429 if (rv != SECSuccess) {
430 SEC_PKCS7DestroyContentInfo (cinfo);
431 return NULL;
434 return cinfo;
438 static SEC_PKCS7Attribute *
439 sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag,
440 SECItem *value, PRBool encoded)
442 SEC_PKCS7Attribute *attr;
443 SECItem **values;
444 void *mark;
446 PORT_Assert (poolp != NULL);
447 mark = PORT_ArenaMark (poolp);
449 attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp,
450 sizeof(SEC_PKCS7Attribute));
451 if (attr == NULL)
452 goto loser;
454 attr->typeTag = SECOID_FindOIDByTag (oidtag);
455 if (attr->typeTag == NULL)
456 goto loser;
458 if (SECITEM_CopyItem (poolp, &(attr->type),
459 &(attr->typeTag->oid)) != SECSuccess)
460 goto loser;
462 values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *));
463 if (values == NULL)
464 goto loser;
466 if (value != NULL) {
467 SECItem *copy;
469 copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem));
470 if (copy == NULL)
471 goto loser;
473 if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess)
474 goto loser;
476 value = copy;
479 values[0] = value;
480 values[1] = NULL;
481 attr->values = values;
482 attr->encoded = encoded;
484 PORT_ArenaUnmark (poolp, mark);
485 return attr;
487 loser:
488 PORT_Assert (mark != NULL);
489 PORT_ArenaRelease (poolp, mark);
490 return NULL;
494 static SECStatus
495 sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo,
496 SEC_PKCS7Attribute ***attrsp,
497 SEC_PKCS7Attribute *attr)
499 SEC_PKCS7Attribute **attrs;
500 SECItem *ct_value;
501 void *mark;
503 PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
504 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
505 return SECFailure;
507 attrs = *attrsp;
508 if (attrs != NULL) {
509 int count;
512 * We already have some attributes, and just need to add this
513 * new one.
517 * We should already have the *required* attributes, which were
518 * created/added at the same time the first attribute was added.
520 PORT_Assert (sec_PKCS7FindAttribute (attrs,
521 SEC_OID_PKCS9_CONTENT_TYPE,
522 PR_FALSE) != NULL);
523 PORT_Assert (sec_PKCS7FindAttribute (attrs,
524 SEC_OID_PKCS9_MESSAGE_DIGEST,
525 PR_FALSE) != NULL);
527 for (count = 0; attrs[count] != NULL; count++)
529 attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs,
530 (count + 1) * sizeof(SEC_PKCS7Attribute *),
531 (count + 2) * sizeof(SEC_PKCS7Attribute *));
532 if (attrs == NULL)
533 return SECFailure;
535 attrs[count] = attr;
536 attrs[count+1] = NULL;
537 *attrsp = attrs;
539 return SECSuccess;
543 * This is the first time an attribute is going in.
544 * We need to create and add the required attributes, and then
545 * we will also add in the one our caller gave us.
549 * There are 2 required attributes, plus the one our caller wants
550 * to add, plus we always end with a NULL one. Thus, four slots.
552 attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp,
553 4 * sizeof(SEC_PKCS7Attribute *));
554 if (attrs == NULL)
555 return SECFailure;
557 mark = PORT_ArenaMark (cinfo->poolp);
560 * First required attribute is the content type of the data
561 * being signed.
563 ct_value = &(cinfo->content.signedData->contentInfo.contentType);
564 attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp,
565 SEC_OID_PKCS9_CONTENT_TYPE,
566 ct_value, PR_FALSE);
568 * Second required attribute is the message digest of the data
569 * being signed; we leave the value NULL for now (just create
570 * the place for it to go), and the encoder will fill it in later.
572 attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp,
573 SEC_OID_PKCS9_MESSAGE_DIGEST,
574 NULL, PR_FALSE);
575 if (attrs[0] == NULL || attrs[1] == NULL) {
576 PORT_ArenaRelease (cinfo->poolp, mark);
577 return SECFailure;
580 attrs[2] = attr;
581 attrs[3] = NULL;
582 *attrsp = attrs;
584 PORT_ArenaUnmark (cinfo->poolp, mark);
585 return SECSuccess;
590 * Add the signing time to the authenticated (i.e. signed) attributes
591 * of "cinfo". This is expected to be included in outgoing signed
592 * messages for email (S/MIME) but is likely useful in other situations.
594 * This should only be added once; a second call will either do
595 * nothing or replace an old signing time with a newer one.
597 * XXX This will probably just shove the current time into "cinfo"
598 * but it will not actually get signed until the entire item is
599 * processed for encoding. Is this (expected to be small) delay okay?
601 * "cinfo" should be of type signedData (the only kind of pkcs7 data
602 * that is allowed authenticated attributes); SECFailure will be returned
603 * if it is not.
605 SECStatus
606 SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo)
608 SEC_PKCS7SignerInfo **signerinfos;
609 SEC_PKCS7Attribute *attr;
610 SECItem stime;
611 SECStatus rv;
612 int si;
614 PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
615 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
616 return SECFailure;
618 signerinfos = cinfo->content.signedData->signerInfos;
620 /* There has to be a signer, or it makes no sense. */
621 if (signerinfos == NULL || signerinfos[0] == NULL)
622 return SECFailure;
624 rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
625 if (rv != SECSuccess)
626 return rv;
628 attr = sec_pkcs7_create_attribute (cinfo->poolp,
629 SEC_OID_PKCS9_SIGNING_TIME,
630 &stime, PR_FALSE);
631 SECITEM_FreeItem (&stime, PR_FALSE);
633 if (attr == NULL)
634 return SECFailure;
636 rv = SECSuccess;
637 for (si = 0; signerinfos[si] != NULL; si++) {
638 SEC_PKCS7Attribute *oattr;
640 oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr,
641 SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
642 PORT_Assert (oattr == NULL);
643 if (oattr != NULL)
644 continue; /* XXX or would it be better to replace it? */
646 rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr),
647 attr);
648 if (rv != SECSuccess)
649 break; /* could try to continue, but may as well give up now */
652 return rv;
657 * Add the specified attribute to the authenticated (i.e. signed) attributes
658 * of "cinfo" -- "oidtag" describes the attribute and "value" is the
659 * value to be associated with it. NOTE! "value" must already be encoded;
660 * no interpretation of "oidtag" is done. Also, it is assumed that this
661 * signedData has only one signer -- if we ever need to add attributes
662 * when there is more than one signature, we need a way to specify *which*
663 * signature should get the attribute.
665 * XXX Technically, a signed attribute can have multiple values; if/when
666 * we ever need to support an attribute which takes multiple values, we
667 * either need to change this interface or create an AddSignedAttributeValue
668 * which can be called subsequently, and would then append a value.
670 * "cinfo" should be of type signedData (the only kind of pkcs7 data
671 * that is allowed authenticated attributes); SECFailure will be returned
672 * if it is not.
674 SECStatus
675 SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo,
676 SECOidTag oidtag,
677 SECItem *value)
679 SEC_PKCS7SignerInfo **signerinfos;
680 SEC_PKCS7Attribute *attr;
682 PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
683 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
684 return SECFailure;
686 signerinfos = cinfo->content.signedData->signerInfos;
689 * No signature or more than one means no deal.
691 if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
692 return SECFailure;
694 attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE);
695 if (attr == NULL)
696 return SECFailure;
698 return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr);
703 * Mark that the signer certificates and their issuing chain should
704 * be included in the encoded data. This is expected to be used
705 * in outgoing signed messages for email (S/MIME).
707 * "certdb" is the cert database to use for finding the chain.
708 * It can be NULL, meaning use the default database.
710 * "cinfo" should be of type signedData or signedAndEnvelopedData;
711 * SECFailure will be returned if it is not.
713 SECStatus
714 SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo,
715 CERTCertDBHandle *certdb)
717 SECOidTag kind;
718 SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
720 kind = SEC_PKCS7ContentType (cinfo);
721 switch (kind) {
722 case SEC_OID_PKCS7_SIGNED_DATA:
723 signerinfos = cinfo->content.signedData->signerInfos;
724 break;
725 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
726 signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
727 break;
728 default:
729 return SECFailure; /* XXX set an error? */
732 if (signerinfos == NULL) /* no signer, no certs? */
733 return SECFailure; /* XXX set an error? */
735 if (certdb == NULL) {
736 certdb = CERT_GetDefaultCertDB();
737 if (certdb == NULL) {
738 PORT_SetError (SEC_ERROR_BAD_DATABASE);
739 return SECFailure;
743 /* XXX Should it be an error if we find no signerinfo or no certs? */
744 while ((signerinfo = *signerinfos++) != NULL) {
745 if (signerinfo->cert != NULL)
746 /* get the cert chain. don't send the root to avoid contamination
747 * of old clients with a new root that they don't trust
749 signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert,
750 certUsageEmailSigner,
751 PR_FALSE);
754 return SECSuccess;
759 * Helper function to add a certificate chain for inclusion in the
760 * bag of certificates in a signedData.
762 static SECStatus
763 sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo,
764 CERTCertificate *cert,
765 CERTCertDBHandle *certdb)
767 SECOidTag kind;
768 CERTCertificateList *certlist, **certlists, ***certlistsp;
769 int count;
771 kind = SEC_PKCS7ContentType (cinfo);
772 switch (kind) {
773 case SEC_OID_PKCS7_SIGNED_DATA:
775 SEC_PKCS7SignedData *sdp;
777 sdp = cinfo->content.signedData;
778 certlistsp = &(sdp->certLists);
780 break;
781 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
783 SEC_PKCS7SignedAndEnvelopedData *saedp;
785 saedp = cinfo->content.signedAndEnvelopedData;
786 certlistsp = &(saedp->certLists);
788 break;
789 default:
790 return SECFailure; /* XXX set an error? */
793 if (certdb == NULL) {
794 certdb = CERT_GetDefaultCertDB();
795 if (certdb == NULL) {
796 PORT_SetError (SEC_ERROR_BAD_DATABASE);
797 return SECFailure;
801 certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE);
802 if (certlist == NULL)
803 return SECFailure;
805 certlists = *certlistsp;
806 if (certlists == NULL) {
807 count = 0;
808 certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp,
809 2 * sizeof(CERTCertificateList *));
810 } else {
811 for (count = 0; certlists[count] != NULL; count++)
813 PORT_Assert (count); /* should be at least one already */
814 certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp,
815 certlists,
816 (count + 1) * sizeof(CERTCertificateList *),
817 (count + 2) * sizeof(CERTCertificateList *));
820 if (certlists == NULL) {
821 CERT_DestroyCertificateList (certlist);
822 return SECFailure;
825 certlists[count] = certlist;
826 certlists[count + 1] = NULL;
828 *certlistsp = certlists;
830 return SECSuccess;
835 * Helper function to add a certificate for inclusion in the bag of
836 * certificates in a signedData.
838 static SECStatus
839 sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo,
840 CERTCertificate *cert)
842 SECOidTag kind;
843 CERTCertificate **certs, ***certsp;
844 int count;
846 kind = SEC_PKCS7ContentType (cinfo);
847 switch (kind) {
848 case SEC_OID_PKCS7_SIGNED_DATA:
850 SEC_PKCS7SignedData *sdp;
852 sdp = cinfo->content.signedData;
853 certsp = &(sdp->certs);
855 break;
856 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
858 SEC_PKCS7SignedAndEnvelopedData *saedp;
860 saedp = cinfo->content.signedAndEnvelopedData;
861 certsp = &(saedp->certs);
863 break;
864 default:
865 return SECFailure; /* XXX set an error? */
868 cert = CERT_DupCertificate (cert);
869 if (cert == NULL)
870 return SECFailure;
872 certs = *certsp;
873 if (certs == NULL) {
874 count = 0;
875 certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp,
876 2 * sizeof(CERTCertificate *));
877 } else {
878 for (count = 0; certs[count] != NULL; count++)
880 PORT_Assert (count); /* should be at least one already */
881 certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs,
882 (count + 1) * sizeof(CERTCertificate *),
883 (count + 2) * sizeof(CERTCertificate *));
886 if (certs == NULL) {
887 CERT_DestroyCertificate (cert);
888 return SECFailure;
891 certs[count] = cert;
892 certs[count + 1] = NULL;
894 *certsp = certs;
896 return SECSuccess;
901 * Create a PKCS7 certs-only container.
903 * "cert" is the (first) cert that will be included.
905 * "include_chain" specifies whether the entire chain for "cert" should
906 * be included.
908 * "certdb" is the cert database to use for finding the chain.
909 * It can be NULL in when "include_chain" is false, or when meaning
910 * use the default database.
912 * More certs and chains can be added via AddCertificate and AddCertChain.
914 * An error results in a return value of NULL and an error set.
915 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
917 SEC_PKCS7ContentInfo *
918 SEC_PKCS7CreateCertsOnly (CERTCertificate *cert,
919 PRBool include_chain,
920 CERTCertDBHandle *certdb)
922 SEC_PKCS7ContentInfo *cinfo;
923 SECStatus rv;
925 cinfo = sec_pkcs7_create_signed_data (NULL, NULL);
926 if (cinfo == NULL)
927 return NULL;
929 if (include_chain)
930 rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
931 else
932 rv = sec_pkcs7_add_certificate (cinfo, cert);
934 if (rv != SECSuccess) {
935 SEC_PKCS7DestroyContentInfo (cinfo);
936 return NULL;
939 return cinfo;
944 * Add "cert" and its entire chain to the set of certs included in "cinfo".
946 * "certdb" is the cert database to use for finding the chain.
947 * It can be NULL, meaning use the default database.
949 * "cinfo" should be of type signedData or signedAndEnvelopedData;
950 * SECFailure will be returned if it is not.
952 SECStatus
953 SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo,
954 CERTCertificate *cert,
955 CERTCertDBHandle *certdb)
957 SECOidTag kind;
959 kind = SEC_PKCS7ContentType (cinfo);
960 if (kind != SEC_OID_PKCS7_SIGNED_DATA
961 && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
962 return SECFailure; /* XXX set an error? */
964 return sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
969 * Add "cert" to the set of certs included in "cinfo".
971 * "cinfo" should be of type signedData or signedAndEnvelopedData;
972 * SECFailure will be returned if it is not.
974 SECStatus
975 SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
977 SECOidTag kind;
979 kind = SEC_PKCS7ContentType (cinfo);
980 if (kind != SEC_OID_PKCS7_SIGNED_DATA
981 && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
982 return SECFailure; /* XXX set an error? */
984 return sec_pkcs7_add_certificate (cinfo, cert);
988 static SECStatus
989 sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo,
990 PRArenaPool *poolp,
991 SECOidTag kind, PRBool detached,
992 SECOidTag encalg, int keysize)
994 SECStatus rv;
996 PORT_Assert (enccinfo != NULL && poolp != NULL);
997 if (enccinfo == NULL || poolp == NULL)
998 return SECFailure;
1001 * XXX Some day we may want to allow for other kinds. That needs
1002 * more work and modifications to the creation interface, etc.
1003 * For now, allow but notice callers who pass in other kinds.
1004 * They are responsible for creating the inner type and encoding,
1005 * if it is other than DATA.
1007 PORT_Assert (kind == SEC_OID_PKCS7_DATA);
1009 enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
1010 PORT_Assert (enccinfo->contentTypeTag
1011 && enccinfo->contentTypeTag->offset == kind);
1013 rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType),
1014 &(enccinfo->contentTypeTag->oid));
1015 if (rv != SECSuccess)
1016 return rv;
1018 /* Save keysize and algorithm for later. */
1019 enccinfo->keysize = keysize;
1020 enccinfo->encalg = encalg;
1022 return SECSuccess;
1027 * Add a recipient to a PKCS7 thing, verifying their cert first.
1028 * Any error returns SECFailure.
1030 static SECStatus
1031 sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo,
1032 CERTCertificate *cert,
1033 SECCertUsage certusage,
1034 CERTCertDBHandle *certdb)
1036 SECOidTag kind;
1037 SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
1038 SECItem *dummy;
1039 void *mark;
1040 int count;
1042 kind = SEC_PKCS7ContentType (cinfo);
1043 switch (kind) {
1044 case SEC_OID_PKCS7_ENVELOPED_DATA:
1046 SEC_PKCS7EnvelopedData *edp;
1048 edp = cinfo->content.envelopedData;
1049 recipientinfosp = &(edp->recipientInfos);
1051 break;
1052 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
1054 SEC_PKCS7SignedAndEnvelopedData *saedp;
1056 saedp = cinfo->content.signedAndEnvelopedData;
1057 recipientinfosp = &(saedp->recipientInfos);
1059 break;
1060 default:
1061 return SECFailure; /* XXX set an error? */
1065 * XXX I think that CERT_VerifyCert should do this if *it* is passed
1066 * a NULL database.
1068 if (certdb == NULL) {
1069 certdb = CERT_GetDefaultCertDB();
1070 if (certdb == NULL)
1071 return SECFailure; /* XXX set an error? */
1074 if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
1075 cinfo->pwfn_arg, NULL) != SECSuccess)
1077 /* XXX Did CERT_VerifyCert set an error? */
1078 return SECFailure;
1081 mark = PORT_ArenaMark (cinfo->poolp);
1083 recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp,
1084 sizeof(SEC_PKCS7RecipientInfo));
1085 if (recipientinfo == NULL) {
1086 PORT_ArenaRelease (cinfo->poolp, mark);
1087 return SECFailure;
1090 dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version,
1091 SEC_PKCS7_RECIPIENT_INFO_VERSION);
1092 if (dummy == NULL) {
1093 PORT_ArenaRelease (cinfo->poolp, mark);
1094 return SECFailure;
1096 PORT_Assert (dummy == &recipientinfo->version);
1098 recipientinfo->cert = CERT_DupCertificate (cert);
1099 if (recipientinfo->cert == NULL) {
1100 PORT_ArenaRelease (cinfo->poolp, mark);
1101 return SECFailure;
1104 recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
1105 if (recipientinfo->issuerAndSN == NULL) {
1106 PORT_ArenaRelease (cinfo->poolp, mark);
1107 return SECFailure;
1111 * Okay, now recipientinfo is all set. We just need to put it into
1112 * the main structure.
1114 * If this is the first recipient, allocate a new recipientinfos array;
1115 * otherwise, reallocate the array, making room for the new entry.
1117 recipientinfos = *recipientinfosp;
1118 if (recipientinfos == NULL) {
1119 count = 0;
1120 recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc (
1121 cinfo->poolp,
1122 2 * sizeof(SEC_PKCS7RecipientInfo *));
1123 } else {
1124 for (count = 0; recipientinfos[count] != NULL; count++)
1126 PORT_Assert (count); /* should be at least one already */
1127 recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow (
1128 cinfo->poolp, recipientinfos,
1129 (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
1130 (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
1133 if (recipientinfos == NULL) {
1134 PORT_ArenaRelease (cinfo->poolp, mark);
1135 return SECFailure;
1138 recipientinfos[count] = recipientinfo;
1139 recipientinfos[count + 1] = NULL;
1141 *recipientinfosp = recipientinfos;
1143 PORT_ArenaUnmark (cinfo->poolp, mark);
1144 return SECSuccess;
1149 * Start a PKCS7 enveloping context.
1151 * "cert" is the cert for the recipient. It will be checked for validity.
1153 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1154 * XXX Maybe SECCertUsage should be split so that our caller just says
1155 * "email" and *we* add the "recipient" part -- otherwise our caller
1156 * could be lying about the usage; we do not want to allow encryption
1157 * certs for signing or vice versa.
1159 * "certdb" is the cert database to use for verifying the cert.
1160 * It can be NULL if a default database is available (like in the client).
1162 * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
1164 * "keysize" specifies the bulk encryption key size, in bits.
1166 * The return value can be passed to functions which add things to
1167 * it like more recipients, then eventually to SEC_PKCS7Encode() or to
1168 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
1169 * SEC_PKCS7DestroyContentInfo().
1171 * An error results in a return value of NULL and an error set.
1172 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1174 extern SEC_PKCS7ContentInfo *
1175 SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert,
1176 SECCertUsage certusage,
1177 CERTCertDBHandle *certdb,
1178 SECOidTag encalg,
1179 int keysize,
1180 SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1182 SEC_PKCS7ContentInfo *cinfo;
1183 SEC_PKCS7EnvelopedData *envd;
1184 SECStatus rv;
1186 cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA,
1187 PR_FALSE, pwfn, pwfn_arg);
1188 if (cinfo == NULL)
1189 return NULL;
1191 rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
1192 if (rv != SECSuccess) {
1193 SEC_PKCS7DestroyContentInfo (cinfo);
1194 return NULL;
1197 envd = cinfo->content.envelopedData;
1198 PORT_Assert (envd != NULL);
1201 * XXX Might we want to allow content types other than data?
1202 * If so, via what interface?
1204 rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo),
1205 cinfo->poolp,
1206 SEC_OID_PKCS7_DATA, PR_FALSE,
1207 encalg, keysize);
1208 if (rv != SECSuccess) {
1209 SEC_PKCS7DestroyContentInfo (cinfo);
1210 return NULL;
1213 /* XXX Anything more to do here? */
1215 return cinfo;
1220 * Add another recipient to an encrypted message.
1222 * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
1223 * SECFailure will be returned if it is not.
1225 * "cert" is the cert for the recipient. It will be checked for validity.
1227 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1228 * XXX Maybe SECCertUsage should be split so that our caller just says
1229 * "email" and *we* add the "recipient" part -- otherwise our caller
1230 * could be lying about the usage; we do not want to allow encryption
1231 * certs for signing or vice versa.
1233 * "certdb" is the cert database to use for verifying the cert.
1234 * It can be NULL if a default database is available (like in the client).
1236 SECStatus
1237 SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo,
1238 CERTCertificate *cert,
1239 SECCertUsage certusage,
1240 CERTCertDBHandle *certdb)
1242 return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
1247 * Create an empty PKCS7 data content info.
1249 * An error results in a return value of NULL and an error set.
1250 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1252 SEC_PKCS7ContentInfo *
1253 SEC_PKCS7CreateData (void)
1255 return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE,
1256 NULL, NULL);
1261 * Create an empty PKCS7 encrypted content info.
1263 * "algorithm" specifies the bulk encryption algorithm to use.
1265 * An error results in a return value of NULL and an error set.
1266 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1268 SEC_PKCS7ContentInfo *
1269 SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize,
1270 SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1272 SEC_PKCS7ContentInfo *cinfo;
1273 SECAlgorithmID *algid;
1274 SEC_PKCS7EncryptedData *enc_data;
1275 SECStatus rv;
1277 cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA,
1278 PR_FALSE, pwfn, pwfn_arg);
1279 if (cinfo == NULL)
1280 return NULL;
1282 enc_data = cinfo->content.encryptedData;
1283 algid = &(enc_data->encContentInfo.contentEncAlg);
1285 if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
1286 rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL);
1287 } else {
1288 /* Assume password-based-encryption.
1289 * Note: we can't generate pkcs5v2 from this interface.
1290 * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
1291 * non-PBE oids and assuming that they are pkcs5v2 oids, but
1292 * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
1293 * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
1294 * to create pkcs5v2 PBEs */
1295 SECAlgorithmID *pbe_algid;
1296 pbe_algid = PK11_CreatePBEAlgorithmID (algorithm, 1, NULL);
1297 if (pbe_algid == NULL) {
1298 rv = SECFailure;
1299 } else {
1300 rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid);
1301 SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
1305 if (rv != SECSuccess) {
1306 SEC_PKCS7DestroyContentInfo (cinfo);
1307 return NULL;
1310 rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo),
1311 cinfo->poolp,
1312 SEC_OID_PKCS7_DATA, PR_FALSE,
1313 algorithm, keysize);
1314 if (rv != SECSuccess) {
1315 SEC_PKCS7DestroyContentInfo (cinfo);
1316 return NULL;
1319 return cinfo;