+/*!
+ @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;
+}
+