Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
5 /*
6 * CMS signedData methods.
7 */
9 #include "cmslocal.h"
11 #include "cert.h"
12 /*#include "cdbhdl.h"*/
13 #include "secasn1.h"
14 #include "secitem.h"
15 #include "secoid.h"
16 #include "pk11func.h"
17 #include "secerr.h"
19 NSSCMSSignedData *
20 NSS_CMSSignedData_Create(NSSCMSMessage *cmsg)
21 {
22 void *mark;
23 NSSCMSSignedData *sigd;
24 PLArenaPool *poolp;
26 if (!cmsg) {
27 PORT_SetError(SEC_ERROR_INVALID_ARGS);
28 return NULL;
29 }
31 poolp = cmsg->poolp;
33 mark = PORT_ArenaMark(poolp);
35 sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc (poolp, sizeof(NSSCMSSignedData));
36 if (sigd == NULL)
37 goto loser;
39 sigd->cmsg = cmsg;
41 /* signerInfos, certs, certlists, crls are all empty */
42 /* version is set in NSS_CMSSignedData_Finalize() */
44 PORT_ArenaUnmark(poolp, mark);
45 return sigd;
47 loser:
48 PORT_ArenaRelease(poolp, mark);
49 return NULL;
50 }
52 void
53 NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd)
54 {
55 CERTCertificate **certs, **tempCerts, *cert;
56 CERTCertificateList **certlists, *certlist;
57 NSSCMSSignerInfo **signerinfos, *si;
59 if (sigd == NULL)
60 return;
62 certs = sigd->certs;
63 tempCerts = sigd->tempCerts;
64 certlists = sigd->certLists;
65 signerinfos = sigd->signerInfos;
67 if (certs != NULL) {
68 while ((cert = *certs++) != NULL)
69 CERT_DestroyCertificate (cert);
70 }
72 if (tempCerts != NULL) {
73 while ((cert = *tempCerts++) != NULL)
74 CERT_DestroyCertificate (cert);
75 }
77 if (certlists != NULL) {
78 while ((certlist = *certlists++) != NULL)
79 CERT_DestroyCertificateList (certlist);
80 }
82 if (signerinfos != NULL) {
83 while ((si = *signerinfos++) != NULL)
84 NSS_CMSSignerInfo_Destroy(si);
85 }
87 /* everything's in a pool, so don't worry about the storage */
88 NSS_CMSContentInfo_Destroy(&(sigd->contentInfo));
90 }
92 /*
93 * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
94 * before start of encoding.
95 *
96 * In detail:
97 * - find out about the right value to put into sigd->version
98 * - come up with a list of digestAlgorithms (which should be the union of the algorithms
99 * in the signerinfos).
100 * If we happen to have a pre-set list of algorithms (and digest values!), we
101 * check if we have all the signerinfos' algorithms. If not, this is an error.
102 */
103 SECStatus
104 NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
105 {
106 NSSCMSSignerInfo *signerinfo;
107 SECOidTag digestalgtag;
108 SECItem *dummy;
109 int version;
110 SECStatus rv;
111 PRBool haveDigests = PR_FALSE;
112 int n, i;
113 PLArenaPool *poolp;
115 if (!sigd) {
116 PORT_SetError(SEC_ERROR_INVALID_ARGS);
117 return SECFailure;
118 }
120 poolp = sigd->cmsg->poolp;
122 /* we assume that we have precomputed digests if there is a list of algorithms, and */
123 /* a chunk of data for each of those algorithms */
124 if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
125 for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
126 if (sigd->digests[i] == NULL)
127 break;
128 }
129 if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
130 haveDigests = PR_TRUE; /* yes: we must have all the digests */
131 }
133 version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;
135 /* RFC2630 5.1 "version is the syntax version number..." */
136 if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
137 version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
139 /* prepare all the SignerInfos (there may be none) */
140 for (i=0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
141 signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
143 /* RFC2630 5.1 "version is the syntax version number..." */
144 if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
145 version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
147 /* collect digestAlgorithms from SignerInfos */
148 /* (we need to know which algorithms we have when the content comes in) */
149 /* do not overwrite any existing digestAlgorithms (and digest) */
150 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
151 n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
152 if (n < 0 && haveDigests) {
153 /* oops, there is a digestalg we do not have a digest for */
154 /* but we were supposed to have all the digests already... */
155 goto loser;
156 } else if (n < 0) {
157 /* add the digestAlgorithm & a NULL digest */
158 rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
159 if (rv != SECSuccess)
160 goto loser;
161 } else {
162 /* found it, nothing to do */
163 }
164 }
166 dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
167 if (dummy == NULL)
168 return SECFailure;
170 /* this is a SET OF, so we need to sort them guys */
171 rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms,
172 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
173 (void **)sigd->digests);
174 if (rv != SECSuccess)
175 return SECFailure;
177 return SECSuccess;
179 loser:
180 return SECFailure;
181 }
183 SECStatus
184 NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd)
185 {
186 SECStatus rv;
187 if (!sigd) {
188 PORT_SetError(SEC_ERROR_INVALID_ARGS);
189 return SECFailure;
190 }
191 rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
192 if (rv != SECSuccess) {
193 return SECFailure;
194 }
195 /* set up the digests */
196 if (sigd->digests && sigd->digests[0]) {
197 sigd->contentInfo.privateInfo->digcx = NULL; /* don't attempt to make new ones. */
198 } else if (sigd->digestAlgorithms != NULL) {
199 sigd->contentInfo.privateInfo->digcx =
200 NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
201 if (sigd->contentInfo.privateInfo->digcx == NULL)
202 return SECFailure;
203 }
204 return SECSuccess;
205 }
207 /*
208 * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
209 * after all the encapsulated data was passed through the encoder.
210 *
211 * In detail:
212 * - create the signatures in all the SignerInfos
213 *
214 * Please note that nothing is done to the Certificates and CRLs in the message - this
215 * is entirely the responsibility of our callers.
216 */
217 SECStatus
218 NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
219 {
220 NSSCMSSignerInfo **signerinfos, *signerinfo;
221 NSSCMSContentInfo *cinfo;
222 SECOidTag digestalgtag;
223 SECStatus ret = SECFailure;
224 SECStatus rv;
225 SECItem *contentType;
226 int certcount;
227 int i, ci, cli, n, rci, si;
228 PLArenaPool *poolp;
229 CERTCertificateList *certlist;
230 extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
232 if (!sigd) {
233 PORT_SetError(SEC_ERROR_INVALID_ARGS);
234 return SECFailure;
235 }
237 poolp = sigd->cmsg->poolp;
238 cinfo = &(sigd->contentInfo);
240 /* did we have digest calculation going on? */
241 if (cinfo->privateInfo && cinfo->privateInfo->digcx) {
242 rv = NSS_CMSDigestContext_FinishMultiple(cinfo->privateInfo->digcx, poolp,
243 &(sigd->digests));
244 /* error has been set by NSS_CMSDigestContext_FinishMultiple */
245 cinfo->privateInfo->digcx = NULL;
246 if (rv != SECSuccess)
247 goto loser;
248 }
250 signerinfos = sigd->signerInfos;
251 certcount = 0;
253 /* prepare all the SignerInfos (there may be none) */
254 for (i=0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
255 signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
257 /* find correct digest for this signerinfo */
258 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
259 n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
260 if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
261 /* oops - digest not found */
262 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
263 goto loser;
264 }
266 /* XXX if our content is anything else but data, we need to force the
267 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
268 * collection...") */
270 /* pass contentType here as we want a contentType attribute */
271 if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
272 goto loser;
274 /* sign the thing */
275 rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
276 if (rv != SECSuccess)
277 goto loser;
279 /* while we're at it, count number of certs in certLists */
280 certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
281 if (certlist)
282 certcount += certlist->len;
283 }
285 /* this is a SET OF, so we need to sort them guys */
286 rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
287 if (rv != SECSuccess)
288 goto loser;
290 /*
291 * now prepare certs & crls
292 */
294 /* count the rest of the certs */
295 if (sigd->certs != NULL) {
296 for (ci = 0; sigd->certs[ci] != NULL; ci++)
297 certcount++;
298 }
300 if (sigd->certLists != NULL) {
301 for (cli = 0; sigd->certLists[cli] != NULL; cli++)
302 certcount += sigd->certLists[cli]->len;
303 }
305 if (certcount == 0) {
306 sigd->rawCerts = NULL;
307 } else {
308 /*
309 * Combine all of the certs and cert chains into rawcerts.
310 * Note: certcount is an upper bound; we may not need that many slots
311 * but we will allocate anyway to avoid having to do another pass.
312 * (The temporary space saving is not worth it.)
313 *
314 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
315 * SetOfDERcertficates implementation
316 */
317 sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
318 if (sigd->rawCerts == NULL)
319 return SECFailure;
321 /*
322 * XXX Want to check for duplicates and not add *any* cert that is
323 * already in the set. This will be more important when we start
324 * dealing with larger sets of certs, dual-key certs (signing and
325 * encryption), etc. For the time being we can slide by...
326 *
327 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
328 * SetOfDERcertficates implementation
329 */
330 rci = 0;
331 if (signerinfos != NULL) {
332 for (si = 0; signerinfos[si] != NULL; si++) {
333 signerinfo = signerinfos[si];
334 for (ci = 0; ci < signerinfo->certList->len; ci++)
335 sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
336 }
337 }
339 if (sigd->certs != NULL) {
340 for (ci = 0; sigd->certs[ci] != NULL; ci++)
341 sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
342 }
344 if (sigd->certLists != NULL) {
345 for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
346 for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
347 sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
348 }
349 }
351 sigd->rawCerts[rci] = NULL;
353 /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
354 NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
355 }
357 ret = SECSuccess;
359 loser:
360 return ret;
361 }
363 SECStatus
364 NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd)
365 {
366 SECStatus rv;
367 if (!sigd) {
368 PORT_SetError(SEC_ERROR_INVALID_ARGS);
369 return SECFailure;
370 }
371 rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
372 if (rv != SECSuccess) {
373 return SECFailure;
374 }
375 /* handle issue with Windows 2003 servers and kerberos */
376 if (sigd->digestAlgorithms != NULL) {
377 int i;
378 for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
379 SECAlgorithmID *algid = sigd->digestAlgorithms[i];
380 SECOidTag senttag= SECOID_FindOIDTag(&algid->algorithm);
381 SECOidTag maptag = NSS_CMSUtil_MapSignAlgs(senttag);
383 if (maptag != senttag) {
384 SECOidData *hashoid = SECOID_FindOIDByTag(maptag);
385 rv = SECITEM_CopyItem(sigd->cmsg->poolp, &algid->algorithm
386 ,&hashoid->oid);
387 if (rv != SECSuccess) {
388 return rv;
389 }
390 }
391 }
392 }
394 /* set up the digests */
395 if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
396 /* if digests are already there, do nothing */
397 sigd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
398 if (sigd->contentInfo.privateInfo->digcx == NULL)
399 return SECFailure;
400 }
401 return SECSuccess;
402 }
404 /*
405 * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a
406 * SignedData after all the encapsulated data was passed through the decoder.
407 */
408 SECStatus
409 NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd)
410 {
411 SECStatus rv = SECSuccess;
413 if (!sigd) {
414 PORT_SetError(SEC_ERROR_INVALID_ARGS);
415 return SECFailure;
416 }
418 /* did we have digest calculation going on? */
419 if (sigd->contentInfo.privateInfo && sigd->contentInfo.privateInfo->digcx) {
420 rv = NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.privateInfo->digcx,
421 sigd->cmsg->poolp, &(sigd->digests));
422 /* error set by NSS_CMSDigestContext_FinishMultiple */
423 sigd->contentInfo.privateInfo->digcx = NULL;
424 }
425 return rv;
426 }
428 /*
429 * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
430 * after all decoding is finished.
431 */
432 SECStatus
433 NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd)
434 {
435 NSSCMSSignerInfo **signerinfos = NULL;
436 int i;
438 if (!sigd) {
439 PORT_SetError(SEC_ERROR_INVALID_ARGS);
440 return SECFailure;
441 }
443 /* set cmsg for all the signerinfos */
444 signerinfos = sigd->signerInfos;
446 /* set cmsg for all the signerinfos */
447 if (signerinfos) {
448 for (i = 0; signerinfos[i] != NULL; i++)
449 signerinfos[i]->cmsg = sigd->cmsg;
450 }
452 return SECSuccess;
453 }
455 /*
456 * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
457 */
458 NSSCMSSignerInfo **
459 NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd)
460 {
461 if (!sigd) {
462 PORT_SetError(SEC_ERROR_INVALID_ARGS);
463 return NULL;
464 }
465 return sigd->signerInfos;
466 }
468 int
469 NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd)
470 {
471 if (!sigd) {
472 PORT_SetError(SEC_ERROR_INVALID_ARGS);
473 return 0;
474 }
475 return NSS_CMSArray_Count((void **)sigd->signerInfos);
476 }
478 NSSCMSSignerInfo *
479 NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i)
480 {
481 if (!sigd) {
482 PORT_SetError(SEC_ERROR_INVALID_ARGS);
483 return NULL;
484 }
485 return sigd->signerInfos[i];
486 }
488 /*
489 * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
490 */
491 SECAlgorithmID **
492 NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd)
493 {
494 if (!sigd) {
495 PORT_SetError(SEC_ERROR_INVALID_ARGS);
496 return NULL;
497 }
498 return sigd->digestAlgorithms;
499 }
501 /*
502 * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
503 */
504 NSSCMSContentInfo *
505 NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd)
506 {
507 if (!sigd) {
508 PORT_SetError(SEC_ERROR_INVALID_ARGS);
509 return NULL;
510 }
511 return &(sigd->contentInfo);
512 }
514 /*
515 * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
516 */
517 SECItem **
518 NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd)
519 {
520 if (!sigd) {
521 PORT_SetError(SEC_ERROR_INVALID_ARGS);
522 return NULL;
523 }
524 return sigd->rawCerts;
525 }
527 SECStatus
528 NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
529 SECCertUsage certusage, PRBool keepcerts)
530 {
531 int certcount;
532 CERTCertificate **certArray = NULL;
533 CERTCertList *certList = NULL;
534 CERTCertListNode *node;
535 SECStatus rv;
536 SECItem **rawArray;
537 int i;
538 PRTime now;
540 if (!sigd) {
541 PORT_SetError(SEC_ERROR_INVALID_ARGS);
542 return SECFailure;
543 }
545 certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);
547 /* get the certs in the temp DB */
548 rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
549 &certArray, PR_FALSE, PR_FALSE, NULL);
550 if (rv != SECSuccess) {
551 goto loser;
552 }
554 /* save the certs so they don't get destroyed */
555 for (i=0; i < certcount; i++) {
556 CERTCertificate *cert = certArray[i];
557 if (cert)
558 NSS_CMSSignedData_AddTempCertificate(sigd, cert);
559 }
561 if (!keepcerts) {
562 goto done;
563 }
565 /* build a CertList for filtering */
566 certList = CERT_NewCertList();
567 if (certList == NULL) {
568 rv = SECFailure;
569 goto loser;
570 }
571 for (i=0; i < certcount; i++) {
572 CERTCertificate *cert = certArray[i];
573 if (cert)
574 cert = CERT_DupCertificate(cert);
575 if (cert)
576 CERT_AddCertToListTail(certList,cert);
577 }
579 /* filter out the certs we don't want */
580 rv = CERT_FilterCertListByUsage(certList,certusage, PR_FALSE);
581 if (rv != SECSuccess) {
582 goto loser;
583 }
585 /* go down the remaining list of certs and verify that they have
586 * valid chains, then import them.
587 */
588 now = PR_Now();
589 for (node = CERT_LIST_HEAD(certList) ; !CERT_LIST_END(node,certList);
590 node= CERT_LIST_NEXT(node)) {
591 CERTCertificateList *certChain;
593 if (CERT_VerifyCert(certdb, node->cert,
594 PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
595 continue;
596 }
598 certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
599 if (!certChain) {
600 continue;
601 }
603 /*
604 * CertChain returns an array of SECItems, import expects an array of
605 * SECItem pointers. Create the SECItem Pointers from the array of
606 * SECItems.
607 */
608 rawArray = (SECItem **)PORT_Alloc(certChain->len*sizeof (SECItem *));
609 if (!rawArray) {
610 CERT_DestroyCertificateList(certChain);
611 continue;
612 }
613 for (i=0; i < certChain->len; i++) {
614 rawArray[i] = &certChain->certs[i];
615 }
616 (void )CERT_ImportCerts(certdb, certusage, certChain->len,
617 rawArray, NULL, keepcerts, PR_FALSE, NULL);
618 PORT_Free(rawArray);
619 CERT_DestroyCertificateList(certChain);
620 }
622 rv = SECSuccess;
624 /* XXX CRL handling */
626 done:
627 if (sigd->signerInfos != NULL) {
628 /* fill in all signerinfo's certs */
629 for (i = 0; sigd->signerInfos[i] != NULL; i++)
630 (void)NSS_CMSSignerInfo_GetSigningCertificate(
631 sigd->signerInfos[i], certdb);
632 }
634 loser:
635 /* now free everything */
636 if (certArray) {
637 CERT_DestroyCertArray(certArray,certcount);
638 }
639 if (certList) {
640 CERT_DestroyCertList(certList);
641 }
643 return rv;
644 }
646 /*
647 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
648 * of external signatures!
649 */
651 /*
652 * NSS_CMSSignedData_VerifySignerInfo - check the signatures.
653 *
654 * The digests were either calculated during decoding (and are stored in the
655 * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
656 *
657 * The verification checks if the signing cert is valid and has a trusted chain
658 * for the purpose specified by "certusage".
659 */
660 SECStatus
661 NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i,
662 CERTCertDBHandle *certdb, SECCertUsage certusage)
663 {
664 NSSCMSSignerInfo *signerinfo;
665 NSSCMSContentInfo *cinfo;
666 SECOidData *algiddata;
667 SECItem *contentType, *digest;
668 SECOidTag oidTag;
669 SECStatus rv;
671 if (!sigd) {
672 PORT_SetError(SEC_ERROR_INVALID_ARGS);
673 return SECFailure;
674 }
676 cinfo = &(sigd->contentInfo);
678 signerinfo = sigd->signerInfos[i];
680 /* verify certificate */
681 rv = NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage);
682 if (rv != SECSuccess)
683 return rv; /* error is set */
685 /* find digest and contentType for signerinfo */
686 algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
687 oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN;
688 digest = NSS_CMSSignedData_GetDigestValue(sigd, oidTag);
689 /* NULL digest is acceptable. */
690 contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo);
691 /* NULL contentType is acceptable. */
693 /* now verify signature */
694 rv = NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType);
695 return rv;
696 }
698 /*
699 * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message
700 */
701 SECStatus
702 NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd,
703 CERTCertDBHandle *certdb,
704 SECCertUsage usage)
705 {
706 CERTCertificate *cert;
707 SECStatus rv = SECSuccess;
708 int i;
709 int count;
710 PRTime now;
712 if (!sigd || !certdb || !sigd->rawCerts) {
713 PORT_SetError(SEC_ERROR_INVALID_ARGS);
714 return SECFailure;
715 }
717 count = NSS_CMSArray_Count((void**)sigd->rawCerts);
718 now = PR_Now();
719 for (i=0; i < count; i++) {
720 if (sigd->certs && sigd->certs[i]) {
721 cert = CERT_DupCertificate(sigd->certs[i]);
722 } else {
723 cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]);
724 if (!cert) {
725 rv = SECFailure;
726 break;
727 }
728 }
729 rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now,
730 NULL, NULL);
731 CERT_DestroyCertificate(cert);
732 }
734 return rv;
735 }
737 /*
738 * NSS_CMSSignedData_HasDigests - see if we have digests in place
739 */
740 PRBool
741 NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd)
742 {
743 if (!sigd) {
744 PORT_SetError(SEC_ERROR_INVALID_ARGS);
745 return PR_FALSE;
746 }
747 return (sigd->digests != NULL);
748 }
750 SECStatus
751 NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist)
752 {
753 SECStatus rv;
755 if (!sigd || !certlist) {
756 PORT_SetError(SEC_ERROR_INVALID_ARGS);
757 return SECFailure;
758 }
760 /* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */
761 rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist);
763 return rv;
764 }
766 /*
767 * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs
768 */
769 SECStatus
770 NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert)
771 {
772 CERTCertificateList *certlist;
773 SECCertUsage usage;
774 SECStatus rv;
776 usage = certUsageEmailSigner;
778 if (!sigd || !cert) {
779 PORT_SetError(SEC_ERROR_INVALID_ARGS);
780 return SECFailure;
781 }
783 /* do not include root */
784 certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
785 if (certlist == NULL)
786 return SECFailure;
788 rv = NSS_CMSSignedData_AddCertList(sigd, certlist);
790 return rv;
791 }
793 extern SECStatus
794 NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
795 {
796 CERTCertificate *c;
797 SECStatus rv;
799 if (!sigd || !cert) {
800 PORT_SetError(SEC_ERROR_INVALID_ARGS);
801 return SECFailure;
802 }
804 c = CERT_DupCertificate(cert);
805 rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->tempCerts), (void *)c);
806 return rv;
807 }
809 SECStatus
810 NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
811 {
812 CERTCertificate *c;
813 SECStatus rv;
815 if (!sigd || !cert) {
816 PORT_SetError(SEC_ERROR_INVALID_ARGS);
817 return SECFailure;
818 }
820 c = CERT_DupCertificate(cert);
821 rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c);
822 return rv;
823 }
825 PRBool
826 NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd)
827 {
828 if (!sigd) {
829 PORT_SetError(SEC_ERROR_INVALID_ARGS);
830 return PR_FALSE;
831 }
832 if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
833 return PR_TRUE;
834 else if (sigd->crls != NULL && sigd->crls[0] != NULL)
835 return PR_TRUE;
836 else
837 return PR_FALSE;
838 }
840 SECStatus
841 NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
842 NSSCMSSignerInfo *signerinfo)
843 {
844 void *mark;
845 SECStatus rv;
846 SECOidTag digestalgtag;
847 PLArenaPool *poolp;
849 if (!sigd || !signerinfo) {
850 PORT_SetError(SEC_ERROR_INVALID_ARGS);
851 return SECFailure;
852 }
854 poolp = sigd->cmsg->poolp;
856 mark = PORT_ArenaMark(poolp);
858 /* add signerinfo */
859 rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
860 if (rv != SECSuccess)
861 goto loser;
863 /*
864 * add empty digest
865 * Empty because we don't have it yet. Either it gets created during encoding
866 * (if the data is present) or has to be set externally.
867 * XXX maybe pass it in optionally?
868 */
869 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
870 rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
871 if (rv != SECSuccess)
872 goto loser;
874 /*
875 * The last thing to get consistency would be adding the digest.
876 */
878 PORT_ArenaUnmark(poolp, mark);
879 return SECSuccess;
881 loser:
882 PORT_ArenaRelease (poolp, mark);
883 return SECFailure;
884 }
886 /*
887 * NSS_CMSSignedData_SetDigests - set a signedData's digests member
888 *
889 * "digestalgs" - array of digest algorithm IDs
890 * "digests" - array of digests corresponding to the digest algorithms
891 */
892 SECStatus
893 NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
894 SECAlgorithmID **digestalgs,
895 SECItem **digests)
896 {
897 int cnt, i, idx;
899 if (!sigd || !digestalgs || !digests) {
900 PORT_SetError(SEC_ERROR_INVALID_ARGS);
901 return SECFailure;
902 }
904 if (sigd->digestAlgorithms == NULL) {
905 PORT_SetError(SEC_ERROR_INVALID_ARGS);
906 return SECFailure;
907 }
909 /* we assume that the digests array is just not there yet */
910 PORT_Assert(sigd->digests == NULL);
911 if (sigd->digests != NULL) {
912 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
913 return SECFailure;
914 }
916 /* now allocate one (same size as digestAlgorithms) */
917 cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
918 sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
919 if (sigd->digests == NULL) {
920 PORT_SetError(SEC_ERROR_NO_MEMORY);
921 return SECFailure;
922 }
924 for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
925 /* try to find the sigd's i'th digest algorithm in the array we passed in */
926 idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
927 if (idx < 0) {
928 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
929 return SECFailure;
930 }
931 if (!digests[idx]) {
932 /* We have no digest for this algorithm, probably because it is
933 ** unrecognized or unsupported. We'll ignore this here. If this
934 ** digest is needed later, an error will be be generated then.
935 */
936 continue;
937 }
939 /* found it - now set it */
940 if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
941 SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
942 {
943 PORT_SetError(SEC_ERROR_NO_MEMORY);
944 return SECFailure;
945 }
946 }
947 return SECSuccess;
948 }
950 SECStatus
951 NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
952 SECOidTag digestalgtag,
953 SECItem *digestdata)
954 {
955 SECItem *digest = NULL;
956 PLArenaPool *poolp;
957 void *mark;
958 int n, cnt;
960 if (!sigd) {
961 PORT_SetError(SEC_ERROR_INVALID_ARGS);
962 return SECFailure;
963 }
965 poolp = sigd->cmsg->poolp;
967 mark = PORT_ArenaMark(poolp);
970 if (digestdata) {
971 digest = (SECItem *) PORT_ArenaZAlloc(poolp,sizeof(SECItem));
973 /* copy digestdata item to arena (in case we have it and are not only making room) */
974 if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
975 goto loser;
976 }
978 /* now allocate one (same size as digestAlgorithms) */
979 if (sigd->digests == NULL) {
980 cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
981 sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
982 if (sigd->digests == NULL) {
983 PORT_SetError(SEC_ERROR_NO_MEMORY);
984 return SECFailure;
985 }
986 }
988 n = -1;
989 if (sigd->digestAlgorithms != NULL)
990 n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
992 /* if not found, add a digest */
993 if (n < 0) {
994 if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
995 goto loser;
996 } else {
997 /* replace NULL pointer with digest item (and leak previous value) */
998 sigd->digests[n] = digest;
999 }
1001 PORT_ArenaUnmark(poolp, mark);
1002 return SECSuccess;
1004 loser:
1005 PORT_ArenaRelease(poolp, mark);
1006 return SECFailure;
1007 }
1009 SECStatus
1010 NSS_CMSSignedData_AddDigest(PLArenaPool *poolp,
1011 NSSCMSSignedData *sigd,
1012 SECOidTag digestalgtag,
1013 SECItem *digest)
1014 {
1015 SECAlgorithmID *digestalg;
1016 void *mark;
1018 if (!sigd || !poolp) {
1019 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1020 return SECFailure;
1021 }
1023 mark = PORT_ArenaMark(poolp);
1025 digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
1026 if (digestalg == NULL)
1027 goto loser;
1029 if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
1030 goto loser;
1032 if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess ||
1033 /* even if digest is NULL, add dummy to have same-size array */
1034 NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess)
1035 {
1036 goto loser;
1037 }
1039 PORT_ArenaUnmark(poolp, mark);
1040 return SECSuccess;
1042 loser:
1043 PORT_ArenaRelease(poolp, mark);
1044 return SECFailure;
1045 }
1047 /* XXX This function doesn't set the error code on failure. */
1048 SECItem *
1049 NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag)
1050 {
1051 int n;
1053 if (!sigd) {
1054 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1055 return NULL;
1056 }
1058 if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) {
1059 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
1060 return NULL;
1061 }
1063 n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
1065 return (n < 0) ? NULL : sigd->digests[n];
1066 }
1068 /* =============================================================================
1069 * Misc. utility functions
1070 */
1072 /*
1073 * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
1074 *
1075 * cert - base certificates that will be included
1076 * include_chain - if true, include the complete cert chain for cert
1077 *
1078 * More certs and chains can be added via AddCertificate and AddCertChain.
1079 *
1080 * An error results in a return value of NULL and an error set.
1081 *
1082 * XXXX CRLs
1083 */
1084 NSSCMSSignedData *
1085 NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain)
1086 {
1087 NSSCMSSignedData *sigd;
1088 void *mark;
1089 PLArenaPool *poolp;
1090 SECStatus rv;
1092 if (!cmsg || !cert) {
1093 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1094 return NULL;
1095 }
1097 poolp = cmsg->poolp;
1098 mark = PORT_ArenaMark(poolp);
1100 sigd = NSS_CMSSignedData_Create(cmsg);
1101 if (sigd == NULL)
1102 goto loser;
1104 /* no signerinfos, thus no digestAlgorithms */
1106 /* but certs */
1107 if (include_chain) {
1108 rv = NSS_CMSSignedData_AddCertChain(sigd, cert);
1109 } else {
1110 rv = NSS_CMSSignedData_AddCertificate(sigd, cert);
1111 }
1112 if (rv != SECSuccess)
1113 goto loser;
1115 /* RFC2630 5.2 sez:
1116 * In the degenerate case where there are no signers, the
1117 * EncapsulatedContentInfo value being "signed" is irrelevant. In this
1118 * case, the content type within the EncapsulatedContentInfo value being
1119 * "signed" should be id-data (as defined in section 4), and the content
1120 * field of the EncapsulatedContentInfo value should be omitted.
1121 */
1122 rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
1123 if (rv != SECSuccess)
1124 goto loser;
1126 PORT_ArenaUnmark(poolp, mark);
1127 return sigd;
1129 loser:
1130 if (sigd)
1131 NSS_CMSSignedData_Destroy(sigd);
1132 PORT_ArenaRelease(poolp, mark);
1133 return NULL;
1134 }
1136 /* TODO:
1137 * NSS_CMSSignerInfo_GetReceiptRequest()
1138 * NSS_CMSSignedData_HasReceiptRequest()
1139 * easy way to iterate over signers
1140 */