]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_smime/lib/cmssiginfo.c
Security-58286.260.20.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmssiginfo.c
index 29c3e1df404b77e88a5fe9a6d04a783696db364b..3c1ff754834dc9263f7b0e62a0540a1289ece22e 100644 (file)
 #include <Security/SecKeyPriv.h>
 #include <CoreFoundation/CFTimeZone.h>
 #include <utilities/SecCFWrappers.h>
 #include <Security/SecKeyPriv.h>
 #include <CoreFoundation/CFTimeZone.h>
 #include <utilities/SecCFWrappers.h>
+#include <utilities/debugging.h>
 #include <AssertMacros.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
 #include <Security/SecPolicyPriv.h>
 #include <AssertMacros.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
 #include <Security/SecPolicyPriv.h>
+#include <Security/SecItem.h>
 
 #include "tsaSupport.h"
 #include "tsaSupportPriv.h"
 
 
 #include "tsaSupport.h"
 #include "tsaSupportPriv.h"
 
+#include <syslog.h>
+
 #define HIDIGIT(v) (((v) / 10) + '0')    
 #define LODIGIT(v) (((v) % 10) + '0')     
 
 #define HIDIGIT(v) (((v) / 10) + '0')    
 #define LODIGIT(v) (((v) % 10) + '0')     
 
@@ -211,12 +215,22 @@ SecCmsSignerInfoCreate(SecCmsMessageRef cmsg, SecIdentityRef identity, SECOidTag
     SecCmsSignerInfoRef signerInfo = NULL;
     SecCertificateRef cert = NULL;
     SecPrivateKeyRef signingKey = NULL;
     SecCmsSignerInfoRef signerInfo = NULL;
     SecCertificateRef cert = NULL;
     SecPrivateKeyRef signingKey = NULL;
+    CFDictionaryRef keyAttrs = NULL;
 
     if (SecIdentityCopyCertificate(identity, &cert))
        goto loser;
     if (SecIdentityCopyPrivateKey(identity, &signingKey))
        goto loser;
 
 
     if (SecIdentityCopyCertificate(identity, &cert))
        goto loser;
     if (SecIdentityCopyPrivateKey(identity, &signingKey))
        goto loser;
 
+    /* In some situations, the "Private Key" in the identity is actually a public key. */
+    keyAttrs = SecKeyCopyAttributes(signingKey);
+    if (!keyAttrs)
+        goto loser;
+    CFTypeRef class = CFDictionaryGetValue(keyAttrs, kSecAttrKeyClass);
+    if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate))
+        goto loser;
+
+
     signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
 
 loser:
     signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
 
 loser:
@@ -224,6 +238,8 @@ loser:
        CFRelease(cert);
     if (signingKey)
        CFRelease(signingKey);
        CFRelease(cert);
     if (signingKey)
        CFRelease(signingKey);
+    if (keyAttrs)
+        CFRelease(keyAttrs);
 
     return signerInfo;
 }
 
     return signerInfo;
 }
@@ -265,8 +281,10 @@ nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, Sec
         if (!subjKeyID)
             goto loser;
         signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
         if (!subjKeyID)
             goto loser;
         signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
-        SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
-                         subjKeyID);
+        if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
+                             subjKeyID)) {
+            goto loser;
+        }
         signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
         if (!signerinfo->pubKey)
             goto loser;
         signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
         if (!signerinfo->pubKey)
             goto loser;
@@ -321,6 +339,21 @@ SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
            (int)CFGetRetainCount(si->timestampCertList));
        CFRelease(si->timestampCertList);
     }
            (int)CFGetRetainCount(si->timestampCertList));
        CFRelease(si->timestampCertList);
     }
