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 signerInfo methods. 36 */ 37 38#include <Security/SecCmsSignerInfo.h> 39#include "SecSMIMEPriv.h" 40 41#include "cmslocal.h" 42 43#include "cert.h" 44#include "SecAsn1Item.h" 45#include "secoid.h" 46#include "cryptohi.h" 47 48#include <security_asn1/secasn1.h> 49#include <security_asn1/secerr.h> 50#include <security_asn1/secport.h> 51 52#if USE_CDSA_CRYPTO 53#include <Security/SecKeychain.h> 54#endif 55 56#include <Security/SecIdentity.h> 57#include <Security/SecCertificateInternal.h> 58#include <Security/SecInternal.h> 59#include <Security/SecKeyPriv.h> 60#include <utilities/SecCFWrappers.h> 61#include <CoreFoundation/CFTimeZone.h> 62 63 64#define HIDIGIT(v) (((v) / 10) + '0') 65#define LODIGIT(v) (((v) % 10) + '0') 66 67#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9')) 68#define CAPTURE(var,p,label) \ 69{ \ 70 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \ 71 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \ 72} 73 74 75static OSStatus 76DER_UTCTimeToCFDate(const SecAsn1Item * utcTime, CFAbsoluteTime *date) 77{ 78 char *string = (char *)utcTime->Data; 79 int year, month, mday, hour, minute, second, hourOff, minOff; 80 81 /* Verify time is formatted properly and capture information */ 82 second = 0; 83 hourOff = 0; 84 minOff = 0; 85 CAPTURE(year,string+0,loser); 86 if (year < 50) { 87 /* ASSUME that year # is in the 2000's, not the 1900's */ 88 year += 2000; 89 } else { 90 year += 1900; 91 } 92 CAPTURE(month,string+2,loser); 93 if ((month == 0) || (month > 12)) goto loser; 94 CAPTURE(mday,string+4,loser); 95 if ((mday == 0) || (mday > 31)) goto loser; 96 CAPTURE(hour,string+6,loser); 97 if (hour > 23) goto loser; 98 CAPTURE(minute,string+8,loser); 99 if (minute > 59) goto loser; 100 if (ISDIGIT(string[10])) { 101 CAPTURE(second,string+10,loser); 102 if (second > 59) goto loser; 103 string += 2; 104 } 105 if (string[10] == '+') { 106 CAPTURE(hourOff,string+11,loser); 107 if (hourOff > 23) goto loser; 108 CAPTURE(minOff,string+13,loser); 109 if (minOff > 59) goto loser; 110 } else if (string[10] == '-') { 111 CAPTURE(hourOff,string+11,loser); 112 if (hourOff > 23) goto loser; 113 hourOff = -hourOff; 114 CAPTURE(minOff,string+13,loser); 115 if (minOff > 59) goto loser; 116 minOff = -minOff; 117 } else if (string[10] != 'Z') { 118 goto loser; 119 } 120 121 if (hourOff == 0 && minOff == 0) { 122 *date = CFAbsoluteTimeForGregorianZuluMoment(year, month, mday, hour, minute, second); 123 } else { 124 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, (hourOff * 60 + minOff) * 60); 125 *date = CFAbsoluteTimeForGregorianMoment(tz, year, month, mday, hour, minute, second); 126 CFReleaseSafe(tz); 127 } 128 129 return SECSuccess; 130 131loser: 132 return SECFailure; 133} 134 135static OSStatus 136DER_CFDateToUTCTime(CFAbsoluteTime date, SecAsn1Item * utcTime) 137{ 138 unsigned char *d; 139 140 utcTime->Length = 13; 141 utcTime->Data = d = PORT_Alloc(13); 142 if (!utcTime->Data) 143 return SECFailure; 144 145 int year; 146 int month; 147 int day; 148 int hour; 149 int minute; 150 int second; 151 152 if (!CFCalendarDecomposeAbsoluteTime(SecCFCalendarGetZulu(), date, "yMdHms", &year, &month, &day, &hour, &minute, &second)) 153 return SECFailure; 154 155 156 /* UTC time does not handle the years before 1950 */ 157 if (year < 1950) 158 return SECFailure; 159 160 /* remove the century since it's added to the year by the 161 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */ 162 year %= 100; 163 164 d[0] = HIDIGIT(year); 165 d[1] = LODIGIT(year); 166 d[2] = HIDIGIT(month); 167 d[3] = LODIGIT(month); 168 d[4] = HIDIGIT(day); 169 d[5] = LODIGIT(day); 170 d[6] = HIDIGIT(hour); 171 d[7] = LODIGIT(hour); 172 d[8] = HIDIGIT(minute); 173 d[9] = LODIGIT(minute); 174 d[10] = HIDIGIT(second); 175 d[11] = LODIGIT(second); 176 d[12] = 'Z'; 177 return SECSuccess; 178} 179 180/* ============================================================================= 181 * SIGNERINFO 182 */ 183SecCmsSignerInfoRef 184nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag); 185 186SecCmsSignerInfoRef 187SecCmsSignerInfoCreateWithSubjKeyID(SecCmsSignedDataRef sigd, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag) 188{ 189 return nss_cmssignerinfo_create(sigd, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag); 190} 191 192SecCmsSignerInfoRef 193SecCmsSignerInfoCreate(SecCmsSignedDataRef sigd, SecIdentityRef identity, SECOidTag digestalgtag) 194{ 195 SecCmsSignerInfoRef signerInfo = NULL; 196 SecCertificateRef cert = NULL; 197 SecPrivateKeyRef signingKey = NULL; 198 199 if (SecIdentityCopyCertificate(identity, &cert)) 200 goto loser; 201 if (SecIdentityCopyPrivateKey(identity, &signingKey)) 202 goto loser; 203 204 signerInfo = nss_cmssignerinfo_create(sigd, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag); 205 206loser: 207 if (cert) 208 CFRelease(cert); 209 if (signingKey) 210 CFRelease(signingKey); 211 212 return signerInfo; 213} 214 215SecCmsSignerInfoRef 216nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag) 217{ 218 void *mark; 219 SecCmsSignerInfoRef signerinfo; 220 int version; 221 PLArenaPool *poolp; 222 223 poolp = sigd->contentInfo.cmsg->poolp; 224 225 mark = PORT_ArenaMark(poolp); 226 227 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo)); 228 if (signerinfo == NULL) { 229 PORT_ArenaRelease(poolp, mark); 230 return NULL; 231 } 232 233 234 signerinfo->signedData = sigd; 235 236 switch(type) { 237 case SecCmsSignerIDIssuerSN: 238 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN; 239 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL) 240 goto loser; 241 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) 242 goto loser; 243 break; 244 case SecCmsSignerIDSubjectKeyID: 245 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID; 246 PORT_Assert(subjKeyID); 247 if (!subjKeyID) 248 goto loser; 249 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item); 250 SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID, 251 subjKeyID); 252 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey); 253 if (!signerinfo->pubKey) 254 goto loser; 255 break; 256 default: 257 goto loser; 258 } 259 260 if (!signingKey) 261 goto loser; 262 263 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey); 264 if (!signerinfo->signingKey) 265 goto loser; 266 267 /* set version right now */ 268 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN; 269 /* RFC2630 5.3 "version is the syntax version number. If the .... " */ 270 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) 271 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY; 272 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version); 273 274 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess) 275 goto loser; 276 277 if (SecCmsSignedDataAddSignerInfo(sigd, signerinfo)) 278 goto loser; 279 280 PORT_ArenaUnmark(poolp, mark); 281 return signerinfo; 282 283loser: 284 PORT_ArenaRelease(poolp, mark); 285 return NULL; 286} 287 288/* 289 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure 290 */ 291void 292SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si) 293{ 294 if (si->cert != NULL) 295 CERT_DestroyCertificate(si->cert); 296 297 if (si->certList != NULL) 298 CFRelease(si->certList); 299 300 /* XXX storage ??? */ 301} 302 303static SecAsn1AlgId SecCertificateGetPublicKeyAlgorithmID(SecCertificateRef cert) 304{ 305 const DERAlgorithmId *length_data_swapped = SecCertificateGetPublicKeyAlgorithm(cert); 306 SecAsn1AlgId temp = { 307 { length_data_swapped->oid.length, length_data_swapped->oid.data }, 308 { length_data_swapped->params.length, length_data_swapped->params.data } }; 309 310 return temp; 311} 312 313/* 314 * SecCmsSignerInfoSign - sign something 315 * 316 */ 317OSStatus 318SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType) 319{ 320 SecCertificateRef cert; 321 SecPrivateKeyRef privkey = NULL; 322 SECOidTag digestalgtag; 323 SECOidTag pubkAlgTag; 324 SecAsn1Item signature = { 0 }; 325 OSStatus rv; 326 PLArenaPool *poolp, *tmppoolp; 327 const SECAlgorithmID *algID = NULL; 328 //CERTSubjectPublicKeyInfo *spki; 329 330 PORT_Assert (digest != NULL); 331 332 poolp = signerinfo->signedData->contentInfo.cmsg->poolp; 333 334 switch (signerinfo->signerIdentifier.identifierType) { 335 case SecCmsSignerIDIssuerSN: 336 privkey = signerinfo->signingKey; 337 signerinfo->signingKey = NULL; 338 cert = signerinfo->cert; 339#if USE_CDSA_CRYPTO 340 if (SecCertificateGetAlgorithmID(cert,&algID)) { 341 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 342 goto loser; 343 } 344#else 345 SecAsn1AlgId _algID = SecCertificateGetPublicKeyAlgorithmID(cert); 346 algID = &_algID; 347#endif 348 break; 349 case SecCmsSignerIDSubjectKeyID: 350 privkey = signerinfo->signingKey; 351 signerinfo->signingKey = NULL; 352#if 0 353 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); 354 SECKEY_DestroyPublicKey(signerinfo->pubKey); 355 signerinfo->pubKey = NULL; 356 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); 357 SECKEY_DestroySubjectPublicKeyInfo(spki); 358 algID = &freeAlgID; 359#else 360#if USE_CDSA_CRYPTO 361 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) { 362 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 363 goto loser; 364 } 365#endif 366#endif 367 CFRelease(signerinfo->pubKey); 368 signerinfo->pubKey = NULL; 369 break; 370 default: 371 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE); 372 goto loser; 373 } 374 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 375 pubkAlgTag = SECOID_GetAlgorithmTag(algID); 376#if USE_CDSA_CRYPTO 377 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) { 378 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); 379 } 380#endif 381#if 0 382 // @@@ Not yet 383 /* Fortezza MISSI have weird signature formats. 384 * Map them to standard DSA formats 385 */ 386 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag); 387#endif 388 389 if (signerinfo->authAttr != NULL) { 390 SecAsn1Item encoded_attrs; 391 392 /* find and fill in the message digest attribute. */ 393 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), 394 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); 395 if (rv != SECSuccess) 396 goto loser; 397 398 if (contentType != NULL) { 399 /* if the caller wants us to, find and fill in the content type attribute. */ 400 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), 401 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); 402 if (rv != SECSuccess) 403 goto loser; 404 } 405 406 if ((tmppoolp = PORT_NewArena (1024)) == NULL) { 407 PORT_SetError(SEC_ERROR_NO_MEMORY); 408 goto loser; 409 } 410 411 /* 412 * Before encoding, reorder the attributes so that when they 413 * are encoded, they will be conforming DER, which is required 414 * to have a specific order and that is what must be used for 415 * the hash/signature. We do this here, rather than building 416 * it into EncodeAttributes, because we do not want to do 417 * such reordering on incoming messages (which also uses 418 * EncodeAttributes) or our old signatures (and other "broken" 419 * implementations) will not verify. So, we want to guarantee 420 * that we send out good DER encodings of attributes, but not 421 * to expect to receive them. 422 */ 423 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess) 424 goto loser; 425 426 encoded_attrs.Data = NULL; 427 encoded_attrs.Length = 0; 428 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr), 429 &encoded_attrs) == NULL) 430 goto loser; 431 432#if USE_CDSA_CRYPTO 433 rv = SEC_SignData(&signature, encoded_attrs.Data, encoded_attrs.Length, 434 privkey, digestalgtag, pubkAlgTag); 435#else 436 signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize); 437 signature.Data = PORT_ZAlloc(signature.Length); 438 if (!signature.Data) { 439 signature.Length = 0; 440 goto loser; 441 } 442 rv = SecKeyDigestAndSign(privkey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signature.Data, &signature.Length); 443 if (rv) { 444 PORT_ZFree(signature.Data, signature.Length); 445 signature.Length = 0; 446 } 447#endif 448 449 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ 450 } else { 451 signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize); 452 signature.Data = PORT_ZAlloc(signature.Length); 453 if (!signature.Data) { 454 signature.Length = 0; 455 goto loser; 456 } 457 rv = SecKeySignDigest(privkey, &signerinfo->digestAlg, digest->Data, digest->Length, 458 signature.Data, &signature.Length); 459 if (rv) { 460 PORT_ZFree(signature.Data, signature.Length); 461 signature.Length = 0; 462 } 463 } 464 SECKEY_DestroyPrivateKey(privkey); 465 privkey = NULL; 466 467 if (rv != SECSuccess) 468 goto loser; 469 470 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) 471 != SECSuccess) 472 goto loser; 473 474 SECITEM_FreeItem(&signature, PR_FALSE); 475 476 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, 477 NULL) != SECSuccess) 478 goto loser; 479 480 return SECSuccess; 481 482loser: 483 if (signature.Length != 0) 484 SECITEM_FreeItem (&signature, PR_FALSE); 485 if (privkey) 486 SECKEY_DestroyPrivateKey(privkey); 487 return SECFailure; 488} 489 490#if !USE_CDSA_CRYPTO 491static CFArrayRef 492SecCmsSignerInfoCopySigningCertificates(SecCmsSignerInfoRef signerinfo) 493{ 494 CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 495 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts; 496 SecAsn1Item *cert_data; 497 if (cert_datas) while ((cert_data = *cert_datas) != NULL) { 498 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length); 499 if (cert) { 500 switch (signerinfo->signerIdentifier.identifierType) { 501 case SecCmsSignerIDIssuerSN: 502 if (CERT_CheckIssuerAndSerial(cert, 503 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer), 504 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber))) 505 CFArrayInsertValueAtIndex(certs, 0, cert); 506 else 507 CFArrayAppendValue(certs, cert); 508 break; 509 case SecCmsSignerIDSubjectKeyID: 510 { 511 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert); 512 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID; 513 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) && 514 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length)) 515 CFArrayInsertValueAtIndex(certs, 0, cert); 516 else 517 CFArrayAppendValue(certs, cert); 518 break; 519 } 520 } 521 CFReleaseNull(cert); 522 } 523 cert_datas++; 524 } 525 526 if ((CFArrayGetCount(certs) == 0) && 527 (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN)) 528 { 529 SecCertificateRef cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN); 530 if (cert) { 531 CFArrayAppendValue(certs, cert); 532 CFRelease(cert); 533 } 534 } 535 return certs; 536} 537#endif 538 539OSStatus 540SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray, 541 CFTypeRef policies, SecTrustRef *trustRef) 542{ 543 CFAbsoluteTime stime; 544 OSStatus rv; 545 546#if USE_CDSA_CRYPTO 547 SecCertificateRef cert; 548 549 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) { 550#else 551 CFArrayRef certs; 552 553 if ((certs = SecCmsSignerInfoCopySigningCertificates(signerinfo)) == NULL) { 554#endif 555 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound; 556 return SECFailure; 557 } 558 /* 559 * Get and convert the signing time; if available, it will be used 560 * both on the cert verification and for importing the sender 561 * email profile. 562 */ 563 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess) 564 stime = CFAbsoluteTimeGetCurrent(); 565 566#if USE_CDSA_CRYPTO 567 rv = CERT_VerifyCert(keychainOrArray, cert, policies, stime, trustRef); 568#else 569 rv = CERT_VerifyCert(keychainOrArray, certs, policies, stime, trustRef); 570 CFRelease(certs); 571#endif 572 if (rv || !trustRef) 573 { 574 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT) 575 { 576 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */ 577 if (signerinfo->verificationStatus == SecCmsVSGoodSignature) 578 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted; 579 } 580 } 581 582 return rv; 583} 584 585/* 586 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo 587 * 588 * Just verifies the signature. The assumption is that verification of the certificate 589 * is done already. 590 */ 591OSStatus 592SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType) 593{ 594 SecPublicKeyRef publickey = NULL; 595 SecCmsAttribute *attr; 596 SecAsn1Item encoded_attrs; 597 SecCertificateRef cert; 598 SecCmsVerificationStatus vs = SecCmsVSUnverified; 599 PLArenaPool *poolp; 600 SECOidTag digestAlgTag, digestEncAlgTag; 601 602 if (signerinfo == NULL) 603 return SECFailure; 604 605 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */ 606 /* cert has not been verified */ 607 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) { 608 vs = SecCmsVSSigningCertNotFound; 609 goto loser; 610 } 611 612#if USE_CDSA_CRYPTO 613 if (SecCertificateCopyPublicKey(cert, &publickey)) { 614 vs = SecCmsVSProcessingError; 615 goto loser; 616 } 617#else 618 publickey = SecCertificateCopyPublicKey(cert); 619 if (publickey == NULL) 620 goto loser; 621#endif 622 623 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg)); 624 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); 625 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { 626 if (contentType) { 627 /* 628 * Check content type 629 * 630 * RFC2630 sez that if there are any authenticated attributes, 631 * then there must be one for content type which matches the 632 * content type of the content being signed, and there must 633 * be one for message digest which matches our message digest. 634 * So check these things first. 635 */ 636 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, 637 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL) 638 { 639 vs = SecCmsVSMalformedSignature; 640 goto loser; 641 } 642 643 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) { 644 vs = SecCmsVSMalformedSignature; 645 goto loser; 646 } 647 } 648 649 /* 650 * Check digest 651 */ 652 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL) 653 { 654 vs = SecCmsVSMalformedSignature; 655 goto loser; 656 } 657 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) { 658 vs = SecCmsVSDigestMismatch; 659 goto loser; 660 } 661 662 if ((poolp = PORT_NewArena (1024)) == NULL) { 663 vs = SecCmsVSProcessingError; 664 goto loser; 665 } 666 667 /* 668 * Check signature 669 * 670 * The signature is based on a digest of the DER-encoded authenticated 671 * attributes. So, first we encode and then we digest/verify. 672 * we trust the decoder to have the attributes in the right (sorted) order 673 */ 674 encoded_attrs.Data = NULL; 675 encoded_attrs.Length = 0; 676 677 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL || 678 encoded_attrs.Data == NULL || encoded_attrs.Length == 0) 679 { 680 vs = SecCmsVSProcessingError; 681 goto loser; 682 } 683 if (errSecSuccess == SecKeyDigestAndVerify(publickey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signerinfo->encDigest.Data, signerinfo->encDigest.Length)) 684 vs = SecCmsVSGoodSignature; 685 else 686 vs = SecCmsVSBadSignature; 687 688 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ 689 690 } else { 691 SecAsn1Item * sig; 692 693 /* No authenticated attributes. The signature is based on the plain message digest. */ 694 sig = &(signerinfo->encDigest); 695 if (sig->Length == 0) 696 goto loser; 697 698 if (SecKeyVerifyDigest(publickey, &signerinfo->digestAlg, digest->Data, digest->Length, sig->Data, sig->Length)) 699 vs = SecCmsVSBadSignature; 700 else 701 vs = SecCmsVSGoodSignature; 702 } 703 704 if (vs == SecCmsVSBadSignature) { 705 /* 706 * XXX Change the generic error into our specific one, because 707 * in that case we get a better explanation out of the Security 708 * Advisor. This is really a bug in our error strings (the 709 * "generic" error has a lousy/wrong message associated with it 710 * which assumes the signature verification was done for the 711 * purposes of checking the issuer signature on a certificate) 712 * but this is at least an easy workaround and/or in the 713 * Security Advisor, which specifically checks for the error 714 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation 715 * in that case but does not similarly check for 716 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would 717 * probably say the wrong thing in the case that it *was* the 718 * certificate signature check that failed during the cert 719 * verification done above. Our error handling is really a mess. 720 */ 721 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) 722 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 723 } 724 725 if (publickey != NULL) 726 CFRelease(publickey); 727 728 signerinfo->verificationStatus = vs; 729 730 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure; 731 732loser: 733 if (publickey != NULL) 734 SECKEY_DestroyPublicKey (publickey); 735 736 signerinfo->verificationStatus = vs; 737 738 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); 739 return SECFailure; 740} 741 742SecCmsVerificationStatus 743SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo) 744{ 745 return signerinfo->verificationStatus; 746} 747 748SECOidData * 749SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo) 750{ 751 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); 752} 753 754SECOidTag 755SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo) 756{ 757 SECOidData *algdata; 758 759 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); 760 if (algdata != NULL) 761 return algdata->offset; 762 else 763 return SEC_OID_UNKNOWN; 764} 765 766CFArrayRef 767SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo) 768{ 769 return signerinfo->certList; 770} 771 772int 773SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo) 774{ 775 unsigned long version; 776 777 /* always take apart the SecAsn1Item */ 778 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess) 779 return 0; 780 else 781 return (int)version; 782} 783 784/* 785 * SecCmsSignerInfoGetSigningTime - return the signing time, 786 * in UTCTime format, of a CMS signerInfo. 787 * 788 * sinfo - signerInfo data for this signer 789 * 790 * Returns a pointer to XXXX (what?) 791 * A return value of NULL is an error. 792 */ 793OSStatus 794SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime) 795{ 796 SecCmsAttribute *attr; 797 SecAsn1Item * value; 798 799 if (sinfo == NULL) 800 return SECFailure; 801 802 if (sinfo->signingTime != 0) { 803 *stime = sinfo->signingTime; /* cached copy */ 804 return SECSuccess; 805 } 806 807 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); 808 /* XXXX multi-valued attributes NIH */ 809 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) 810 return SECFailure; 811 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess) 812 return SECFailure; 813 sinfo->signingTime = *stime; /* make cached copy */ 814 return SECSuccess; 815} 816 817/* 818 * Return the signing cert of a CMS signerInfo. 819 * 820 * the certs in the enclosing SignedData must have been imported already 821 */ 822SecCertificateRef 823SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray) 824{ 825 SecCertificateRef cert = NULL; 826 827 if (signerinfo->cert != NULL) 828 return signerinfo->cert; 829 830 /* @@@ Make sure we search though all the certs in the cms message itself as well, it's silly 831 to require them to be added to a keychain first. */ 832 833#if USE_CDSA_CRYPTO 834 SecCmsSignerIdentifier *sid; 835 836 /* 837 * This cert will also need to be freed, but since we save it 838 * in signerinfo for later, we do not want to destroy it when 839 * we leave this function -- we let the clean-up of the entire 840 * cinfo structure later do the destroy of this cert. 841 */ 842 sid = &signerinfo->signerIdentifier; 843 switch (sid->identifierType) { 844 case SecCmsSignerIDIssuerSN: 845 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, sid->id.issuerAndSN); 846 break; 847 case SecCmsSignerIDSubjectKeyID: 848 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, sid->id.subjectKeyID); 849 break; 850 default: 851 cert = NULL; 852 break; 853 } 854 855 /* cert can be NULL at that point */ 856 signerinfo->cert = cert; /* earmark it */ 857#else 858 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts; 859 SecAsn1Item *cert_data; 860 if (cert_datas) while ((cert_data = *cert_datas) != NULL) { 861 cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length); 862 if (cert) { 863 switch (signerinfo->signerIdentifier.identifierType) { 864 case SecCmsSignerIDIssuerSN: 865 if (CERT_CheckIssuerAndSerial(cert, 866 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer), 867 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber))) 868 signerinfo->cert = cert; 869 break; 870 case SecCmsSignerIDSubjectKeyID: { 871 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert); 872 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID; 873 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) && 874 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length)) 875 signerinfo->cert = cert; 876 } 877 } 878 if (signerinfo->cert) 879 break; 880 CFReleaseNull(cert); 881 } 882 cert_datas++; 883 } 884 885 if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN)) { 886 cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN); 887 signerinfo->cert = cert; 888 } 889#endif 890 891 return cert; 892} 893 894 895/* 896 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer 897 * 898 * sinfo - signerInfo data for this signer 899 * 900 * Returns a CFStringRef containing the common name of the signer. 901 * A return value of NULL is an error. 902 */ 903CFStringRef 904SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo) 905{ 906 SecCertificateRef signercert; 907 CFStringRef commonName = NULL; 908 909 /* will fail if cert is not verified */ 910 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) 911 return NULL; 912 913#if USE_CDSA_CRYPTO 914 SecCertificateGetCommonName(signercert, &commonName); 915#else 916 CFArrayRef commonNames = SecCertificateCopyCommonNames(signercert); 917 if (commonNames) { 918 /* SecCertificateCopyCommonNames doesn't return empty arrays */ 919 commonName = (CFStringRef)CFArrayGetValueAtIndex(commonNames, CFArrayGetCount(commonNames) - 1); 920 CFRetain(commonName); 921 CFRelease(commonNames); 922 } 923#endif 924 925 return commonName; 926} 927 928/* 929 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer 930 * 931 * sinfo - signerInfo data for this signer 932 * 933 * Returns a CFStringRef containing the name of the signer. 934 * A return value of NULL is an error. 935 */ 936CFStringRef 937SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo) 938{ 939 SecCertificateRef signercert; 940 CFStringRef emailAddress = NULL; 941 942 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) 943 return NULL; 944 945#if USE_CDSA_CRYPTO 946 SecCertificateGetEmailAddress(signercert, &emailAddress); 947#else 948 CFArrayRef names = SecCertificateCopyRFC822Names(signercert); 949 if (names) { 950 if (CFArrayGetCount(names) > 0) 951 emailAddress = (CFStringRef)CFArrayGetValueAtIndex(names, 0); 952 if (emailAddress) 953 CFRetain(emailAddress); 954 CFRelease(names); 955 } 956#endif 957 return emailAddress; 958} 959 960 961/* 962 * SecCmsSignerInfoAddAuthAttr - add an attribute to the 963 * authenticated (i.e. signed) attributes of "signerinfo". 964 */ 965OSStatus 966SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr) 967{ 968 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->authAttr), attr); 969} 970 971/* 972 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the 973 * unauthenticated attributes of "signerinfo". 974 */ 975OSStatus 976SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr) 977{ 978 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->unAuthAttr), attr); 979} 980 981/* 982 * SecCmsSignerInfoAddSigningTime - add the signing time to the 983 * authenticated (i.e. signed) attributes of "signerinfo". 984 * 985 * This is expected to be included in outgoing signed 986 * messages for email (S/MIME) but is likely useful in other situations. 987 * 988 * This should only be added once; a second call will do nothing. 989 * 990 * XXX This will probably just shove the current time into "signerinfo" 991 * but it will not actually get signed until the entire item is 992 * processed for encoding. Is this (expected to be small) delay okay? 993 */ 994OSStatus 995SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t) 996{ 997 SecCmsAttribute *attr; 998 SecAsn1Item stime; 999 void *mark; 1000 PLArenaPool *poolp; 1001 1002 poolp = signerinfo->signedData->contentInfo.cmsg->poolp; 1003 1004 mark = PORT_ArenaMark(poolp); 1005 1006 /* create new signing time attribute */ 1007 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess) 1008 goto loser; 1009 1010 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) { 1011 SECITEM_FreeItem (&stime, PR_FALSE); 1012 goto loser; 1013 } 1014 1015 SECITEM_FreeItem (&stime, PR_FALSE); 1016 1017 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1018 goto loser; 1019 1020 PORT_ArenaUnmark (poolp, mark); 1021 1022 return SECSuccess; 1023 1024loser: 1025 PORT_ArenaRelease (poolp, mark); 1026 return SECFailure; 1027} 1028 1029/* 1030 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the 1031 * authenticated (i.e. signed) attributes of "signerinfo". 1032 * 1033 * This is expected to be included in outgoing signed 1034 * messages for email (S/MIME). 1035 */ 1036OSStatus 1037SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo) 1038{ 1039 SecCmsAttribute *attr; 1040 SecAsn1Item * smimecaps = NULL; 1041 void *mark; 1042 PLArenaPool *poolp; 1043 1044 poolp = signerinfo->signedData->contentInfo.cmsg->poolp; 1045 1046 mark = PORT_ArenaMark(poolp); 1047 1048 smimecaps = SECITEM_AllocItem(poolp, NULL, 0); 1049 if (smimecaps == NULL) 1050 goto loser; 1051 1052 /* create new signing time attribute */ 1053#if 1 1054 // @@@ We don't do Fortezza yet. 1055 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, PR_FALSE) != SECSuccess) 1056#else 1057 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, 1058 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess) 1059#endif 1060 goto loser; 1061 1062 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL) 1063 goto loser; 1064 1065 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1066 goto loser; 1067 1068 PORT_ArenaUnmark (poolp, mark); 1069 return SECSuccess; 1070 1071loser: 1072 PORT_ArenaRelease (poolp, mark); 1073 return SECFailure; 1074} 1075 1076/* 1077 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 1078 * authenticated (i.e. signed) attributes of "signerinfo". 1079 * 1080 * This is expected to be included in outgoing signed messages for email (S/MIME). 1081 */ 1082OSStatus 1083SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) 1084{ 1085 SecCmsAttribute *attr; 1086 SecAsn1Item * smimeekp = NULL; 1087 void *mark; 1088 PLArenaPool *poolp; 1089 1090#if 0 1091 CFTypeRef policy; 1092 1093 /* verify this cert for encryption */ 1094 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); 1095 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { 1096 CFRelease(policy); 1097 return SECFailure; 1098 } 1099 CFRelease(policy); 1100#endif 1101 1102 poolp = signerinfo->signedData->contentInfo.cmsg->poolp; 1103 mark = PORT_ArenaMark(poolp); 1104 1105 smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 1106 if (smimeekp == NULL) 1107 goto loser; 1108 1109 /* create new signing time attribute */ 1110 if (SecSMIMECreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) 1111 goto loser; 1112 1113 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 1114 goto loser; 1115 1116 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1117 goto loser; 1118 1119 PORT_ArenaUnmark (poolp, mark); 1120 return SECSuccess; 1121 1122loser: 1123 PORT_ArenaRelease (poolp, mark); 1124 return SECFailure; 1125} 1126 1127/* 1128 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 1129 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft. 1130 * 1131 * This is expected to be included in outgoing signed messages for email (S/MIME), 1132 * if compatibility with Microsoft mail clients is wanted. 1133 */ 1134OSStatus 1135SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) 1136{ 1137 SecCmsAttribute *attr; 1138 SecAsn1Item * smimeekp = NULL; 1139 void *mark; 1140 PLArenaPool *poolp; 1141 1142#if 0 1143 CFTypeRef policy; 1144 1145 /* verify this cert for encryption */ 1146 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); 1147 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { 1148 CFRelease(policy); 1149 return SECFailure; 1150 } 1151 CFRelease(policy); 1152#endif 1153 1154 poolp = signerinfo->signedData->contentInfo.cmsg->poolp; 1155 mark = PORT_ArenaMark(poolp); 1156 1157 smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 1158 if (smimeekp == NULL) 1159 goto loser; 1160 1161 /* create new signing time attribute */ 1162 if (SecSMIMECreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) 1163 goto loser; 1164 1165 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 1166 goto loser; 1167 1168 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1169 goto loser; 1170 1171 PORT_ArenaUnmark (poolp, mark); 1172 return SECSuccess; 1173 1174loser: 1175 PORT_ArenaRelease (poolp, mark); 1176 return SECFailure; 1177} 1178 1179/* 1180 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo 1181 * 1182 * 1. digest the DER-encoded signature value of the original signerinfo 1183 * 2. create new signerinfo with correct version, sid, digestAlg 1184 * 3. add message-digest authAttr, but NO content-type 1185 * 4. sign the authAttrs 1186 * 5. DER-encode the new signerInfo 1187 * 6. add the whole thing to original signerInfo's unAuthAttrs 1188 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute 1189 * 1190 * XXXX give back the new signerinfo? 1191 */ 1192OSStatus 1193SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo, 1194 SECOidTag digestalg, SecIdentityRef identity) 1195{ 1196 /* XXXX TBD XXXX */ 1197 return SECFailure; 1198} 1199 1200/* 1201 * XXXX the following needs to be done in the S/MIME layer code 1202 * after signature of a signerinfo is verified 1203 */ 1204OSStatus 1205SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo) 1206{ 1207 return -4 /*unImp*/; 1208} 1209 1210/* 1211 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer 1212 */ 1213OSStatus 1214SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage) 1215{ 1216 if (signerinfo->cert == NULL) 1217 return SECFailure; 1218 1219 /* don't leak if we get called twice */ 1220 if (signerinfo->certList != NULL) { 1221 CFRelease(signerinfo->certList); 1222 signerinfo->certList = NULL; 1223 } 1224 1225 switch (cm) { 1226 case SecCmsCMNone: 1227 signerinfo->certList = NULL; 1228 break; 1229 case SecCmsCMCertOnly: 1230 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert); 1231 break; 1232 case SecCmsCMCertChain: 1233 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE); 1234 break; 1235 case SecCmsCMCertChainWithRoot: 1236 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE); 1237 break; 1238 } 1239 1240 if (cm != SecCmsCMNone && signerinfo->certList == NULL) 1241 return SECFailure; 1242 1243 return SECSuccess; 1244} 1245