/ libsecurity_smime / lib / cmssigdata.c
cmssigdata.c
   1  /*
   2   * The contents of this file are subject to the Mozilla Public
   3   * License Version 1.1 (the "License"); you may not use this file
   4   * except in compliance with the License. You may obtain a copy of
   5   * the License at http://www.mozilla.org/MPL/
   6   * 
   7   * Software distributed under the License is distributed on an "AS
   8   * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
   9   * implied. See the License for the specific language governing
  10   * rights and limitations under the License.
  11   * 
  12   * The Original Code is the Netscape security libraries.
  13   * 
  14   * The Initial Developer of the Original Code is Netscape
  15   * Communications Corporation.  Portions created by Netscape are 
  16   * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
  17   * Rights Reserved.
  18   * 
  19   * Contributor(s):
  20   * 
  21   * Alternatively, the contents of this file may be used under the
  22   * terms of the GNU General Public License Version 2 or later (the
  23   * "GPL"), in which case the provisions of the GPL are applicable 
  24   * instead of those above.  If you wish to allow use of your 
  25   * version of this file only under the terms of the GPL and not to
  26   * allow others to use your version of this file under the MPL,
  27   * indicate your decision by deleting the provisions above and
  28   * replace them with the notice and other provisions required by
  29   * the GPL.  If you do not delete the provisions above, a recipient
  30   * may use your version of this file under either the MPL or the
  31   * GPL.
  32   */
  33  
  34  /*
  35   * CMS signedData methods.
  36   */
  37  
  38  #include <Security/SecCmsSignedData.h>
  39  
  40  #include <Security/SecCmsContentInfo.h>
  41  #include <Security/SecCmsDigestContext.h>
  42  #include <Security/SecCmsSignerInfo.h>
  43  
  44  #include "cmslocal.h"
  45  
  46  #include "cert.h"
  47  #include "SecAsn1Item.h"
  48  #include "secoid.h"
  49  
  50  #include <security_asn1/secasn1.h>
  51  #include <security_asn1/secerr.h>
  52  #include <security_asn1/secport.h>
  53  
  54  #if !USE_CDSA_CRYPTO
  55  #include <Security/SecCertificatePriv.h>
  56  #endif
  57  
  58  SecCmsSignedDataRef
  59  SecCmsSignedDataCreate(SecCmsMessageRef cmsg)
  60  {
  61      void *mark;
  62      SecCmsSignedDataRef sigd;
  63      PLArenaPool *poolp;
  64  
  65      poolp = cmsg->poolp;
  66  
  67      mark = PORT_ArenaMark(poolp);
  68  
  69      sigd = (SecCmsSignedDataRef)PORT_ArenaZAlloc (poolp, sizeof(SecCmsSignedData));
  70      if (sigd == NULL)
  71  	goto loser;
  72  
  73      sigd->contentInfo.cmsg = cmsg;
  74  
  75      /* signerInfos, certs, certlists, crls are all empty */
  76      /* version is set in SecCmsSignedDataFinalize() */
  77  
  78      PORT_ArenaUnmark(poolp, mark);
  79      return sigd;
  80  
  81  loser:
  82      PORT_ArenaRelease(poolp, mark);
  83      return NULL;
  84  }
  85  
  86  void
  87  SecCmsSignedDataDestroy(SecCmsSignedDataRef sigd)
  88  {
  89      SecCmsSignerInfoRef *signerinfos, si;
  90  
  91      if (sigd == NULL)
  92  	return;
  93  
  94      if (sigd->certs != NULL)
  95  	CFRelease(sigd->certs);
  96  
  97      signerinfos = sigd->signerInfos;
  98      if (signerinfos != NULL) {
  99  	while ((si = *signerinfos++) != NULL)
 100  	    SecCmsSignerInfoDestroy(si);
 101      }
 102  
 103      /* everything's in a pool, so don't worry about the storage */
 104     SecCmsContentInfoDestroy(&(sigd->contentInfo));
 105  }
 106  
 107  /*
 108   * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData
 109   *     before start of encoding.
 110   *
 111   * In detail:
 112   *  - find out about the right value to put into sigd->version
 113   *  - come up with a list of digestAlgorithms (which should be the union of the algorithms
 114   *         in the signerinfos).
 115   *         If we happen to have a pre-set list of algorithms (and digest values!), we
 116   *         check if we have all the signerinfos' algorithms. If not, this is an error.
 117   */
 118  OSStatus
 119  SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd)
 120  {
 121      SecCmsSignerInfoRef signerinfo;
 122      SECOidTag digestalgtag;
 123      SecAsn1Item * dummy;
 124      int version;
 125      OSStatus rv;
 126      Boolean haveDigests = PR_FALSE;
 127      int n, i;
 128      PLArenaPool *poolp;
 129  
 130      poolp = sigd->contentInfo.cmsg->poolp;
 131  
 132      /* we assume that we have precomputed digests if there is a list of algorithms, and */
 133      /* a chunk of data for each of those algorithms */
 134      if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
 135  	for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
 136  	    if (sigd->digests[i] == NULL)
 137  		break;
 138  	}
 139  	if (sigd->digestAlgorithms[i] == NULL)	/* reached the end of the array? */
 140  	    haveDigests = PR_TRUE;		/* yes: we must have all the digests */
 141      }
 142  	    
 143      version = SEC_CMS_SIGNED_DATA_VERSION_BASIC;
 144  
 145      /* RFC2630 5.1 "version is the syntax version number..." */
 146      if (SecCmsContentInfoGetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
 147  	version = SEC_CMS_SIGNED_DATA_VERSION_EXT;
 148  
 149      /* prepare all the SignerInfos (there may be none) */
 150      for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
 151  	signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
 152  
 153  	/* RFC2630 5.1 "version is the syntax version number..." */
 154  	if (SecCmsSignerInfoGetVersion(signerinfo) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN)
 155  	    version = SEC_CMS_SIGNED_DATA_VERSION_EXT;
 156  	
 157  	/* collect digestAlgorithms from SignerInfos */
 158  	/* (we need to know which algorithms we have when the content comes in) */
 159  	/* do not overwrite any existing digestAlgorithms (and digest) */
 160  	digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
 161  	n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
 162  	if (n < 0 && haveDigests) {
 163  	    /* oops, there is a digestalg we do not have a digest for */
 164  	    /* but we were supposed to have all the digests already... */
 165  	    goto loser;
 166  	} else if (n < 0) {
 167  	    /* add the digestAlgorithm & a NULL digest */
 168  	    rv = SecCmsSignedDataAddDigest(poolp, sigd, digestalgtag, NULL);
 169  	    if (rv != SECSuccess)
 170  		goto loser;
 171  	} else {
 172  	    /* found it, nothing to do */
 173  	}
 174      }
 175  
 176      dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
 177      if (dummy == NULL)
 178  	return SECFailure;
 179  
 180      /* this is a SET OF, so we need to sort them guys */
 181      rv = SecCmsArraySortByDER((void **)sigd->digestAlgorithms, 
 182                                  SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
 183  				(void **)sigd->digests);
 184      if (rv != SECSuccess)
 185  	return SECFailure;
 186      
 187      return SECSuccess;
 188  
 189  loser:
 190      return SECFailure;
 191  }
 192  
 193  OSStatus
 194  SecCmsSignedDataEncodeBeforeData(SecCmsSignedDataRef sigd)
 195  {
 196      /* set up the digests */
 197      if (sigd->digestAlgorithms != NULL) {
 198  	sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms);
 199  	if (sigd->contentInfo.digcx == NULL)
 200  	    return SECFailure;
 201      }
 202      return SECSuccess;
 203  }
 204  
 205  /*
 206   * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData
 207   *     after all the encapsulated data was passed through the encoder.
 208   *
 209   * In detail:
 210   *  - create the signatures in all the SignerInfos
 211   *
 212   * Please note that nothing is done to the Certificates and CRLs in the message - this
 213   * is entirely the responsibility of our callers.
 214   */
 215  OSStatus
 216  SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd)
 217  {
 218      SecCmsSignerInfoRef *signerinfos, signerinfo;
 219      SecCmsContentInfoRef cinfo;
 220      SECOidTag digestalgtag;
 221      OSStatus ret = SECFailure;
 222      OSStatus rv;
 223      SecAsn1Item * contentType;
 224      CFIndex certcount;
 225      int i, ci, n, rci, si;
 226      PLArenaPool *poolp;
 227      CFArrayRef certlist;
 228      extern const SecAsn1Template SecCmsSignerInfoTemplate[];
 229  
 230      cinfo = &(sigd->contentInfo);
 231      poolp = cinfo->cmsg->poolp;
 232  
 233      /* did we have digest calculation going on? */
 234      if (cinfo->digcx) {
 235  	SecAsn1Item **digests = NULL;
 236  	SECAlgorithmID **digestalgs = NULL;
 237  	rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, &digestalgs, &digests);
 238  	if (rv != SECSuccess)
 239  	    goto loser;		/* error has been set by SecCmsDigestContextFinishMultiple */
 240          if (digestalgs && digests) {
 241              rv = SecCmsSignedDataSetDigests(sigd, digestalgs, digests);
 242              if (rv != SECSuccess)
 243                  goto loser;		/* error has been set by SecCmsSignedDataSetDigests */
 244          }
 245  	SecCmsDigestContextDestroy(cinfo->digcx);
 246  	cinfo->digcx = NULL;
 247      }
 248  
 249      signerinfos = sigd->signerInfos;
 250      certcount = 0;
 251  
 252      /* prepare all the SignerInfos (there may be none) */
 253      for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
 254  	signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
 255  
 256  	/* find correct digest for this signerinfo */
 257  	digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
 258  	n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
 259  	if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
 260  	    /* oops - digest not found */
 261  	    PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
 262  	    goto loser;
 263  	}
 264  
 265  	/* XXX if our content is anything else but data, we need to force the
 266  	 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
 267  	 * collection...") */
 268  
 269  	/* pass contentType here as we want a contentType attribute */
 270  	if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL)
 271  	    goto loser;
 272  
 273  	/* sign the thing */
 274  	rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType);
 275  	if (rv != SECSuccess)
 276  	    goto loser;
 277  
 278  	/* while we're at it, count number of certs in certLists */
 279  	certlist = SecCmsSignerInfoGetCertList(signerinfo);
 280  	if (certlist)
 281  	    certcount += CFArrayGetCount(certlist);
 282      }
 283  
 284      /* this is a SET OF, so we need to sort them guys */
 285      rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL);
 286      if (rv != SECSuccess)
 287  	goto loser;
 288  
 289      /*
 290       * now prepare certs & crls
 291       */
 292  
 293      /* count the rest of the certs */
 294      if (sigd->certs != NULL)
 295  	certcount += CFArrayGetCount(sigd->certs);
 296  
 297      if (certcount == 0) {
 298  	sigd->rawCerts = NULL;
 299      } else {
 300  	/*
 301  	 * Combine all of the certs and cert chains into rawcerts.
 302  	 * Note: certcount is an upper bound; we may not need that many slots
 303  	 * but we will allocate anyway to avoid having to do another pass.
 304  	 * (The temporary space saving is not worth it.)
 305  	 *
 306  	 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
 307  	 *  SetOfDERcertficates implementation
 308  	 */
 309  	sigd->rawCerts = (SecAsn1Item * *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SecAsn1Item *));
 310  	if (sigd->rawCerts == NULL)
 311  	    return SECFailure;
 312  
 313  	/*
 314  	 * XXX Want to check for duplicates and not add *any* cert that is
 315  	 * already in the set.  This will be more important when we start
 316  	 * dealing with larger sets of certs, dual-key certs (signing and
 317  	 * encryption), etc.  For the time being we can slide by...
 318  	 *
 319  	 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
 320  	 *  SetOfDERcertficates implementation
 321  	 */
 322  	rci = 0;
 323  	if (signerinfos != NULL) {
 324  	    for (si = 0; signerinfos[si] != NULL; si++) {
 325  		signerinfo = signerinfos[si];
 326  		for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) {
 327  		    sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(SecAsn1Item));
 328  		    SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci);
 329  #if USE_CDSA_CRYPTO    
 330  		    SecCertificateGetData(cert, sigd->rawCerts[rci++]);
 331  #else
 332                      SecAsn1Item cert_data = { SecCertificateGetLength(cert),
 333                          (uint8_t *)SecCertificateGetBytePtr(cert) };
 334                      *(sigd->rawCerts[rci++]) = cert_data;
 335  #endif
 336  		}
 337  	    }
 338  	}
 339  
 340  	if (sigd->certs != NULL) {
 341  	    for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) {
 342  		sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(SecAsn1Item));
 343  		SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci);
 344  #if USE_CDSA_CRYPTO    
 345                  SecCertificateGetData(cert, sigd->rawCerts[rci++]);
 346  #else
 347                      SecAsn1Item cert_data = { SecCertificateGetLength(cert),
 348                          (uint8_t *)SecCertificateGetBytePtr(cert) };
 349                      *(sigd->rawCerts[rci++]) = cert_data;
 350  #endif
 351  	    }
 352  	}
 353  
 354  	sigd->rawCerts[rci] = NULL;
 355  
 356  	/* this is a SET OF, so we need to sort them guys - we have the DER already, though */
 357  	SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL);
 358      }
 359  
 360      ret = SECSuccess;
 361  
 362  loser:
 363      return ret;
 364  }
 365  
 366  OSStatus
 367  SecCmsSignedDataDecodeBeforeData(SecCmsSignedDataRef sigd)
 368  {
 369      /* set up the digests, if we have digest algorithms, no digests yet, and content is attached */
 370      if (sigd->digestAlgorithms != NULL && sigd->digests == NULL /* && sigd->contentInfo.content.pointer != NULL*/) {
 371  	/* if digests are already there, do nothing */
 372  	sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms);
 373  	if (sigd->contentInfo.digcx == NULL)
 374  	    return SECFailure;
 375      }
 376      return SECSuccess;
 377  }
 378  
 379  /*
 380   * SecCmsSignedDataDecodeAfterData - do all the necessary things to a SignedData
 381   *     after all the encapsulated data was passed through the decoder.
 382   */
 383  OSStatus
 384  SecCmsSignedDataDecodeAfterData(SecCmsSignedDataRef sigd)
 385  {
 386      OSStatus rv = SECSuccess;
 387  
 388      /* did we have digest calculation going on? */
 389      if (sigd->contentInfo.digcx) {
 390          /* @@@ we should see if data was absent vs. zero length */
 391          if (sigd->contentInfo.content.data && sigd->contentInfo.content.data->Length) {
 392              SecAsn1Item * *digests = NULL;
 393              SECAlgorithmID **digestalgs = NULL;
 394              rv = SecCmsDigestContextFinishMultiple(sigd->contentInfo.digcx, &digestalgs, &digests);
 395              if (rv != SECSuccess)
 396                  goto loser;		/* error has been set by SecCmsDigestContextFinishMultiple */
 397              rv = SecCmsSignedDataSetDigests(sigd, digestalgs, digests);
 398              if (rv != SECSuccess)
 399                  goto loser;		/* error has been set by SecCmsSignedDataSetDigests */
 400          }
 401  	SecCmsDigestContextDestroy(sigd->contentInfo.digcx);
 402  	sigd->contentInfo.digcx = NULL;
 403      }
 404  
 405  loser:
 406      return rv;
 407  }
 408  
 409  /*
 410   * SecCmsSignedDataDecodeAfterEnd - do all the necessary things to a SignedData
 411   *     after all decoding is finished.
 412   */
 413  OSStatus
 414  SecCmsSignedDataDecodeAfterEnd(SecCmsSignedDataRef sigd)
 415  {
 416      SecCmsSignerInfoRef *signerinfos;
 417      int i;
 418  
 419      if (!sigd) {
 420          return SECFailure;
 421      }
 422  
 423      /* set cmsg for all the signerinfos */
 424      signerinfos = sigd->signerInfos;
 425  
 426      /* set signedData for all the signerinfos */
 427      if (signerinfos) {
 428  	for (i = 0; signerinfos[i] != NULL; i++)
 429  	    signerinfos[i]->signedData = sigd;
 430      }
 431  
 432      return SECSuccess;
 433  }
 434  
 435  /* 
 436   * SecCmsSignedDataGetSignerInfos - retrieve the SignedData's signer list
 437   */
 438  SecCmsSignerInfoRef *
 439  SecCmsSignedDataGetSignerInfos(SecCmsSignedDataRef sigd)
 440  {
 441      return sigd->signerInfos;
 442  }
 443  
 444  int
 445  SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd)
 446  {
 447      return SecCmsArrayCount((void **)sigd->signerInfos);
 448  }
 449  
 450  SecCmsSignerInfoRef
 451  SecCmsSignedDataGetSignerInfo(SecCmsSignedDataRef sigd, int i)
 452  {
 453      return sigd->signerInfos[i];
 454  }
 455  
 456  /* 
 457   * SecCmsSignedDataGetDigestAlgs - retrieve the SignedData's digest algorithm list
 458   */
 459  SECAlgorithmID **
 460  SecCmsSignedDataGetDigestAlgs(SecCmsSignedDataRef sigd)
 461  {
 462      return sigd->digestAlgorithms;
 463  }
 464  
 465  /*
 466   * SecCmsSignedDataGetContentInfo - return pointer to this signedData's contentinfo
 467   */
 468  SecCmsContentInfoRef
 469  SecCmsSignedDataGetContentInfo(SecCmsSignedDataRef sigd)
 470  {
 471      return &(sigd->contentInfo);
 472  }
 473  
 474  /* 
 475   * SecCmsSignedDataGetCertificateList - retrieve the SignedData's certificate list
 476   */
 477  SecAsn1Item * *
 478  SecCmsSignedDataGetCertificateList(SecCmsSignedDataRef sigd)
 479  {
 480      return sigd->rawCerts;
 481  }
 482  
 483  OSStatus
 484  SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd, SecKeychainRef keychain,
 485  				SECCertUsage certusage, Boolean keepcerts)
 486  {
 487      OSStatus rv = -1;
 488  
 489  #if USE_CDSA_CRYPTO
 490      int ix, certcount = SecCmsArrayCount((void **)sigd->rawCerts);
 491      rv = CERT_ImportCerts(keychain, certusage, certcount, sigd->rawCerts, NULL,
 492  			  keepcerts, PR_FALSE, NULL);
 493      /* XXX CRL handling */
 494  
 495      if (sigd->signerInfos != NULL) {
 496  	/* fill in all signerinfo's certs */
 497  	for (ix = 0; sigd->signerInfos[ix] != NULL; i++)
 498  	    (void)SecCmsSignerInfoGetSigningCertificate(sigd->signerInfos[ix], keychain);
 499      }
 500  #else
 501      // XXX we should only ever import certs for a cert only data blob
 502  #endif
 503  
 504      return rv;
 505  }
 506  
 507  /*
 508   * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
 509   *     of external signatures!
 510   */
 511  
 512  
 513  /*
 514   * SecCmsSignedDataVerifySignerInfo - check the signatures.
 515   *
 516   * The digests were either calculated during decoding (and are stored in the
 517   * signedData itself) or set after decoding using SecCmsSignedDataSetDigests.
 518   *
 519   * The verification checks if the signing cert is valid and has a trusted chain
 520   * for the purpose specified by "policies".
 521   *
 522   * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly.
 523   * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate().
 524   */
 525  OSStatus
 526  SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd, int i, 
 527  			    SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef)
 528  {
 529      SecCmsSignerInfoRef signerinfo;
 530      SecCmsContentInfoRef cinfo;
 531      SECOidData *algiddata;
 532      SecAsn1Item *contentType, *digest;
 533      OSStatus status;
 534  
 535      if (sigd == NULL || sigd->signerInfos == NULL || i >= SecCmsSignedDataSignerInfoCount(sigd)) {
 536          return errSecParam;
 537      }
 538  
 539      cinfo = &(sigd->contentInfo);
 540      signerinfo = sigd->signerInfos[i];
 541  
 542      /* Signature or digest level verificationStatus errors should supercede
 543         certificate level errors, so check the digest and signature first.  */
 544  
 545      /* Find digest and contentType for signerinfo */
 546      algiddata = SecCmsSignerInfoGetDigestAlg(signerinfo);
 547      if (algiddata == NULL) {
 548          return errSecInvalidDigestAlgorithm;
 549      }
 550  
 551      if (!sigd->digests) {
 552          SECAlgorithmID **digestalgs = SecCmsSignedDataGetDigestAlgs(sigd);
 553          SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestalgs);
 554          SecCmsSignedDataSetDigestContext(sigd, digcx);
 555          SecCmsDigestContextDestroy(digcx);
 556      }
 557  
 558      digest = SecCmsSignedDataGetDigestByAlgTag(sigd, algiddata->offset);
 559      if (digest == NULL) {
 560          return errSecDataNotAvailable;
 561      }
 562  
 563      contentType = SecCmsContentInfoGetContentTypeOID(cinfo);
 564  
 565      /* verify signature */
 566      status = SecCmsSignerInfoVerify(signerinfo, digest, contentType);
 567  #if SECTRUST_VERBOSE_DEBUG
 568  	syslog(LOG_ERR, "SecCmsSignedDataVerifySignerInfo: SecCmsSignerInfoVerify returned %d, will %sverify cert",
 569  		(int)status, (status) ? "NOT " : "");
 570  #endif
 571      if (status) {
 572          return status;
 573      }
 574  
 575      /* Now verify the certificate.  We only do this when the signature verification succeeds. Note that this
 576         behavior is different than the macOS code. */
 577      status = SecCmsSignerInfoVerifyCertificate(signerinfo, keychainOrArray, policies, trustRef);
 578  #if SECTRUST_VERBOSE_DEBUG
 579  	syslog(LOG_ERR, "SecCmsSignedDataVerifySignerInfo: SecCmsSignerInfoVerifyCertificate returned %d", (int)status);
 580  #endif
 581  
 582      return status;
 583  }
 584  
 585  #if USE_CDSA_CRYPTO
 586  
 587  /*
 588   * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message
 589   */
 590  OSStatus
 591  SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, 
 592                                    SecKeychainRef keychainOrArray, 
 593                                    CFTypeRef policies)
 594  {
 595      SecCertificateRef cert;
 596      OSStatus rv = SECSuccess;
 597      int i;
 598      int count;
 599  
 600      if (!sigd || !keychainOrArray || !sigd->rawCerts) {
 601  	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 602  	return SECFailure;
 603      }
 604  
 605      count = SecCmsArrayCount((void**)sigd->rawCerts);
 606      for (i=0; i < count; i++) {
 607  	if (sigd->certs && CFArrayGetCount(sigd->certs) > i) {
 608  	    cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i);
 609  	    CFRetain(cert);
 610  	} else {
 611  	    cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]);
 612  	    if (!cert) {
 613  		rv = SECFailure;
 614  		break;
 615  	    }
 616  	}
 617  	rv |= CERT_VerifyCert(keychainOrArray, cert, policies, CFAbsoluteTimeGetCurrent(), NULL);
 618  	CFRelease(cert);
 619      }
 620  
 621      return rv;
 622  }
 623  #else
 624  OSStatus
 625  SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, 
 626                                    SecKeychainRef keychainOrArray, 
 627                                    CFTypeRef policies)
 628  {
 629      OSStatus rv = SECSuccess;
 630  
 631      if (!sigd || !keychainOrArray || !sigd->rawCerts) {
 632  	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 633  	return SECFailure;
 634      }
 635  
 636      SecAsn1Item **cert_datas = sigd->rawCerts;
 637      SecAsn1Item *cert_data;
 638      while ((cert_data = *cert_datas++) != NULL) {
 639          SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
 640          if (cert) {
 641              CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **)&cert, 1, NULL);
 642              rv |= CERT_VerifyCert(keychainOrArray, certs, policies, CFAbsoluteTimeGetCurrent(), NULL);
 643              CFRelease(certs);
 644              CFRelease(cert);
 645          }
 646          else
 647              rv |= SECFailure;
 648      }
 649  
 650      return rv;
 651  }
 652  #endif
 653  
 654  /*
 655   * SecCmsSignedDataHasDigests - see if we have digests in place
 656   */
 657  Boolean
 658  SecCmsSignedDataHasDigests(SecCmsSignedDataRef sigd)
 659  {
 660      return (sigd->digests != NULL);
 661  }
 662  
 663  OSStatus
 664  SecCmsSignedDataAddCertList(SecCmsSignedDataRef sigd, CFArrayRef certlist)
 665  {
 666      PORT_Assert(certlist != NULL);
 667  
 668      if (certlist == NULL)
 669  	return SECFailure;
 670  
 671      if (!sigd->certs)
 672  	sigd->certs = CFArrayCreateMutableCopy(NULL, 0, certlist);
 673      else
 674      {
 675  	CFRange certlistRange = { 0, CFArrayGetCount(certlist) };
 676  	CFArrayAppendArray(sigd->certs, certlist, certlistRange);
 677      }
 678  
 679      return SECSuccess;
 680  }
 681  
 682  /*
 683   * SecCmsSignedDataAddCertChain - add cert and its entire chain to the set of certs 
 684   */
 685  OSStatus
 686  SecCmsSignedDataAddCertChain(SecCmsSignedDataRef sigd, SecCertificateRef cert)
 687  {
 688      CFArrayRef certlist;
 689      SECCertUsage usage;
 690      OSStatus rv;
 691  
 692      usage = certUsageEmailSigner;
 693  
 694      /* do not include root */
 695      certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE, PR_FALSE);
 696      if (certlist == NULL)
 697  	return SECFailure;
 698  
 699      rv = SecCmsSignedDataAddCertList(sigd, certlist);
 700      CFRelease(certlist);
 701  
 702      return rv;
 703  }
 704  
 705  OSStatus
 706  SecCmsSignedDataAddCertificate(SecCmsSignedDataRef sigd, SecCertificateRef cert)
 707  {
 708      PORT_Assert(cert != NULL);
 709  
 710      if (cert == NULL)
 711  	return SECFailure;
 712  
 713      if (!sigd->certs)
 714  	sigd->certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
 715  
 716      CFArrayAppendValue(sigd->certs, cert);
 717  
 718      return SECSuccess;
 719  }
 720  
 721  Boolean
 722  SecCmsSignedDataContainsCertsOrCrls(SecCmsSignedDataRef sigd)
 723  {
 724      if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
 725  	return PR_TRUE;
 726      else if (sigd->rawCrls != NULL && sigd->rawCrls[0] != NULL)
 727  	return PR_TRUE;
 728      else
 729  	return PR_FALSE;
 730  }
 731  
 732  OSStatus
 733  SecCmsSignedDataAddSignerInfo(SecCmsSignedDataRef sigd,
 734  			      SecCmsSignerInfoRef signerinfo)
 735  {
 736      void *mark;
 737      OSStatus rv;
 738      SECOidTag digestalgtag;
 739      PLArenaPool *poolp;
 740  
 741      poolp = sigd->contentInfo.cmsg->poolp;
 742  
 743      mark = PORT_ArenaMark(poolp);
 744  
 745      /* add signerinfo */
 746      rv = SecCmsArrayAdd(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
 747      if (rv != SECSuccess)
 748  	goto loser;
 749  
 750      /*
 751       * add empty digest
 752       * Empty because we don't have it yet. Either it gets created during encoding
 753       * (if the data is present) or has to be set externally.
 754       * XXX maybe pass it in optionally?
 755       */
 756      digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
 757      rv = SecCmsSignedDataSetDigestValue(sigd, digestalgtag, NULL);
 758      if (rv != SECSuccess)
 759  	goto loser;
 760  
 761      /*
 762       * The last thing to get consistency would be adding the digest.
 763       */
 764  
 765      PORT_ArenaUnmark(poolp, mark);
 766      return SECSuccess;
 767  
 768  loser:
 769      PORT_ArenaRelease (poolp, mark);
 770      return SECFailure;
 771  }
 772  
 773  SecAsn1Item *
 774  SecCmsSignedDataGetDigestByAlgTag(SecCmsSignedDataRef sigd, SECOidTag algtag)
 775  {
 776      int idx;
 777  
 778      if(sigd == NULL || sigd->digests == NULL) {
 779          return NULL;
 780      }
 781      idx = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, algtag);
 782      return (idx >= 0)?(sigd->digests)[idx]:NULL;
 783  }
 784  
 785  OSStatus
 786  SecCmsSignedDataSetDigestContext(SecCmsSignedDataRef sigd,
 787  				 SecCmsDigestContextRef digestContext)
 788  {
 789      SECAlgorithmID **digestalgs;
 790      SecAsn1Item * *digests;
 791  
 792      if (SecCmsDigestContextFinishMultiple(digestContext, &digestalgs, &digests) != SECSuccess)
 793  	goto loser;
 794      if (SecCmsSignedDataSetDigests(sigd, digestalgs, digests) != SECSuccess)
 795  	goto loser;
 796  
 797      return 0;
 798  loser:
 799      return PORT_GetError();
 800  }
 801  
 802  /*
 803   * SecCmsSignedDataSetDigests - set a signedData's digests member
 804   *
 805   * "digestalgs" - array of digest algorithm IDs
 806   * "digests"    - array of digests corresponding to the digest algorithms
 807   */
 808  OSStatus
 809  SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd,
 810  				SECAlgorithmID **digestalgs,
 811  				SecAsn1Item * *digests)
 812  {
 813      int cnt, i, idx;
 814  
 815      /* Check input structure and items in structure */
 816      if (sigd == NULL || sigd->digestAlgorithms == NULL || sigd->contentInfo.cmsg == NULL ||
 817          sigd->contentInfo.cmsg->poolp == NULL) {
 818          PORT_SetError(SEC_ERROR_INVALID_ARGS);
 819          return SECFailure;
 820      }
 821  
 822      /* Since we'll generate a empty digest for content-less messages
 823         whether or not they're detached, we have to avoid overwriting
 824         externally set digest for detached content => return early */
 825      if (sigd->digests && sigd->digests[0])
 826  	return 0;
 827  
 828      /* we assume that the digests array is just not there yet */
 829  /*
 830      PORT_Assert(sigd->digests == NULL);
 831      if (sigd->digests != NULL) {
 832  	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
 833  	return SECFailure;
 834      }
 835  */
 836      /* now allocate one (same size as digestAlgorithms) */
 837      if (sigd->digests == NULL) {
 838          cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
 839          sigd->digests = PORT_ArenaZAlloc(sigd->contentInfo.cmsg->poolp, (cnt + 1) * sizeof(SecAsn1Item *));
 840          if (sigd->digests == NULL) {
 841              PORT_SetError(SEC_ERROR_NO_MEMORY);
 842              return SECFailure;
 843          }
 844      }
 845      
 846      for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
 847  	/* try to find the sigd's i'th digest algorithm in the array we passed in */
 848  	idx = SecCmsAlgArrayGetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
 849  	if (idx < 0) {
 850  	    PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
 851  	    return SECFailure;
 852  	}
 853  
 854  	/* found it - now set it */
 855  	if ((sigd->digests[i] = SECITEM_AllocItem(sigd->contentInfo.cmsg->poolp, NULL, 0)) == NULL ||
 856  	    SECITEM_CopyItem(sigd->contentInfo.cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
 857  	{
 858  	    PORT_SetError(SEC_ERROR_NO_MEMORY);
 859  	    return SECFailure;
 860  	}
 861      }
 862      return SECSuccess;
 863  }
 864  
 865  OSStatus
 866  SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd,
 867  				SECOidTag digestalgtag,
 868  				SecAsn1Item * digestdata)
 869  {
 870      SecAsn1Item * digest = NULL;
 871      PLArenaPool *poolp;
 872      void *mark;
 873      int n, cnt;
 874  
 875      poolp = sigd->contentInfo.cmsg->poolp;
 876  
 877      mark = PORT_ArenaMark(poolp);
 878  
 879     
 880      if (digestdata) {
 881          digest = (SecAsn1Item *) PORT_ArenaZAlloc(poolp,sizeof(SecAsn1Item));
 882  
 883  	/* copy digestdata item to arena (in case we have it and are not only making room) */
 884  	if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
 885  	    goto loser;
 886      }
 887  
 888      /* now allocate one (same size as digestAlgorithms) */
 889      if (sigd->digests == NULL) {
 890          cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
 891          sigd->digests = PORT_ArenaZAlloc(sigd->contentInfo.cmsg->poolp, (cnt + 1) * sizeof(SecAsn1Item *));
 892          if (sigd->digests == NULL) {
 893  	        PORT_SetError(SEC_ERROR_NO_MEMORY);
 894  	        return SECFailure;
 895          }
 896      }
 897  
 898      n = -1;
 899      if (sigd->digestAlgorithms != NULL)
 900  	n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
 901  
 902      /* if not found, add a digest */
 903      if (n < 0) {
 904  	if (SecCmsSignedDataAddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
 905  	    goto loser;
 906      } else {
 907  	/* replace NULL pointer with digest item (and leak previous value) */
 908  	sigd->digests[n] = digest;
 909      }
 910  
 911      PORT_ArenaUnmark(poolp, mark);
 912      return SECSuccess;
 913  
 914  loser:
 915      PORT_ArenaRelease(poolp, mark);
 916      return SECFailure;
 917  }
 918  
 919  OSStatus
 920  SecCmsSignedDataAddDigest(PRArenaPool *poolp,
 921  				SecCmsSignedDataRef sigd,
 922  				SECOidTag digestalgtag,
 923  				SecAsn1Item * digest)
 924  {
 925      SECAlgorithmID *digestalg;
 926      void *mark;
 927  
 928      mark = PORT_ArenaMark(poolp);
 929  
 930      digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
 931      if (digestalg == NULL)
 932  	goto loser;
 933  
 934      if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
 935  	goto loser;
 936  
 937      if (SecCmsArrayAdd(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess ||
 938  	/* even if digest is NULL, add dummy to have same-size array */
 939  	SecCmsArrayAdd(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess)
 940      {
 941  	goto loser;
 942      }
 943  
 944      PORT_ArenaUnmark(poolp, mark);
 945      return SECSuccess;
 946  
 947  loser:
 948      PORT_ArenaRelease(poolp, mark);
 949      return SECFailure;
 950  }
 951  
 952  SecAsn1Item *
 953  SecCmsSignedDataGetDigestValue(SecCmsSignedDataRef sigd, SECOidTag digestalgtag)
 954  {
 955      int n;
 956  
 957      if (sigd->digestAlgorithms == NULL)
 958  	return NULL;
 959  
 960      n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
 961  
 962      return (n < 0) ? NULL : sigd->digests[n];
 963  }
 964  
 965  /* =============================================================================
 966   * Misc. utility functions
 967   */
 968  
 969  /*
 970   * SecCmsSignedDataCreateCertsOnly - create a certs-only SignedData.
 971   *
 972   * cert          - base certificates that will be included
 973   * include_chain - if true, include the complete cert chain for cert
 974   *
 975   * More certs and chains can be added via AddCertificate and AddCertChain.
 976   *
 977   * An error results in a return value of NULL and an error set.
 978   *
 979   * XXXX CRLs
 980   */
 981  SecCmsSignedDataRef
 982  SecCmsSignedDataCreateCertsOnly(SecCmsMessageRef cmsg, SecCertificateRef cert, Boolean include_chain)
 983  {
 984      SecCmsSignedDataRef sigd;
 985      void *mark;
 986      PLArenaPool *poolp;
 987      OSStatus rv;
 988  
 989      poolp = cmsg->poolp;
 990      mark = PORT_ArenaMark(poolp);
 991  
 992      sigd = SecCmsSignedDataCreate(cmsg);
 993      if (sigd == NULL)
 994  	goto loser;
 995  
 996      /* no signerinfos, thus no digestAlgorithms */
 997  
 998      /* but certs */
 999      if (include_chain) {
1000  	rv = SecCmsSignedDataAddCertChain(sigd, cert);
1001      } else {
1002  	rv = SecCmsSignedDataAddCertificate(sigd, cert);
1003      }
1004      if (rv != SECSuccess)
1005  	goto loser;
1006  
1007      /* RFC2630 5.2 sez:
1008       * In the degenerate case where there are no signers, the
1009       * EncapsulatedContentInfo value being "signed" is irrelevant.  In this
1010       * case, the content type within the EncapsulatedContentInfo value being
1011       * "signed" should be id-data (as defined in section 4), and the content
1012       * field of the EncapsulatedContentInfo value should be omitted.
1013       */
1014      rv = SecCmsContentInfoSetContentData(&(sigd->contentInfo), NULL, PR_TRUE);
1015      if (rv != SECSuccess)
1016  	goto loser;
1017  
1018      PORT_ArenaUnmark(poolp, mark);
1019      return sigd;
1020  
1021  loser:
1022      if (sigd)
1023  	SecCmsSignedDataDestroy(sigd);
1024      PORT_ArenaRelease(poolp, mark);
1025      return NULL;
1026  }
1027  
1028  /* TODO:
1029   * SecCmsSignerInfoGetReceiptRequest()
1030   * SecCmsSignedDataHasReceiptRequest()
1031   * easy way to iterate over signers
1032   */
1033