+    if (si->timestampCert != NULL) {
+        dprintfRC("SecCmsSignerInfoDestroy top: timestampCert.rc %d\n",
+                  (int)CFGetRetainCount(si->timestampCert));
+        CFRelease(si->timestampCert);
+    }
+    if (si->hashAgilityAttrValue != NULL) {
+        dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n",
+                  (int)CFGetRetainCount(si->hashAgilityAttrValue));
+        CFRelease(si->hashAgilityAttrValue);
+    }
+    if (si->hashAgilityV2AttrValues != NULL) {
+        dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityV2AttrValues.rc %d\n",
+                  (int)CFGetRetainCount(si->hashAgilityV2AttrValues));
+        CFRelease(si->hashAgilityV2AttrValues);
+    }
     /* XXX storage ??? */
 }
 
     /* XXX storage ??? */
 }
 
@@ -494,11 +527,6 @@ loser:
        SECITEM_FreeItem (&signature, PR_FALSE);
     if (privkey)
        SECKEY_DestroyPrivateKey(privkey);
        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);
-    }
     if (tmppoolp)
        PORT_FreeArena(tmppoolp, PR_FALSE);
     return SECFailure;
     if (tmppoolp)
        PORT_FreeArena(tmppoolp, PR_FALSE);
     return SECFailure;
@@ -584,11 +612,11 @@ OSStatus
 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
 {
     SecPublicKeyRef publickey = NULL;
 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
 {
     SecPublicKeyRef publickey = NULL;
-    SecCmsAttribute *attr;
+    SecCmsAttribute *attr = NULL;
     CSSM_DATA encoded_attrs;
     CSSM_DATA encoded_attrs;
-    SecCertificateRef cert;
+    SecCertificateRef cert = NULL;
     SecCmsVerificationStatus vs = SecCmsVSUnverified;
     SecCmsVerificationStatus vs = SecCmsVSUnverified;
-    PLArenaPool *poolp;
+    PLArenaPool *poolp = NULL;
     SECOidTag digestAlgTag, digestEncAlgTag;
     
     if (signerinfo == NULL)
     SECOidTag digestAlgTag, digestEncAlgTag;
     
     if (signerinfo == NULL)
@@ -605,8 +633,8 @@ SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeSt
     dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
     
     debugShowSigningCertificate(signerinfo);
     dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
     
     debugShowSigningCertificate(signerinfo);
-    
-    if (SecCertificateCopyPublicKey(cert, &publickey)) {
+
+    if (NULL == (publickey = SecCertificateCopyKey(cert))) {
        vs = SecCmsVSProcessingError;
        goto loser;
     }
        vs = SecCmsVSProcessingError;
        goto loser;
     }
@@ -714,8 +742,9 @@ SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeSt
         dprintf("found an unAuthAttr\n");
         OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
         dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
         dprintf("found an unAuthAttr\n");
         OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
         dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
-        if (rux)
+        if (rux) {
             goto loser;
             goto loser;
+        }
     }
 
     if (vs == SecCmsVSBadSignature) {
     }
 
     if (vs == SecCmsVSBadSignature) {
@@ -791,6 +820,10 @@ SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTyp
     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);
     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);
+    if (status != errSecSuccess) {
+        secerror("timestamp verification failed: %d", (int)status);
+    }
+
 xit:
     return status;
 }
 xit:
     return status;
 }
@@ -836,12 +869,18 @@ SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
 CFArrayRef
 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
 {
 CFArrayRef
 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
 {
-    dprintfRC("SecCmsSignerInfoGetCertList: timestampCertList.rc %d\n",
+    dprintfRC("SecCmsSignerInfoGetTimestampCertList: timestampCertList.rc %d\n",
            (int)CFGetRetainCount(signerinfo->timestampCertList));
     return signerinfo->timestampCertList;
 }
 
            (int)CFGetRetainCount(signerinfo->timestampCertList));
     return signerinfo->timestampCertList;
 }
 
