#include <Security/SecKeyPriv.h>
#include <utilities/SecCFWrappers.h>
#include <CoreFoundation/CFTimeZone.h>
+#include <Security/SecBasePriv.h>
+#include <Security/SecItem.h>
#define HIDIGIT(v) (((v) / 10) + '0')
utcTime->Data = d = PORT_Alloc(13);
if (!utcTime->Data)
return SECFailure;
-
- int year;
- int month;
- int day;
- int hour;
- int minute;
- int second;
- if (!CFCalendarDecomposeAbsoluteTime(SecCFCalendarGetZulu(), date, "yMdHms", &year, &month, &day, &hour, &minute, &second))
+ __block int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+ __block bool result;
+ SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
+ result = CFCalendarDecomposeAbsoluteTime(zuluCalendar, date, "yMdHms", &year, &month, &day, &hour, &minute, &second);
+ });
+ if (!result)
return SECFailure;
-
/* UTC time does not handle the years before 1950 */
if (year < 1950)
return SECFailure;
SecCmsSignerInfoRef signerInfo = NULL;
SecCertificateRef cert = NULL;
SecPrivateKeyRef signingKey = NULL;
+ CFDictionaryRef keyAttrs = NULL;
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. Check. */
+ 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(sigd, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
loser:
CFRelease(cert);
if (signingKey)
CFRelease(signingKey);
+ if (keyAttrs)
+ CFRelease(keyAttrs);
return signerInfo;
}
if (!subjKeyID)
goto loser;
signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
- 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;
void
SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
{
- if (si->cert != NULL)
- CERT_DestroyCertificate(si->cert);
+ if (si->cert != NULL) {
+ CERT_DestroyCertificate(si->cert);
+ }
- if (si->certList != NULL)
- CFRelease(si->certList);
+ if (si->certList != NULL) {
+ CFRelease(si->certList);
+ }
+
+ if (si->hashAgilityAttrValue != NULL) {
+ CFRelease(si->hashAgilityAttrValue);
+ }
/* XXX storage ??? */
}
SECOidTag pubkAlgTag;
SecAsn1Item signature = { 0 };
OSStatus rv;
- PLArenaPool *poolp, *tmppoolp;
+ PLArenaPool *poolp, *tmppoolp = NULL;
const SECAlgorithmID *algID = NULL;
//CERTSubjectPublicKeyInfo *spki;
poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
+ SecAsn1AlgId _algID;
+
switch (signerinfo->signerIdentifier.identifierType) {
case SecCmsSignerIDIssuerSN:
privkey = signerinfo->signingKey;
goto loser;
}
#else
- SecAsn1AlgId _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
+ _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
algID = &_algID;
#endif
break;
}
digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
pubkAlgTag = SECOID_GetAlgorithmTag(algID);
+
+ /* we no longer support signing with MD5 */
+ if (digestalgtag == SEC_OID_MD5) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
#if USE_CDSA_CRYPTO
if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
#endif
PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
+ tmppoolp = 0;
} else {
signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
signature.Data = PORT_ZAlloc(signature.Length);
SECITEM_FreeItem (&signature, PR_FALSE);
if (privkey)
SECKEY_DestroyPrivateKey(privkey);
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
return SECFailure;
}
CFRelease(cert);
}
}
+
+ if ((CFArrayGetCount(certs) == 0) &&
+ (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID))
+ {
+ SecCertificateRef cert = CERT_FindCertificateBySubjectKeyID(signerinfo->signedData->certs,
+ signerinfo->signerIdentifier.id.subjectKeyID);
+ if (cert) {
+ CFArrayAppendValue(certs, cert);
+ CFRelease(cert);
+ }
+ }
return certs;
}
#endif
{
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;
+ /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
+#if 0
+#warning DEBUG - SecCmsSignerInfoVerifyCertificate trusts everything!
+ if (signerinfo->verificationStatus == SecCmsVSGoodSignature) {
+ syslog(LOG_ERR, "SecCmsSignerInfoVerifyCertificate ignoring SEC_ERROR_UNTRUSTED_CERT");
+ rv = SECSuccess;
+ }
+#else
+ if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
+ signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
+#endif
}
}
SecCertificateRef cert;
SecCmsVerificationStatus vs = SecCmsVSUnverified;
PLArenaPool *poolp;
- SECOidTag digestAlgTag, digestEncAlgTag;
if (signerinfo == NULL)
return SECFailure;
goto loser;
#endif
- digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
- digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
if (contentType) {
/*
return SECSuccess;
}
+/*!
+ @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;
+ SecAsn1Item *value;
+
+ if (sinfo == NULL || sdata == NULL)
+ return errSecParam;
+
+ *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;
+}
+
/*
* Return the signing cert of a CMS signerInfo.
*
cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
signerinfo->cert = cert;
}
+ if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)) {
+ cert = CERT_FindCertificateBySubjectKeyID(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.subjectKeyID);
+ signerinfo->cert = cert;
+ }
#endif
return cert;
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 signed Apple code signatures.
+ */
+OSStatus
+SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
+{
+ SecCmsAttribute *attr;
+ PLArenaPool *poolp = signerinfo->signedData->contentInfo.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.
+ */
+ SecAsn1Item 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 = errSecInternal;
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return status;
+}
+
+SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
+ SecCertificateRef cert = NULL;
+ SecCmsAttribute *attr;
+ SecAsn1Item *ekp;
+
+ /* sanity check - see if verification status is ok (unverified does not count...) */
+ if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
+ return NULL;
+
+ /* 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;
+
+ SecAsn1Item **rawCerts = NULL;
+ if (signerinfo->signedData) {
+ rawCerts = signerinfo->signedData->rawCerts;
+ }
+ cert = SecSMIMEGetCertFromEncryptionKeyPreference(rawCerts, ekp);
+ }
+ return cert;
+}
+
/*
* XXXX the following needs to be done in the S/MIME layer code
* after signature of a signerinfo is verified