PageRenderTime 115ms CodeModel.GetById 16ms app.highlight 91ms RepoModel.GetById 1ms app.codeStats 0ms

/security/nss/lib/smime/cmssigdata.c

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