-
+SecCertificateRef
+SecCmsSignerInfoGetTimestampSigningCert(SecCmsSignerInfoRef signerinfo)
+{
+    dprintfRC("SecCmsSignerInfoGetTimestampSigningCert: timestampCert.rc %d\n",
+              (int)CFGetRetainCount(signerinfo->timestampCert));
+    return signerinfo->timestampCert;
+}
 
 int
 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
 
 int
 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
@@ -914,6 +953,186 @@ xit:
     return status;
 }
 
     return status;
 }
 
+/*!
+     @function
+     @abstract Return the data in the signed Codesigning Hash Agility attribute.
+     @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
+     @discussion Returns a CFDataRef containing the value of the attribute
+     @result A return value of errSecInternal is an error trying to look up the oid.
+             A status value of success with null result data indicates the attribute was not present.
+ */
+OSStatus
+SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
+{
+    SecCmsAttribute *attr;
+    CSSM_DATA_PTR value;
+
+    if (sinfo == NULL || sdata == NULL)
+        return paramErr;
+
+    *sdata = NULL;
+
+    if (sinfo->hashAgilityAttrValue != NULL) {
+        *sdata = sinfo->hashAgilityAttrValue;  /* cached copy */
+        return SECSuccess;
+    }
+
+    attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
+
+    /* attribute not found */
+    if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
+        return SECSuccess;
+
+    sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length);      /* make cached copy */
+    if (sinfo->hashAgilityAttrValue) {
+        *sdata = sinfo->hashAgilityAttrValue;
+        return SECSuccess;
+    }
+    return errSecAllocate;
+}
+
+/* AgileHash ::= SEQUENCE {
+    hashType OBJECT IDENTIFIER,
+    hashValues OCTET STRING }
+ */
+typedef struct {
+    SecAsn1Item digestOID;
+    SecAsn1Item digestValue;
+} CMSAppleAgileHash;
+
+static const SecAsn1Template CMSAppleAgileHashTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+        0, NULL, sizeof(CMSAppleAgileHash) },
+    { SEC_ASN1_OBJECT_ID,
+        offsetof(CMSAppleAgileHash, digestOID), },
+    { SEC_ASN1_OCTET_STRING,
+        offsetof(CMSAppleAgileHash, digestValue), },
+    { 0, }
+};
+
+static OSStatus CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary, SecAsn1Item *DERAgileHash) {
+    PLArenaPool *tmppoolp = NULL;
+    OSStatus status = errSecSuccess;
+    CMSAppleAgileHash agileHash;
+    CFDataRef digestValue = NULL;
+    CFNumberRef digestTag = NULL;
+
+    tmppoolp = PORT_NewArena(1024);
+    if (tmppoolp == NULL) {
+        return errSecAllocate;
+    }
+
+    if ((status = SEC_ASN1DecodeItem(tmppoolp, &agileHash, CMSAppleAgileHashTemplate, DERAgileHash)) != errSecSuccess) {
+        goto loser;
+    }
+
+    int64_t tag = SECOID_FindOIDTag(&agileHash.digestOID);
+    digestTag = CFNumberCreate(NULL, kCFNumberSInt64Type, &tag);
+    digestValue = CFDataCreate(NULL, agileHash.digestValue.Data, agileHash.digestValue.Length);
+    CFDictionaryAddValue(dictionary, digestTag, digestValue);
+
+loser:
+    CFReleaseNull(digestValue);
+    CFReleaseNull(digestTag);
+    if (tmppoolp) {
+        PORT_FreeArena(tmppoolp, PR_FALSE);
+    }
+    return status;
+}
+
+/*!
+ @function
+ @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
+ @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
+ @discussion Returns a CFDictionaryRef containing the values of the attribute
+ @result A return value of errSecInternal is an error trying to look up the oid.
+ A status value of success with null result data indicates the attribute was not present.
+ */
+OSStatus
+SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo, CFDictionaryRef *sdict)
+{
+    SecCmsAttribute *attr;
+
+    if (sinfo == NULL || sdict == NULL) {
+        return errSecParam;
+    }
+
+    *sdict = NULL;
+
+    if (sinfo->hashAgilityV2AttrValues != NULL) {
+        *sdict = sinfo->hashAgilityV2AttrValues;    /* cached copy */
+        return SECSuccess;
+    }
+
+    attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY_V2, PR_TRUE);
+
+    /* attribute not found */
+    if (attr == NULL) {
+        return SECSuccess;
+    }
+
+    /* attrValues SET OF AttributeValue
+     * AttributeValue ::= ANY
+     */
+    CSSM_DATA_PTR *values = attr->values;
+    if (values == NULL) { /* There must be values */
+        return errSecDecode;
+    }
+
+    CFMutableDictionaryRef agileHashValues = CFDictionaryCreateMutable(NULL, SecCmsArrayCount((void **)values),
+                                                                       &kCFTypeDictionaryKeyCallBacks,
+                                                                       &kCFTypeDictionaryValueCallBacks);
+    while (*values != NULL) {
+        (void)CMSAddAgileHashToDictionary(agileHashValues, *values++);
+    }
+    if (CFDictionaryGetCount(agileHashValues) != SecCmsArrayCount((void **)attr->values)) {
+        CFReleaseNull(agileHashValues);
+        return errSecDecode;
+    }
+
+    sinfo->hashAgilityV2AttrValues = agileHashValues;    /* make cached copy */
+    if (sinfo->hashAgilityV2AttrValues) {
+        *sdict = sinfo->hashAgilityV2AttrValues;
+        return SECSuccess;
+    }
+    return errSecAllocate;
+}
+
+/*
+ * SecCmsSignerInfoGetAppleExpirationTime - return the expiration 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
+SecCmsSignerInfoGetAppleExpirationTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *etime)
+{
+    SecCmsAttribute *attr = NULL;
+    SecAsn1Item * value = NULL;
+
+    if (sinfo == NULL || etime == NULL) {
+        return SECFailure;
+    }
+
+    if (sinfo->expirationTime != 0) {
+        *etime = sinfo->expirationTime;    /* cached copy */
+        return SECSuccess;
+    }
+
+    attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_EXPIRATION_TIME, PR_TRUE);
+    if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) {
+        return SECFailure;
+    }
+    if (DER_UTCTimeToCFDate(value, etime) != SECSuccess) {
+        return SECFailure;
+    }
+    sinfo->expirationTime = *etime;    /* make cached copy */
+    return SECSuccess;
+}
+
 /*
  * Return the signing cert of a CMS signerInfo.
  *
 /*
  * Return the signing cert of a CMS signerInfo.
  *
@@ -948,11 +1167,11 @@ SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychai
     sid = &signerinfo->signerIdentifier;
     switch (sid->identifierType) {
     case SecCmsSignerIDIssuerSN:
     sid = &signerinfo->signerIdentifier;
     switch (sid->identifierType) {
     case SecCmsSignerIDIssuerSN:
-       cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->cmsg->poolp,
+       cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->sigd->certs, signerinfo->cmsg->poolp,
            sid->id.issuerAndSN);
        break;
     case SecCmsSignerIDSubjectKeyID:
            sid->id.issuerAndSN);
        break;
     case SecCmsSignerIDSubjectKeyID:
-       cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, sid->id.subjectKeyID);
+       cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, signerinfo->sigd->certs, sid->id.subjectKeyID);
        break;
     default:
        cert = NULL;
        break;
     default:
        cert = NULL;
@@ -985,7 +1204,9 @@ SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
     if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
        return NULL;
 
     if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
        return NULL;
 
-    SecCertificateCopyCommonName(signercert, &commonName);
+    if (errSecSuccess != SecCertificateCopyCommonName(signercert, &commonName)) {
+        return NULL;
+    }
 
     return commonName;
 }
 
     return commonName;
 }
@@ -1181,7 +1402,7 @@ loser:
 
 /* 
  * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
 
 /* 
  * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
- * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
+ * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
  *
  * This is expected to be included in outgoing signed messages for email (S/MIME),
  * if compatibility with Microsoft mail clients is wanted.
  *
  * This is expected to be included in outgoing signed messages for email (S/MIME),
  * if compatibility with Microsoft mail clients is wanted.
@@ -1306,6 +1527,221 @@ SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
     return SECFailure;
 }
 
     return SECFailure;
 }
 
+/*!
+     @function
+     @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
+     @discussion This is expected to be included in outgoing Apple code signatures.
+ */
+OSStatus
+SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
+{
+    SecCmsAttribute *attr;
+    PLArenaPool *poolp = signerinfo->cmsg->poolp;
+    void *mark = PORT_ArenaMark(poolp);
+    OSStatus status = SECFailure;
+
+    /* The value is required for this attribute. */
+    if (!attrValue) {
+        status = errSecParam;
+        goto loser;
+    }
+
+    /*
+     * SecCmsAttributeCreate makes a copy of the data in value, so
+     * we don't need to copy into the CSSM_DATA struct.
+     */
+    CSSM_DATA value;
+    value.Length = CFDataGetLength(attrValue);
+    value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
+
+    if ((attr = SecCmsAttributeCreate(poolp,
+                                      SEC_OID_APPLE_HASH_AGILITY,
+                                      &value,
+                                      PR_FALSE)) == NULL) {
+        status = errSecAllocate;
+        goto loser;
+    }
+
+    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
+        status = errSecInternalError;
+        goto loser;
+    }
+
+    PORT_ArenaUnmark(poolp, mark);
+    return SECSuccess;
+
+loser:
+    PORT_ArenaRelease(poolp, mark);
+    return status;
+}
+
+static OSStatus CMSAddAgileHashToAttribute(PLArenaPool *poolp, SecCmsAttribute *attr, CFNumberRef cftag, CFDataRef value) {
+    PLArenaPool *tmppoolp = NULL;
+    int64_t tag;
+    SECOidData *digestOid = NULL;
+    CMSAppleAgileHash agileHash;
+    SecAsn1Item attrValue = { .Data = NULL, .Length = 0 };
+    OSStatus status = errSecSuccess;
+
+    memset(&agileHash, 0, sizeof(agileHash));
+
+    if(!CFNumberGetValue(cftag, kCFNumberSInt64Type, &tag)) {
+        return errSecParam;
+    }
+    digestOid = SECOID_FindOIDByTag((SECOidTag)tag);
+
+    agileHash.digestValue.Data = (uint8_t *)CFDataGetBytePtr(value);
+    agileHash.digestValue.Length = CFDataGetLength(value);
+    agileHash.digestOID.Data = digestOid->oid.Data;
+    agileHash.digestOID.Length = digestOid->oid.Length;
+
+    tmppoolp = PORT_NewArena(1024);
+    if (tmppoolp == NULL) {
+        return errSecAllocate;
+    }
+
+    if (SEC_ASN1EncodeItem(tmppoolp, &attrValue, &agileHash, CMSAppleAgileHashTemplate) == NULL) {
+        status = errSecParam;
+        goto loser;
+    }
+
+    status = SecCmsAttributeAddValue(poolp, attr, &attrValue);
+
+loser:
+    if (tmppoolp) {
+        PORT_FreeArena(tmppoolp, PR_FALSE);
+    }
+    return status;
+}
+
+/*!
+ @function
+ @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
+ @discussion This is expected to be included in outgoing Apple code signatures.
+ */
+OSStatus
+SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo, CFDictionaryRef attrValues)
+{
+    __block SecCmsAttribute *attr;
+    __block PLArenaPool *poolp = signerinfo->cmsg->poolp;
+    void *mark = PORT_ArenaMark(poolp);
+    OSStatus status = SECFailure;
+
+    /* The value is required for this attribute. */
+    if (!attrValues) {
+        status = errSecParam;
+        goto loser;
+    }
+
+    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_HASH_AGILITY_V2,
+                                      NULL, PR_TRUE)) == NULL) {
+        status = errSecAllocate;
+        goto loser;
+    }
+
+    CFDictionaryForEach(attrValues, ^(const void *key, const void *value) {
+        if (!isNumber(key) || !isData(value)) {
+            return;
+        }
+        (void)CMSAddAgileHashToAttribute(poolp, attr, (CFNumberRef)key, (CFDataRef)value);
+    });
+
+    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
+        status = errSecInternal;
+        goto loser;
+    }
+
+    PORT_ArenaUnmark(poolp, mark);
+    return SECSuccess;
+
+loser:
+    PORT_ArenaRelease(poolp, mark);
+    return status;
+}
+
+/*
+ * SecCmsSignerInfoAddAppleExpirationTime - add the expiration time to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * This is expected to be included in outgoing signed
+ * messages for Asset Receipts but is likely useful in other situations.
+ *
+ * This should only be added once; a second call will do nothing.
+ */
+OSStatus
+SecCmsSignerInfoAddAppleExpirationTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
+{
+    SecCmsAttribute *attr = NULL;
+    PLArenaPool *poolp = signerinfo->cmsg->poolp;
+    void *mark = PORT_ArenaMark(poolp);
+
+    /* create new expiration time attribute */
+    SecAsn1Item etime;
+    if (DER_CFDateToUTCTime(t, &etime) != SECSuccess) {
+        goto loser;
+    }
+
+    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_EXPIRATION_TIME, &etime, PR_FALSE)) == NULL) {
+        SECITEM_FreeItem (&etime, PR_FALSE);
+        goto loser;
+    }
+
+    SECITEM_FreeItem(&etime, PR_FALSE);
+
+    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
+        goto loser;
+    }
+
+    PORT_ArenaUnmark(poolp, mark);
+    return SECSuccess;
+
+loser:
+    PORT_ArenaRelease(poolp, mark);
+    return SECFailure;
+}
+
+SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
+    SecCertificateRef cert = NULL;
+    SecCmsAttribute *attr;
+    CSSM_DATA_PTR ekp;
+    SecKeychainRef keychainOrArray;
+
+    (void)SecKeychainCopyDefault(&keychainOrArray);
+
+    /* sanity check - see if verification status is ok (unverified does not count...) */
+    if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
+        return NULL;
+
+    /* Prep the raw certs */
+    CSSM_DATA_PTR *rawCerts = NULL;
+    if (signerinfo->sigd) {
+        rawCerts = signerinfo->sigd->rawCerts;
+    }
+
+    /* 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! Find the cert. */
+        ekp = SecCmsAttributeGetValue(attr);
+        if (ekp == NULL)
+            return NULL;
+        cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
+    }
+    if(cert) return cert;
+
+    if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
+        (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
+                                                     SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
+    { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
+        ekp = SecCmsAttributeGetValue(attr);
+        if (ekp == NULL)
+            return NULL;
+        cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
+    }
+    return cert;
+}
+
 /*
  * XXXX the following needs to be done in the S/MIME layer code
  * after signature of a signerinfo is verified
 /*
  * XXXX the following needs to be done in the S/MIME layer code
  * after signature of a signerinfo is verified
@@ -1341,7 +1777,7 @@ SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
 
        /* we assume that all certs coming with the message have been imported to the */
        /* temporary database */
 
        /* we assume that all certs coming with the message have been imported to the */
        /* temporary database */
-       cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, ekp);
+       cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, NULL, ekp);
        if (cert == NULL)
            return SECFailure;
        must_free_cert = PR_TRUE;
        if (cert == NULL)
            return SECFailure;
        must_free_cert = PR_TRUE;