cmssiginfo.c (36643B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * CMS signerInfo methods. 7 */ 8 9 #include "cmslocal.h" 10 11 #include "cert.h" 12 #include "keyhi.h" 13 #include "secasn1.h" 14 #include "secitem.h" 15 #include "secoid.h" 16 #include "pk11func.h" 17 #include "prtime.h" 18 #include "secerr.h" 19 #include "secder.h" 20 #include "cryptohi.h" 21 22 #include "smime.h" 23 24 /* ============================================================================= 25 * SIGNERINFO 26 */ 27 NSSCMSSignerInfo * 28 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 29 CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 30 SECKEYPrivateKey *signingKey, SECOidTag digestalgtag); 31 32 NSSCMSSignerInfo * 33 NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID, 34 SECKEYPublicKey *pubKey, 35 SECKEYPrivateKey *signingKey, SECOidTag digestalgtag) 36 { 37 return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL, 38 subjKeyID, pubKey, signingKey, digestalgtag); 39 } 40 41 NSSCMSSignerInfo * 42 NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag) 43 { 44 return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL, 45 NULL, NULL, digestalgtag); 46 } 47 48 NSSCMSSignerInfo * 49 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 50 CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 51 SECKEYPrivateKey *signingKey, SECOidTag digestalgtag) 52 { 53 void *mark; 54 NSSCMSSignerInfo *signerinfo; 55 int version; 56 PLArenaPool *poolp; 57 SECStatus rv; 58 59 poolp = cmsg->poolp; 60 61 mark = PORT_ArenaMark(poolp); 62 63 signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo)); 64 if (signerinfo == NULL) { 65 PORT_ArenaRelease(poolp, mark); 66 return NULL; 67 } 68 69 signerinfo->cmsg = cmsg; 70 71 switch (type) { 72 case NSSCMSSignerID_IssuerSN: 73 signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN; 74 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL) 75 goto loser; 76 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) 77 goto loser; 78 break; 79 case NSSCMSSignerID_SubjectKeyID: 80 signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID; 81 PORT_Assert(subjKeyID); 82 if (!subjKeyID) 83 goto loser; 84 85 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem); 86 rv = SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID, 87 subjKeyID); 88 if (rv != SECSuccess) { 89 goto loser; 90 } 91 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey); 92 if (!signerinfo->signingKey) 93 goto loser; 94 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey); 95 if (!signerinfo->pubKey) 96 goto loser; 97 break; 98 default: 99 goto loser; 100 } 101 102 /* set version right now */ 103 version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN; 104 /* RFC2630 5.3 "version is the syntax version number. If the .... " */ 105 if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) 106 version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY; 107 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version); 108 109 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess) 110 goto loser; 111 112 PORT_ArenaUnmark(poolp, mark); 113 return signerinfo; 114 115 loser: 116 PORT_ArenaRelease(poolp, mark); 117 return NULL; 118 } 119 120 /* 121 * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure 122 */ 123 void 124 NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si) 125 { 126 if (si->cert != NULL) 127 CERT_DestroyCertificate(si->cert); 128 129 if (si->certList != NULL) 130 CERT_DestroyCertificateList(si->certList); 131 132 /* XXX storage ??? */ 133 } 134 static SECOidTag 135 NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType, 136 SECOidTag pubkAlgTag, 137 SECOidTag signAlgTag) 138 { 139 switch (keyType) { 140 case rsaKey: 141 return pubkAlgTag; 142 case rsaPssKey: 143 case dsaKey: 144 case ecKey: 145 return signAlgTag; 146 default: 147 return SEC_OID_UNKNOWN; 148 } 149 } 150 151 /* 152 * NSS_CMSSignerInfo_Sign - sign something 153 * 154 */ 155 SECStatus 156 NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, 157 SECItem *contentType) 158 { 159 CERTCertificate *cert; 160 SECKEYPrivateKey *privkey = NULL; 161 SECOidTag digestalgtag; 162 SECOidTag pubkAlgTag; 163 SECOidTag signAlgTag; 164 SECOidTag cmsSignAlgTag; 165 SECItem signature = { 0 }; 166 SECStatus rv; 167 PLArenaPool *poolp, *tmppoolp = NULL; 168 SECAlgorithmID *algID, freeAlgID; 169 CERTSubjectPublicKeyInfo *spki; 170 171 PORT_Assert(digest != NULL); 172 173 poolp = signerinfo->cmsg->poolp; 174 175 switch (signerinfo->signerIdentifier.identifierType) { 176 case NSSCMSSignerID_IssuerSN: 177 cert = signerinfo->cert; 178 179 privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg); 180 if (privkey == NULL) 181 goto loser; 182 algID = &cert->subjectPublicKeyInfo.algorithm; 183 break; 184 case NSSCMSSignerID_SubjectKeyID: 185 privkey = signerinfo->signingKey; 186 signerinfo->signingKey = NULL; 187 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); 188 SECKEY_DestroyPublicKey(signerinfo->pubKey); 189 signerinfo->pubKey = NULL; 190 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); 191 SECKEY_DestroySubjectPublicKeyInfo(spki); 192 algID = &freeAlgID; 193 break; 194 default: 195 goto loser; 196 } 197 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); 198 /* 199 * XXX I think there should be a cert-level interface for this, 200 * so that I do not have to know about subjectPublicKeyInfo... 201 */ 202 pubkAlgTag = SECOID_GetAlgorithmTag(algID); 203 if (algID == &freeAlgID) { 204 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); 205 } 206 207 signAlgTag = SEC_GetSignatureAlgorithmOidTagByKey(privkey, NULL, 208 digestalgtag); 209 if (signAlgTag == SEC_OID_UNKNOWN) { 210 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 211 goto loser; 212 } 213 214 cmsSignAlgTag = NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag( 215 SECKEY_GetPrivateKeyType(privkey), pubkAlgTag, signAlgTag); 216 if (cmsSignAlgTag == SEC_OID_UNKNOWN) { 217 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 218 goto loser; 219 } 220 221 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), 222 cmsSignAlgTag, NULL) != SECSuccess) 223 goto loser; 224 225 if (!NSS_SMIMEUtil_SigningAllowed(&signerinfo->digestEncAlg)) { 226 PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); 227 goto loser; 228 } 229 230 if (signerinfo->authAttr != NULL) { 231 SECItem encoded_attrs; 232 233 /* find and fill in the message digest attribute. */ 234 rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 235 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); 236 if (rv != SECSuccess) 237 goto loser; 238 239 if (contentType != NULL) { 240 /* if the caller wants us to, find and fill in the content type attribute. */ 241 rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 242 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); 243 if (rv != SECSuccess) 244 goto loser; 245 } 246 247 if ((tmppoolp = PORT_NewArena(1024)) == NULL) { 248 PORT_SetError(SEC_ERROR_NO_MEMORY); 249 goto loser; 250 } 251 252 /* 253 * Before encoding, reorder the attributes so that when they 254 * are encoded, they will be conforming DER, which is required 255 * to have a specific order and that is what must be used for 256 * the hash/signature. We do this here, rather than building 257 * it into EncodeAttributes, because we do not want to do 258 * such reordering on incoming messages (which also uses 259 * EncodeAttributes) or our old signatures (and other "broken" 260 * implementations) will not verify. So, we want to guarantee 261 * that we send out good DER encodings of attributes, but not 262 * to expect to receive them. 263 */ 264 if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess) 265 goto loser; 266 267 encoded_attrs.data = NULL; 268 encoded_attrs.len = 0; 269 if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), 270 &encoded_attrs) == NULL) 271 goto loser; 272 273 rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, 274 privkey, signAlgTag); 275 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ 276 tmppoolp = 0; 277 } else { 278 rv = SGN_Digest(privkey, digestalgtag, &signature, digest); 279 } 280 SECKEY_DestroyPrivateKey(privkey); 281 privkey = NULL; 282 283 if (rv != SECSuccess) 284 goto loser; 285 286 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess) 287 goto loser; 288 289 SECITEM_FreeItem(&signature, PR_FALSE); 290 291 return SECSuccess; 292 293 loser: 294 if (signature.len != 0) 295 SECITEM_FreeItem(&signature, PR_FALSE); 296 if (privkey) 297 SECKEY_DestroyPrivateKey(privkey); 298 if (tmppoolp) 299 PORT_FreeArena(tmppoolp, PR_FALSE); 300 return SECFailure; 301 } 302 303 SECStatus 304 NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb, 305 SECCertUsage certusage) 306 { 307 CERTCertificate *cert; 308 PRTime stime; 309 310 if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) { 311 signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound; 312 return SECFailure; 313 } 314 315 /* 316 * Get and convert the signing time; if available, it will be used 317 * both on the cert verification and for importing the sender 318 * email profile. 319 */ 320 if (NSS_CMSSignerInfo_GetSigningTime(signerinfo, &stime) != SECSuccess) 321 stime = PR_Now(); /* not found or conversion failed, so check against now */ 322 323 /* 324 * XXX This uses the signing time, if available. Additionally, we 325 * might want to, if there is no signing time, get the message time 326 * from the mail header itself, and use that. That would require 327 * a change to our interface though, and for S/MIME callers to pass 328 * in a time (and for non-S/MIME callers to pass in nothing, or 329 * maybe make them pass in the current time, always?). 330 */ 331 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, 332 signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 333 signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted; 334 return SECFailure; 335 } 336 return SECSuccess; 337 } 338 339 /* 340 * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo 341 * 342 * Just verifies the signature. The assumption is that verification of 343 * the certificate is done already. 344 */ 345 SECStatus 346 NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, 347 SECItem *digest, /* may be NULL */ 348 SECItem *contentType) /* may be NULL */ 349 { 350 SECKEYPublicKey *publickey = NULL; 351 NSSCMSAttribute *attr; 352 SECItem encoded_attrs; 353 CERTCertificate *cert; 354 NSSCMSVerificationStatus vs = NSSCMSVS_Unverified; 355 PLArenaPool *poolp; 356 SECOidTag digestalgtag; 357 SECOidTag pubkAlgTag; 358 SECOidTag digestalgtagCmp; 359 SECOidTag sigAlgTag; 360 361 if (signerinfo == NULL) 362 return SECFailure; 363 364 /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL 365 ** and cert has not been verified 366 */ 367 cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL); 368 if (cert == NULL) { 369 vs = NSSCMSVS_SigningCertNotFound; 370 goto loser; 371 } 372 373 if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) { 374 vs = NSSCMSVS_ProcessingError; 375 goto loser; 376 } 377 378 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); 379 pubkAlgTag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); 380 sigAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); 381 if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN) || 382 (sigAlgTag == SEC_OID_UNKNOWN)) { 383 vs = NSSCMSVS_SignatureAlgorithmUnknown; 384 goto loser; 385 } 386 if (!NSS_SMIMEUtil_SigningAllowed(&signerinfo->digestEncAlg)) { 387 vs = NSSCMSVS_SignatureAlgorithmUnsupported; 388 goto loser; 389 } 390 391 if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { 392 if (contentType) { 393 /* 394 * Check content type 395 * 396 * RFC2630 sez that if there are any authenticated attributes, 397 * then there must be one for content type which matches the 398 * content type of the content being signed, and there must 399 * be one for message digest which matches our message digest. 400 * So check these things first. 401 */ 402 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 403 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); 404 if (attr == NULL) { 405 vs = NSSCMSVS_MalformedSignature; 406 goto loser; 407 } 408 409 if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) { 410 vs = NSSCMSVS_MalformedSignature; 411 goto loser; 412 } 413 } 414 415 /* 416 * Check digest 417 */ 418 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 419 SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); 420 if (attr == NULL) { 421 vs = NSSCMSVS_MalformedSignature; 422 goto loser; 423 } 424 if (!digest || 425 NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) { 426 vs = NSSCMSVS_DigestMismatch; 427 goto loser; 428 } 429 430 if ((poolp = PORT_NewArena(1024)) == NULL) { 431 vs = NSSCMSVS_ProcessingError; 432 goto loser; 433 } 434 435 /* 436 * Check signature 437 * 438 * The signature is based on a digest of the DER-encoded authenticated 439 * attributes. So, first we encode and then we digest/verify. 440 * we trust the decoder to have the attributes in the right (sorted) 441 * order 442 */ 443 encoded_attrs.data = NULL; 444 encoded_attrs.len = 0; 445 446 if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), 447 &encoded_attrs) == NULL || 448 encoded_attrs.data == NULL || encoded_attrs.len == 0) { 449 PORT_FreeArena(poolp, PR_FALSE); 450 vs = NSSCMSVS_ProcessingError; 451 goto loser; 452 } 453 454 if (sigAlgTag == pubkAlgTag) { 455 /* This is to handle cases in which signatureAlgorithm field 456 * specifies the public key algorithm rather than a signature 457 * algorithm. */ 458 vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len, 459 publickey, &(signerinfo->encDigest), pubkAlgTag, 460 digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess) 461 ? NSSCMSVS_BadSignature 462 : NSSCMSVS_GoodSignature; 463 } else { 464 if (VFY_VerifyDataWithAlgorithmID(encoded_attrs.data, 465 encoded_attrs.len, publickey, &(signerinfo->encDigest), 466 &(signerinfo->digestEncAlg), &digestalgtagCmp, 467 signerinfo->cmsg->pwfn_arg) != SECSuccess) { 468 vs = NSSCMSVS_BadSignature; 469 } else if (digestalgtagCmp != digestalgtag) { 470 PORT_SetError(SEC_ERROR_BAD_SIGNATURE); 471 vs = NSSCMSVS_BadSignature; 472 } else { 473 vs = NSSCMSVS_GoodSignature; 474 } 475 } 476 477 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ 478 479 } else { 480 SECItem *sig; 481 482 /* No authenticated attributes. 483 ** The signature is based on the plain message digest. 484 */ 485 sig = &(signerinfo->encDigest); 486 if (sig->len == 0) 487 goto loser; 488 489 if (sigAlgTag == pubkAlgTag) { 490 /* This is to handle cases in which signatureAlgorithm field 491 * specifies the public key algorithm rather than a signature 492 * algorithm. */ 493 vs = (!digest || 494 VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag, 495 digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess) 496 ? NSSCMSVS_BadSignature 497 : NSSCMSVS_GoodSignature; 498 } else { 499 vs = (!digest || 500 VFY_VerifyDigestWithAlgorithmID(digest, publickey, sig, 501 &(signerinfo->digestEncAlg), digestalgtag, 502 signerinfo->cmsg->pwfn_arg) != SECSuccess) 503 ? NSSCMSVS_BadSignature 504 : NSSCMSVS_GoodSignature; 505 } 506 } 507 508 if (vs == NSSCMSVS_BadSignature) { 509 int error = PORT_GetError(); 510 /* 511 * XXX Change the generic error into our specific one, because 512 * in that case we get a better explanation out of the Security 513 * Advisor. This is really a bug in the PSM error strings (the 514 * "generic" error has a lousy/wrong message associated with it 515 * which assumes the signature verification was done for the 516 * purposes of checking the issuer signature on a certificate) 517 * but this is at least an easy workaround and/or in the 518 * Security Advisor, which specifically checks for the error 519 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation 520 * in that case but does not similarly check for 521 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would 522 * probably say the wrong thing in the case that it *was* the 523 * certificate signature check that failed during the cert 524 * verification done above. Our error handling is really a mess. 525 */ 526 if (error == SEC_ERROR_BAD_SIGNATURE) 527 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 528 /* 529 * map algorithm failures to NSSCMSVS values 530 */ 531 if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) || 532 (error == SEC_ERROR_INVALID_ALGORITHM)) { 533 /* keep the same error code as 3.11 and before */ 534 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 535 vs = NSSCMSVS_SignatureAlgorithmUnsupported; 536 } 537 } 538 539 if (publickey != NULL) 540 SECKEY_DestroyPublicKey(publickey); 541 542 signerinfo->verificationStatus = vs; 543 544 return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure; 545 546 loser: 547 if (publickey != NULL) 548 SECKEY_DestroyPublicKey(publickey); 549 550 signerinfo->verificationStatus = vs; 551 552 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 553 return SECFailure; 554 } 555 556 NSSCMSVerificationStatus 557 NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo) 558 { 559 return signerinfo->verificationStatus; 560 } 561 562 SECOidData * 563 NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo) 564 { 565 SECOidData *algdata; 566 SECOidTag algtag; 567 568 algdata = SECOID_FindOID(&(signerinfo->digestAlg.algorithm)); 569 if (algdata == NULL) { 570 return algdata; 571 } 572 /* Windows may have given us a signer algorithm oid instead of a digest 573 * algorithm oid. This call will map to a signer oid to a digest one, 574 * otherwise it leaves the oid alone and let the chips fall as they may 575 * if it's not a digest oid. 576 */ 577 algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset); 578 if (algtag != algdata->offset) { 579 /* if the tags don't match, then we must have received a signer 580 * algorithID. Now we need to get the oid data for the digest 581 * oid, which the rest of the code is expecting */ 582 algdata = SECOID_FindOIDByTag(algtag); 583 } 584 585 return algdata; 586 } 587 588 SECOidTag 589 NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo) 590 { 591 SECOidData *algdata; 592 593 if (!signerinfo) { 594 PORT_SetError(SEC_ERROR_INVALID_ARGS); 595 return SEC_OID_UNKNOWN; 596 } 597 598 algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo); 599 if (algdata != NULL) 600 return algdata->offset; 601 else 602 return SEC_OID_UNKNOWN; 603 } 604 605 CERTCertificateList * 606 NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo) 607 { 608 return signerinfo->certList; 609 } 610 611 int 612 NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo) 613 { 614 unsigned long version; 615 616 /* always take apart the SECItem */ 617 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess) 618 return 0; 619 else 620 return (int)version; 621 } 622 623 /* 624 * NSS_CMSSignerInfo_GetSigningTime - return the signing time, 625 * in UTCTime or GeneralizedTime format, 626 * of a CMS signerInfo. 627 * 628 * sinfo - signerInfo data for this signer 629 * 630 * Returns a pointer to XXXX (what?) 631 * A return value of NULL is an error. 632 */ 633 SECStatus 634 NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime) 635 { 636 NSSCMSAttribute *attr; 637 SECItem *value; 638 639 if (sinfo == NULL) 640 return SECFailure; 641 642 if (sinfo->signingTime != 0) { 643 *stime = sinfo->signingTime; /* cached copy */ 644 return SECSuccess; 645 } 646 647 attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, 648 SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); 649 /* XXXX multi-valued attributes NIH */ 650 if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL) 651 return SECFailure; 652 if (DER_DecodeTimeChoice(stime, value) != SECSuccess) 653 return SECFailure; 654 sinfo->signingTime = *stime; /* make cached copy */ 655 return SECSuccess; 656 } 657 658 /* 659 * Return the signing cert of a CMS signerInfo. 660 * 661 * the certs in the enclosing SignedData must have been imported already 662 */ 663 CERTCertificate * 664 NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb) 665 { 666 CERTCertificate *cert; 667 NSSCMSSignerIdentifier *sid; 668 669 if (signerinfo->cert != NULL) 670 return signerinfo->cert; 671 672 /* no certdb, and cert hasn't been set yet? */ 673 if (certdb == NULL) 674 return NULL; 675 676 /* 677 * This cert will also need to be freed, but since we save it 678 * in signerinfo for later, we do not want to destroy it when 679 * we leave this function -- we let the clean-up of the entire 680 * cinfo structure later do the destroy of this cert. 681 */ 682 sid = &signerinfo->signerIdentifier; 683 switch (sid->identifierType) { 684 case NSSCMSSignerID_IssuerSN: 685 cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN); 686 break; 687 case NSSCMSSignerID_SubjectKeyID: 688 cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID); 689 break; 690 default: 691 cert = NULL; 692 break; 693 } 694 695 /* cert can be NULL at that point */ 696 signerinfo->cert = cert; /* earmark it */ 697 698 return cert; 699 } 700 701 /* 702 * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer 703 * 704 * sinfo - signerInfo data for this signer 705 * 706 * Returns a pointer to allocated memory, which must be freed with PORT_Free. 707 * A return value of NULL is an error. 708 */ 709 char * 710 NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo) 711 { 712 CERTCertificate *signercert; 713 714 /* will fail if cert is not verified */ 715 if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL) 716 return NULL; 717 718 return (CERT_GetCommonName(&signercert->subject)); 719 } 720 721 /* 722 * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer 723 * 724 * sinfo - signerInfo data for this signer 725 * 726 * Returns a pointer to allocated memory, which must be freed. 727 * A return value of NULL is an error. 728 */ 729 char * 730 NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo) 731 { 732 CERTCertificate *signercert; 733 734 if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL) 735 return NULL; 736 737 if (!signercert->emailAddr || !signercert->emailAddr[0]) 738 return NULL; 739 740 return (PORT_Strdup(signercert->emailAddr)); 741 } 742 743 /* 744 * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the 745 * authenticated (i.e. signed) attributes of "signerinfo". 746 */ 747 SECStatus 748 NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr) 749 { 750 return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr); 751 } 752 753 /* 754 * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the 755 * unauthenticated attributes of "signerinfo". 756 */ 757 SECStatus 758 NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr) 759 { 760 return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr); 761 } 762 763 /* 764 * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the 765 * authenticated (i.e. signed) attributes of "signerinfo". 766 * 767 * This is expected to be included in outgoing signed 768 * messages for email (S/MIME) but is likely useful in other situations. 769 * 770 * This should only be added once; a second call will do nothing. 771 * 772 * XXX This will probably just shove the current time into "signerinfo" 773 * but it will not actually get signed until the entire item is 774 * processed for encoding. Is this (expected to be small) delay okay? 775 */ 776 SECStatus 777 NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t) 778 { 779 NSSCMSAttribute *attr; 780 SECItem stime; 781 void *mark; 782 PLArenaPool *poolp; 783 784 poolp = signerinfo->cmsg->poolp; 785 786 mark = PORT_ArenaMark(poolp); 787 788 /* create new signing time attribute */ 789 if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess) 790 goto loser; 791 792 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) { 793 SECITEM_FreeItem(&stime, PR_FALSE); 794 goto loser; 795 } 796 797 SECITEM_FreeItem(&stime, PR_FALSE); 798 799 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 800 goto loser; 801 802 PORT_ArenaUnmark(poolp, mark); 803 804 return SECSuccess; 805 806 loser: 807 PORT_ArenaRelease(poolp, mark); 808 return SECFailure; 809 } 810 811 /* 812 * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the 813 * authenticated (i.e. signed) attributes of "signerinfo". 814 * 815 * This is expected to be included in outgoing signed 816 * messages for email (S/MIME). 817 */ 818 SECStatus 819 NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo) 820 { 821 NSSCMSAttribute *attr; 822 SECItem *smimecaps = NULL; 823 void *mark; 824 PLArenaPool *poolp; 825 826 poolp = signerinfo->cmsg->poolp; 827 828 mark = PORT_ArenaMark(poolp); 829 830 smimecaps = SECITEM_AllocItem(poolp, NULL, 0); 831 if (smimecaps == NULL) 832 goto loser; 833 834 /* create new signing time attribute */ 835 if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess) 836 goto loser; 837 838 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL) 839 goto loser; 840 841 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 842 goto loser; 843 844 PORT_ArenaUnmark(poolp, mark); 845 return SECSuccess; 846 847 loser: 848 PORT_ArenaRelease(poolp, mark); 849 return SECFailure; 850 } 851 852 /* 853 * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 854 * authenticated (i.e. signed) attributes of "signerinfo". 855 * 856 * This is expected to be included in outgoing signed messages for email (S/MIME). 857 */ 858 SECStatus 859 NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb) 860 { 861 NSSCMSAttribute *attr; 862 SECItem *smimeekp = NULL; 863 void *mark; 864 PLArenaPool *poolp; 865 866 /* verify this cert for encryption */ 867 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 868 return SECFailure; 869 } 870 871 poolp = signerinfo->cmsg->poolp; 872 mark = PORT_ArenaMark(poolp); 873 874 smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 875 if (smimeekp == NULL) 876 goto loser; 877 878 /* create new signing time attribute */ 879 if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) 880 goto loser; 881 882 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 883 goto loser; 884 885 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 886 goto loser; 887 888 PORT_ArenaUnmark(poolp, mark); 889 return SECSuccess; 890 891 loser: 892 PORT_ArenaRelease(poolp, mark); 893 return SECFailure; 894 } 895 896 /* 897 * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 898 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft. 899 * 900 * This is expected to be included in outgoing signed messages for email (S/MIME), 901 * if compatibility with Microsoft mail clients is wanted. 902 */ 903 SECStatus 904 NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb) 905 { 906 NSSCMSAttribute *attr; 907 SECItem *smimeekp = NULL; 908 void *mark; 909 PLArenaPool *poolp; 910 911 /* verify this cert for encryption */ 912 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 913 return SECFailure; 914 } 915 916 poolp = signerinfo->cmsg->poolp; 917 mark = PORT_ArenaMark(poolp); 918 919 smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 920 if (smimeekp == NULL) 921 goto loser; 922 923 /* create new signing time attribute */ 924 if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) 925 goto loser; 926 927 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 928 goto loser; 929 930 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 931 goto loser; 932 933 PORT_ArenaUnmark(poolp, mark); 934 return SECSuccess; 935 936 loser: 937 PORT_ArenaRelease(poolp, mark); 938 return SECFailure; 939 } 940 941 /* 942 * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo 943 * 944 * 1. digest the DER-encoded signature value of the original signerinfo 945 * 2. create new signerinfo with correct version, sid, digestAlg 946 * 3. add message-digest authAttr, but NO content-type 947 * 4. sign the authAttrs 948 * 5. DER-encode the new signerInfo 949 * 6. add the whole thing to original signerInfo's unAuthAttrs 950 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute 951 * 952 * XXXX give back the new signerinfo? 953 */ 954 SECStatus 955 NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo, 956 SECOidTag digestalg, CERTCertificate signingcert) 957 { 958 /* XXXX TBD XXXX */ 959 return SECFailure; 960 } 961 962 /* 963 * XXXX the following needs to be done in the S/MIME layer code 964 * after signature of a signerinfo is verified 965 */ 966 SECStatus 967 NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo) 968 { 969 CERTCertificate *cert = NULL; 970 SECItem *profile = NULL; 971 NSSCMSAttribute *attr; 972 SECItem *stime = NULL; 973 SECItem *ekp; 974 CERTCertDBHandle *certdb; 975 int save_error; 976 SECStatus rv; 977 PRBool must_free_cert = PR_FALSE; 978 979 certdb = CERT_GetDefaultCertDB(); 980 981 /* sanity check - see if verification status is ok (unverified does not count...) */ 982 if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature) 983 return SECFailure; 984 985 /* find preferred encryption cert */ 986 if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) && 987 (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 988 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ 989 ekp = NSS_CMSAttribute_GetValue(attr); 990 if (ekp == NULL) 991 return SECFailure; 992 993 /* we assume that all certs coming with the message have been imported to the */ 994 /* temporary database */ 995 cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp); 996 if (cert == NULL) 997 return SECFailure; 998 must_free_cert = PR_TRUE; 999 } 1000 1001 if (cert == NULL) { 1002 /* no preferred cert found? 1003 * find the cert the signerinfo is signed with instead */ 1004 cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb); 1005 if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0]) 1006 return SECFailure; 1007 } 1008 1009 /* verify this cert for encryption (has been verified for signing so far) */ 1010 /* don't verify this cert for encryption. It may just be a signing cert. 1011 * that's OK, we can still save the S/MIME profile. The encryption cert 1012 * should have already been saved */ 1013 #ifdef notdef 1014 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 1015 if (must_free_cert) 1016 CERT_DestroyCertificate(cert); 1017 return SECFailure; 1018 } 1019 #endif 1020 1021 /* XXX store encryption cert permanently? */ 1022 1023 /* 1024 * Remember the current error set because we do not care about 1025 * anything set by the functions we are about to call. 1026 */ 1027 save_error = PORT_GetError(); 1028 1029 if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { 1030 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1031 SEC_OID_PKCS9_SMIME_CAPABILITIES, 1032 PR_TRUE); 1033 profile = NSS_CMSAttribute_GetValue(attr); 1034 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1035 SEC_OID_PKCS9_SIGNING_TIME, 1036 PR_TRUE); 1037 stime = NSS_CMSAttribute_GetValue(attr); 1038 } 1039 1040 rv = CERT_SaveSMimeProfile(cert, profile, stime); 1041 if (must_free_cert) 1042 CERT_DestroyCertificate(cert); 1043 1044 /* 1045 * Restore the saved error in case the calls above set a new 1046 * one that we do not actually care about. 1047 */ 1048 PORT_SetError(save_error); 1049 1050 return rv; 1051 } 1052 1053 /* 1054 * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer 1055 */ 1056 SECStatus 1057 NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, 1058 NSSCMSCertChainMode cm, SECCertUsage usage) 1059 { 1060 if (signerinfo->cert == NULL) 1061 return SECFailure; 1062 1063 /* don't leak if we get called twice */ 1064 if (signerinfo->certList != NULL) { 1065 CERT_DestroyCertificateList(signerinfo->certList); 1066 signerinfo->certList = NULL; 1067 } 1068 1069 switch (cm) { 1070 case NSSCMSCM_None: 1071 signerinfo->certList = NULL; 1072 break; 1073 case NSSCMSCM_CertOnly: 1074 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert); 1075 break; 1076 case NSSCMSCM_CertChain: 1077 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, 1078 usage, PR_FALSE); 1079 break; 1080 case NSSCMSCM_CertChainWithRoot: 1081 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, 1082 usage, PR_TRUE); 1083 break; 1084 } 1085 1086 if (cm != NSSCMSCM_None && signerinfo->certList == NULL) 1087 return SECFailure; 1088 1089 return SECSuccess; 1090 }