X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_smime/lib/cmssiginfo.c?ds=sidebyside diff --git a/Security/libsecurity_smime/lib/cmssiginfo.c b/Security/libsecurity_smime/lib/cmssiginfo.c new file mode 100644 index 00000000..1913239b --- /dev/null +++ b/Security/libsecurity_smime/lib/cmssiginfo.c @@ -0,0 +1,1429 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * CMS signerInfo methods. + */ + +#include +#include "SecSMIMEPriv.h" + +#include "cmslocal.h" + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "cryptohi.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tsaSupport.h" +#include "tsaSupportPriv.h" + +#define HIDIGIT(v) (((v) / 10) + '0') +#define LODIGIT(v) (((v) % 10) + '0') + +#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9')) +#define CAPTURE(var,p,label) \ +{ \ + if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \ + (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \ +} + +#ifndef NDEBUG +#define SIGINFO_DEBUG 1 +#endif + +#if SIGINFO_DEBUG +#define dprintf(args...) printf(args) +#else +#define dprintf(args...) +#endif + +#if RELEASECOUNTDEBUG +#define dprintfRC(args...) dprintf(args) +#else +#define dprintfRC(args...) +#endif + +static OSStatus +DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime, CFAbsoluteTime *date) +{ + CFGregorianDate gdate; + char *string = (char *)utcTime->Data; + long year, month, mday, hour, minute, second, hourOff, minOff; + CFTimeZoneRef timeZone; + + /* Verify time is formatted properly and capture information */ + second = 0; + hourOff = 0; + minOff = 0; + CAPTURE(year,string+0,loser); + if (year < 50) { + /* ASSUME that year # is in the 2000's, not the 1900's */ + year += 100; + } + CAPTURE(month,string+2,loser); + if ((month == 0) || (month > 12)) goto loser; + CAPTURE(mday,string+4,loser); + if ((mday == 0) || (mday > 31)) goto loser; + CAPTURE(hour,string+6,loser); + if (hour > 23) goto loser; + CAPTURE(minute,string+8,loser); + if (minute > 59) goto loser; + if (ISDIGIT(string[10])) { + CAPTURE(second,string+10,loser); + if (second > 59) goto loser; + string += 2; + } + if (string[10] == '+') { + CAPTURE(hourOff,string+11,loser); + if (hourOff > 23) goto loser; + CAPTURE(minOff,string+13,loser); + if (minOff > 59) goto loser; + } else if (string[10] == '-') { + CAPTURE(hourOff,string+11,loser); + if (hourOff > 23) goto loser; + hourOff = -hourOff; + CAPTURE(minOff,string+13,loser); + if (minOff > 59) goto loser; + minOff = -minOff; + } else if (string[10] != 'Z') { + goto loser; + } + + gdate.year = (SInt32)(year + 1900); + gdate.month = month; + gdate.day = mday; + gdate.hour = hour; + gdate.minute = minute; + gdate.second = second; + + if (hourOff == 0 && minOff == 0) + timeZone = NULL; /* GMT */ + else + { + timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, (hourOff * 60 + minOff) * 60); + } + + *date = CFGregorianDateGetAbsoluteTime(gdate, timeZone); + if (timeZone) + CFRelease(timeZone); + + return SECSuccess; + +loser: + return SECFailure; +} + +static OSStatus +DER_CFDateToUTCTime(CFAbsoluteTime date, CSSM_DATA_PTR utcTime) +{ + CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(date, NULL /* GMT */); + unsigned char *d; + SInt8 second; + + utcTime->Length = 13; + utcTime->Data = d = PORT_Alloc(13); + if (!utcTime->Data) + return SECFailure; + + /* UTC time does not handle the years before 1950 */ + if (gdate.year < 1950) + return SECFailure; + + /* remove the century since it's added to the year by the + CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */ + gdate.year %= 100; + second = gdate.second + 0.5; + + d[0] = HIDIGIT(gdate.year); + d[1] = LODIGIT(gdate.year); + d[2] = HIDIGIT(gdate.month); + d[3] = LODIGIT(gdate.month); + d[4] = HIDIGIT(gdate.day); + d[5] = LODIGIT(gdate.day); + d[6] = HIDIGIT(gdate.hour); + d[7] = LODIGIT(gdate.hour); + d[8] = HIDIGIT(gdate.minute); + d[9] = LODIGIT(gdate.minute); + d[10] = HIDIGIT(second); + d[11] = LODIGIT(second); + d[12] = 'Z'; + return SECSuccess; +} + +/* ============================================================================= + * SIGNERINFO + */ +SecCmsSignerInfoRef +nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag); + +SecCmsSignerInfoRef +SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag) +{ + return nss_cmssignerinfo_create(cmsg, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag); +} + +SecCmsSignerInfoRef +SecCmsSignerInfoCreate(SecCmsMessageRef cmsg, SecIdentityRef identity, SECOidTag digestalgtag) +{ + SecCmsSignerInfoRef signerInfo = NULL; + SecCertificateRef cert = NULL; + SecPrivateKeyRef signingKey = NULL; + + if (SecIdentityCopyCertificate(identity, &cert)) + goto loser; + if (SecIdentityCopyPrivateKey(identity, &signingKey)) + goto loser; + + signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag); + +loser: + if (cert) + CFRelease(cert); + if (signingKey) + CFRelease(signingKey); + + return signerInfo; +} + +SecCmsSignerInfoRef +nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag) +{ + void *mark; + SecCmsSignerInfoRef signerinfo; + int version; + PLArenaPool *poolp; + + poolp = cmsg->poolp; + + mark = PORT_ArenaMark(poolp); + + signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo)); + if (signerinfo == NULL) { + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + + signerinfo->cmsg = cmsg; + + switch(type) { + case SecCmsSignerIDIssuerSN: + signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN; + if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL) + goto loser; + if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) + goto loser; + dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n", + (int)CFGetRetainCount(signerinfo->cert)); + break; + case SecCmsSignerIDSubjectKeyID: + signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID; + PORT_Assert(subjKeyID); + if (!subjKeyID) + goto loser; + signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA); + SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID, + subjKeyID); + signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey); + if (!signerinfo->pubKey) + goto loser; + break; + default: + goto loser; + } + + if (!signingKey) + goto loser; + + signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey); + if (!signerinfo->signingKey) + goto loser; + + /* set version right now */ + version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN; + /* RFC2630 5.3 "version is the syntax version number. If the .... " */ + if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) + version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY; + (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version); + + if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess) + goto loser; + + PORT_ArenaUnmark(poolp, mark); + return signerinfo; + +loser: + PORT_ArenaRelease(poolp, mark); + return NULL; +} + +/* + * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure + */ +void +SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si) +{ + if (si->cert != NULL) { + dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n", + si->cert, (int)CFGetRetainCount(si->cert)); + CERT_DestroyCertificate(si->cert); + } + if (si->certList != NULL) { + dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n", + (int)CFGetRetainCount(si->certList)); + CFRelease(si->certList); + } + if (si->timestampCertList != NULL) { + dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n", + (int)CFGetRetainCount(si->timestampCertList)); + CFRelease(si->timestampCertList); + } + /* XXX storage ??? */ +} + +/* + * SecCmsSignerInfoSign - sign something + * + */ +OSStatus +SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) +{ + SecCertificateRef cert; + SecPrivateKeyRef privkey = NULL; + SECOidTag digestalgtag; + SECOidTag pubkAlgTag; + CSSM_DATA signature = { 0 }; + OSStatus rv; + PLArenaPool *poolp, *tmppoolp; + const SECAlgorithmID *algID; + SECAlgorithmID freeAlgID; + //CERTSubjectPublicKeyInfo *spki; + + PORT_Assert (digest != NULL); + + poolp = signerinfo->cmsg->poolp; + + switch (signerinfo->signerIdentifier.identifierType) { + case SecCmsSignerIDIssuerSN: + privkey = signerinfo->signingKey; + signerinfo->signingKey = NULL; + cert = signerinfo->cert; + if (SecCertificateGetAlgorithmID(cert,&algID)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + goto loser; + } + break; + case SecCmsSignerIDSubjectKeyID: + privkey = signerinfo->signingKey; + signerinfo->signingKey = NULL; +#if 0 + spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); + SECKEY_DestroyPublicKey(signerinfo->pubKey); + signerinfo->pubKey = NULL; + SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); + SECKEY_DestroySubjectPublicKeyInfo(spki); + algID = &freeAlgID; +#else + if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + goto loser; + } + CFRelease(signerinfo->pubKey); + signerinfo->pubKey = NULL; +#endif + break; + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE); + goto loser; + } + digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); + /* + * XXX I think there should be a cert-level interface for this, + * so that I do not have to know about subjectPublicKeyInfo... + */ + pubkAlgTag = SECOID_GetAlgorithmTag(algID); + if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) { + SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); + } + +#if 0 + // @@@ Not yet + /* Fortezza MISSI have weird signature formats. + * Map them to standard DSA formats + */ + pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag); +#endif + + if (signerinfo->authAttr != NULL) { + CSSM_DATA encoded_attrs; + + /* find and fill in the message digest attribute. */ + rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), + SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); + if (rv != SECSuccess) + goto loser; + + if (contentType != NULL) { + /* if the caller wants us to, find and fill in the content type attribute. */ + rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), + SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); + if (rv != SECSuccess) + goto loser; + } + + if ((tmppoolp = PORT_NewArena (1024)) == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* + * Before encoding, reorder the attributes so that when they + * are encoded, they will be conforming DER, which is required + * to have a specific order and that is what must be used for + * the hash/signature. We do this here, rather than building + * it into EncodeAttributes, because we do not want to do + * such reordering on incoming messages (which also uses + * EncodeAttributes) or our old signatures (and other "broken" + * implementations) will not verify. So, we want to guarantee + * that we send out good DER encodings of attributes, but not + * to expect to receive them. + */ + if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess) + goto loser; + + encoded_attrs.Data = NULL; + encoded_attrs.Length = 0; + if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr), + &encoded_attrs) == NULL) + goto loser; + + rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length, + privkey, digestalgtag, pubkAlgTag); + PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ + } else { + rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest); + } + SECKEY_DestroyPrivateKey(privkey); + privkey = NULL; + + if (rv != SECSuccess) + goto loser; + + if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) + != SECSuccess) + goto loser; + + SECITEM_FreeItem(&signature, PR_FALSE); + + if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) { + /* + * RFC 3278 section section 2.1.1 states that the signatureAlgorithm + * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey + * as would appear in other forms of signed datas. However Microsoft doesn't + * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there, + * MS can't verify - presumably because it takes the digest of the digest + * before feeding it to ECDSA. + * We handle this with a preference; default if it's not there is + * "Microsoft compatibility mode". + */ + if(!SecCmsMsEcdsaCompatMode()) { + pubkAlgTag = SEC_OID_ECDSA_WithSHA1; + } + /* else violating the spec for compatibility */ + } + + if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, + NULL) != SECSuccess) + goto loser; + + return SECSuccess; + +loser: + if (signature.Length != 0) + SECITEM_FreeItem (&signature, PR_FALSE); + if (privkey) + SECKEY_DestroyPrivateKey(privkey); + if((algID != NULL) & (algID != &freeAlgID)) { + /* this is dicey - this was actually mallocd by either SecCertificate or + * by SecKey...it all boils down to a free() in the end though. */ + SECOID_DestroyAlgorithmID((SECAlgorithmID *)algID, PR_FALSE); + } + return SECFailure; +} + +OSStatus +SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray, + CFTypeRef policies, SecTrustRef *trustRef) +{ + SecCertificateRef cert; + CFAbsoluteTime stime; + OSStatus rv; + CSSM_DATA_PTR *otherCerts; + + if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) { + dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n"); + signerinfo->verificationStatus = SecCmsVSSigningCertNotFound; + return SECFailure; + } + + /* + * Get and convert the signing time; if available, it will be used + * both on the cert verification and for importing the sender + * email profile. + */ + CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies); + if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess) + if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess) + stime = CFAbsoluteTimeGetCurrent(); + CFReleaseSafe(timeStampPolicies); + + rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts); + if(rv) { + return rv; + } + rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef); + dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n", + cert, (int)CFGetRetainCount(cert)); + if (rv || !trustRef) + { + if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT) + { + /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */ + if (signerinfo->verificationStatus == SecCmsVSGoodSignature) + signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted; + } + } + /* FIXME isn't this leaking the cert? */ + dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv); + return rv; +} + +static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo) +{ +#if SIGINFO_DEBUG + CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo); + if (cn) + { + char *ccn = cfStringToChar(cn); + if (ccn) + { + dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn); + free(ccn); + } + CFRelease(cn); + } +#endif +} + +/* + * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo + * + * Just verifies the signature. The assumption is that verification of the certificate + * is done already. + */ +OSStatus +SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) +{ + return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType); +} + +OSStatus +SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) +{ + SecPublicKeyRef publickey = NULL; + SecCmsAttribute *attr; + CSSM_DATA encoded_attrs; + SecCertificateRef cert; + SecCmsVerificationStatus vs = SecCmsVSUnverified; + PLArenaPool *poolp; + SECOidTag digestAlgTag, digestEncAlgTag; + + if (signerinfo == NULL) + return SECFailure; + + /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */ + /* cert has not been verified */ + if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) { + dprintf("SecCmsSignerInfoVerify: no signing cert\n"); + vs = SecCmsVSSigningCertNotFound; + goto loser; + } + + dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert)); + + debugShowSigningCertificate(signerinfo); + + if (SecCertificateCopyPublicKey(cert, &publickey)) { + vs = SecCmsVSProcessingError; + goto loser; + } + + digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg)); + digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); + + /* + * Gross hack necessitated by RFC 3278 section 2.1.1, which states + * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1, + * *not* (as in all other algorithms) the raw signature algorithm, e.g. + * pkcs1RSAEncryption. + */ + if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) { + digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY; + } + + if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { + if (contentType) { + /* + * Check content type + * + * RFC2630 sez that if there are any authenticated attributes, + * then there must be one for content type which matches the + * content type of the content being signed, and there must + * be one for message digest which matches our message digest. + * So check these things first. + */ + if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, + SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL) + { + vs = SecCmsVSMalformedSignature; + goto loser; + } + + if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) { + vs = SecCmsVSMalformedSignature; + goto loser; + } + } + + /* + * Check digest + */ + if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL) + { + vs = SecCmsVSMalformedSignature; + goto loser; + } + if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) { + vs = SecCmsVSDigestMismatch; + goto loser; + } + + if ((poolp = PORT_NewArena (1024)) == NULL) { + vs = SecCmsVSProcessingError; + goto loser; + } + + /* + * Check signature + * + * The signature is based on a digest of the DER-encoded authenticated + * attributes. So, first we encode and then we digest/verify. + * we trust the decoder to have the attributes in the right (sorted) order + */ + encoded_attrs.Data = NULL; + encoded_attrs.Length = 0; + + if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL || + encoded_attrs.Data == NULL || encoded_attrs.Length == 0) + { + vs = SecCmsVSProcessingError; + goto loser; + } + + vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length, + publickey, &(signerinfo->encDigest), + digestAlgTag, digestEncAlgTag, + signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; + + dprintf("VFY_VerifyData (authenticated attributes): %s\n", + (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature"); + + PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ + + } else { + CSSM_DATA_PTR sig; + + /* No authenticated attributes. The signature is based on the plain message digest. */ + sig = &(signerinfo->encDigest); + if (sig->Length == 0) + goto loser; + + vs = (VFY_VerifyDigest(digest, publickey, sig, + digestAlgTag, digestEncAlgTag, + signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; + + dprintf("VFY_VerifyData (plain message digest): %s\n", + (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature"); + } + + if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr)) + { + dprintf("found an unAuthAttr\n"); + OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy); + dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux); + if (rux) + goto loser; + } + + if (vs == SecCmsVSBadSignature) { + /* + * XXX Change the generic error into our specific one, because + * in that case we get a better explanation out of the Security + * Advisor. This is really a bug in our error strings (the + * "generic" error has a lousy/wrong message associated with it + * which assumes the signature verification was done for the + * purposes of checking the issuer signature on a certificate) + * but this is at least an easy workaround and/or in the + * Security Advisor, which specifically checks for the error + * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation + * in that case but does not similarly check for + * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would + * probably say the wrong thing in the case that it *was* the + * certificate signature check that failed during the cert + * verification done above. Our error handling is really a mess. + */ + if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) + PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); + } + + if (publickey != NULL) + CFRelease(publickey); + + signerinfo->verificationStatus = vs; + dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n", + cert, (int)CFGetRetainCount(cert)); + + dprintf("verificationStatus: %d\n", vs); + + return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure; + +loser: + if (publickey != NULL) + SECKEY_DestroyPublicKey (publickey); + + dprintf("verificationStatus2: %d\n", vs); + signerinfo->verificationStatus = vs; + + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + return SECFailure; +} + +OSStatus +SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) { + return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL); +} + +OSStatus +SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy) +{ + /* + unAuthAttr is an array of attributes; we expect to + see just one: the timestamp blob. If we have an unAuthAttr, + but don't see a timestamp, return an error since we have + no other cases where this would be present. + */ + + SecCmsAttribute *attr = NULL; + OSStatus status = SECFailure; + + require(signerinfo, xit); + attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr, + SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE); + if (attr == NULL) + { + status = errSecTimestampMissing; + goto xit; + } + + dprintf("found an id-ct-TSTInfo\n"); + // Don't check the nonce in this case + status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0); +xit: + return status; +} + +CSSM_DATA * +SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo) +{ + return &signerinfo->encDigest; +} + +SecCmsVerificationStatus +SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo) +{ + return signerinfo->verificationStatus; +} + +SECOidData * +SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo) +{ + return SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); +} + +SECOidTag +SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo) +{ + SECOidData *algdata; + + algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); + if (algdata != NULL) + return algdata->offset; + else + return SEC_OID_UNKNOWN; +} + +CFArrayRef +SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo) +{ + dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n", + (int)CFGetRetainCount(signerinfo->certList)); + return signerinfo->certList; +} + +CFArrayRef +SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo) +{ + dprintfRC("SecCmsSignerInfoGetCertList: timestampCertList.rc %d\n", + (int)CFGetRetainCount(signerinfo->timestampCertList)); + return signerinfo->timestampCertList; +} + + + +int +SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo) +{ + unsigned long version; + + /* always take apart the CSSM_DATA */ + if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess) + return 0; + else + return (int)version; +} + +/* + * SecCmsSignerInfoGetSigningTime - return the signing time, + * in UTCTime format, of a CMS signerInfo. + * + * sinfo - signerInfo data for this signer + * + * Returns a pointer to XXXX (what?) + * A return value of NULL is an error. + */ +OSStatus +SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime) +{ + SecCmsAttribute *attr; + CSSM_DATA_PTR value; + + if (sinfo == NULL) + return paramErr; + + if (sinfo->signingTime != 0) { + *stime = sinfo->signingTime; /* cached copy */ + return SECSuccess; + } + + attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); + /* XXXX multi-valued attributes NIH */ + if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) + return errSecSigningTimeMissing; + if (DER_UTCTimeToCFDate(value, stime) != SECSuccess) + return errSecSigningTimeMissing; + sinfo->signingTime = *stime; /* make cached copy */ + return SECSuccess; +} + +OSStatus +SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime) +{ + return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime); +} + +OSStatus +SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime) +{ + OSStatus status = paramErr; + + require(sinfo && stime, xit); + + if (sinfo->timestampTime != 0) + { + *stime = sinfo->timestampTime; /* cached copy */ + return noErr; + } + + // A bit heavyweight if haven't already called verify + status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy); + *stime = sinfo->timestampTime; +xit: + return status; +} + +/* + * Return the signing cert of a CMS signerInfo. + * + * the certs in the enclosing SignedData must have been imported already + */ +SecCertificateRef +SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray) +{ + SecCertificateRef cert; + SecCmsSignerIdentifier *sid; + OSStatus ortn; + CSSM_DATA_PTR *rawCerts; + + if (signerinfo->cert != NULL) { + dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n", + signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert)); + return signerinfo->cert; + } + ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts); + if(ortn) { + return NULL; + } + dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n", + SecCmsArrayCount((void **)rawCerts)); + + /* + * This cert will also need to be freed, but since we save it + * in signerinfo for later, we do not want to destroy it when + * we leave this function -- we let the clean-up of the entire + * cinfo structure later do the destroy of this cert. + */ + sid = &signerinfo->signerIdentifier; + switch (sid->identifierType) { + case SecCmsSignerIDIssuerSN: + cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->cmsg->poolp, + sid->id.issuerAndSN); + break; + case SecCmsSignerIDSubjectKeyID: + cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, sid->id.subjectKeyID); + break; + default: + cert = NULL; + break; + } + + /* cert can be NULL at that point */ + signerinfo->cert = cert; /* earmark it */ + dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n", + signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert)); + + return cert; +} + +/* + * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer + * + * sinfo - signerInfo data for this signer + * + * Returns a CFStringRef containing the common name of the signer. + * A return value of NULL is an error. + */ +CFStringRef +SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo) +{ + SecCertificateRef signercert; + CFStringRef commonName = NULL; + + /* will fail if cert is not verified */ + if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) + return NULL; + + SecCertificateCopyCommonName(signercert, &commonName); + + return commonName; +} + +/* + * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer + * + * sinfo - signerInfo data for this signer + * + * Returns a CFStringRef containing the name of the signer. + * A return value of NULL is an error. + */ +CFStringRef +SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo) +{ + SecCertificateRef signercert; + CFStringRef emailAddress = NULL; + + if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL) + return NULL; + + SecCertificateGetEmailAddress(signercert, &emailAddress); + + return emailAddress; +} + + +/* + * SecCmsSignerInfoAddAuthAttr - add an attribute to the + * authenticated (i.e. signed) attributes of "signerinfo". + */ +OSStatus +SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr) +{ + return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr); +} + +/* + * SecCmsSignerInfoAddUnauthAttr - add an attribute to the + * unauthenticated attributes of "signerinfo". + */ +OSStatus +SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr) +{ + return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr); +} + +/* + * SecCmsSignerInfoAddSigningTime - add the signing time to the + * authenticated (i.e. signed) attributes of "signerinfo". + * + * This is expected to be included in outgoing signed + * messages for email (S/MIME) but is likely useful in other situations. + * + * This should only be added once; a second call will do nothing. + * + * XXX This will probably just shove the current time into "signerinfo" + * but it will not actually get signed until the entire item is + * processed for encoding. Is this (expected to be small) delay okay? + */ +OSStatus +SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t) +{ + SecCmsAttribute *attr; + CSSM_DATA stime; + void *mark; + PLArenaPool *poolp; + + poolp = signerinfo->cmsg->poolp; + + mark = PORT_ArenaMark(poolp); + + /* create new signing time attribute */ + if (DER_CFDateToUTCTime(t, &stime) != SECSuccess) + goto loser; + + if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) { + SECITEM_FreeItem (&stime, PR_FALSE); + goto loser; + } + + SECITEM_FreeItem (&stime, PR_FALSE); + + if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) + goto loser; + + PORT_ArenaUnmark (poolp, mark); + + return SECSuccess; + +loser: + PORT_ArenaRelease (poolp, mark); + return SECFailure; +} + +/* + * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the + * authenticated (i.e. signed) attributes of "signerinfo". + * + * This is expected to be included in outgoing signed + * messages for email (S/MIME). + */ +OSStatus +SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo) +{ + SecCmsAttribute *attr; + CSSM_DATA_PTR smimecaps = NULL; + void *mark; + PLArenaPool *poolp; + + poolp = signerinfo->cmsg->poolp; + + mark = PORT_ArenaMark(poolp); + + smimecaps = SECITEM_AllocItem(poolp, NULL, 0); + if (smimecaps == NULL) + goto loser; + + /* create new signing time attribute */ +#if 1 + // @@@ We don't do Fortezza yet. + if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess) +#else + if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, + PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess) +#endif + goto loser; + + if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL) + goto loser; + + if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) + goto loser; + + PORT_ArenaUnmark (poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease (poolp, mark); + return SECFailure; +} + +/* + * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the + * authenticated (i.e. signed) attributes of "signerinfo". + * + * This is expected to be included in outgoing signed messages for email (S/MIME). + */ +OSStatus +SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) +{ + SecCmsAttribute *attr; + CSSM_DATA_PTR smimeekp = NULL; + void *mark; + PLArenaPool *poolp; + +#if 0 + CFTypeRef policy; + + /* verify this cert for encryption */ + policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); + if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { + CFRelease(policy); + return SECFailure; + } + CFRelease(policy); +#endif + + poolp = signerinfo->cmsg->poolp; + mark = PORT_ArenaMark(poolp); + + smimeekp = SECITEM_AllocItem(poolp, NULL, 0); + if (smimeekp == NULL) + goto loser; + + /* create new signing time attribute */ + if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess) + goto loser; + + if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) + goto loser; + + if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) + goto loser; + + PORT_ArenaUnmark (poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease (poolp, mark); + return SECFailure; +} + +/* + * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the + * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft. + * + * This is expected to be included in outgoing signed messages for email (S/MIME), + * if compatibility with Microsoft mail clients is wanted. + */ +OSStatus +SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) +{ + SecCmsAttribute *attr; + CSSM_DATA_PTR smimeekp = NULL; + void *mark; + PLArenaPool *poolp; + +#if 0 + CFTypeRef policy; + + /* verify this cert for encryption */ + policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); + if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { + CFRelease(policy); + return SECFailure; + } + CFRelease(policy); +#endif + + poolp = signerinfo->cmsg->poolp; + mark = PORT_ArenaMark(poolp); + + smimeekp = SECITEM_AllocItem(poolp, NULL, 0); + if (smimeekp == NULL) + goto loser; + + /* create new signing time attribute */ + if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess) + goto loser; + + if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) + goto loser; + + if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) + goto loser; + + PORT_ArenaUnmark (poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease (poolp, mark); + return SECFailure; +} + +/* + * SecCmsSignerInfoAddTimeStamp - add time stamp to the + * unauthenticated (i.e. unsigned) attributes of "signerinfo". + * + * This will initially be used for time stamping signed applications + * by using a Time Stamping Authority. It may also be included in outgoing signed + * messages for email (S/MIME), and may be useful in other situations. + * + * This should only be added once; a second call will do nothing. + * + */ + +/* +Countersignature attribute values have ASN.1 type Countersignature: + Countersignature ::= SignerInfo + Countersignature values have the same meaning as SignerInfo values + for ordinary signatures, except that: + 1. The signedAttributes field MUST NOT contain a content-type + attribute; there is no content type for countersignatures. + 2. The signedAttributes field MUST contain a message-digest + attribute if it contains any other attributes. + 3. The input to the message-digesting process is the contents octets + of the DER encoding of the signatureValue field of the SignerInfo + value with which the attribute is associated. +*/ + +/*! + @function + @abstract Create a timestamp unsigned attribute with a TimeStampToken. +*/ + +OSStatus +SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken) +{ + SecCmsAttribute *attr; + PLArenaPool *poolp = signerinfo->cmsg->poolp; + void *mark = PORT_ArenaMark(poolp); + + // We have already encoded this ourselves, so last param is PR_TRUE + if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL) + goto loser; + + if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess) + goto loser; + + PORT_ArenaUnmark (poolp, mark); + + return SECSuccess; + +loser: + PORT_ArenaRelease (poolp, mark); + return SECFailure; +} + +/* + * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo + * + * 1. digest the DER-encoded signature value of the original signerinfo + * 2. create new signerinfo with correct version, sid, digestAlg + * 3. add message-digest authAttr, but NO content-type + * 4. sign the authAttrs + * 5. DER-encode the new signerInfo + * 6. add the whole thing to original signerInfo's unAuthAttrs + * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute + * + * XXXX give back the new signerinfo? + */ +OSStatus +SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo, + SECOidTag digestalg, SecIdentityRef identity) +{ + /* XXXX TBD XXXX */ + return SECFailure; +} + +/* + * XXXX the following needs to be done in the S/MIME layer code + * after signature of a signerinfo is verified + */ +OSStatus +SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo) +{ + SecCertificateRef cert = NULL; + CSSM_DATA_PTR profile = NULL; + SecCmsAttribute *attr; + CSSM_DATA_PTR utc_stime = NULL; + CSSM_DATA_PTR ekp; + int save_error; + OSStatus rv; + Boolean must_free_cert = PR_FALSE; + OSStatus status; + SecKeychainRef keychainOrArray; + + status = SecKeychainCopyDefault(&keychainOrArray); + + /* sanity check - see if verification status is ok (unverified does not count...) */ + if (signerinfo->verificationStatus != SecCmsVSGoodSignature) + return SECFailure; + + /* find preferred encryption cert */ + if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) && + (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, + SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) + { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ + ekp = SecCmsAttributeGetValue(attr); + if (ekp == NULL) + return SECFailure; + + /* we assume that all certs coming with the message have been imported to the */ + /* temporary database */ + cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, ekp); + if (cert == NULL) + return SECFailure; + must_free_cert = PR_TRUE; + } + + if (cert == NULL) { + /* no preferred cert found? + * find the cert the signerinfo is signed with instead */ + CFStringRef emailAddress=NULL; + + cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray); + if (cert == NULL) + return SECFailure; + if (SecCertificateGetEmailAddress(cert,&emailAddress)) + return SECFailure; + } + + /* 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. + * that's OK, we can still save the S/MIME profile. The encryption cert + * should have already been saved */ +#ifdef notdef + if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { + if (must_free_cert) + CERT_DestroyCertificate(cert); + return SECFailure; + } +#endif + + /* XXX store encryption cert permanently? */ + + /* + * Remember the current error set because we do not care about + * anything set by the functions we are about to call. + */ + save_error = PORT_GetError(); + + if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { + attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, + SEC_OID_PKCS9_SMIME_CAPABILITIES, + PR_TRUE); + profile = SecCmsAttributeGetValue(attr); + attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, + SEC_OID_PKCS9_SIGNING_TIME, + PR_TRUE); + utc_stime = SecCmsAttributeGetValue(attr); + } + + rv = CERT_SaveSMimeProfile (cert, profile, utc_stime); + if (must_free_cert) + CERT_DestroyCertificate(cert); + + /* + * Restore the saved error in case the calls above set a new + * one that we do not actually care about. + */ + PORT_SetError (save_error); + + return rv; +} + +/* + * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer + */ +OSStatus +SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage) +{ + if (signerinfo->cert == NULL) + return SECFailure; + + /* don't leak if we get called twice */ + if (signerinfo->certList != NULL) { + CFRelease(signerinfo->certList); + signerinfo->certList = NULL; + } + + switch (cm) { + case SecCmsCMNone: + signerinfo->certList = NULL; + break; + case SecCmsCMCertOnly: + signerinfo->certList = CERT_CertListFromCert(signerinfo->cert); + break; + case SecCmsCMCertChain: + signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE); + break; + case SecCmsCMCertChainWithRoot: + signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE); + break; + } + + if (cm != SecCmsCMNone && signerinfo->certList == NULL) + return SECFailure; + + return SECSuccess; +}