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 "secitem.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/SecKeychain.h> 51#include <Security/SecIdentity.h> 52#include <Security/SecCertificatePriv.h> 53#include <Security/SecKeyPriv.h> 54#include <CoreFoundation/CFTimeZone.h> 55#include <utilities/SecCFWrappers.h> 56#include <AssertMacros.h> 57#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 58#include <Security/SecPolicyPriv.h> 59 60#include "tsaSupport.h" 61#include "tsaSupportPriv.h" 62 63#define HIDIGIT(v) (((v) / 10) + '0') 64#define LODIGIT(v) (((v) % 10) + '0') 65 66#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9')) 67#define CAPTURE(var,p,label) \ 68{ \ 69 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \ 70 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \ 71} 72 73#ifndef NDEBUG 74#define SIGINFO_DEBUG 1 75#endif 76 77#if SIGINFO_DEBUG 78#define dprintf(args...) printf(args) 79#else 80#define dprintf(args...) 81#endif 82 83#if RELEASECOUNTDEBUG 84#define dprintfRC(args...) dprintf(args) 85#else 86#define dprintfRC(args...) 87#endif 88 89static OSStatus 90DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime, CFAbsoluteTime *date) 91{ 92 CFGregorianDate gdate; 93 char *string = (char *)utcTime->Data; 94 long year, month, mday, hour, minute, second, hourOff, minOff; 95 CFTimeZoneRef timeZone; 96 97 /* Verify time is formatted properly and capture information */ 98 second = 0; 99 hourOff = 0; 100 minOff = 0; 101 CAPTURE(year,string+0,loser); 102 if (year < 50) { 103 /* ASSUME that year # is in the 2000's, not the 1900's */ 104 year += 100; 105 } 106 CAPTURE(month,string+2,loser); 107 if ((month == 0) || (month > 12)) goto loser; 108 CAPTURE(mday,string+4,loser); 109 if ((mday == 0) || (mday > 31)) goto loser; 110 CAPTURE(hour,string+6,loser); 111 if (hour > 23) goto loser; 112 CAPTURE(minute,string+8,loser); 113 if (minute > 59) goto loser; 114 if (ISDIGIT(string[10])) { 115 CAPTURE(second,string+10,loser); 116 if (second > 59) goto loser; 117 string += 2; 118 } 119 if (string[10] == '+') { 120 CAPTURE(hourOff,string+11,loser); 121 if (hourOff > 23) goto loser; 122 CAPTURE(minOff,string+13,loser); 123 if (minOff > 59) goto loser; 124 } else if (string[10] == '-') { 125 CAPTURE(hourOff,string+11,loser); 126 if (hourOff > 23) goto loser; 127 hourOff = -hourOff; 128 CAPTURE(minOff,string+13,loser); 129 if (minOff > 59) goto loser; 130 minOff = -minOff; 131 } else if (string[10] != 'Z') { 132 goto loser; 133 } 134 135 gdate.year = (SInt32)(year + 1900); 136 gdate.month = month; 137 gdate.day = mday; 138 gdate.hour = hour; 139 gdate.minute = minute; 140 gdate.second = second; 141 142 if (hourOff == 0 && minOff == 0) 143 timeZone = NULL; /* GMT */ 144 else 145 { 146 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, (hourOff * 60 + minOff) * 60); 147 } 148 149 *date = CFGregorianDateGetAbsoluteTime(gdate, timeZone); 150 if (timeZone) 151 CFRelease(timeZone); 152 153 return SECSuccess; 154 155loser: 156 return SECFailure; 157} 158 159static OSStatus 160DER_CFDateToUTCTime(CFAbsoluteTime date, CSSM_DATA_PTR utcTime) 161{ 162 CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(date, NULL /* GMT */); 163 unsigned char *d; 164 SInt8 second; 165 166 utcTime->Length = 13; 167 utcTime->Data = d = PORT_Alloc(13); 168 if (!utcTime->Data) 169 return SECFailure; 170 171 /* UTC time does not handle the years before 1950 */ 172 if (gdate.year < 1950) 173 return SECFailure; 174 175 /* remove the century since it's added to the year by the 176 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */ 177 gdate.year %= 100; 178 second = gdate.second + 0.5; 179 180 d[0] = HIDIGIT(gdate.year); 181 d[1] = LODIGIT(gdate.year); 182 d[2] = HIDIGIT(gdate.month); 183 d[3] = LODIGIT(gdate.month); 184 d[4] = HIDIGIT(gdate.day); 185 d[5] = LODIGIT(gdate.day); 186 d[6] = HIDIGIT(gdate.hour); 187 d[7] = LODIGIT(gdate.hour); 188 d[8] = HIDIGIT(gdate.minute); 189 d[9] = LODIGIT(gdate.minute); 190 d[10] = HIDIGIT(second); 191 d[11] = LODIGIT(second); 192 d[12] = 'Z'; 193 return SECSuccess; 194} 195 196/* ============================================================================= 197 * SIGNERINFO 198 */ 199SecCmsSignerInfoRef 200nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag); 201 202SecCmsSignerInfoRef 203SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag) 204{ 205 return nss_cmssignerinfo_create(cmsg, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag); 206} 207 208SecCmsSignerInfoRef 209SecCmsSignerInfoCreate(SecCmsMessageRef cmsg, SecIdentityRef identity, SECOidTag digestalgtag) 210{ 211 SecCmsSignerInfoRef signerInfo = NULL; 212 SecCertificateRef cert = NULL; 213 SecPrivateKeyRef signingKey = NULL; 214 215 if (SecIdentityCopyCertificate(identity, &cert)) 216 goto loser; 217 if (SecIdentityCopyPrivateKey(identity, &signingKey)) 218 goto loser; 219 220 signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag); 221 222loser: 223 if (cert) 224 CFRelease(cert); 225 if (signingKey) 226 CFRelease(signingKey); 227 228 return signerInfo; 229} 230 231SecCmsSignerInfoRef 232nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag) 233{ 234 void *mark; 235 SecCmsSignerInfoRef signerinfo; 236 int version; 237 PLArenaPool *poolp; 238 239 poolp = cmsg->poolp; 240 241 mark = PORT_ArenaMark(poolp); 242 243 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo)); 244 if (signerinfo == NULL) { 245 PORT_ArenaRelease(poolp, mark); 246 return NULL; 247 } 248 249 250 signerinfo->cmsg = cmsg; 251 252 switch(type) { 253 case SecCmsSignerIDIssuerSN: 254 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN; 255 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL) 256 goto loser; 257 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) 258 goto loser; 259 dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n", 260 (int)CFGetRetainCount(signerinfo->cert)); 261 break; 262 case SecCmsSignerIDSubjectKeyID: 263 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID; 264 PORT_Assert(subjKeyID); 265 if (!subjKeyID) 266 goto loser; 267 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA); 268 SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID, 269 subjKeyID); 270 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey); 271 if (!signerinfo->pubKey) 272 goto loser; 273 break; 274 default: 275 goto loser; 276 } 277 278 if (!signingKey) 279 goto loser; 280 281 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey); 282 if (!signerinfo->signingKey) 283 goto loser; 284 285 /* set version right now */ 286 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN; 287 /* RFC2630 5.3 "version is the syntax version number. If the .... " */ 288 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) 289 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY; 290 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version); 291 292 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess) 293 goto loser; 294 295 PORT_ArenaUnmark(poolp, mark); 296 return signerinfo; 297 298loser: 299 PORT_ArenaRelease(poolp, mark); 300 return NULL; 301} 302 303/* 304 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure 305 */ 306void 307SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si) 308{ 309 if (si->cert != NULL) { 310 dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n", 311 si->cert, (int)CFGetRetainCount(si->cert)); 312 CERT_DestroyCertificate(si->cert); 313 } 314 if (si->certList != NULL) { 315 dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n", 316 (int)CFGetRetainCount(si->certList)); 317 CFRelease(si->certList); 318 } 319 if (si->timestampCertList != NULL) { 320 dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n", 321 (int)CFGetRetainCount(si->timestampCertList)); 322 CFRelease(si->timestampCertList); 323 } 324 /* XXX storage ??? */ 325} 326 327/* 328 * SecCmsSignerInfoSign - sign something 329 * 330 */ 331OSStatus 332SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) 333{ 334 SecCertificateRef cert; 335 SecPrivateKeyRef privkey = NULL; 336 SECOidTag digestalgtag; 337 SECOidTag pubkAlgTag; 338 CSSM_DATA signature = { 0 }; 339 OSStatus rv; 340 PLArenaPool *poolp, *tmppoolp; 341 const SECAlgorithmID *algID; 342 SECAlgorithmID freeAlgID; 343 //CERTSubjectPublicKeyInfo *spki; 344 345 PORT_Assert (digest != NULL); 346 347 poolp = signerinfo->cmsg->poolp; 348 349 switch (signerinfo->signerIdentifier.identifierType) { 350 case SecCmsSignerIDIssuerSN: 351 privkey = signerinfo->signingKey; 352 signerinfo->signingKey = NULL; 353 cert = signerinfo->cert; 354 if (SecCertificateGetAlgorithmID(cert,&algID)) { 355 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 356 goto loser; 357 } 358 break; 359 case SecCmsSignerIDSubjectKeyID: 360 privkey = signerinfo->signingKey; 361 signerinfo->signingKey = NULL; 362#if 0 363 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); 364 SECKEY_DestroyPublicKey(signerinfo->pubKey); 365 signerinfo->pubKey = NULL; 366 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); 367 SECKEY_DestroySubjectPublicKeyInfo(spki); 368 algID = &freeAlgID; 369#else 370 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) { 371 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 372 goto loser; 373 } 374 CFRelease(signerinfo->pubKey); 375 signerinfo->pubKey = NULL; 376#endif 377 break; 378 default: 379 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE); 380 goto loser; 381 } 382 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 383 /* 384 * XXX I think there should be a cert-level interface for this, 385 * so that I do not have to know about subjectPublicKeyInfo... 386 */ 387 pubkAlgTag = SECOID_GetAlgorithmTag(algID); 388 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) { 389 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); 390 } 391 392#if 0 393 // @@@ Not yet 394 /* Fortezza MISSI have weird signature formats. 395 * Map them to standard DSA formats 396 */ 397 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag); 398#endif 399 400 if (signerinfo->authAttr != NULL) { 401 CSSM_DATA encoded_attrs; 402 403 /* find and fill in the message digest attribute. */ 404 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), 405 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); 406 if (rv != SECSuccess) 407 goto loser; 408 409 if (contentType != NULL) { 410 /* if the caller wants us to, find and fill in the content type attribute. */ 411 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), 412 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); 413 if (rv != SECSuccess) 414 goto loser; 415 } 416 417 if ((tmppoolp = PORT_NewArena (1024)) == NULL) { 418 PORT_SetError(SEC_ERROR_NO_MEMORY); 419 goto loser; 420 } 421 422 /* 423 * Before encoding, reorder the attributes so that when they 424 * are encoded, they will be conforming DER, which is required 425 * to have a specific order and that is what must be used for 426 * the hash/signature. We do this here, rather than building 427 * it into EncodeAttributes, because we do not want to do 428 * such reordering on incoming messages (which also uses 429 * EncodeAttributes) or our old signatures (and other "broken" 430 * implementations) will not verify. So, we want to guarantee 431 * that we send out good DER encodings of attributes, but not 432 * to expect to receive them. 433 */ 434 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess) 435 goto loser; 436 437 encoded_attrs.Data = NULL; 438 encoded_attrs.Length = 0; 439 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr), 440 &encoded_attrs) == NULL) 441 goto loser; 442 443 rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length, 444 privkey, digestalgtag, pubkAlgTag); 445 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ 446 } else { 447 rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest); 448 } 449 SECKEY_DestroyPrivateKey(privkey); 450 privkey = NULL; 451 452 if (rv != SECSuccess) 453 goto loser; 454 455 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) 456 != SECSuccess) 457 goto loser; 458 459 SECITEM_FreeItem(&signature, PR_FALSE); 460 461 if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) { 462 /* 463 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm 464 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey 465 * as would appear in other forms of signed datas. However Microsoft doesn't 466 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there, 467 * MS can't verify - presumably because it takes the digest of the digest 468 * before feeding it to ECDSA. 469 * We handle this with a preference; default if it's not there is 470 * "Microsoft compatibility mode". 471 */ 472 if(!SecCmsMsEcdsaCompatMode()) { 473 pubkAlgTag = SEC_OID_ECDSA_WithSHA1; 474 } 475 /* else violating the spec for compatibility */ 476 } 477 478 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, 479 NULL) != SECSuccess) 480 goto loser; 481 482 return SECSuccess; 483 484loser: 485 if (signature.Length != 0) 486 SECITEM_FreeItem (&signature, PR_FALSE); 487 if (privkey) 488 SECKEY_DestroyPrivateKey(privkey); 489 if((algID != NULL) & (algID != &freeAlgID)) { 490 /* this is dicey - this was actually mallocd by either SecCertificate or 491 * by SecKey...it all boils down to a free() in the end though. */ 492 SECOID_DestroyAlgorithmID((SECAlgorithmID *)algID, PR_FALSE); 493 } 494 return SECFailure; 495} 496 497OSStatus 498SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray, 499 CFTypeRef policies, SecTrustRef *trustRef) 500{ 501 SecCertificateRef cert; 502 CFAbsoluteTime stime; 503 OSStatus rv; 504 CSSM_DATA_PTR *otherCerts; 505 506 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) { 507 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n"); 508 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound; 509 return SECFailure; 510 } 511 512 /* 513 * Get and convert the signing time; if available, it will be used 514 * both on the cert verification and for importing the sender 515 * email profile. 516 */ 517 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies); 518 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess) 519 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess) 520 stime = CFAbsoluteTimeGetCurrent(); 521 CFReleaseSafe(timeStampPolicies); 522 523 rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts); 524 if(rv) { 525 return rv; 526 } 527 rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef); 528 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n", 529 cert, (int)CFGetRetainCount(cert)); 530 if (rv || !trustRef) 531 { 532 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT) 533 { 534 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */ 535 if (signerinfo->verificationStatus == SecCmsVSGoodSignature) 536 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted; 537 } 538 } 539 /* FIXME isn't this leaking the cert? */ 540 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv); 541 return rv; 542} 543 544static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo) 545{ 546#if SIGINFO_DEBUG 547 CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo); 548 if (cn) 549 { 550 char *ccn = cfStringToChar(cn); 551 if (ccn) 552 { 553 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn); 554 free(ccn); 555 } 556 CFRelease(cn); 557 } 558#endif 559} 560 561/* 562 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo 563 * 564 * Just verifies the signature. The assumption is that verification of the certificate 565 * is done already. 566 */ 567OSStatus 568SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) 569{ 570 return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType); 571} 572 573OSStatus 574SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) 575{ 576 SecPublicKeyRef publickey = NULL; 577 SecCmsAttribute *attr; 578 CSSM_DATA encoded_attrs; 579 SecCertificateRef cert; 580 SecCmsVerificationStatus vs = SecCmsVSUnverified; 581 PLArenaPool *poolp; 582 SECOidTag digestAlgTag, digestEncAlgTag; 583 584 if (signerinfo == NULL) 585 return SECFailure; 586 587 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */ 588 /* cert has not been verified */ 589 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) { 590 dprintf("SecCmsSignerInfoVerify: no signing cert\n"); 591 vs = SecCmsVSSigningCertNotFound; 592 goto loser; 593 } 594 595 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert)); 596 597 debugShowSigningCertificate(signerinfo); 598 599 if (SecCertificateCopyPublicKey(cert, &publickey)) { 600 vs = SecCmsVSProcessingError; 601 goto loser; 602 } 603 604 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg)); 605 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); 606 607 /* 608 * Gross hack necessitated by RFC 3278 section 2.1.1, which states 609 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1, 610 * *not* (as in all other algorithms) the raw signature algorithm, e.g. 611 * pkcs1RSAEncryption. 612 */ 613 if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) { 614 digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY; 615 } 616 617 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { 618 if (contentType) { 619 /* 620 * Check content type 621 * 622 * RFC2630 sez that if there are any authenticated attributes, 623 * then there must be one for content type which matches the 624 * content type of the content being signed, and there must 625 * be one for message digest which matches our message digest. 626 * So check these things first. 627 */ 628 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, 629 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL) 630 { 631 vs = SecCmsVSMalformedSignature; 632 goto loser; 633 } 634 635 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) { 636 vs = SecCmsVSMalformedSignature; 637 goto loser; 638 } 639 } 640 641 /* 642 * Check digest 643 */ 644 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL) 645 { 646 vs = SecCmsVSMalformedSignature; 647 goto loser; 648 } 649 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) { 650 vs = SecCmsVSDigestMismatch; 651 goto loser; 652 } 653 654 if ((poolp = PORT_NewArena (1024)) == NULL) { 655 vs = SecCmsVSProcessingError; 656 goto loser; 657 } 658 659 /* 660 * Check signature 661 * 662 * The signature is based on a digest of the DER-encoded authenticated 663 * attributes. So, first we encode and then we digest/verify. 664 * we trust the decoder to have the attributes in the right (sorted) order 665 */ 666 encoded_attrs.Data = NULL; 667 encoded_attrs.Length = 0; 668 669 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL || 670 encoded_attrs.Data == NULL || encoded_attrs.Length == 0) 671 { 672 vs = SecCmsVSProcessingError; 673 goto loser; 674 } 675 676 vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length, 677 publickey, &(signerinfo->encDigest), 678 digestAlgTag, digestEncAlgTag, 679 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; 680 681 dprintf("VFY_VerifyData (authenticated attributes): %s\n", 682 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature"); 683 684 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ 685 686 } else { 687 CSSM_DATA_PTR sig; 688 689 /* No authenticated attributes. The signature is based on the plain message digest. */ 690 sig = &(signerinfo->encDigest); 691 if (sig->Length == 0) 692 goto loser; 693 694 vs = (VFY_VerifyDigest(digest, publickey, sig, 695 digestAlgTag, digestEncAlgTag, 696 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; 697 698 dprintf("VFY_VerifyData (plain message digest): %s\n", 699 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature"); 700 } 701 702 if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr)) 703 { 704 dprintf("found an unAuthAttr\n"); 705 OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy); 706 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux); 707 if (rux) 708 goto loser; 709 } 710 711 if (vs == SecCmsVSBadSignature) { 712 /* 713 * XXX Change the generic error into our specific one, because 714 * in that case we get a better explanation out of the Security 715 * Advisor. This is really a bug in our error strings (the 716 * "generic" error has a lousy/wrong message associated with it 717 * which assumes the signature verification was done for the 718 * purposes of checking the issuer signature on a certificate) 719 * but this is at least an easy workaround and/or in the 720 * Security Advisor, which specifically checks for the error 721 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation 722 * in that case but does not similarly check for 723 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would 724 * probably say the wrong thing in the case that it *was* the 725 * certificate signature check that failed during the cert 726 * verification done above. Our error handling is really a mess. 727 */ 728 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) 729 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 730 } 731 732 if (publickey != NULL) 733 CFRelease(publickey); 734 735 signerinfo->verificationStatus = vs; 736 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n", 737 cert, (int)CFGetRetainCount(cert)); 738 739 dprintf("verificationStatus: %d\n", vs); 740 741 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure; 742 743loser: 744 if (publickey != NULL) 745 SECKEY_DestroyPublicKey (publickey); 746 747 dprintf("verificationStatus2: %d\n", vs); 748 signerinfo->verificationStatus = vs; 749 750 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); 751 return SECFailure; 752} 753 754OSStatus 755SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) { 756 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL); 757} 758 759OSStatus 760SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy) 761{ 762 /* 763 unAuthAttr is an array of attributes; we expect to 764 see just one: the timestamp blob. If we have an unAuthAttr, 765 but don't see a timestamp, return an error since we have 766 no other cases where this would be present. 767 */ 768 769 SecCmsAttribute *attr = NULL; 770 OSStatus status = SECFailure; 771 772 require(signerinfo, xit); 773 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr, 774 SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE); 775 if (attr == NULL) 776 { 777 status = errSecTimestampMissing; 778 goto xit; 779 } 780 781 dprintf("found an id-ct-TSTInfo\n"); 782 // Don't check the nonce in this case 783 status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0); 784xit: 785 return status; 786} 787 788CSSM_DATA * 789SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo) 790{ 791 return &signerinfo->encDigest; 792} 793 794SecCmsVerificationStatus 795SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo) 796{ 797 return signerinfo->verificationStatus; 798} 799 800SECOidData * 801SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo) 802{ 803 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); 804} 805 806SECOidTag 807SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo) 808{ 809 SECOidData *algdata; 810 811 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); 812 if (algdata != NULL) 813 return algdata->offset; 814 else 815 return SEC_OID_UNKNOWN; 816} 817 818CFArrayRef 819SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo) 820{ 821 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n", 822 (int)CFGetRetainCount(signerinfo->certList)); 823 return signerinfo->certList; 824} 825 826CFArrayRef 827SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo) 828{ 829 dprintfRC("SecCmsSignerInfoGetCertList: timestampCertList.rc %d\n", 830 (int)CFGetRetainCount(signerinfo->timestampCertList)); 831 return signerinfo->timestampCertList; 832} 833 834 835 836int 837SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo) 838{ 839 unsigned long version; 840 841 /* always take apart the CSSM_DATA */ 842 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess) 843 return 0; 844 else 845 return (int)version; 846} 847 848/* 849 * SecCmsSignerInfoGetSigningTime - return the signing time, 850 * in UTCTime format, of a CMS signerInfo. 851 * 852 * sinfo - signerInfo data for this signer 853 * 854 * Returns a pointer to XXXX (what?) 855 * A return value of NULL is an error. 856 */ 857OSStatus 858SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime) 859{ 860 SecCmsAttribute *attr; 861 CSSM_DATA_PTR value; 862 863 if (sinfo == NULL) 864 return paramErr; 865 866 if (sinfo->signingTime != 0) { 867 *stime = sinfo->signingTime; /* cached copy */ 868 return SECSuccess; 869 } 870 871 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); 872 /* XXXX multi-valued attributes NIH */ 873 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) 874 return errSecSigningTimeMissing; 875 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess) 876 return errSecSigningTimeMissing; 877 sinfo->signingTime = *stime; /* make cached copy */ 878 return SECSuccess; 879} 880 881OSStatus 882SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime) 883{ 884 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime); 885} 886 887OSStatus 888SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime) 889{ 890 OSStatus status = paramErr; 891 892 require(sinfo && stime, xit); 893 894 if (sinfo->timestampTime != 0) 895 { 896 *stime = sinfo->timestampTime; /* cached copy */ 897 return noErr; 898 } 899 900 // A bit heavyweight if haven't already called verify 901 status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy); 902 *stime = sinfo->timestampTime; 903xit: 904 return status; 905} 906 907/* 908 * Return the signing cert of a CMS signerInfo. 909 * 910 * the certs in the enclosing SignedData must have been imported already 911 */ 912SecCertificateRef 913SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray) 914{ 915 SecCertificateRef cert; 916 SecCmsSignerIdentifier *sid; 917 OSStatus ortn; 918 CSSM_DATA_PTR *rawCerts; 919 920 if (signerinfo->cert != NULL) { 921 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n", 922 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert)); 923 return signerinfo->cert; 924 } 925 ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts); 926 if(ortn) { 927 return NULL; 928 } 929 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n", 930 SecCmsArrayCount((void **)rawCerts)); 931 932 /* 933 * This cert will also need to be freed, but since we save it 934 * in signerinfo for later, we do not want to destroy it when 935 * we leave this function -- we let the clean-up of the entire 936 * cinfo structure later do the destroy of this cert. 937 */ 938 sid = &signerinfo->signerIdentifier; 939 switch (sid->identifierType) { 940 case SecCmsSignerIDIssuerSN: 941 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->cmsg->poolp, 942 sid->id.issuerAndSN); 943 break; 944 case SecCmsSignerIDSubjectKeyID: 945 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, sid->id.subjectKeyID); 946 break; 947 default: 948 cert = NULL; 949 break; 950 } 951 952 /* cert can be NULL at that point */ 953 signerinfo->cert = cert; /* earmark it */ 954 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n", 955 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert)); 956 957 return cert; 958} 959 960/* 961 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer 962 * 963 * sinfo - signerInfo data for this signer 964 * 965 * Returns a CFStringRef containing the common name of the signer. 966 * A return value of NULL is an error. 967 */ 968CFStringRef 969SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo) 970{ 971 SecCertificateRef signercert; 972 CFStringRef commonName = NULL; 973 974 /* will fail if cert is not verified */ 975 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) 976 return NULL; 977 978 SecCertificateCopyCommonName(signercert, &commonName); 979 980 return commonName; 981} 982 983/* 984 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer 985 * 986 * sinfo - signerInfo data for this signer 987 * 988 * Returns a CFStringRef containing the name of the signer. 989 * A return value of NULL is an error. 990 */ 991CFStringRef 992SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo) 993{ 994 SecCertificateRef signercert; 995 CFStringRef emailAddress = NULL; 996 997 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) 998 return NULL; 999 1000 SecCertificateGetEmailAddress(signercert, &emailAddress); 1001 1002 return emailAddress; 1003} 1004 1005 1006/* 1007 * SecCmsSignerInfoAddAuthAttr - add an attribute to the 1008 * authenticated (i.e. signed) attributes of "signerinfo". 1009 */ 1010OSStatus 1011SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr) 1012{ 1013 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr); 1014} 1015 1016/* 1017 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the 1018 * unauthenticated attributes of "signerinfo". 1019 */ 1020OSStatus 1021SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr) 1022{ 1023 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr); 1024} 1025 1026/* 1027 * SecCmsSignerInfoAddSigningTime - add the signing time to the 1028 * authenticated (i.e. signed) attributes of "signerinfo". 1029 * 1030 * This is expected to be included in outgoing signed 1031 * messages for email (S/MIME) but is likely useful in other situations. 1032 * 1033 * This should only be added once; a second call will do nothing. 1034 * 1035 * XXX This will probably just shove the current time into "signerinfo" 1036 * but it will not actually get signed until the entire item is 1037 * processed for encoding. Is this (expected to be small) delay okay? 1038 */ 1039OSStatus 1040SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t) 1041{ 1042 SecCmsAttribute *attr; 1043 CSSM_DATA stime; 1044 void *mark; 1045 PLArenaPool *poolp; 1046 1047 poolp = signerinfo->cmsg->poolp; 1048 1049 mark = PORT_ArenaMark(poolp); 1050 1051 /* create new signing time attribute */ 1052 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess) 1053 goto loser; 1054 1055 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) { 1056 SECITEM_FreeItem (&stime, PR_FALSE); 1057 goto loser; 1058 } 1059 1060 SECITEM_FreeItem (&stime, PR_FALSE); 1061 1062 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1063 goto loser; 1064 1065 PORT_ArenaUnmark (poolp, mark); 1066 1067 return SECSuccess; 1068 1069loser: 1070 PORT_ArenaRelease (poolp, mark); 1071 return SECFailure; 1072} 1073 1074/* 1075 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the 1076 * authenticated (i.e. signed) attributes of "signerinfo". 1077 * 1078 * This is expected to be included in outgoing signed 1079 * messages for email (S/MIME). 1080 */ 1081OSStatus 1082SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo) 1083{ 1084 SecCmsAttribute *attr; 1085 CSSM_DATA_PTR smimecaps = NULL; 1086 void *mark; 1087 PLArenaPool *poolp; 1088 1089 poolp = signerinfo->cmsg->poolp; 1090 1091 mark = PORT_ArenaMark(poolp); 1092 1093 smimecaps = SECITEM_AllocItem(poolp, NULL, 0); 1094 if (smimecaps == NULL) 1095 goto loser; 1096 1097 /* create new signing time attribute */ 1098#if 1 1099 // @@@ We don't do Fortezza yet. 1100 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess) 1101#else 1102 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, 1103 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess) 1104#endif 1105 goto loser; 1106 1107 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL) 1108 goto loser; 1109 1110 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1111 goto loser; 1112 1113 PORT_ArenaUnmark (poolp, mark); 1114 return SECSuccess; 1115 1116loser: 1117 PORT_ArenaRelease (poolp, mark); 1118 return SECFailure; 1119} 1120 1121/* 1122 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 1123 * authenticated (i.e. signed) attributes of "signerinfo". 1124 * 1125 * This is expected to be included in outgoing signed messages for email (S/MIME). 1126 */ 1127OSStatus 1128SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) 1129{ 1130 SecCmsAttribute *attr; 1131 CSSM_DATA_PTR smimeekp = NULL; 1132 void *mark; 1133 PLArenaPool *poolp; 1134 1135#if 0 1136 CFTypeRef policy; 1137 1138 /* verify this cert for encryption */ 1139 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); 1140 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { 1141 CFRelease(policy); 1142 return SECFailure; 1143 } 1144 CFRelease(policy); 1145#endif 1146 1147 poolp = signerinfo->cmsg->poolp; 1148 mark = PORT_ArenaMark(poolp); 1149 1150 smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 1151 if (smimeekp == NULL) 1152 goto loser; 1153 1154 /* create new signing time attribute */ 1155 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess) 1156 goto loser; 1157 1158 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 1159 goto loser; 1160 1161 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1162 goto loser; 1163 1164 PORT_ArenaUnmark (poolp, mark); 1165 return SECSuccess; 1166 1167loser: 1168 PORT_ArenaRelease (poolp, mark); 1169 return SECFailure; 1170} 1171 1172/* 1173 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 1174 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft. 1175 * 1176 * This is expected to be included in outgoing signed messages for email (S/MIME), 1177 * if compatibility with Microsoft mail clients is wanted. 1178 */ 1179OSStatus 1180SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) 1181{ 1182 SecCmsAttribute *attr; 1183 CSSM_DATA_PTR smimeekp = NULL; 1184 void *mark; 1185 PLArenaPool *poolp; 1186 1187#if 0 1188 CFTypeRef policy; 1189 1190 /* verify this cert for encryption */ 1191 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); 1192 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { 1193 CFRelease(policy); 1194 return SECFailure; 1195 } 1196 CFRelease(policy); 1197#endif 1198 1199 poolp = signerinfo->cmsg->poolp; 1200 mark = PORT_ArenaMark(poolp); 1201 1202 smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 1203 if (smimeekp == NULL) 1204 goto loser; 1205 1206 /* create new signing time attribute */ 1207 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess) 1208 goto loser; 1209 1210 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 1211 goto loser; 1212 1213 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) 1214 goto loser; 1215 1216 PORT_ArenaUnmark (poolp, mark); 1217 return SECSuccess; 1218 1219loser: 1220 PORT_ArenaRelease (poolp, mark); 1221 return SECFailure; 1222} 1223 1224/* 1225 * SecCmsSignerInfoAddTimeStamp - add time stamp to the 1226 * unauthenticated (i.e. unsigned) attributes of "signerinfo". 1227 * 1228 * This will initially be used for time stamping signed applications 1229 * by using a Time Stamping Authority. It may also be included in outgoing signed 1230 * messages for email (S/MIME), and may be useful in other situations. 1231 * 1232 * This should only be added once; a second call will do nothing. 1233 * 1234 */ 1235 1236/* 1237Countersignature attribute values have ASN.1 type Countersignature: 1238 Countersignature ::= SignerInfo 1239 Countersignature values have the same meaning as SignerInfo values 1240 for ordinary signatures, except that: 1241 1. The signedAttributes field MUST NOT contain a content-type 1242 attribute; there is no content type for countersignatures. 1243 2. The signedAttributes field MUST contain a message-digest 1244 attribute if it contains any other attributes. 1245 3. The input to the message-digesting process is the contents octets 1246 of the DER encoding of the signatureValue field of the SignerInfo 1247 value with which the attribute is associated. 1248*/ 1249 1250/*! 1251 @function 1252 @abstract Create a timestamp unsigned attribute with a TimeStampToken. 1253*/ 1254 1255OSStatus 1256SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken) 1257{ 1258 SecCmsAttribute *attr; 1259 PLArenaPool *poolp = signerinfo->cmsg->poolp; 1260 void *mark = PORT_ArenaMark(poolp); 1261 1262 // We have already encoded this ourselves, so last param is PR_TRUE 1263 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL) 1264 goto loser; 1265 1266 if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess) 1267 goto loser; 1268 1269 PORT_ArenaUnmark (poolp, mark); 1270 1271 return SECSuccess; 1272 1273loser: 1274 PORT_ArenaRelease (poolp, mark); 1275 return SECFailure; 1276} 1277 1278/* 1279 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo 1280 * 1281 * 1. digest the DER-encoded signature value of the original signerinfo 1282 * 2. create new signerinfo with correct version, sid, digestAlg 1283 * 3. add message-digest authAttr, but NO content-type 1284 * 4. sign the authAttrs 1285 * 5. DER-encode the new signerInfo 1286 * 6. add the whole thing to original signerInfo's unAuthAttrs 1287 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute 1288 * 1289 * XXXX give back the new signerinfo? 1290 */ 1291OSStatus 1292SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo, 1293 SECOidTag digestalg, SecIdentityRef identity) 1294{ 1295 /* XXXX TBD XXXX */ 1296 return SECFailure; 1297} 1298 1299/* 1300 * XXXX the following needs to be done in the S/MIME layer code 1301 * after signature of a signerinfo is verified 1302 */ 1303OSStatus 1304SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo) 1305{ 1306 SecCertificateRef cert = NULL; 1307 CSSM_DATA_PTR profile = NULL; 1308 SecCmsAttribute *attr; 1309 CSSM_DATA_PTR utc_stime = NULL; 1310 CSSM_DATA_PTR ekp; 1311 int save_error; 1312 OSStatus rv; 1313 Boolean must_free_cert = PR_FALSE; 1314 OSStatus status; 1315 SecKeychainRef keychainOrArray; 1316 1317 status = SecKeychainCopyDefault(&keychainOrArray); 1318 1319 /* sanity check - see if verification status is ok (unverified does not count...) */ 1320 if (signerinfo->verificationStatus != SecCmsVSGoodSignature) 1321 return SECFailure; 1322 1323 /* find preferred encryption cert */ 1324 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) && 1325 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, 1326 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) 1327 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ 1328 ekp = SecCmsAttributeGetValue(attr); 1329 if (ekp == NULL) 1330 return SECFailure; 1331 1332 /* we assume that all certs coming with the message have been imported to the */ 1333 /* temporary database */ 1334 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, ekp); 1335 if (cert == NULL) 1336 return SECFailure; 1337 must_free_cert = PR_TRUE; 1338 } 1339 1340 if (cert == NULL) { 1341 /* no preferred cert found? 1342 * find the cert the signerinfo is signed with instead */ 1343 CFStringRef emailAddress=NULL; 1344 1345 cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray); 1346 if (cert == NULL) 1347 return SECFailure; 1348 if (SecCertificateGetEmailAddress(cert,&emailAddress)) 1349 return SECFailure; 1350 } 1351 1352 /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert. 1353 * that's OK, we can still save the S/MIME profile. The encryption cert 1354 * should have already been saved */ 1355#ifdef notdef 1356 if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { 1357 if (must_free_cert) 1358 CERT_DestroyCertificate(cert); 1359 return SECFailure; 1360 } 1361#endif 1362 1363 /* XXX store encryption cert permanently? */ 1364 1365 /* 1366 * Remember the current error set because we do not care about 1367 * anything set by the functions we are about to call. 1368 */ 1369 save_error = PORT_GetError(); 1370 1371 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { 1372 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, 1373 SEC_OID_PKCS9_SMIME_CAPABILITIES, 1374 PR_TRUE); 1375 profile = SecCmsAttributeGetValue(attr); 1376 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, 1377 SEC_OID_PKCS9_SIGNING_TIME, 1378 PR_TRUE); 1379 utc_stime = SecCmsAttributeGetValue(attr); 1380 } 1381 1382 rv = CERT_SaveSMimeProfile (cert, profile, utc_stime); 1383 if (must_free_cert) 1384 CERT_DestroyCertificate(cert); 1385 1386 /* 1387 * Restore the saved error in case the calls above set a new 1388 * one that we do not actually care about. 1389 */ 1390 PORT_SetError (save_error); 1391 1392 return rv; 1393} 1394 1395/* 1396 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer 1397 */ 1398OSStatus 1399SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage) 1400{ 1401 if (signerinfo->cert == NULL) 1402 return SECFailure; 1403 1404 /* don't leak if we get called twice */ 1405 if (signerinfo->certList != NULL) { 1406 CFRelease(signerinfo->certList); 1407 signerinfo->certList = NULL; 1408 } 1409 1410 switch (cm) { 1411 case SecCmsCMNone: 1412 signerinfo->certList = NULL; 1413 break; 1414 case SecCmsCMCertOnly: 1415 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert); 1416 break; 1417 case SecCmsCMCertChain: 1418 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE); 1419 break; 1420 case SecCmsCMCertChainWithRoot: 1421 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE); 1422 break; 1423 } 1424 1425 if (cm != SecCmsCMNone && signerinfo->certList == NULL) 1426 return SECFailure; 1427 1428 return SECSuccess; 1429} 1430