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