X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/sec/Security/SecCertificate.c diff --git a/sec/Security/SecCertificate.c b/sec/Security/SecCertificate.c deleted file mode 100644 index 6067db94..00000000 --- a/sec/Security/SecCertificate.c +++ /dev/null @@ -1,5265 +0,0 @@ -/* - * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - * SecCertificate.c - CoreFoundation based certificate object - */ - -#ifdef STANDALONE -/* Allows us to build genanchors against the BaseSDK. */ -#undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ -#undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "SecBasePriv.h" -#include "SecRSAKey.h" -#include "SecFramework.h" -#include "SecItem.h" -#include "SecItemPriv.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "SecBase64.h" -#include "AppleBaselineEscrowCertificates.h" -#include "securityd_client.h" - -typedef struct SecCertificateExtension { - DERItem extnID; - bool critical; - DERItem extnValue; -} SecCertificateExtension; - -#if 0 -typedef struct KnownExtension { - bool critical; - DERItem extnValue; -} KnownExtension; - -enum { - kSecSelfSignedUnknown = 0, - kSecSelfSignedFalse, - kSecSelfSignedTrue, -}; -#endif - -struct __SecCertificate { - CFRuntimeBase _base; - - DERItem _der; /* Entire certificate in DER form. */ - DERItem _tbs; /* To Be Signed cert DER bytes. */ - DERAlgorithmId _sigAlg; /* Top level signature algorithm. */ - DERItem _signature; /* The content of the sig bit string. */ - - UInt8 _version; - DERItem _serialNum; /* Integer. */ - DERAlgorithmId _tbsSigAlg; /* sig alg MUST be same as _sigAlg. */ - DERItem _issuer; /* Sequence of RDN. */ - CFAbsoluteTime _notBefore; - CFAbsoluteTime _notAfter; - DERItem _subject; /* Sequence of RDN. */ - DERAlgorithmId _algId; /* oid and params of _pubKeyDER. */ - DERItem _pubKeyDER; /* contents of bit string */ - DERItem _issuerUniqueID; /* bit string, optional */ - DERItem _subjectUniqueID; /* bit string, optional */ - -#if 0 - /* Known extensions if the certificate contains them, - extnValue.length will be > 0. */ - KnownExtension _authorityKeyID; - - /* This extension is used to uniquely identify a certificate from among - several that have the same subject name. If the extension is not - present, its value is calculated by performing a SHA-1 hash of the - certificate's DER encoded subjectPublicKeyInfo, as recommended by - PKIX. */ - KnownExtension _subjectKeyID; - KnownExtension _keyUsage; - KnownExtension _extendedKeyUsage; - KnownExtension _basicConstraints; - KnownExtension _netscapeCertType; - KnownExtension _subjectAltName; - KnownExtension _qualCertStatements; - -#endif - bool _foundUnknownCriticalExtension; - - /* Well known certificate extensions. */ - SecCEBasicConstraints _basicConstraints; - SecCEPolicyConstraints _policyConstraints; - CFDictionaryRef _policyMappings; - SecCECertificatePolicies _certificatePolicies; - - /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX, - value of the SkipCerts field of the InhibitAnyPolicy extension - otherwise. */ - uint32_t _inhibitAnyPolicySkipCerts; - - /* If KeyUsage extension is not present this is 0, otherwise it's - the value of the extension. */ - SecKeyUsage _keyUsage; - - /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier. - Length = 0 if not present. */ - DERItem _subjectKeyIdentifier; - - /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier. - Length = 0 if not present. */ - DERItem _authorityKeyIdentifier; - /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and - _authorityKeyIdentifierSerialNumber have non zero length if present. - Both are either present or absent together. */ - DERItem _authorityKeyIdentifierIssuer; - DERItem _authorityKeyIdentifierSerialNumber; - - /* Subject alt name extension, if present. Not malloced, it's just a - pointer to an element in the _extensions array. */ - const SecCertificateExtension *_subjectAltName; - - /* Parsed extension values. */ - - /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */ - CFMutableArrayRef _crlDistributionPoints; - - /* Array of CFURLRefs containing the URI values of accessLocations of each - id-ad-ocsp AccessDescription in the Authority Information Access - extension. */ - CFMutableArrayRef _ocspResponders; - - /* Array of CFURLRefs containing the URI values of accessLocations of each - id-ad-caIssuers AccessDescription in the Authority Information Access - extension. */ - CFMutableArrayRef _caIssuers; - - /* All other (non known) extensions. The _extensions array is malloced. */ - CFIndex _extensionCount; - SecCertificateExtension *_extensions; - - /* Optional cached fields. */ - SecKeyRef _pubKey; - CFDataRef _der_data; - CFArrayRef _properties; - CFDataRef _serialNumber; - CFDataRef _normalizedIssuer; - CFDataRef _normalizedSubject; - CFDataRef _authorityKeyID; - CFDataRef _subjectKeyID; - - CFDataRef _sha1Digest; - uint8_t _isSelfSigned; - -}; - -#define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v)); - -SEC_CONST_DECL (kSecCertificateProductionEscrowKey, "ProductionEscrowKey"); -SEC_CONST_DECL (kSecCertificateEscrowFileName, "AppleESCertificates"); - -/* Public Constants for property list keys. */ -SEC_CONST_DECL (kSecPropertyKeyType, "type"); -SEC_CONST_DECL (kSecPropertyKeyLabel, "label"); -SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel, "localized label"); -SEC_CONST_DECL (kSecPropertyKeyValue, "value"); - -/* Public Constants for property list values. */ -SEC_CONST_DECL (kSecPropertyTypeWarning, "warning"); -SEC_CONST_DECL (kSecPropertyTypeError, "error"); -SEC_CONST_DECL (kSecPropertyTypeSuccess, "success"); -SEC_CONST_DECL (kSecPropertyTypeTitle, "title"); -SEC_CONST_DECL (kSecPropertyTypeSection, "section"); -SEC_CONST_DECL (kSecPropertyTypeData, "data"); -SEC_CONST_DECL (kSecPropertyTypeString, "string"); -SEC_CONST_DECL (kSecPropertyTypeURL, "url"); -SEC_CONST_DECL (kSecPropertyTypeDate, "date"); - -/* Extension parsing routine. */ -typedef void (*SecCertificateExtensionParser)(SecCertificateRef certificate, - const SecCertificateExtension *extn); - -/* CFRuntime registration data. */ -static pthread_once_t kSecCertificateRegisterClass = PTHREAD_ONCE_INIT; -static CFTypeID kSecCertificateTypeID = _kCFRuntimeNotATypeID; - -/* Mapping from extension OIDs (as a DERItem *) to - SecCertificateExtensionParser extension parsing routines. */ -static CFDictionaryRef gExtensionParsers; - -/* Forward declarations of static functions. */ -static CFStringRef SecCertificateDescribe(CFTypeRef cf); -static void SecCertificateDestroy(CFTypeRef cf); -static bool derDateGetAbsoluteTime(const DERItem *dateChoice, - CFAbsoluteTime *absTime); - -/* Static functions. */ -static CF_RETURNS_RETAINED CFStringRef SecCertificateDescribe(CFTypeRef cf) { - SecCertificateRef certificate = (SecCertificateRef)cf; - CFStringRef subject = SecCertificateCopySubjectSummary(certificate); - CFStringRef issuer = SecCertificateCopyIssuerSummary(certificate); - CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR(""), certificate, subject, issuer); - CFReleaseSafe(issuer); - CFReleaseSafe(subject); - return desc; -} - -static void SecCertificateDestroy(CFTypeRef cf) { - SecCertificateRef certificate = (SecCertificateRef)cf; - if (certificate->_certificatePolicies.policies) - free(certificate->_certificatePolicies.policies); - CFReleaseSafe(certificate->_policyMappings); - CFReleaseSafe(certificate->_crlDistributionPoints); - CFReleaseSafe(certificate->_ocspResponders); - CFReleaseSafe(certificate->_caIssuers); - if (certificate->_extensions) { - free(certificate->_extensions); - } - CFReleaseSafe(certificate->_pubKey); - CFReleaseSafe(certificate->_der_data); - CFReleaseSafe(certificate->_properties); - CFReleaseSafe(certificate->_serialNumber); - CFReleaseSafe(certificate->_normalizedIssuer); - CFReleaseSafe(certificate->_normalizedSubject); - CFReleaseSafe(certificate->_authorityKeyID); - CFReleaseSafe(certificate->_subjectKeyID); - CFReleaseSafe(certificate->_sha1Digest); -} - -static Boolean SecCertificateEqual(CFTypeRef cf1, CFTypeRef cf2) { - SecCertificateRef cert1 = (SecCertificateRef)cf1; - SecCertificateRef cert2 = (SecCertificateRef)cf2; - if (cert1 == cert2) - return true; - if (!cert2 || cert1->_der.length != cert2->_der.length) - return false; - return !memcmp(cert1->_der.data, cert2->_der.data, cert1->_der.length); -} - -/* Hash of the certificate is der length + signature length + last 4 bytes - of signature. */ -static CFHashCode SecCertificateHash(CFTypeRef cf) { - SecCertificateRef certificate = (SecCertificateRef)cf; - size_t der_length = certificate->_der.length; - size_t sig_length = certificate->_signature.length; - size_t ix = (sig_length > 4) ? sig_length - 4 : 0; - CFHashCode hashCode = 0; - for (; ix < sig_length; ++ix) - hashCode = (hashCode << 8) + certificate->_signature.data[ix]; - - return (hashCode + der_length + sig_length); -} - -#if 1 - -/************************************************************************/ -/************************* General Name Parsing *************************/ -/************************************************************************/ - -typedef OSStatus (*parseGeneralNameCallback)(void *context, - SecCEGeneralNameType type, const DERItem *value); - - -/* - GeneralName ::= CHOICE { - otherName [0] OtherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER} - - OtherName ::= SEQUENCE { - type-id OBJECT IDENTIFIER, - value [0] EXPLICIT ANY DEFINED BY type-id } - - EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - */ -static OSStatus parseGeneralNameContentProperty(DERTag tag, - const DERItem *generalNameContent, - void *context, parseGeneralNameCallback callback) { - switch (tag) { - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0: - return callback(context, GNT_OtherName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 1: - return callback(context, GNT_RFC822Name, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 2: - return callback(context, GNT_DNSName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3: - return callback(context, GNT_X400Address, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4: - return callback(context, GNT_DirectoryName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5: - return callback(context, GNT_EdiPartyName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - { - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ - DERDecodedInfo uriContent; - require_noerr(DERDecodeItem(generalNameContent, &uriContent), badDER); - require(uriContent.tag == ASN1_IA5_STRING, badDER); - return callback(context, GNT_URI, &uriContent.content); - } - case ASN1_CONTEXT_SPECIFIC | 6: - return callback(context, GNT_URI, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 7: - return callback(context, GNT_IPAddress, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 8: - return callback(context, GNT_RegisteredID, generalNameContent); - default: - goto badDER; - } -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseGeneralNamesContent(const DERItem *generalNamesContent, - void *context, parseGeneralNameCallback callback) { - DERSequence gnSeq; - DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo generalNameContent; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - OSStatus status = parseGeneralNameContentProperty( - generalNameContent.tag, &generalNameContent.content, context, - callback); - if (status) - return status; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return errSecSuccess; - -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseGeneralNames(const DERItem *generalNames, void *context, - parseGeneralNameCallback callback) { - DERDecodedInfo generalNamesContent; - DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent); - require_noerr_quiet(drtn, badDER); - require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - return parseGeneralNamesContent(&generalNamesContent.content, context, - callback); -badDER: - return errSecInvalidCertificate; -} - -#else - -/* - GeneralName ::= CHOICE { - otherName [0] OtherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER} - - EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - */ -static OSStatus parseGeneralNameContentProperty(DERTag tag, - const DERItem *generalNameContent, SecCEGeneralName *generalName) { - switch (tag) { - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0: - generalName->nameType = GNT_OtherName; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 1: - /* IA5String. */ - generalName->nameType = GNT_RFC822Name; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 2: - /* IA5String. */ - generalName->nameType = GNT_DNSName; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3: - generalName->nameType = GNT_X400Address; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4: - generalName->nameType = GNT_DirectoryName; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5: - generalName->nameType = GNT_EdiPartyName; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - { - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ - DERDecodedInfo decoded; - require_noerr(DERDecodeItem(generalNameContent, &decoded), badDER); - require(decoded.tag == ASN1_IA5_STRING, badDER); - generalName->nameType = GNT_URI; - generalName->berEncoded = false; - generalName->name = decoded.content; - break; - } - case ASN1_CONTEXT_SPECIFIC | 6: - generalName->nameType = GNT_URI; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 7: - /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's - 8 octects, addr/mask for ipv6 it's 32. */ - generalName->nameType = GNT_IPAddress; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 8: - /* name is the content of an OID. */ - generalName->nameType = GNT_RegisteredID; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - default: - goto badDER; - break; - } - return errSecSuccess; -badDER: - return errSecInvalidCertificate; -} - -/* - GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - */ -static OSStatus parseGeneralNamesContent(const DERItem *generalNamesContent, - CFIndex *count, SecCEGeneralName **name) { - SecCEGeneralName *generalNames = NULL; - DERSequence gnSeq; - DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo generalNameContent; - CFIndex generalNamesCount = 0; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - ++generalNamesCount; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - - require(generalNames = calloc(generalNamesCount, sizeof(SecCEGeneralName)), - badDER); - DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - CFIndex ix = 0; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - if (!parseGeneralNameContentProperty(generalNameContent.tag, - &generalNameContent.content, &generalNames[ix])) { - goto badDER; - } - ++ix; - } - *count = generalNamesCount; - *name = generalNames; - return errSecSuccess; - -badDER: - if (generalNames) - free(generalNames); - return errSecInvalidCertificate; -} - -static OSStatus parseGeneralNames(const DERItem *generalNames, - CFIndex *count, SecCEGeneralName **name) { - DERDecodedInfo generalNamesContent; - DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent); - require_noerr_quiet(drtn, badDER); - require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, - badDER); - parseGeneralNamesContent(&generalNamesContent.content, count, name); - return errSecSuccess; -badDER: - return errSecInvalidCertificate; -} -#endif - -/************************************************************************/ -/************************** X.509 Name Parsing **************************/ -/************************************************************************/ - -typedef OSStatus (*parseX501NameCallback)(void *context, const DERItem *type, - const DERItem *value, CFIndex rdnIX); - -static OSStatus parseRDNContent(const DERItem *rdnSetContent, void *context, - parseX501NameCallback callback) { - DERSequence rdn; - DERReturn drtn = DERDecodeSeqContentInit(rdnSetContent, &rdn); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo atvContent; - CFIndex rdnIX = 0; - while ((drtn = DERDecodeSeqNext(&rdn, &atvContent)) == DR_Success) { - require_quiet(atvContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERAttributeTypeAndValue atv; - drtn = DERParseSequenceContent(&atvContent.content, - DERNumAttributeTypeAndValueItemSpecs, - DERAttributeTypeAndValueItemSpecs, - &atv, sizeof(atv)); - require_noerr_quiet(drtn, badDER); - require_quiet(atv.type.length != 0, badDER); - OSStatus status = callback(context, &atv.type, &atv.value, rdnIX++); - if (status) - return status; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - - return errSecSuccess; -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseX501NameContent(const DERItem *x501NameContent, void *context, - parseX501NameCallback callback) { - DERSequence derSeq; - DERReturn drtn = DERDecodeSeqContentInit(x501NameContent, &derSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo currDecoded; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { - require_quiet(currDecoded.tag == ASN1_CONSTR_SET, badDER); - OSStatus status = parseRDNContent(&currDecoded.content, context, - callback); - if (status) - return status; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - - return errSecSuccess; - -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseX501Name(const DERItem *x501Name, void *context, - parseX501NameCallback callback) { - DERDecodedInfo x501NameContent; - if (DERDecodeItem(x501Name, &x501NameContent) || - x501NameContent.tag != ASN1_CONSTR_SEQUENCE) { - return errSecInvalidCertificate; - } else { - return parseX501NameContent(&x501NameContent.content, context, - callback); - } -} - -/************************************************************************/ -/********************** Extension Parsing Routines **********************/ -/************************************************************************/ - -static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERDecodedInfo keyIdentifier; - DERReturn drtn = DERDecodeItem(&extn->extnValue, &keyIdentifier); - require_noerr_quiet(drtn, badDER); - require_quiet(keyIdentifier.tag == ASN1_OCTET_STRING, badDER); - certificate->_subjectKeyIdentifier = keyIdentifier.content; - - return; -badDER: - secwarning("Invalid SubjectKeyIdentifier Extension"); -} - -static void SecCEPKeyUsage(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - SecKeyUsage keyUsage = extn->critical ? kSecKeyUsageCritical : 0; - DERDecodedInfo bitStringContent; - DERReturn drtn = DERDecodeItem(&extn->extnValue, &bitStringContent); - require_noerr_quiet(drtn, badDER); - require_quiet(bitStringContent.tag == ASN1_BIT_STRING, badDER); - DERSize len = bitStringContent.content.length - 1; - require_quiet(len == 1 || len == 2, badDER); - DERByte numUnusedBits = bitStringContent.content.data[0]; - require_quiet(numUnusedBits < 8, badDER); - /* Flip the bits in the bit string so the first bit in the lsb. */ - uint_fast16_t bits = 8 * len - numUnusedBits; - uint_fast16_t value = bitStringContent.content.data[1]; - uint_fast16_t mask; - if (len > 1) { - value = (value << 8) + bitStringContent.content.data[2]; - mask = 0x8000; - } else { - mask = 0x80; - } - uint_fast16_t ix; - for (ix = 0; ix < bits; ++ix) { - if (value & mask) { - keyUsage |= 1 << ix; - } - mask >>= 1; - } - certificate->_keyUsage = keyUsage; - return; -badDER: - certificate->_keyUsage = kSecKeyUsageUnspecified; -} - -static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPSubjectAltName(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - certificate->_subjectAltName = extn; -} - -static void SecCEPIssuerAltName(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPBasicConstraints(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERBasicConstraints basicConstraints; - require_noerr_quiet(DERParseSequence(&extn->extnValue, - DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs, - &basicConstraints, sizeof(basicConstraints)), badDER); - require_noerr_quiet(DERParseBoolean(&basicConstraints.cA, false, - &certificate->_basicConstraints.isCA), badDER); - if (basicConstraints.pathLenConstraint.length != 0) { - require_noerr_quiet(DERParseInteger( - &basicConstraints.pathLenConstraint, - &certificate->_basicConstraints.pathLenConstraint), badDER); - certificate->_basicConstraints.pathLenConstraintPresent = true; - } - certificate->_basicConstraints.present = true; - certificate->_basicConstraints.critical = extn->critical; - return; -badDER: - certificate->_basicConstraints.present = false; - secwarning("Invalid BasicConstraints Extension"); -} - -static void SecCEPCrlDistributionPoints(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -/* - certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation - - PolicyInformation ::= SEQUENCE { - policyIdentifier CertPolicyId, - policyQualifiers SEQUENCE SIZE (1..MAX) OF - PolicyQualifierInfo OPTIONAL } - - CertPolicyId ::= OBJECT IDENTIFIER - - PolicyQualifierInfo ::= SEQUENCE { - policyQualifierId PolicyQualifierId, - qualifier ANY DEFINED BY policyQualifierId } -*/ -static void SecCEPCertificatePolicies(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence piSeq; - SecCEPolicyInformation *policies = NULL; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo piContent; - DERSize policy_count = 0; - while ((drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) { - require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - policy_count++; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - policies = (SecCEPolicyInformation *)malloc(sizeof(SecCEPolicyInformation) - * policy_count); - DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq); - DERSize policy_ix = 0; - while ((drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) { - DERPolicyInformation pi; - drtn = DERParseSequenceContent(&piContent.content, - DERNumPolicyInformationItemSpecs, - DERPolicyInformationItemSpecs, - &pi, sizeof(pi)); - require_noerr_quiet(drtn, badDER); - policies[policy_ix].policyIdentifier = pi.policyIdentifier; - policies[policy_ix++].policyQualifiers = pi.policyQualifiers; - } - certificate->_certificatePolicies.present = true; - certificate->_certificatePolicies.critical = extn->critical; - certificate->_certificatePolicies.numPolicies = policy_count; - certificate->_certificatePolicies.policies = policies; - return; -badDER: - if (policies) - free(policies); - certificate->_certificatePolicies.present = false; - secwarning("Invalid CertificatePolicies Extension"); -} - -/* - id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } - - PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { - issuerDomainPolicy CertPolicyId, - subjectDomainPolicy CertPolicyId } -*/ -#if 0 -static void SecCEPPolicyMappings(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence pmSeq; - SecCEPolicyMapping *mappings = NULL; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo pmContent; - DERSize mapping_count = 0; - while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) { - require_quiet(pmContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - mapping_count++; - } - mappings = (SecCEPolicyMapping *)malloc(sizeof(SecCEPolicyMapping) - * mapping_count); - DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq); - DERSize mapping_ix = 0; - while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) { - DERPolicyMapping pm; - drtn = DERParseSequenceContent(&pmContent.content, - DERNumPolicyMappingItemSpecs, - DERPolicyMappingItemSpecs, - &pm, sizeof(pm)); - require_noerr_quiet(drtn, badDER); - mappings[mapping_ix].issuerDomainPolicy = pm.issuerDomainPolicy; - mappings[mapping_ix++].subjectDomainPolicy = pm.subjectDomainPolicy; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - certificate->_policyMappings.present = true; - certificate->_policyMappings.critical = extn->critical; - certificate->_policyMappings.numMappings = mapping_count; - certificate->_policyMappings.mappings = mappings; - return; -badDER: - if (mappings) - free(mappings); - CFReleaseSafe(mappings); - certificate->_policyMappings.present = false; - secwarning("Invalid CertificatePolicies Extension"); -} -#else -static void SecCEPPolicyMappings(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence pmSeq; - CFMutableDictionaryRef mappings = NULL; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo pmContent; - require_quiet(mappings = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), - badDER);; - while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) { - require_quiet(pmContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERPolicyMapping pm; - drtn = DERParseSequenceContent(&pmContent.content, - DERNumPolicyMappingItemSpecs, - DERPolicyMappingItemSpecs, - &pm, sizeof(pm)); - require_noerr_quiet(drtn, badDER); - CFDataRef idp, sdp; - require_quiet(idp = CFDataCreate(kCFAllocatorDefault, - pm.issuerDomainPolicy.data, pm.issuerDomainPolicy.length), badDER); - require_quiet(sdp = CFDataCreate(kCFAllocatorDefault, - pm.subjectDomainPolicy.data, pm.subjectDomainPolicy.length), badDER); - CFMutableArrayRef sdps = - (CFMutableArrayRef)CFDictionaryGetValue(mappings, idp); - if (sdps) { - CFArrayAppendValue(sdps, sdp); - } else { - require_quiet(sdps = CFArrayCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeArrayCallBacks), badDER); - CFDictionarySetValue(mappings, idp, sdps); - CFRelease(sdps); - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - certificate->_policyMappings = mappings; - return; -badDER: - CFReleaseSafe(mappings); - certificate->_policyMappings = NULL; - secwarning("Invalid CertificatePolicies Extension"); -} -#endif - -/* -AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - -- authorityCertIssuer and authorityCertSerialNumber MUST both - -- be present or both be absent - -KeyIdentifier ::= OCTET STRING -*/ -static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERAuthorityKeyIdentifier akid; - DERReturn drtn; - drtn = DERParseSequence(&extn->extnValue, - DERNumAuthorityKeyIdentifierItemSpecs, - DERAuthorityKeyIdentifierItemSpecs, - &akid, sizeof(akid)); - require_noerr_quiet(drtn, badDER); - if (akid.keyIdentifier.length) { - certificate->_authorityKeyIdentifier = akid.keyIdentifier; - } - if (akid.authorityCertIssuer.length || - akid.authorityCertSerialNumber.length) { - require_quiet(akid.authorityCertIssuer.length && - akid.authorityCertSerialNumber.length, badDER); - /* Perhaps put in a subsection called Authority Certificate Issuer. */ - certificate->_authorityKeyIdentifierIssuer = akid.authorityCertIssuer; - certificate->_authorityKeyIdentifierSerialNumber = akid.authorityCertSerialNumber; - } - - return; -badDER: - secwarning("Invalid AuthorityKeyIdentifier Extension"); -} - -static void SecCEPPolicyConstraints(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERPolicyConstraints pc; - DERReturn drtn; - drtn = DERParseSequence(&extn->extnValue, - DERNumPolicyConstraintsItemSpecs, - DERPolicyConstraintsItemSpecs, - &pc, sizeof(pc)); - require_noerr_quiet(drtn, badDER); - if (pc.requireExplicitPolicy.length) { - require_noerr_quiet(DERParseInteger( - &pc.requireExplicitPolicy, - &certificate->_policyConstraints.requireExplicitPolicy), badDER); - certificate->_policyConstraints.requireExplicitPolicyPresent = true; - } - if (pc.inhibitPolicyMapping.length) { - require_noerr_quiet(DERParseInteger( - &pc.inhibitPolicyMapping, - &certificate->_policyConstraints.inhibitPolicyMapping), badDER); - certificate->_policyConstraints.inhibitPolicyMappingPresent = true; - } - - certificate->_policyConstraints.present = true; - certificate->_policyConstraints.critical = extn->critical; - - return; -badDER: - certificate->_policyConstraints.present = false; - secwarning("Invalid PolicyConstraints Extension"); -} - -static void SecCEPExtendedKeyUsage(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -/* - InhibitAnyPolicy ::= SkipCerts - - SkipCerts ::= INTEGER (0..MAX) -*/ -static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - require_noerr_quiet(DERParseInteger( - &extn->extnValue, - &certificate->_inhibitAnyPolicySkipCerts), badDER); - return; -badDER: - certificate->_inhibitAnyPolicySkipCerts = UINT32_MAX; - secwarning("Invalid InhibitAnyPolicy Extension"); -} - -/* - id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } - - AuthorityInfoAccessSyntax ::= - SEQUENCE SIZE (1..MAX) OF AccessDescription - - AccessDescription ::= SEQUENCE { - accessMethod OBJECT IDENTIFIER, - accessLocation GeneralName } - - id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } - - id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } - - id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } - */ -static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence adSeq; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &adSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo adContent; - while ((drtn = DERDecodeSeqNext(&adSeq, &adContent)) == DR_Success) { - require_quiet(adContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERAccessDescription ad; - drtn = DERParseSequenceContent(&adContent.content, - DERNumAccessDescriptionItemSpecs, - DERAccessDescriptionItemSpecs, - &ad, sizeof(ad)); - require_noerr_quiet(drtn, badDER); - CFMutableArrayRef *urls; - if (DEROidCompare(&ad.accessMethod, &oidAdOCSP)) - urls = &certificate->_ocspResponders; - else if (DEROidCompare(&ad.accessMethod, &oidAdCAIssuer)) - urls = &certificate->_caIssuers; - else - continue; - - DERDecodedInfo generalNameContent; - drtn = DERDecodeItem(&ad.accessLocation, &generalNameContent); - require_noerr_quiet(drtn, badDER); - switch (generalNameContent.tag) { -#if 0 - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ -#endif - case ASN1_CONTEXT_SPECIFIC | 6: - { - CFURLRef url = CFURLCreateWithBytes(kCFAllocatorDefault, - generalNameContent.content.data, generalNameContent.content.length, - kCFStringEncodingASCII, NULL); - if (url) { - if (!*urls) - *urls = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - CFArrayAppendValue(*urls, url); - CFRelease(url); - } - break; - } - default: - secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s", - generalNameContent.tag, (int) generalNameContent.content.length, generalNameContent.content.data); - goto badDER; - break; - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - secdebug("cert", "failed to parse Authority Information Access extension"); -} - -/* Apple Worldwide Developer Relations Certificate Authority subject name. - * This is a DER sequence with the leading tag and length bytes removed, - * to match what tbsCert.issuer contains. - */ -static const unsigned char Apple_WWDR_CA_Subject_Name[]={ - 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, - 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65, - 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23, - 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20, - 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69, - 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70, - 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65, - 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E, - 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, - 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79 -}; - -static void checkForMissingRevocationInfo(SecCertificateRef certificate) { - if (!certificate || - certificate->_crlDistributionPoints || - certificate->_ocspResponders) { - /* We already have an OCSP or CRL URI (or no cert) */ - return; - } - /* Specify an appropriate OCSP responder if we recognize the issuer. */ - CFURLRef url = NULL; - if (sizeof(Apple_WWDR_CA_Subject_Name) == certificate->_issuer.length && - !memcmp(certificate->_issuer.data, Apple_WWDR_CA_Subject_Name, - sizeof(Apple_WWDR_CA_Subject_Name))) { - const char *WWDR_OCSP_URI = "http://ocsp.apple.com/ocsp-wwdr01"; - url = CFURLCreateWithBytes(kCFAllocatorDefault, - (const UInt8*)WWDR_OCSP_URI, strlen(WWDR_OCSP_URI), - kCFStringEncodingASCII, NULL); - } - if (url) { - CFMutableArrayRef *urls = &certificate->_ocspResponders; - *urls = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - CFArrayAppendValue(*urls, url); - CFRelease(url); - } -} - -static void SecCEPSubjectInfoAccess(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPNetscapeCertType(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPEntrustVersInfo(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPEscrowMarker(SecCertificateRef certificate, - const SecCertificateExtension *extn) { - secdebug("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - - -/* Dictionary key callback for comparing to DERItems. */ -static Boolean SecDERItemEqual(const void *value1, const void *value2) { - return DEROidCompare((const DERItem *)value1, (const DERItem *)value2); -} - -/* Dictionary key callback calculating the hash of a DERItem. */ -static CFHashCode SecDERItemHash(const void *value) { - const DERItem *derItem = (const DERItem *)value; - CFHashCode hash = derItem->length; - DERSize ix = derItem->length > 8 ? derItem->length - 8 : 0; - for (; ix < derItem->length; ++ix) { - hash = (hash << 9) + (hash >> 23) + derItem->data[ix]; - } - - return hash; -} - -/* Dictionary key callbacks using the above 2 functions. */ -static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks = { - 0, /* version */ - NULL, /* retain */ - NULL, /* release */ - NULL, /* copyDescription */ - SecDERItemEqual, /* equal */ - SecDERItemHash /* hash */ -}; - -static void SecCertificateRegisterClass(void) { - static const CFRuntimeClass kSecCertificateClass = { - 0, /* version */ - "SecCertificate", /* class name */ - NULL, /* init */ - NULL, /* copy */ - SecCertificateDestroy, /* dealloc */ - SecCertificateEqual, /* equal */ - SecCertificateHash, /* hash */ - NULL, /* copyFormattingDesc */ - SecCertificateDescribe /* copyDebugDesc */ - }; - - kSecCertificateTypeID = _CFRuntimeRegisterClass(&kSecCertificateClass); - - /* Build a dictionary that maps from extension OIDs to callback functions - which can parse the extension of the type given. */ - static const void *extnOIDs[] = { - &oidSubjectKeyIdentifier, - &oidKeyUsage, - &oidPrivateKeyUsagePeriod, - &oidSubjectAltName, - &oidIssuerAltName, - &oidBasicConstraints, - &oidCrlDistributionPoints, - &oidCertificatePolicies, - &oidPolicyMappings, - &oidAuthorityKeyIdentifier, - &oidPolicyConstraints, - &oidExtendedKeyUsage, - &oidInhibitAnyPolicy, - &oidAuthorityInfoAccess, - &oidSubjectInfoAccess, - &oidNetscapeCertType, - &oidEntrustVersInfo, - &oidApplePolicyEscrowService - }; - static const void *extnParsers[] = { - SecCEPSubjectKeyIdentifier, - SecCEPKeyUsage, - SecCEPPrivateKeyUsagePeriod, - SecCEPSubjectAltName, - SecCEPIssuerAltName, - SecCEPBasicConstraints, - SecCEPCrlDistributionPoints, - SecCEPCertificatePolicies, - SecCEPPolicyMappings, - SecCEPAuthorityKeyIdentifier, - SecCEPPolicyConstraints, - SecCEPExtendedKeyUsage, - SecCEPInhibitAnyPolicy, - SecCEPAuthorityInfoAccess, - SecCEPSubjectInfoAccess, - SecCEPNetscapeCertType, - SecCEPEntrustVersInfo, - SecCEPEscrowMarker, - }; - gExtensionParsers = CFDictionaryCreate(kCFAllocatorDefault, extnOIDs, - extnParsers, array_size(extnOIDs), - &SecDERItemKeyCallBacks, NULL); -} - -/* Given the contents of an X.501 Name return the contents of a normalized - X.501 name. */ -CFDataRef createNormalizedX501Name(CFAllocatorRef allocator, - const DERItem *x501name) { - CFMutableDataRef result = CFDataCreateMutable(allocator, x501name->length); - CFIndex length = x501name->length; - CFDataSetLength(result, length); - UInt8 *base = CFDataGetMutableBytePtr(result); - - DERSequence rdnSeq; - DERReturn drtn = DERDecodeSeqContentInit(x501name, &rdnSeq); - - require_noerr_quiet(drtn, badDER); - DERDecodedInfo rdn; - - /* Always points to last rdn tag. */ - const DERByte *rdnTag = rdnSeq.nextItem; - /* Offset relative to base of current rdn set tag. */ - CFIndex rdnTagLocation = 0; - while ((drtn = DERDecodeSeqNext(&rdnSeq, &rdn)) == DR_Success) { - require_quiet(rdn.tag == ASN1_CONSTR_SET, badDER); - /* We don't allow empty RDNs. */ - require_quiet(rdn.content.length != 0, badDER); - /* Length of the tag and length of the current rdn. */ - CFIndex rdnTLLength = rdn.content.data - rdnTag; - CFIndex rdnContentLength = rdn.content.length; - /* Copy the tag and length of the RDN. */ - memcpy(base + rdnTagLocation, rdnTag, rdnTLLength); - - DERSequence atvSeq; - drtn = DERDecodeSeqContentInit(&rdn.content, &atvSeq); - DERDecodedInfo atv; - /* Always points to tag of current atv sequence. */ - const DERByte *atvTag = atvSeq.nextItem; - /* Offset relative to base of current atv sequence tag. */ - CFIndex atvTagLocation = rdnTagLocation + rdnTLLength; - while ((drtn = DERDecodeSeqNext(&atvSeq, &atv)) == DR_Success) { - require_quiet(atv.tag == ASN1_CONSTR_SEQUENCE, badDER); - /* Length of the tag and length of the current atv. */ - CFIndex atvTLLength = atv.content.data - atvTag; - CFIndex atvContentLength = atv.content.length; - /* Copy the tag and length of the atv and the atv itself. */ - memcpy(base + atvTagLocation, atvTag, - atvTLLength + atv.content.length); - - /* Now decode the atv sequence. */ - DERAttributeTypeAndValue atvPair; - drtn = DERParseSequenceContent(&atv.content, - DERNumAttributeTypeAndValueItemSpecs, - DERAttributeTypeAndValueItemSpecs, - &atvPair, sizeof(atvPair)); - require_noerr_quiet(drtn, badDER); - require_quiet(atvPair.type.length != 0, badDER); - DERDecodedInfo value; - drtn = DERDecodeItem(&atvPair.value, &value); - require_noerr_quiet(drtn, badDER); - - /* (c) attribute values in PrintableString are not case sensitive - (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and - - (d) attribute values in PrintableString are compared after - removing leading and trailing white space and converting internal - substrings of one or more consecutive white space characters to a - single space. */ - if (value.tag == ASN1_PRINTABLE_STRING) { - /* Offset relative to base of current value tag. */ - CFIndex valueTagLocation = atvTagLocation + atvPair.value.data - atvTag; - CFIndex valueTLLength = value.content.data - atvPair.value.data; - CFIndex valueContentLength = value.content.length; - - /* Now copy all the bytes, but convert to upper case while - doing so and convert multiple whitespace chars into a - single space. */ - bool lastWasBlank = false; - CFIndex valueLocation = valueTagLocation + valueTLLength; - CFIndex valueCurrentLocation = valueLocation; - CFIndex ix; - for (ix = 0; ix < valueContentLength; ++ix) { - UInt8 ch = value.content.data[ix]; - if (isblank(ch)) { - if (lastWasBlank) { - continue; - } else { - /* Don't insert a space for first character - we encounter. */ - if (valueCurrentLocation > valueLocation) { - base[valueCurrentLocation++] = ' '; - } - lastWasBlank = true; - } - } else { - lastWasBlank = false; - if ('a' <= ch && ch <= 'z') { - base[valueCurrentLocation++] = ch + 'A' - 'a'; - } else { - base[valueCurrentLocation++] = ch; - } - } - } - /* Finally if lastWasBlank remove the trailing space. */ - if (lastWasBlank && valueCurrentLocation > valueLocation) { - valueCurrentLocation--; - } - /* Adjust content length to normalized length. */ - valueContentLength = valueCurrentLocation - valueLocation; - - /* Number of bytes by which the length should be shorted. */ - CFIndex lengthDiff = value.content.length - valueContentLength; - if (lengthDiff == 0) { - /* Easy case no need to adjust lengths. */ - } else { - /* Hard work we need to go back and fix up length fields - for: - 1) The value itself. - 2) The ATV Sequence containing type/value - 3) The RDN Set containing one or more atv pairs. - 4) The result. - */ - - /* Step 1 fix up length of value. */ - /* Length of value tag and length minus the tag. */ - DERSize newValueTLLength = valueTLLength - 1; - drtn = DEREncodeLength(valueContentLength, - base + valueTagLocation + 1, &newValueTLLength); - /* Add the length of the tag back in. */ - newValueTLLength++; - CFIndex valueLLDiff = valueTLLength - newValueTLLength; - if (valueLLDiff) { - /* The size of the length field changed, let's slide - the value back by valueLLDiff bytes. */ - memmove(base + valueTagLocation + newValueTLLength, - base + valueTagLocation + valueTLLength, - valueContentLength); - /* The length diff for the enclosing object. */ - lengthDiff += valueLLDiff; - } - - /* Step 2 fix up length of the enclosing ATV Sequence. */ - atvContentLength -= lengthDiff; - DERSize newATVTLLength = atvTLLength - 1; - drtn = DEREncodeLength(atvContentLength, - base + atvTagLocation + 1, &newATVTLLength); - /* Add the length of the tag back in. */ - newATVTLLength++; - CFIndex atvLLDiff = atvTLLength - newATVTLLength; - if (atvLLDiff) { - /* The size of the length field changed, let's slide - the value back by valueLLDiff bytes. */ - memmove(base + atvTagLocation + newATVTLLength, - base + atvTagLocation + atvTLLength, - atvContentLength); - /* The length diff for the enclosing object. */ - lengthDiff += atvLLDiff; - atvTLLength = newATVTLLength; - } - - /* Step 3 fix up length of enclosing RDN Set. */ - rdnContentLength -= lengthDiff; - DERSize newRDNTLLength = rdnTLLength - 1; - drtn = DEREncodeLength(rdnContentLength, - base + rdnTagLocation + 1, &newRDNTLLength); - /* Add the length of the tag back in. */ - newRDNTLLength++; - CFIndex rdnLLDiff = rdnTLLength - newRDNTLLength; - if (rdnLLDiff) { - /* The size of the length field changed, let's slide - the value back by valueLLDiff bytes. */ - memmove(base + rdnTagLocation + newRDNTLLength, - base + rdnTagLocation + rdnTLLength, - rdnContentLength); - /* The length diff for the enclosing object. */ - lengthDiff += rdnLLDiff; - rdnTLLength = newRDNTLLength; - - /* Adjust the locations that might have changed due to - this slide. */ - atvTagLocation -= rdnLLDiff; - } - } - } - atvTagLocation += atvTLLength + atvContentLength; - atvTag = atvSeq.nextItem; - } - rdnTagLocation += rdnTLLength + rdnContentLength; - rdnTag = rdnSeq.nextItem; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - /* Truncate the result to the proper length. */ - CFDataSetLength(result, rdnTagLocation); - - return result; - -badDER: - CFRelease(result); - return NULL; -} - -CFDataRef SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name) -{ - const DERItem name = { (unsigned char *)CFDataGetBytePtr(distinguished_name), CFDataGetLength(distinguished_name) }; - DERDecodedInfo content; - /* Decode top level sequence into DERItem */ - if (!DERDecodeItem(&name, &content) && (content.tag == ASN1_CONSTR_SEQUENCE)) - return createNormalizedX501Name(kCFAllocatorDefault, &content.content); - return NULL; -} - -/* AUDIT[securityd]: - certificate->_der is a caller provided data of any length (might be 0). - - Top level certificate decode. - */ -static bool SecCertificateParse(SecCertificateRef certificate) -{ - DERReturn drtn; - - check(certificate); - require_quiet(certificate, badCert); - CFAllocatorRef allocator = CFGetAllocator(certificate); - - /* top level decode */ - DERSignedCertCrl signedCert; - drtn = DERParseSequence(&certificate->_der, DERNumSignedCertCrlItemSpecs, - DERSignedCertCrlItemSpecs, &signedCert, - sizeof(signedCert)); - require_noerr_quiet(drtn, badCert); - /* Store tbs since we need to digest it for verification later on. */ - certificate->_tbs = signedCert.tbs; - - /* decode the TBSCert - it was saved in full DER form */ - DERTBSCert tbsCert; - drtn = DERParseSequence(&signedCert.tbs, - DERNumTBSCertItemSpecs, DERTBSCertItemSpecs, - &tbsCert, sizeof(tbsCert)); - require_noerr_quiet(drtn, badCert); - - /* sequence we're given: decode the signedCerts Signature Algorithm. */ - /* This MUST be the same as the certificate->_tbsSigAlg with the exception - of the params field. */ - drtn = DERParseSequenceContent(&signedCert.sigAlg, - DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, - &certificate->_sigAlg, sizeof(certificate->_sigAlg)); - require_noerr_quiet(drtn, badCert); - - /* The contents of signedCert.sig is a bit string whose contents - are the signature itself. */ - DERByte numUnusedBits; - drtn = DERParseBitString(&signedCert.sig, - &certificate->_signature, &numUnusedBits); - require_noerr_quiet(drtn, badCert); - - /* Now decode the tbsCert. */ - - /* First we turn the optional version into an int. */ - if (tbsCert.version.length) { - DERDecodedInfo decoded; - drtn = DERDecodeItem(&tbsCert.version, &decoded); - require_noerr_quiet(drtn, badCert); - require_quiet(decoded.tag == ASN1_INTEGER, badCert); - require_quiet(decoded.content.length == 1, badCert); - certificate->_version = decoded.content.data[0]; - require_quiet(certificate->_version > 0, badCert); - require_quiet(certificate->_version < 3, badCert); - } else { - certificate->_version = 0; - } - - /* The serial number is in the tbsCert.serialNum - it was saved in - INTEGER form without the tag and length. */ - certificate->_serialNum = tbsCert.serialNum; - certificate->_serialNumber = CFDataCreate(allocator, - tbsCert.serialNum.data, tbsCert.serialNum.length); - - /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */ - drtn = DERParseSequenceContent(&tbsCert.tbsSigAlg, - DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, - &certificate->_tbsSigAlg, sizeof(certificate->_tbsSigAlg)); - require_noerr_quiet(drtn, badCert); - - /* The issuer is in the tbsCert.issuer - it's a sequence without the tag - and length fields. */ - certificate->_issuer = tbsCert.issuer; - certificate->_normalizedIssuer = createNormalizedX501Name(allocator, - &tbsCert.issuer); - - /* sequence we're given: decode the tbsCerts Validity sequence. */ - DERValidity validity; - drtn = DERParseSequenceContent(&tbsCert.validity, - DERNumValidityItemSpecs, DERValidityItemSpecs, - &validity, sizeof(validity)); - require_noerr_quiet(drtn, badCert); - require_quiet(derDateGetAbsoluteTime(&validity.notBefore, - &certificate->_notBefore), badCert); - require_quiet(derDateGetAbsoluteTime(&validity.notAfter, - &certificate->_notAfter), badCert); - - /* The subject is in the tbsCert.subject - it's a sequence without the tag - and length fields. */ - certificate->_subject = tbsCert.subject; - certificate->_normalizedSubject = createNormalizedX501Name(allocator, - &tbsCert.subject); - - /* sequence we're given: encoded DERSubjPubKeyInfo */ - DERSubjPubKeyInfo pubKeyInfo; - drtn = DERParseSequenceContent(&tbsCert.subjectPubKey, - DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs, - &pubKeyInfo, sizeof(pubKeyInfo)); - require_noerr_quiet(drtn, badCert); - - /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */ - drtn = DERParseSequenceContent(&pubKeyInfo.algId, - DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, - &certificate->_algId, sizeof(certificate->_algId)); - require_noerr_quiet(drtn, badCert); - - /* Now we can figure out the key's algorithm id and params based on - certificate->_algId.oid. */ - - /* The contents of pubKeyInfo.pubKey is a bit string whose contents - are a PKCS1 format RSA key. */ - drtn = DERParseBitString(&pubKeyInfo.pubKey, - &certificate->_pubKeyDER, &numUnusedBits); - require_noerr_quiet(drtn, badCert); - - /* The contents of tbsCert.issuerID is a bit string. */ - certificate->_issuerUniqueID = tbsCert.issuerID; - - /* The contents of tbsCert.subjectID is a bit string. */ - certificate->_subjectUniqueID = tbsCert.subjectID; - - /* Extensions. */ - if (tbsCert.extensions.length) { - CFIndex extensionCount = 0; - DERSequence derSeq; - DERTag tag; - drtn = DERDecodeSeqInit(&tbsCert.extensions, &tag, - &derSeq); - require_noerr_quiet(drtn, badCert); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badCert); - DERDecodedInfo currDecoded; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { -#if 0 -/* ! = MUST recognize ? = SHOULD recognize -*/ - - KnownExtension _subjectKeyID; /* ?SubjectKeyIdentifier id-ce 14 */ - KnownExtension _keyUsage; /* !KeyUsage id-ce 15 */ - KnownExtension _subjectAltName; /* !SubjectAltName id-ce 17 */ - KnownExtension _basicConstraints; /* !BasicConstraints id-ce 19 */ - KnownExtension _authorityKeyID; /* ?AuthorityKeyIdentifier id-ce 35 */ - KnownExtension _extKeyUsage; /* !ExtKeyUsage id-ce 37 */ - KnownExtension _netscapeCertType; /* 2.16.840.1.113730.1.1 netscape 1 1 */ - KnownExtension _qualCertStatements; /* QCStatements id-pe 3 */ - - KnownExtension _issuerAltName; /* IssuerAltName id-ce 18 */ - KnownExtension _nameConstraints; /* !NameConstraints id-ce 30 */ - KnownExtension _cRLDistributionPoints; /* CRLDistributionPoints id-ce 31 */ - KnownExtension _certificatePolicies; /* !CertificatePolicies id-ce 32 */ - KnownExtension _policyMappings; /* ?PolicyMappings id-ce 33 */ - KnownExtension _policyConstraints; /* !PolicyConstraints id-ce 36 */ - KnownExtension _freshestCRL; /* FreshestCRL id-ce 46 */ - KnownExtension _inhibitAnyPolicy; /* !InhibitAnyPolicy id-ce 54 */ - - KnownExtension _authorityInfoAccess; /* AuthorityInfoAccess id-pe 1 */ - KnownExtension _subjectInfoAccess; /* SubjectInfoAccess id-pe 11 */ -#endif - - extensionCount++; - } - require_quiet(drtn == DR_EndOfSequence, badCert); - - /* Put some upper limit on the number of extensions allowed. */ - require_quiet(extensionCount < 10000, badCert); - certificate->_extensionCount = extensionCount; - certificate->_extensions = - malloc(sizeof(SecCertificateExtension) * extensionCount); - - CFIndex ix = 0; - drtn = DERDecodeSeqInit(&tbsCert.extensions, &tag, &derSeq); - require_noerr_quiet(drtn, badCert); - for (ix = 0; ix < extensionCount; ++ix) { - drtn = DERDecodeSeqNext(&derSeq, &currDecoded); - require_quiet(drtn == DR_Success || - (ix == extensionCount - 1 && drtn == DR_EndOfSequence), badCert); - require_quiet(currDecoded.tag == ASN1_CONSTR_SEQUENCE, badCert); - DERExtension extn; - drtn = DERParseSequenceContent(&currDecoded.content, - DERNumExtensionItemSpecs, DERExtensionItemSpecs, - &extn, sizeof(extn)); - require_noerr_quiet(drtn, badCert); - /* Copy stuff into certificate->extensions[ix]. */ - certificate->_extensions[ix].extnID = extn.extnID; - require_noerr_quiet(drtn = DERParseBoolean(&extn.critical, false, - &certificate->_extensions[ix].critical), badCert); - certificate->_extensions[ix].extnValue = extn.extnValue; - - SecCertificateExtensionParser parser = - (SecCertificateExtensionParser)CFDictionaryGetValue( - gExtensionParsers, &certificate->_extensions[ix].extnID); - if (parser) { - /* Invoke the parser. */ - parser(certificate, &certificate->_extensions[ix]); - } else if (certificate->_extensions[ix].critical) { - secdebug("cert", "Found unknown critical extension"); - certificate->_foundUnknownCriticalExtension = true; - } else { - secdebug("cert", "Found unknown non critical extension"); - } - } - } - checkForMissingRevocationInfo(certificate); - - return true; - -badCert: - return false; -} - - -/* Public API functions. */ -CFTypeID SecCertificateGetTypeID(void) { - pthread_once(&kSecCertificateRegisterClass, SecCertificateRegisterClass); - return kSecCertificateTypeID; -} - -SecCertificateRef SecCertificateCreateWithBytes(CFAllocatorRef allocator, - const UInt8 *der_bytes, CFIndex der_length) { - check(der_bytes); - check(der_length); - CFIndex size = sizeof(struct __SecCertificate) + der_length; - SecCertificateRef result = (SecCertificateRef)_CFRuntimeCreateInstance( - allocator, SecCertificateGetTypeID(), size - sizeof(CFRuntimeBase), 0); - if (result) { - memset((char*)result + sizeof(result->_base), 0, - sizeof(*result) - sizeof(result->_base)); - result->_der.data = ((DERByte *)result + sizeof(*result)); - result->_der.length = der_length; - memcpy(result->_der.data, der_bytes, der_length); - if (!SecCertificateParse(result)) { - CFRelease(result); - return NULL; - } - } - return result; -} - -/* @@@ Placeholder until iap submits a binary is fixed. */ -SecCertificateRef SecCertificateCreate(CFAllocatorRef allocator, - const UInt8 *der_bytes, CFIndex der_length); - -SecCertificateRef SecCertificateCreate(CFAllocatorRef allocator, - const UInt8 *der_bytes, CFIndex der_length) { - return SecCertificateCreateWithBytes(allocator, der_bytes, der_length); -} -/* @@@ End of placeholder. */ - -/* AUDIT[securityd](done): - der_certificate is a caller provided data of any length (might be 0), only - its cf type has been checked. - */ -SecCertificateRef SecCertificateCreateWithData(CFAllocatorRef allocator, - CFDataRef der_certificate) { - check(der_certificate); - CFIndex size = sizeof(struct __SecCertificate); - SecCertificateRef result = (SecCertificateRef)_CFRuntimeCreateInstance( - allocator, SecCertificateGetTypeID(), size - sizeof(CFRuntimeBase), 0); - if (result) { - memset((char*)result + sizeof(result->_base), 0, size - sizeof(result->_base)); - result->_der_data = CFDataCreateCopy(allocator, der_certificate); - result->_der.data = (DERByte *)CFDataGetBytePtr(result->_der_data); - result->_der.length = CFDataGetLength(result->_der_data); - if (!SecCertificateParse(result)) { - CFRelease(result); - return NULL; - } - } - return result; -} - -CFDataRef SecCertificateCopyData(SecCertificateRef certificate) { - check(certificate); - CFDataRef result; - if (certificate->_der_data) { - CFRetain(certificate->_der_data); - result = certificate->_der_data; - } else { - result = CFDataCreate(CFGetAllocator(certificate), - certificate->_der.data, certificate->_der.length); -#if 0 - /* FIXME: If we wish to cache result we need to lock the certificate. - Also this create 2 copies of the certificate data which is somewhat - suboptimal. */ - CFRetain(result); - certificate->_der_data = result; -#endif - } - - return result; -} - -CFIndex SecCertificateGetLength(SecCertificateRef certificate) { - return certificate->_der.length; -} - -const UInt8 *SecCertificateGetBytePtr(SecCertificateRef certificate) { - return certificate->_der.data; -} - -/* From rfc3280 - Appendix B. ASN.1 Notes - - Object Identifiers (OIDs) are used throughout this specification to - identify certificate policies, public key and signature algorithms, - certificate extensions, etc. There is no maximum size for OIDs. - This specification mandates support for OIDs which have arc elements - with values that are less than 2^28, that is, they MUST be between 0 - and 268,435,455, inclusive. This allows each arc element to be - represented within a single 32 bit word. Implementations MUST also - support OIDs where the length of the dotted decimal (see [RFC 2252], - section 4.1) string representation can be up to 100 bytes - (inclusive). Implementations MUST be able to handle OIDs with up to - 20 elements (inclusive). CAs SHOULD NOT issue certificates which - contain OIDs that exceed these requirements. Likewise, CRL issuers - SHOULD NOT issue CRLs which contain OIDs that exceed these - requirements. -*/ - -/* Oids longer than this are considered invalid. */ -#define MAX_OID_SIZE 32 - -CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator, - const DERItem *oid) { - - if (oid->length == 0) { - return SecCopyCertString(SEC_NULL_KEY); - } - if (oid->length > MAX_OID_SIZE) { - return SecCopyCertString(SEC_OID_TOO_LONG_KEY); - } - - CFMutableStringRef result = CFStringCreateMutable(allocator, 0); - - // The first two levels are encoded into one byte, since the root level - // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then - // y may be > 39, so we have to add special-case handling for this. - uint32_t x = oid->data[0] / 40; - uint32_t y = oid->data[0] % 40; - if (x > 2) - { - // Handle special case for large y if x = 2 - y += (x - 2) * 40; - x = 2; - } - CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y); - - uint32_t value = 0; - for (x = 1; x < oid->length; ++x) - { - value = (value << 7) | (oid->data[x] & 0x7F); - /* @@@ value may not span more than 4 bytes. */ - /* A max number of 20 values is allowed. */ - if (!(oid->data[x] & 0x80)) - { - CFStringAppendFormat(result, NULL, CFSTR(".%" PRIu32), value); - value = 0; - } - } - return result; -} - -static CFStringRef copyLocalizedOidDescription(CFAllocatorRef allocator, - const DERItem *oid) { - if (oid->length == 0) { - return SecCopyCertString(SEC_NULL_KEY); - } - - /* Build the key we use to lookup the localized OID description. */ - CFMutableStringRef oidKey = CFStringCreateMutable(allocator, - oid->length * 3 + 5); - CFStringAppendFormat(oidKey, NULL, CFSTR("06 %02lX"), oid->length); - DERSize ix; - for (ix = 0; ix < oid->length; ++ix) - CFStringAppendFormat(oidKey, NULL, CFSTR(" %02X"), oid->data[ix]); - - CFStringRef name = SecFrameworkCopyLocalizedString(oidKey, CFSTR("OID")); - if (CFEqual(oidKey, name)) { - CFRelease(name); - name = SecDERItemCopyOIDDecimalRepresentation(allocator, oid); - } - CFRelease(oidKey); - - return name; -} - -/* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated - 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't - have a length of exactly 4 or 16 octects. */ -static CFStringRef copyIPAddressContentDescription(CFAllocatorRef allocator, - const DERItem *ip) { - /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's - 4 octects addr, or 8 octects, addr/mask for ipv6 it's - 16 octects addr, or 32 octects addr/mask. */ - CFStringRef value = NULL; - if (ip->length == 4) { - value = CFStringCreateWithFormat(allocator, NULL, - CFSTR("%u.%u.%u.%u"), - ip->data[0], ip->data[1], ip->data[2], ip->data[3]); - } else if (ip->length == 16) { - value = CFStringCreateWithFormat(allocator, NULL, - CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:" - "%02x%02x:%02x%02x:%02x%02x:%02x%02x"), - ip->data[0], ip->data[1], ip->data[2], ip->data[3], - ip->data[4], ip->data[5], ip->data[6], ip->data[7], - ip->data[8], ip->data[9], ip->data[10], ip->data[11], - ip->data[12], ip->data[13], ip->data[14], ip->data[15]); - } - - return value; -} - -#if 0 -static CFStringRef copyFullOidDescription(CFAllocatorRef allocator, - const DERItem *oid) { - CFStringRef decimal = SecDERItemCopyOIDDecimalRepresentation(allocator, oid); - CFStringRef name = copyLocalizedOidDescription(allocator, oid); - CFStringRef oid_string = CFStringCreateWithFormat(allocator, NULL, - CFSTR("%@ (%@)"), name, decimal); - CFRelease(name); - CFRelease(decimal); - return oid_string; -} -#endif - -void appendProperty(CFMutableArrayRef properties, CFStringRef propertyType, - CFStringRef label, CFStringRef localizedLabel, CFTypeRef value) { - CFDictionaryRef property; - if (label) { - CFStringRef ll; - if (localizedLabel) { - ll = NULL; - } else { - ll = localizedLabel = SecCopyCertString(label); - } - const void *all_keys[4]; - all_keys[0] = kSecPropertyKeyType; - all_keys[1] = kSecPropertyKeyLabel; - all_keys[2] = kSecPropertyKeyLocalizedLabel; - all_keys[3] = kSecPropertyKeyValue; - const void *property_values[] = { - propertyType, - label, - localizedLabel, - value, - }; - property = CFDictionaryCreate(CFGetAllocator(properties), - all_keys, property_values, value ? 4 : 3, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFReleaseSafe(ll); - } else { - const void *nolabel_keys[2]; - nolabel_keys[0] = kSecPropertyKeyType; - nolabel_keys[1] = kSecPropertyKeyValue; - const void *property_values[] = { - propertyType, - value, - }; - property = CFDictionaryCreate(CFGetAllocator(properties), - nolabel_keys, property_values, 2, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - } - - CFArrayAppendValue(properties, property); - CFRelease(property); -} - -/* YYMMDDhhmmZ */ -#define UTC_TIME_NOSEC_ZULU_LEN 11 -/* YYMMDDhhmmssZ */ -#define UTC_TIME_ZULU_LEN 13 -/* YYMMDDhhmmssThhmm */ -#define UTC_TIME_LOCALIZED_LEN 17 -/* YYYYMMDDhhmmssZ */ -#define GENERALIZED_TIME_ZULU_LEN 15 -/* YYYYMMDDhhmmssThhmm */ -#define GENERALIZED_TIME_LOCALIZED_LEN 19 - -/* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also - advance *p by 2. */ -static inline int parseDecimalPair(const DERByte **p) { - const DERByte *cp = *p; - *p += 2; - return 10 * (cp[0] - '0') + cp[1] - '0'; -} - -/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return - true if the date was valid and properly decoded, also return the result in - absTime. Return false otherwise. */ -CFAbsoluteTime SecAbsoluteTimeFromDateContent(DERTag tag, const uint8_t *bytes, - size_t length) { - check(bytes); - if (length == 0) - return NULL_TIME; - - bool isUtcLength = false; - bool isLocalized = false; - bool noSeconds = false; - switch (length) { - case UTC_TIME_NOSEC_ZULU_LEN: /* YYMMDDhhmmZ */ - isUtcLength = true; - noSeconds = true; - break; - case UTC_TIME_ZULU_LEN: /* YYMMDDhhmmssZ */ - isUtcLength = true; - break; - case GENERALIZED_TIME_ZULU_LEN: /* YYYYMMDDhhmmssZ */ - break; - case UTC_TIME_LOCALIZED_LEN: /* YYMMDDhhmmssThhmm (where T=[+,-]) */ - isUtcLength = true; - /*DROPTHROUGH*/ - case GENERALIZED_TIME_LOCALIZED_LEN:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */ - isLocalized = true; - break; - default: /* unknown format */ - return NULL_TIME; - } - - /* Make sure the der tag fits the thing inside it. */ - if (tag == ASN1_UTC_TIME) { - if (!isUtcLength) - return NULL_TIME; - } else if (tag == ASN1_GENERALIZED_TIME) { - if (isUtcLength) - return NULL_TIME; - } else { - return NULL_TIME; - } - - const DERByte *cp = bytes; - /* Check that all characters are digits, except if localized the timezone - indicator or if not localized the 'Z' at the end. */ - DERSize ix; - for (ix = 0; ix < length; ++ix) { - if (!(isdigit(cp[ix]))) { - if ((isLocalized && ix == length - 5 && - (cp[ix] == '+' || cp[ix] == '-')) || - (!isLocalized && ix == length - 1 && cp[ix] == 'Z')) { - continue; - } - return NULL_TIME; - } - } - - /* Parse the date and time fields. */ - int year, month, day, hour, minute, second; - if (isUtcLength) { - year = parseDecimalPair(&cp); - if (year < 50) { - /* 0 <= year < 50 : assume century 21 */ - year += 2000; - } else if (year < 70) { - /* 50 <= year < 70 : illegal per PKIX */ - return false; - } else { - /* 70 < year <= 99 : assume century 20 */ - year += 1900; - } - } else { - year = 100 * parseDecimalPair(&cp) + parseDecimalPair(&cp); - } - month = parseDecimalPair(&cp); - day = parseDecimalPair(&cp); - hour = parseDecimalPair(&cp); - minute = parseDecimalPair(&cp); - if (noSeconds) { - second = 0; - } else { - second = parseDecimalPair(&cp); - } - - CFTimeInterval timeZoneOffset; - if (isLocalized) { - /* ZONE INDICATOR */ - int multiplier = *cp++ == '+' ? 60 : -60; - timeZoneOffset = multiplier * - (parseDecimalPair(&cp) * 60 + parseDecimalPair(&cp)); - } else { - timeZoneOffset = 0; - } - - secdebug("dateparse", - "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g", - (int) length, bytes, year, month, - day, hour, minute, second, - timeZoneOffset / 60); - - static int mdays[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; - int is_leap_year = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? 1 : 0; - if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 - || (month == 2 && day > mdays[month] - mdays[month - 1] + is_leap_year) - || (month != 2 && day > mdays[month] - mdays[month - 1])) { - /* Invalid date. */ - return NULL_TIME; - } - - int dy = year - 2001; - if (dy < 0) { - dy += 1; - day -= 1; - } - int leap_days = dy / 4 - dy / 100 + dy / 400; - day += ((year - 2001) * 365 + leap_days) + mdays[month - 1] - 1; - if (month > 2) - day += is_leap_year; - - CFAbsoluteTime absTime = (CFAbsoluteTime)((day * 24 + hour) * 60 + minute) * 60 + second; - return absTime - timeZoneOffset; -} - -static bool derDateContentGetAbsoluteTime(DERTag tag, const DERItem *date, - CFAbsoluteTime *pabsTime) { - CFAbsoluteTime absTime = SecAbsoluteTimeFromDateContent(tag, date->data, - date->length); - if (absTime == NULL_TIME) - return false; - - *pabsTime = absTime; - return true; -} - -/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return - true if the date was valid and properly decoded, also return the result in - absTime. Return false otherwise. */ -static bool derDateGetAbsoluteTime(const DERItem *dateChoice, - CFAbsoluteTime *absTime) { - check(dateChoice); - check(absTime); - if (dateChoice->length == 0) - return false; - - DERDecodedInfo decoded; - if (DERDecodeItem(dateChoice, &decoded)) - return false; - - return derDateContentGetAbsoluteTime(decoded.tag, &decoded.content, - absTime); -} - -static void appendDataProperty(CFMutableArrayRef properties, - CFStringRef label, CFStringRef localizedLabel, const DERItem *der_data) { - CFDataRef data = CFDataCreate(CFGetAllocator(properties), - der_data->data, der_data->length); - appendProperty(properties, kSecPropertyTypeData, label, localizedLabel, - data); - CFRelease(data); -} - -static void appendRelabeledProperty(CFMutableArrayRef properties, - CFStringRef label, - CFStringRef localizedLabel, - const DERItem *der_data, - CFStringRef labelFormat) { - CFStringRef newLabel = - CFStringCreateWithFormat(CFGetAllocator(properties), NULL, - labelFormat, label); - CFStringRef ll; - if (localizedLabel) { - ll = NULL; - } else { - ll = localizedLabel = SecCopyCertString(label); - } - CFStringRef localizedLabelFormat = SecCopyCertString(labelFormat); - CFStringRef newLocalizedLabel = - CFStringCreateWithFormat(CFGetAllocator(properties), NULL, - localizedLabelFormat, localizedLabel); - CFReleaseSafe(ll); - CFReleaseSafe(localizedLabelFormat); - appendDataProperty(properties, newLabel, newLocalizedLabel, der_data); - CFReleaseSafe(newLabel); - CFReleaseSafe(newLocalizedLabel); -} - - -static void appendUnparsedProperty(CFMutableArrayRef properties, - CFStringRef label, CFStringRef localizedLabel, const DERItem *der_data) { - appendRelabeledProperty(properties, label, localizedLabel, der_data, - SEC_UNPARSED_KEY); -} - -static void appendInvalidProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *der_data) { - appendRelabeledProperty(properties, label, NULL, der_data, SEC_INVALID_KEY); -} - -static void appendDateContentProperty(CFMutableArrayRef properties, - CFStringRef label, DERTag tag, - const DERItem *dateContent) { - CFAbsoluteTime absTime; - if (!derDateContentGetAbsoluteTime(tag, dateContent, &absTime)) { - /* Date decode failure insert hex bytes instead. */ - return appendInvalidProperty(properties, label, dateContent); - } - CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime); - appendProperty(properties, kSecPropertyTypeDate, label, NULL, date); - CFRelease(date); -} - -static void appendDateProperty(CFMutableArrayRef properties, - CFStringRef label, CFAbsoluteTime absTime) { - CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime); - appendProperty(properties, kSecPropertyTypeDate, label, NULL, date); - CFRelease(date); -} - -static void appendIPAddressContentProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *ip) { - CFStringRef value = - copyIPAddressContentDescription(CFGetAllocator(properties), ip); - if (value) { - appendProperty(properties, kSecPropertyTypeString, label, NULL, value); - CFRelease(value); - } else { - appendUnparsedProperty(properties, label, NULL, ip); - } -} - -static void appendURLContentProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *urlContent) { - CFURLRef url = CFURLCreateWithBytes(CFGetAllocator(properties), - urlContent->data, urlContent->length, kCFStringEncodingASCII, NULL); - if (url) { - appendProperty(properties, kSecPropertyTypeURL, label, NULL, url); - CFRelease(url); - } else { - appendInvalidProperty(properties, label, urlContent); - } -} - -static void appendURLProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *url) { - DERDecodedInfo decoded; - DERReturn drtn; - - drtn = DERDecodeItem(url, &decoded); - if (drtn || decoded.tag != ASN1_IA5_STRING) { - appendInvalidProperty(properties, label, url); - } else { - appendURLContentProperty(properties, label, &decoded.content); - } -} - -static void appendOIDProperty(CFMutableArrayRef properties, - CFStringRef label, CFStringRef llabel, const DERItem *oid) { - CFStringRef oid_string = - copyLocalizedOidDescription(CFGetAllocator(properties), oid); - appendProperty(properties, kSecPropertyTypeString, label, llabel, - oid_string); - CFRelease(oid_string); -} - -static void appendAlgorithmProperty(CFMutableArrayRef properties, - CFStringRef label, const DERAlgorithmId *algorithm) { - CFMutableArrayRef alg_props = - CFArrayCreateMutable(CFGetAllocator(properties), 0, - &kCFTypeArrayCallBacks); - appendOIDProperty(alg_props, SEC_ALGORITHM_KEY, NULL, &algorithm->oid); - if (algorithm->params.length) { - if (algorithm->params.length == 2 && - algorithm->params.data[0] == ASN1_NULL && - algorithm->params.data[1] == 0) { - CFStringRef value = SecCopyCertString(SEC_NONE_KEY); - appendProperty(alg_props, kSecPropertyTypeString, - SEC_PARAMETERS_KEY, NULL, value); - CFRelease(value); - } else { - appendUnparsedProperty(alg_props, SEC_PARAMETERS_KEY, NULL, - &algorithm->params); - } - } - appendProperty(properties, kSecPropertyTypeSection, label, NULL, alg_props); - CFRelease(alg_props); -} - -static CFStringRef copyHexDescription(CFAllocatorRef allocator, - const DERItem *blob) { - CFIndex ix, length = blob->length /* < 24 ? blob->length : 24 */; - CFMutableStringRef string = CFStringCreateMutable(allocator, - blob->length * 3 - 1); - for (ix = 0; ix < length; ++ix) - if (ix == 0) - CFStringAppendFormat(string, NULL, CFSTR("%02X"), blob->data[ix]); - else - CFStringAppendFormat(string, NULL, CFSTR(" %02X"), blob->data[ix]); - - return string; -} - -/* Returns a (localized) blob string. */ -static CFStringRef copyBlobString(CFAllocatorRef allocator, - CFStringRef blobType, CFStringRef quanta, const DERItem *blob) { - CFStringRef localizedBlobType = SecCopyCertString(blobType); - CFStringRef localizedQuanta = SecCopyCertString(quanta); - /* "format string for encoded field data (e.g. Sequence; 128 bytes; " - "data = 00 00 ...)" */ - CFStringRef blobFormat = SecCopyCertString(SEC_BLOB_KEY); - CFStringRef hex = copyHexDescription(allocator, blob); - CFStringRef result = CFStringCreateWithFormat(allocator, NULL, - blobFormat, localizedBlobType, blob->length, localizedQuanta, hex); - CFRelease(hex); - CFRelease(blobFormat); - CFReleaseSafe(localizedQuanta); - CFReleaseSafe(localizedBlobType); - - return result; -} - -/* Return a string verbatim (unlocalized) from a DER field. */ -static CFStringRef copyContentString(CFAllocatorRef allocator, - const DERItem *string, CFStringEncoding encoding, - bool printableOnly) { - /* Strip potential bogus trailing zero from printable strings. */ - DERSize length = string->length; - if (length && string->data[length - 1] == 0) { - /* Don't mess with the length of UTF16 strings though. */ - if (encoding != kCFStringEncodingUTF16) - length--; - } - /* A zero length string isn't considered printable. */ - if (!length && printableOnly) - return NULL; - - /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes - it treat kCFStringEncodingUTF16 as big endian by default, whereas - passing false makes it treat it as native endian by default. */ - CFStringRef result = CFStringCreateWithBytes(allocator, string->data, - length, encoding, encoding == kCFStringEncodingUTF16); - if (result) - return result; - - return printableOnly ? NULL : copyHexDescription(allocator, string); -} - -/* From rfc3280 - Appendix B. ASN.1 Notes - - CAs MUST force the serialNumber to be a non-negative integer, that - is, the sign bit in the DER encoding of the INTEGER value MUST be - zero - this can be done by adding a leading (leftmost) `00'H octet if - necessary. This removes a potential ambiguity in mapping between a - string of octets and an integer value. - - As noted in section 4.1.2.2, serial numbers can be expected to - contain long integers. Certificate users MUST be able to handle - serialNumber values up to 20 octets in length. Conformant CAs MUST - NOT use serialNumber values longer than 20 octets. -*/ - -/* Return the given numeric data as a string: decimal up to 64 bits, - hex otherwise. */ -static CFStringRef copyIntegerContentDescription(CFAllocatorRef allocator, - const DERItem *integer) { - uint64_t value = 0; - CFIndex ix, length = integer->length; - - if (length == 0 || length > 8) - return copyHexDescription(allocator, integer); - - for(ix = 0; ix < length; ++ix) { - value <<= 8; - value += integer->data[ix]; - } - - return CFStringCreateWithFormat(allocator, NULL, CFSTR("%llu"), value); -} - -static CFStringRef copyDERThingContentDescription(CFAllocatorRef allocator, - DERTag tag, const DERItem *derThing, bool printableOnly) { - switch(tag) { - case ASN1_INTEGER: - case ASN1_BOOLEAN: - return printableOnly ? NULL : copyIntegerContentDescription(allocator, derThing); - case ASN1_PRINTABLE_STRING: - case ASN1_IA5_STRING: - return copyContentString(allocator, derThing, kCFStringEncodingASCII, printableOnly); - case ASN1_UTF8_STRING: - case ASN1_GENERAL_STRING: - case ASN1_UNIVERSAL_STRING: - return copyContentString(allocator, derThing, kCFStringEncodingUTF8, printableOnly); - case ASN1_T61_STRING: // 20, also BER_TAG_TELETEX_STRING - case ASN1_VIDEOTEX_STRING: // 21 - case ASN1_VISIBLE_STRING: // 26 - return copyContentString(allocator, derThing, kCFStringEncodingISOLatin1, printableOnly); - case ASN1_BMP_STRING: // 30 - return copyContentString(allocator, derThing, kCFStringEncodingUTF16, printableOnly); - case ASN1_OCTET_STRING: - return printableOnly ? NULL : - copyBlobString(allocator, SEC_BYTE_STRING_KEY, SEC_BYTES_KEY, - derThing); - //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing); - case ASN1_BIT_STRING: - return printableOnly ? NULL : - copyBlobString(allocator, SEC_BIT_STRING_KEY, SEC_BITS_KEY, - derThing); - case ASN1_CONSTR_SEQUENCE: - return printableOnly ? NULL : - copyBlobString(allocator, SEC_SEQUENCE_KEY, SEC_BYTES_KEY, - derThing); - case ASN1_CONSTR_SET: - return printableOnly ? NULL : - copyBlobString(allocator, SEC_SET_KEY, SEC_BYTES_KEY, derThing); - case ASN1_OBJECT_ID: - return printableOnly ? NULL : copyLocalizedOidDescription(allocator, derThing); - default: - if (printableOnly) { - return NULL; - } else { - CFStringRef fmt = SecCopyCertString(SEC_NOT_DISPLAYED_KEY); - CFStringRef result = CFStringCreateWithFormat(allocator, NULL, fmt, - tag, derThing->length); - CFRelease(fmt); - return result; - } - } -} - -static CFStringRef copyDERThingDescription(CFAllocatorRef allocator, - const DERItem *derThing, bool printableOnly) { - DERDecodedInfo decoded; - DERReturn drtn; - - drtn = DERDecodeItem(derThing, &decoded); - if (drtn) { - /* TODO: Perhaps put something in the label saying we couldn't parse - the DER? */ - return printableOnly ? NULL : copyHexDescription(allocator, derThing); - } else { - return copyDERThingContentDescription(allocator, decoded.tag, - &decoded.content, false); - } -} - -static void appendDERThingProperty(CFMutableArrayRef properties, - CFStringRef label, CFStringRef localizedLabel, const DERItem *derThing) { - CFStringRef value = copyDERThingDescription(CFGetAllocator(properties), - derThing, false); - appendProperty(properties, kSecPropertyTypeString, label, localizedLabel, - value); - CFRelease(value); -} - -static OSStatus appendRDNProperty(void *context, const DERItem *rdnType, - const DERItem *rdnValue, CFIndex rdnIX) { - CFMutableArrayRef properties = (CFMutableArrayRef)context; - if (rdnIX > 0) { - /* If there is more than one value pair we create a subsection for the - second pair, and append things to the subsection for subsequent - pairs. */ - CFIndex lastIX = CFArrayGetCount(properties) - 1; - CFTypeRef lastValue = CFArrayGetValueAtIndex(properties, lastIX); - if (rdnIX == 1) { - /* Since this is the second rdn pair for a given rdn, we setup a - new subsection for this rdn. We remove the first property - from the properties array and make it the first element in the - subsection instead. */ - CFMutableArrayRef rdn_props = CFArrayCreateMutable( - CFGetAllocator(properties), 0, &kCFTypeArrayCallBacks); - CFArrayAppendValue(rdn_props, lastValue); - CFArrayRemoveValueAtIndex(properties, lastIX); - appendProperty(properties, kSecPropertyTypeSection, NULL, NULL, - rdn_props); - properties = rdn_props; - } else { - /* Since this is the third or later rdn pair we have already - created a subsection in the top level properties array. Instead - of appending to that directly we append to the array inside the - subsection. */ - properties = (CFMutableArrayRef)CFDictionaryGetValue( - (CFDictionaryRef)lastValue, kSecPropertyKeyValue); - } - } - - /* Finally we append the new rdn value to the property array. */ - CFStringRef label = SecDERItemCopyOIDDecimalRepresentation( - CFGetAllocator(properties), rdnType); - CFStringRef localizedLabel = - copyLocalizedOidDescription(CFGetAllocator(properties), rdnType); - appendDERThingProperty(properties, label, localizedLabel, rdnValue); - CFRelease(label); - CFRelease(localizedLabel); - return errSecSuccess; -} - -static CFArrayRef createPropertiesForRDNContent(CFAllocatorRef allocator, - const DERItem *rdnSetContent) { - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - OSStatus status = parseRDNContent(rdnSetContent, properties, - appendRDNProperty); - if (status) { - CFArrayRemoveAllValues(properties); - appendInvalidProperty(properties, SEC_RDN_KEY, rdnSetContent); - } - - return properties; -} - -/* - From rfc3739 - 3.1.2. Subject - - When parsing the subject here are some tips for a short name of the cert. - Choice I: commonName - Choice II: givenName - Choice III: pseudonym - - The commonName attribute value SHALL, when present, contain a name - of the subject. This MAY be in the subject's preferred - presentation format, or a format preferred by the CA, or some - other format. Pseudonyms, nicknames, and names with spelling - other than defined by the registered name MAY be used. To - understand the nature of the name presented in commonName, - complying applications MAY have to examine present values of the - givenName and surname attributes, or the pseudonym attribute. - -*/ -static CFArrayRef createPropertiesForX501NameContent(CFAllocatorRef allocator, - const DERItem *x501NameContent) { - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - OSStatus status = parseX501NameContent(x501NameContent, properties, - appendRDNProperty); - if (status) { - CFArrayRemoveAllValues(properties); - appendInvalidProperty(properties, SEC_X501_NAME_KEY, x501NameContent); - } - - return properties; -} - -static CFArrayRef createPropertiesForX501Name(CFAllocatorRef allocator, - const DERItem *x501Name) { - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - OSStatus status = parseX501Name(x501Name, properties, appendRDNProperty); - if (status) { - CFArrayRemoveAllValues(properties); - appendInvalidProperty(properties, SEC_X501_NAME_KEY, x501Name); - } - - return properties; -} - -static void appendIntegerProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *integer) { - CFStringRef string = copyIntegerContentDescription( - CFGetAllocator(properties), integer); - appendProperty(properties, kSecPropertyTypeString, label, NULL, string); - CFRelease(string); -} - -static void appendBoolProperty(CFMutableArrayRef properties, - CFStringRef label, bool boolean) { - CFStringRef value = SecCopyCertString(boolean ? SEC_YES_KEY : SEC_NO_KEY); - appendProperty(properties, kSecPropertyTypeString, label, NULL, value); - CFRelease(value); -} - -static void appendBooleanProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *boolean, bool defaultValue) { - bool result; - DERReturn drtn = DERParseBoolean(boolean, defaultValue, &result); - if (drtn) { - /* Couldn't parse boolean; dump the raw unparsed data as hex. */ - appendInvalidProperty(properties, label, boolean); - } else { - appendBoolProperty(properties, label, result); - } -} - -static void appendBitStringContentNames(CFMutableArrayRef properties, - CFStringRef label, const DERItem *bitStringContent, - const CFStringRef *names, CFIndex namesCount) { - DERSize len = bitStringContent->length - 1; - require_quiet(len == 1 || len == 2, badDER); - DERByte numUnusedBits = bitStringContent->data[0]; - require_quiet(numUnusedBits < 8, badDER); - uint_fast16_t bits = 8 * len - numUnusedBits; - require_quiet(bits <= (uint_fast16_t)namesCount, badDER); - uint_fast16_t value = bitStringContent->data[1]; - uint_fast16_t mask; - if (len > 1) { - value = (value << 8) + bitStringContent->data[2]; - mask = 0x8000; - } else { - mask = 0x80; - } - uint_fast16_t ix; - CFStringRef fmt = SecCopyCertString(SEC_STRING_LIST_KEY); - CFStringRef string = NULL; - for (ix = 0; ix < bits; ++ix) { - if (value & mask) { - if (string) { - CFStringRef s = - CFStringCreateWithFormat(CFGetAllocator(properties), - NULL, fmt, string, names[ix]); - CFRelease(string); - string = s; - } else { - string = names[ix]; - CFRetain(string); - } - } - mask >>= 1; - } - CFRelease(fmt); - appendProperty(properties, kSecPropertyTypeString, label, NULL, - string ? string : CFSTR("")); - CFReleaseSafe(string); - return; -badDER: - appendInvalidProperty(properties, label, bitStringContent); -} - -static void appendBitStringNames(CFMutableArrayRef properties, - CFStringRef label, const DERItem *bitString, - const CFStringRef *names, CFIndex namesCount) { - DERDecodedInfo bitStringContent; - DERReturn drtn = DERDecodeItem(bitString, &bitStringContent); - require_noerr_quiet(drtn, badDER); - require_quiet(bitStringContent.tag == ASN1_BIT_STRING, badDER); - appendBitStringContentNames(properties, label, &bitStringContent.content, - names, namesCount); - return; -badDER: - appendInvalidProperty(properties, label, bitString); -} - -#if 0 -typedef uint16_t SecKeyUsage; - -#define kSecKeyUsageDigitalSignature 0x8000 -#define kSecKeyUsageNonRepudiation 0x4000 -#define kSecKeyUsageKeyEncipherment 0x2000 -#define kSecKeyUsageDataEncipherment 0x1000 -#define kSecKeyUsageKeyAgreement 0x0800 -#define kSecKeyUsageKeyCertSign 0x0400 -#define kSecKeyUsageCRLSign 0x0200 -#define kSecKeyUsageEncipherOnly 0x0100 -#define kSecKeyUsageDecipherOnly 0x0080 - -/* - KeyUsage ::= BIT STRING { - digitalSignature (0), - nonRepudiation (1), - keyEncipherment (2), - dataEncipherment (3), - keyAgreement (4), - keyCertSign (5), - cRLSign (6), - encipherOnly (7), - decipherOnly (8) } - */ -static void appendKeyUsage(CFMutableArrayRef properties, - const DERItem *extnValue) { - if ((extnValue->length != 4 && extnValue->length != 5) || - extnValue->data[0] != ASN1_BIT_STRING || - extnValue->data[1] < 2 || extnValue->data[1] > 3 || - extnValue->data[2] > 7) { - appendInvalidProperty(properties, CFSTR("KeyUsage Extension"), - extnValue); - } else { - CFMutableStringRef string = - CFStringCreateMutable(CFGetAllocator(properties), 0); - SecKeyUsage usage = (extnValue->data[3] << 8); - if (extnValue->length == 5) - usage += extnValue->data[4]; - secdebug("keyusage", "keyusage: %04X", usage); - static const CFStringRef usageNames[] = { - CFSTR("Digital Signature"), - CFSTR("Non-Repudiation"), - CFSTR("Key Encipherment"), - CFSTR("Data Encipherment"), - CFSTR("Key Agreement"), - CFSTR("Cert Sign"), - CFSTR("CRL Sign"), - CFSTR("Encipher"), - CFSTR("Decipher"), - }; - bool didOne = false; - SecKeyUsage mask = kSecKeyUsageDigitalSignature; - CFIndex ix, bits = (extnValue->data[1] - 1) * 8 - extnValue->data[2]; - for (ix = 0; ix < bits; ++ix) { - if (usage & mask) { - if (didOne) { - CFStringAppend(string, CFSTR(", ")); - } else { - didOne = true; - } - /* @@@ Localize usageNames[ix]. */ - CFStringAppend(string, usageNames[ix]); - } - mask >>= 1; - } - appendProperty(properties, kSecPropertyTypeString, CFSTR("Usage"), - string); - CFRelease(string); - } -} -#else -static void appendKeyUsage(CFMutableArrayRef properties, - const DERItem *extnValue) { - static const CFStringRef usageNames[] = { - SEC_DIGITAL_SIGNATURE_KEY, - SEC_NON_REPUDIATION_KEY, - SEC_KEY_ENCIPHERMENT_KEY, - SEC_DATA_ENCIPHERMENT_KEY, - SEC_KEY_AGREEMENT_KEY, - SEC_CERT_SIGN_KEY, - SEC_CRL_SIGN_KEY, - SEC_ENCIPHER_ONLY_KEY, - SEC_DECIPHER_ONLY_KEY - }; - appendBitStringNames(properties, SEC_USAGE_KEY, extnValue, - usageNames, array_size(usageNames)); -} -#endif - -static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERPrivateKeyUsagePeriod pkup; - DERReturn drtn = DERParseSequence(extnValue, - DERNumPrivateKeyUsagePeriodItemSpecs, DERPrivateKeyUsagePeriodItemSpecs, - &pkup, sizeof(pkup)); - require_noerr_quiet(drtn, badDER); - if (pkup.notBefore.length) { - appendDateContentProperty(properties, SEC_NOT_VALID_BEFORE_KEY, - ASN1_GENERALIZED_TIME, &pkup.notBefore); - } - if (pkup.notAfter.length) { - appendDateContentProperty(properties, SEC_NOT_VALID_AFTER_KEY, - ASN1_GENERALIZED_TIME, &pkup.notAfter); - } - return; -badDER: - appendInvalidProperty(properties, SEC_PRIVATE_KU_PERIOD_KEY, extnValue); -} - -static void appendStringContentProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *stringContent, - CFStringEncoding encoding) { - CFStringRef string = CFStringCreateWithBytes(CFGetAllocator(properties), - stringContent->data, stringContent->length, encoding, FALSE); - if (string) { - appendProperty(properties, kSecPropertyTypeString, label, NULL, string); - CFRelease(string); - } else { - appendInvalidProperty(properties, label, stringContent); - } -} - -/* - OtherName ::= SEQUENCE { - type-id OBJECT IDENTIFIER, - value [0] EXPLICIT ANY DEFINED BY type-id } -*/ -static void appendOtherNameContentProperty(CFMutableArrayRef properties, - const DERItem *otherNameContent) { - DEROtherName on; - DERReturn drtn = DERParseSequenceContent(otherNameContent, - DERNumOtherNameItemSpecs, DEROtherNameItemSpecs, - &on, sizeof(on)); - require_noerr_quiet(drtn, badDER); - CFAllocatorRef allocator = CFGetAllocator(properties); - CFStringRef label = - SecDERItemCopyOIDDecimalRepresentation(allocator, &on.typeIdentifier); - CFStringRef localizedLabel = - copyLocalizedOidDescription(allocator, &on.typeIdentifier); - CFStringRef value_string = copyDERThingDescription(allocator, &on.value, false); - if (value_string) - appendProperty(properties, kSecPropertyTypeString, label, - localizedLabel, value_string); - else - appendUnparsedProperty(properties, label, localizedLabel, &on.value); - CFRelease(label); - CFRelease(localizedLabel); - return; -badDER: - appendInvalidProperty(properties, SEC_OTHER_NAME_KEY, otherNameContent); -} - -/* - GeneralName ::= CHOICE { - otherName [0] OtherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER} - - EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - */ -static bool appendGeneralNameContentProperty(CFMutableArrayRef properties, - DERTag tag, const DERItem *generalName) { - switch (tag) { - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0: - appendOtherNameContentProperty(properties, generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 1: - /* IA5String. */ - appendStringContentProperty(properties, SEC_EMAIL_ADDRESS_KEY, - generalName, kCFStringEncodingASCII); - break; - case ASN1_CONTEXT_SPECIFIC | 2: - /* IA5String. */ - appendStringContentProperty(properties, SEC_DNS_NAME_KEY, generalName, - kCFStringEncodingASCII); - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3: - appendUnparsedProperty(properties, SEC_X400_ADDRESS_KEY, NULL, - generalName); - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4: - { - CFArrayRef directory_plist = - createPropertiesForX501Name(CFGetAllocator(properties), - generalName); - appendProperty(properties, kSecPropertyTypeSection, - SEC_DIRECTORY_NAME_KEY, NULL, directory_plist); - CFRelease(directory_plist); - break; - } - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5: - appendUnparsedProperty(properties, SEC_EDI_PARTY_NAME_KEY, NULL, - generalName); - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ - appendURLProperty(properties, SEC_URI_KEY, generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 6: - appendURLContentProperty(properties, SEC_URI_KEY, generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 7: - appendIPAddressContentProperty(properties, SEC_IP_ADDRESS_KEY, - generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 8: - appendOIDProperty(properties, SEC_REGISTERED_ID_KEY, NULL, generalName); - break; - default: - goto badDER; - break; - } - return true; -badDER: - return false; -} - -static void appendGeneralNameProperty(CFMutableArrayRef properties, - const DERItem *generalName) { - DERDecodedInfo generalNameContent; - DERReturn drtn = DERDecodeItem(generalName, &generalNameContent); - require_noerr_quiet(drtn, badDER); - if (appendGeneralNameContentProperty(properties, generalNameContent.tag, - &generalNameContent.content)) - return; -badDER: - appendInvalidProperty(properties, SEC_GENERAL_NAME_KEY, generalName); -} - - -/* - GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - */ -static void appendGeneralNamesContent(CFMutableArrayRef properties, - const DERItem *generalNamesContent) { - DERSequence gnSeq; - DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo generalNameContent; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - if (!appendGeneralNameContentProperty(properties, - generalNameContent.tag, &generalNameContent.content)) { - goto badDER; - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, SEC_GENERAL_NAMES_KEY, - generalNamesContent); -} - -static void appendGeneralNames(CFMutableArrayRef properties, - const DERItem *generalNames) { - DERDecodedInfo generalNamesContent; - DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent); - require_noerr_quiet(drtn, badDER); - require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, - badDER); - appendGeneralNamesContent(properties, &generalNamesContent.content); - return; -badDER: - appendInvalidProperty(properties, SEC_GENERAL_NAMES_KEY, generalNames); -} - -/* -BasicConstraints ::= SEQUENCE { - cA BOOLEAN DEFAULT FALSE, - pathLenConstraint INTEGER (0..MAX) OPTIONAL } -*/ -static void appendBasicConstraints(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERBasicConstraints basicConstraints; - DERReturn drtn = DERParseSequence(extnValue, - DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs, - &basicConstraints, sizeof(basicConstraints)); - require_noerr_quiet(drtn, badDER); - - appendBooleanProperty(properties, SEC_CERT_AUTHORITY_KEY, - &basicConstraints.cA, false); - - if (basicConstraints.pathLenConstraint.length != 0) { - appendIntegerProperty(properties, SEC_PATH_LEN_CONSTRAINT_KEY, - &basicConstraints.pathLenConstraint); - } - return; -badDER: - appendInvalidProperty(properties, SEC_BASIC_CONSTRAINTS_KEY, extnValue); -} - -/* - CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - - DistributionPoint ::= SEQUENCE { - distributionPoint [0] DistributionPointName OPTIONAL, - reasons [1] ReasonFlags OPTIONAL, - cRLIssuer [2] GeneralNames OPTIONAL } - - DistributionPointName ::= CHOICE { - fullName [0] GeneralNames, - nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - - ReasonFlags ::= BIT STRING { - unused (0), - keyCompromise (1), - cACompromise (2), - affiliationChanged (3), - superseded (4), - cessationOfOperation (5), - certificateHold (6), - privilegeWithdrawn (7), - aACompromise (8) } -*/ -static void appendCrlDistributionPoints(CFMutableArrayRef properties, - const DERItem *extnValue) { - CFAllocatorRef allocator = CFGetAllocator(properties); - DERTag tag; - DERSequence dpSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &dpSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo dpSeqContent; - while ((drtn = DERDecodeSeqNext(&dpSeq, &dpSeqContent)) == DR_Success) { - require_quiet(dpSeqContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDistributionPoint dp; - drtn = DERParseSequenceContent(&dpSeqContent.content, - DERNumDistributionPointItemSpecs, - DERDistributionPointItemSpecs, - &dp, sizeof(dp)); - require_noerr_quiet(drtn, badDER); - if (dp.distributionPoint.length) { - DERDecodedInfo distributionPointName; - drtn = DERDecodeItem(&dp.distributionPoint, &distributionPointName); - require_noerr_quiet(drtn, badDER); - if (distributionPointName.tag == - (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0)) { - /* Full Name */ - appendGeneralNamesContent(properties, - &distributionPointName.content); - } else if (distributionPointName.tag == - (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1)) { - CFArrayRef rdn_props = createPropertiesForRDNContent(allocator, - &dp.reasons); - appendProperty(properties, kSecPropertyTypeSection, - SEC_NAME_REL_CRL_ISSUER_KEY, NULL, rdn_props); - CFRelease(rdn_props); - } else { - goto badDER; - } - } - if (dp.reasons.length) { - static const CFStringRef reasonNames[] = { - SEC_UNUSED_KEY, - SEC_KEY_COMPROMISE_KEY, - SEC_CA_COMPROMISE_KEY, - SEC_AFFILIATION_CHANGED_KEY, - SEC_SUPERSEDED_KEY, - SEC_CESSATION_OF_OPER_KEY, - SEC_CERTIFICATE_HOLD_KEY, - SEC_PRIV_WITHDRAWN_KEY, - SEC_AA_COMPROMISE_KEY - }; - appendBitStringContentNames(properties, SEC_REASONS_KEY, - &dp.reasons, - reasonNames, array_size(reasonNames)); - } - if (dp.cRLIssuer.length) { - CFMutableArrayRef crlIssuer = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - appendProperty(properties, kSecPropertyTypeSection, - SEC_CRL_ISSUER_KEY, NULL, crlIssuer); - CFRelease(crlIssuer); - appendGeneralNames(crlIssuer, &dp.cRLIssuer); - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, SEC_CRL_DISTR_POINTS_KEY, extnValue); -} - -/* Decode a sequence of integers into a comma separated list of ints. */ -static void appendIntegerSequenceContent(CFMutableArrayRef properties, - CFStringRef label, const DERItem *intSequenceContent) { - CFAllocatorRef allocator = CFGetAllocator(properties); - DERSequence intSeq; - DERReturn drtn = DERDecodeSeqContentInit(intSequenceContent, &intSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo intContent; - CFStringRef value = NULL; - CFStringRef fmt = SecCopyCertString(SEC_STRING_LIST_KEY); - while ((drtn = DERDecodeSeqNext(&intSeq, &intContent)) == DR_Success) { - require_quiet(intContent.tag == ASN1_INTEGER, badDER); - CFStringRef intDesc = copyIntegerContentDescription( - allocator, &intContent.content); - if (value) { - CFStringRef v; - v = CFStringCreateWithFormat(allocator, NULL, fmt, value, intDesc); - CFRelease(value); - value = v; - CFRelease(intDesc); - } else { - value = intDesc; - } - } - CFRelease(fmt); - require_quiet(drtn == DR_EndOfSequence, badDER); - if (value) { - appendProperty(properties, kSecPropertyTypeString, label, NULL, value); - CFRelease(value); - return; - } - /* DROPTHOUGH if !value. */ -badDER: - appendInvalidProperty(properties, label, intSequenceContent); -} - -static void appendCertificatePolicies(CFMutableArrayRef properties, - const DERItem *extnValue) { - CFAllocatorRef allocator = CFGetAllocator(properties); - DERTag tag; - DERSequence piSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &piSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo piContent; - int pin = 1; - while ((drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) { - require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERPolicyInformation pi; - drtn = DERParseSequenceContent(&piContent.content, - DERNumPolicyInformationItemSpecs, - DERPolicyInformationItemSpecs, - &pi, sizeof(pi)); - require_noerr_quiet(drtn, badDER); - CFStringRef piLabel = CFStringCreateWithFormat(allocator, NULL, - SEC_POLICY_IDENTIFIER_KEY, pin); - CFStringRef piFmt = SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY); - CFStringRef lpiLabel = CFStringCreateWithFormat(allocator, NULL, - piFmt, pin++); - CFRelease(piFmt); - appendOIDProperty(properties, piLabel, lpiLabel, &pi.policyIdentifier); - CFRelease(piLabel); - CFRelease(lpiLabel); - if (pi.policyQualifiers.length == 0) - continue; - - DERSequence pqSeq; - drtn = DERDecodeSeqContentInit(&pi.policyQualifiers, &pqSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo pqContent; - int pqn = 1; - while ((drtn = DERDecodeSeqNext(&pqSeq, &pqContent)) == DR_Success) { - DERPolicyQualifierInfo pqi; - drtn = DERParseSequenceContent(&pqContent.content, - DERNumPolicyQualifierInfoItemSpecs, - DERPolicyQualifierInfoItemSpecs, - &pqi, sizeof(pqi)); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo qualifierContent; - drtn = DERDecodeItem(&pqi.qualifier, &qualifierContent); - require_noerr_quiet(drtn, badDER); - CFStringRef pqLabel = CFStringCreateWithFormat(allocator, NULL, - SEC_POLICY_QUALIFIER_KEY, pqn); - CFStringRef pqFmt = SecCopyCertString(SEC_POLICY_QUALIFIER_KEY); - CFStringRef lpqLabel = CFStringCreateWithFormat(allocator, NULL, - pqFmt, pqn++); - CFRelease(pqFmt); - appendOIDProperty(properties, pqLabel, lpqLabel, - &pqi.policyQualifierID); - CFRelease(pqLabel); - CFRelease(lpqLabel); - if (DEROidCompare(&oidQtCps, &pqi.policyQualifierID)) { - require_quiet(qualifierContent.tag == ASN1_IA5_STRING, badDER); - appendURLContentProperty(properties, SEC_CPS_URI_KEY, - &qualifierContent.content); - } else if (DEROidCompare(&oidQtUNotice, &pqi.policyQualifierID)) { - require_quiet(qualifierContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERUserNotice un; - drtn = DERParseSequenceContent(&qualifierContent.content, - DERNumUserNoticeItemSpecs, - DERUserNoticeItemSpecs, - &un, sizeof(un)); - require_noerr_quiet(drtn, badDER); - if (un.noticeRef.length) { - DERNoticeReference nr; - drtn = DERParseSequenceContent(&un.noticeRef, - DERNumNoticeReferenceItemSpecs, - DERNoticeReferenceItemSpecs, - &nr, sizeof(nr)); - require_noerr_quiet(drtn, badDER); - appendDERThingProperty(properties, - SEC_ORGANIZATION_KEY, NULL, - &nr.organization); - appendIntegerSequenceContent(properties, - SEC_NOTICE_NUMBERS_KEY, &nr.noticeNumbers); - } - if (un.explicitText.length) { - appendDERThingProperty(properties, SEC_EXPLICIT_TEXT_KEY, - NULL, &un.explicitText); - } - } else { - appendUnparsedProperty(properties, SEC_QUALIFIER_KEY, NULL, - &pqi.qualifier); - } - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, SEC_CERT_POLICIES_KEY, extnValue); -} - -static void appendSubjectKeyIdentifier(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERReturn drtn; - DERDecodedInfo keyIdentifier; - drtn = DERDecodeItem(extnValue, &keyIdentifier); - require_noerr_quiet(drtn, badDER); - require_quiet(keyIdentifier.tag == ASN1_OCTET_STRING, badDER); - appendDataProperty(properties, SEC_KEY_IDENTIFIER_KEY, NULL, - &keyIdentifier.content); - - return; -badDER: - appendInvalidProperty(properties, SEC_SUBJ_KEY_ID_KEY, - extnValue); -} - -/* -AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - -- authorityCertIssuer and authorityCertSerialNumber MUST both - -- be present or both be absent - -KeyIdentifier ::= OCTET STRING -*/ -static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERAuthorityKeyIdentifier akid; - DERReturn drtn; - drtn = DERParseSequence(extnValue, - DERNumAuthorityKeyIdentifierItemSpecs, - DERAuthorityKeyIdentifierItemSpecs, - &akid, sizeof(akid)); - require_noerr_quiet(drtn, badDER); - if (akid.keyIdentifier.length) { - appendDataProperty(properties, SEC_KEY_IDENTIFIER_KEY, NULL, - &akid.keyIdentifier); - } - if (akid.authorityCertIssuer.length || - akid.authorityCertSerialNumber.length) { - require_quiet(akid.authorityCertIssuer.length && - akid.authorityCertSerialNumber.length, badDER); - /* Perhaps put in a subsection called Authority Certificate Issuer. */ - appendGeneralNamesContent(properties, - &akid.authorityCertIssuer); - appendIntegerProperty(properties, SEC_AUTH_CERT_SERIAL_KEY, - &akid.authorityCertSerialNumber); - } - - return; -badDER: - appendInvalidProperty(properties, SEC_AUTHORITY_KEY_ID_KEY, extnValue); -} - -/* - PolicyConstraints ::= SEQUENCE { - requireExplicitPolicy [0] SkipCerts OPTIONAL, - inhibitPolicyMapping [1] SkipCerts OPTIONAL } - - SkipCerts ::= INTEGER (0..MAX) -*/ -static void appendPolicyConstraints(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERPolicyConstraints pc; - DERReturn drtn; - drtn = DERParseSequence(extnValue, - DERNumPolicyConstraintsItemSpecs, - DERPolicyConstraintsItemSpecs, - &pc, sizeof(pc)); - require_noerr_quiet(drtn, badDER); - if (pc.requireExplicitPolicy.length) { - appendIntegerProperty(properties, SEC_REQUIRE_EXPL_POLICY_KEY, - &pc.requireExplicitPolicy); - } - if (pc.inhibitPolicyMapping.length) { - appendIntegerProperty(properties, SEC_INHIBIT_POLICY_MAP_KEY, - &pc.inhibitPolicyMapping); - } - - return; - -badDER: - appendInvalidProperty(properties, SEC_POLICY_CONSTRAINTS_KEY, extnValue); -} - -/* -extendedKeyUsage EXTENSION ::= { - SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId - IDENTIFIED BY id-ce-extKeyUsage } - -KeyPurposeId ::= OBJECT IDENTIFIER -*/ -static void appendExtendedKeyUsage(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERTag tag; - DERSequence derSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &derSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo currDecoded; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { - require_quiet(currDecoded.tag == ASN1_OBJECT_ID, badDER); - appendOIDProperty(properties, SEC_PURPOSE_KEY, NULL, - &currDecoded.content); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, SEC_EXTENDED_KEY_USAGE_KEY, extnValue); -} - -/* - id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } - - AuthorityInfoAccessSyntax ::= - SEQUENCE SIZE (1..MAX) OF AccessDescription - - AccessDescription ::= SEQUENCE { - accessMethod OBJECT IDENTIFIER, - accessLocation GeneralName } - - id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } - - id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } - - id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } -*/ -static void appendInfoAccess(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERTag tag; - DERSequence adSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &adSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo adContent; - while ((drtn = DERDecodeSeqNext(&adSeq, &adContent)) == DR_Success) { - require_quiet(adContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERAccessDescription ad; - drtn = DERParseSequenceContent(&adContent.content, - DERNumAccessDescriptionItemSpecs, - DERAccessDescriptionItemSpecs, - &ad, sizeof(ad)); - require_noerr_quiet(drtn, badDER); - appendOIDProperty(properties, SEC_ACCESS_METHOD_KEY, NULL, - &ad.accessMethod); - //TODO: Do something with SEC_ACCESS_LOCATION_KEY - appendGeneralNameProperty(properties, &ad.accessLocation); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, SEC_AUTH_INFO_ACCESS_KEY, extnValue); -} - -static void appendNetscapeCertType(CFMutableArrayRef properties, - const DERItem *extnValue) { - static const CFStringRef certTypes[] = { - SEC_SSL_CLIENT_KEY, - SEC_SSL_SERVER_KEY, - SEC_SMIME_KEY, - SEC_OBJECT_SIGNING_KEY, - SEC_RESERVED_KEY, - SEC_SSL_CA_KEY, - SEC_SMIME_CA_KEY, - SEC_OBJECT_SIGNING_CA_KEY - }; - appendBitStringNames(properties, SEC_USAGE_KEY, extnValue, - certTypes, array_size(certTypes)); -} - -#if 0 -static void appendEntrustVersInfo(CFMutableArrayRef properties, - const DERItem *extnValue) { -} - -/* - * The list of Qualified Cert Statement statementIds we understand, even though - * we don't actually do anything with them; if these are found in a Qualified - * Cert Statement that's critical, we can truthfully say "yes we understand this". - */ -static const CSSM_OID_PTR knownQualifiedCertStatements[] = -{ - /* id-qcs := { id-pkix 11 } */ - (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V1, /* id-qcs 1 */ - (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V2, /* id-qcs 2 */ - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_COMPLIANCE, - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE, - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_RETENTION, - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_SSCD -}; -#define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR)) -*/ -static void appendQCCertStatements(CFMutableArrayRef properties, - const DERItem *extnValue) { -} - -#endif - -static bool appendPrintableDERSequence(CFMutableArrayRef properties, - CFStringRef label, const DERItem *sequence) { - DERTag tag; - DERSequence derSeq; - DERReturn drtn = DERDecodeSeqInit(sequence, &tag, &derSeq); - require_noerr_quiet(drtn, badSequence); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badSequence); - DERDecodedInfo currDecoded; - bool appendedSomething = false; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { - switch (currDecoded.tag) - { - case 0: // 0 - case ASN1_SEQUENCE: // 16 - case ASN1_SET: // 17 - // skip constructed object lengths - break; - - case ASN1_UTF8_STRING: // 12 - case ASN1_NUMERIC_STRING: // 18 - case ASN1_PRINTABLE_STRING: // 19 - case ASN1_T61_STRING: // 20, also ASN1_TELETEX_STRING - case ASN1_VIDEOTEX_STRING: // 21 - case ASN1_IA5_STRING: // 22 - case ASN1_GRAPHIC_STRING: // 25 - case ASN1_VISIBLE_STRING: // 26, also ASN1_ISO646_STRING - case ASN1_GENERAL_STRING: // 27 - case ASN1_UNIVERSAL_STRING: // 28 - { - CFStringRef string = - copyDERThingContentDescription(CFGetAllocator(properties), - currDecoded.tag, &currDecoded.content, false); - //CFStringRef cleanString = copyStringRemovingPercentEscapes(string); - - appendProperty(properties, kSecPropertyTypeString, label, NULL, - string); - CFRelease(string); - appendedSomething = true; - break; - } - default: - break; - } - } - require_quiet(drtn == DR_EndOfSequence, badSequence); - return appendedSomething; -badSequence: - return false; -} - -static void appendExtension(CFMutableArrayRef parent, - const SecCertificateExtension *extn) { - CFAllocatorRef allocator = CFGetAllocator(parent); - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - const DERItem - *extnID = &extn->extnID, - *extnValue = &extn->extnValue; - - appendBoolProperty(properties, SEC_CRITICAL_KEY, extn->critical); - -#if 1 - bool handeled = true; - /* Extensions that we know how to handle ourselves... */ - if (extnID->length == oidSubjectKeyIdentifier.length && - !memcmp(extnID->data, oidSubjectKeyIdentifier.data, extnID->length - 1)) - { - switch (extnID->data[extnID->length - 1]) { - case 14: /* SubjectKeyIdentifier id-ce 14 */ - appendSubjectKeyIdentifier(properties, extnValue); - break; - case 15: /* KeyUsage id-ce 15 */ - appendKeyUsage(properties, extnValue); - break; - case 16: /* PrivateKeyUsagePeriod id-ce 16 */ - appendPrivateKeyUsagePeriod(properties, extnValue); - break; - case 17: /* SubjectAltName id-ce 17 */ - case 18: /* IssuerAltName id-ce 18 */ - appendGeneralNames(properties, extnValue); - break; - case 19: /* BasicConstraints id-ce 19 */ - appendBasicConstraints(properties, extnValue); - break; - case 30: /* NameConstraints id-ce 30 */ - handeled = false; - break; - case 31: /* CRLDistributionPoints id-ce 31 */ - appendCrlDistributionPoints(properties, extnValue); - break; - case 32: /* CertificatePolicies id-ce 32 */ - appendCertificatePolicies(properties, extnValue); - break; - case 33: /* PolicyMappings id-ce 33 */ - handeled = false; - break; - case 35: /* AuthorityKeyIdentifier id-ce 35 */ - appendAuthorityKeyIdentifier(properties, extnValue); - break; - case 36: /* PolicyConstraints id-ce 36 */ - appendPolicyConstraints(properties, extnValue); - break; - case 37: /* ExtKeyUsage id-ce 37 */ - appendExtendedKeyUsage(properties, extnValue); - break; - case 46: /* FreshestCRL id-ce 46 */ - handeled = false; - break; - case 54: /* InhibitAnyPolicy id-ce 54 */ - handeled = false; - break; - default: - handeled = false; - break; - } - } else if (extnID->length == oidAuthorityInfoAccess.length && - !memcmp(extnID->data, oidAuthorityInfoAccess.data, extnID->length - 1)) - { - switch (extnID->data[extnID->length - 1]) { - case 1: /* AuthorityInfoAccess id-pe 1 */ - appendInfoAccess(properties, extnValue); - break; - case 3: /* QCStatements id-pe 3 */ - handeled = false; - break; - case 11: /* SubjectInfoAccess id-pe 11 */ - appendInfoAccess(properties, extnValue); - break; - default: - handeled = false; - break; - } - } else if (DEROidCompare(extnID, &oidNetscapeCertType)) { - /* 2.16.840.1.113730.1.1 netscape 1 1 */ - appendNetscapeCertType(properties, extnValue); - } else { - handeled = false; - } - - if (!handeled) { - /* Try to parse and display printable string(s). */ - if (appendPrintableDERSequence(properties, SEC_DATA_KEY, extnValue)) { - /* Nothing to do here appendPrintableDERSequence did the work. */ - } else { - /* Couldn't parse extension; dump the raw unparsed data as hex. */ - appendUnparsedProperty(properties, SEC_DATA_KEY, NULL, extnValue); - } - } -#else - /* Extensions that we know how to handle ourselves... */ - if (DEROidCompare(extnID, &oidSubjectKeyIdentifier)) { - appendSubjectKeyIdentifier(properties, extnValue); - } else if (DEROidCompare(extnID, &oidKeyUsage)) { - appendKeyUsage(properties, extnValue); - } else if (DEROidCompare(extnID, &oidPrivateKeyUsagePeriod)) { - appendPrivateKeyUsagePeriod(properties, extnValue); - } else if (DEROidCompare(extnID, &oidSubjectAltName)) { - appendGeneralNames(properties, extnValue); - } else if (DEROidCompare(extnID, &oidIssuerAltName)) { - appendGeneralNames(properties, extnValue); - } else if (DEROidCompare(extnID, &oidBasicConstraints)) { - appendBasicConstraints(properties, extnValue); - } else if (DEROidCompare(extnID, &oidCrlDistributionPoints)) { - appendCrlDistributionPoints(properties, extnValue); - } else if (DEROidCompare(extnID, &oidCertificatePolicies)) { - appendCertificatePolicies(properties, extnValue); - } else if (DEROidCompare(extnID, &oidAuthorityKeyIdentifier)) { - appendAuthorityKeyIdentifier(properties, extnValue); - } else if (DEROidCompare(extnID, &oidPolicyConstraints)) { - appendPolicyConstraints(properties, extnValue); - } else if (DEROidCompare(extnID, &oidExtendedKeyUsage)) { - appendExtendedKeyUsage(properties, extnValue); - } else if (DEROidCompare(extnID, &oidAuthorityInfoAccess)) { - appendInfoAccess(properties, extnValue); - } else if (DEROidCompare(extnID, &oidSubjectInfoAccess)) { - appendInfoAccess(properties, extnValue); - } else if (DEROidCompare(extnID, &oidNetscapeCertType)) { - appendNetscapeCertType(properties, extnValue); -#if 0 - } else if (DEROidCompare(extnID, &oidEntrustVersInfo)) { - appendEntrustVersInfo(properties, extnValue); -#endif - } else - /* Try to parse and display printable string(s). */ - if (appendPrintableDERSequence(properties, SEC_DATA_KEY, extnValue)) { - /* Nothing to do here appendPrintableDERSequence did the work. */ - } else { - /* Couldn't parse extension; dump the raw unparsed data as hex. */ - appendUnparsedProperty(properties, SEC_DATA_KEY, NULL, extnValue); - } -#endif - CFStringRef label = SecDERItemCopyOIDDecimalRepresentation(allocator, - extnID); - CFStringRef localizedLabel = copyLocalizedOidDescription(allocator, - extnID); - appendProperty(parent, kSecPropertyTypeSection, label, localizedLabel, - properties); - CFRelease(localizedLabel); - CFRelease(label); - CFRelease(properties); -} - -/* Different types of summary types from least desired to most desired. */ -enum SummaryType { - kSummaryTypeNone, - kSummaryTypePrintable, - kSummaryTypeOrganizationName, - kSummaryTypeOrganizationalUnitName, - kSummaryTypeCommonName, -}; - -struct Summary { - enum SummaryType type; - CFStringRef summary; - CFStringRef description; -}; - -static OSStatus obtainSummaryFromX501Name(void *context, - const DERItem *type, const DERItem *value, CFIndex rdnIX) { - struct Summary *summary = (struct Summary *)context; - enum SummaryType stype = kSummaryTypeNone; - CFStringRef string = NULL; - if (DEROidCompare(type, &oidCommonName)) { - stype = kSummaryTypeCommonName; - } else if (DEROidCompare(type, &oidOrganizationalUnitName)) { - stype = kSummaryTypeOrganizationalUnitName; - } else if (DEROidCompare(type, &oidOrganizationName)) { - stype = kSummaryTypeOrganizationName; - } else if (DEROidCompare(type, &oidDescription)) { - string = copyDERThingDescription(kCFAllocatorDefault, value, true); - if (string) { - if (summary->description) { - CFStringRef fmt = SecCopyCertString(SEC_STRING_LIST_KEY); - CFStringRef newDescription = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, fmt, string, summary->description); - CFRelease(fmt); - CFRelease(summary->description); - summary->description = newDescription; - } else { - summary->description = string; - CFRetain(string); - } - stype = kSummaryTypePrintable; - } - } else { - stype = kSummaryTypePrintable; - } - - /* Build a string with all instances of the most desired - component type in reverse order encountered comma separated list, - The order of desirability is defined by enum SummaryType. */ - if (summary->type <= stype) { - if (!string) - string = copyDERThingDescription(kCFAllocatorDefault, value, true); - - if (string) { - if (summary->type == stype) { - CFStringRef fmt = SecCopyCertString(SEC_STRING_LIST_KEY); - CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, fmt, string, summary->summary); - CFRelease(fmt); - CFRelease(string); - string = newSummary; - } else { - summary->type = stype; - } - CFReleaseSafe(summary->summary); - summary->summary = string; - } - } else { - CFReleaseSafe(string); - } - - return errSecSuccess; -} - -CFStringRef SecCertificateCopySubjectSummary(SecCertificateRef certificate) { - struct Summary summary = {}; - parseX501NameContent(&certificate->_subject, &summary, obtainSummaryFromX501Name); - /* If we found a description and a common name we change the summary to - CommonName (Description). */ - if (summary.description) { - if (summary.type == kSummaryTypeCommonName) { - CFStringRef fmt = SecCopyCertString(SEC_COMMON_NAME_DESC_KEY); - CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, fmt, summary.summary, summary.description); - CFRelease(fmt); - CFRelease(summary.summary); - summary.summary = newSummary; - } - CFRelease(summary.description); - } - - if (!summary.summary) { - /* If we didn't find a suitable printable string in the subject at all, we try - the first email address in the certificate instead. */ - CFArrayRef names = SecCertificateCopyRFC822Names(certificate); - if (!names) { - /* If we didn't find any email addresses in the certificate, we try finding - a DNS name instead. */ - names = SecCertificateCopyDNSNames(certificate); - } - if (names) { - summary.summary = CFArrayGetValueAtIndex(names, 0); - CFRetain(summary.summary); - CFRelease(names); - } - } - - return summary.summary; -} - -CFStringRef SecCertificateCopyIssuerSummary(SecCertificateRef certificate) { - struct Summary summary = {}; - parseX501NameContent(&certificate->_issuer, &summary, obtainSummaryFromX501Name); - /* If we found a description and a common name we change the summary to - CommonName (Description). */ - if (summary.description) { - if (summary.type == kSummaryTypeCommonName) { - CFStringRef fmt = SecCopyCertString(SEC_COMMON_NAME_DESC_KEY); - CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, fmt, summary.summary, summary.description); - CFRelease(fmt); - CFRelease(summary.summary); - summary.summary = newSummary; - } - CFRelease(summary.description); - } - - return summary.summary; -} - -/* Return the earliest date on which all certificates in this chain are still - valid. */ -static CFAbsoluteTime SecCertificateGetChainsLastValidity( - SecCertificateRef certificate) { - CFAbsoluteTime earliest = certificate->_notAfter; -#if 0 - while (certificate->_parent) { - certificate = certificate->_parent; - if (earliest > certificate->_notAfter) - earliest = certificate->_notAfter; - } -#endif - - return earliest; -} - -/* Return the latest date on which all certificates in this chain will be - valid. */ -static CFAbsoluteTime SecCertificateGetChainsFirstValidity( - SecCertificateRef certificate) { - CFAbsoluteTime latest = certificate->_notBefore; -#if 0 - while (certificate->_parent) { - certificate = certificate->_parent; - if (latest < certificate->_notBefore) - latest = certificate->_notBefore; - } -#endif - - return latest; -} - -bool SecCertificateIsValid(SecCertificateRef certificate, - CFAbsoluteTime verifyTime) { - check(certificate); - return certificate->_notBefore <= verifyTime && - verifyTime <= certificate->_notAfter; -} - -CFIndex SecCertificateVersion(SecCertificateRef certificate) { - return certificate->_version + 1; -} - -CFAbsoluteTime SecCertificateNotValidBefore(SecCertificateRef certificate) { - return certificate->_notBefore; -} - -CFAbsoluteTime SecCertificateNotValidAfter(SecCertificateRef certificate) { - return certificate->_notAfter; -} - -CFMutableArrayRef SecCertificateCopySummaryProperties( - SecCertificateRef certificate, CFAbsoluteTime verifyTime) { - CFAllocatorRef allocator = CFGetAllocator(certificate); - CFMutableArrayRef summary = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - - /* First we put the subject summary name. */ - CFStringRef ssummary = SecCertificateCopySubjectSummary(certificate); - if (ssummary) { - appendProperty(summary, kSecPropertyTypeTitle, - NULL, NULL, ssummary); - CFRelease(ssummary); - } -#if 0 - CFStringRef isummary = SEC_ISSUER_SUMMARY_KEY; - appendProperty(summary, kSecPropertyTypeString, - SEC_ISSUED_BY_KEY, isummary); - CFRelease(isummary); -#endif - - /* Let see if this certificate is currently valid. */ - CFStringRef label; - CFAbsoluteTime when; - CFStringRef message; - CFStringRef ptype; - if (verifyTime > certificate->_notAfter) { - label = SEC_EXPIRED_KEY; - when = certificate->_notAfter; - ptype = kSecPropertyTypeError; - message = SEC_CERT_EXPIRED_KEY; - } else if (certificate->_notBefore > verifyTime) { - label = SEC_VALID_FROM_KEY; - when = certificate->_notBefore; - ptype = kSecPropertyTypeError; - message = SEC_CERT_NOT_YET_VALID_KEY; - } else { - CFAbsoluteTime last = SecCertificateGetChainsLastValidity(certificate); - CFAbsoluteTime first = SecCertificateGetChainsFirstValidity(certificate); - if (verifyTime > last) { - label = SEC_EXPIRED_KEY; - when = last; - ptype = kSecPropertyTypeError; - message = SEC_ISSUER_EXPIRED_KEY; - } else if (verifyTime < first) { - label = SEC_VALID_FROM_KEY; - when = first; - ptype = kSecPropertyTypeError; - message = SEC_ISSR_NOT_YET_VALID_KEY; - } else { - label = SEC_EXPIRES_KEY; - when = certificate->_notAfter; - ptype = kSecPropertyTypeSuccess; - message = SEC_CERT_VALID_KEY; - } - } - - appendDateProperty(summary, label, when); - CFStringRef lmessage = SecCopyCertString(message); - appendProperty(summary, ptype, NULL, NULL, lmessage); - CFRelease(lmessage); - - return summary; -} - -CFArrayRef SecCertificateCopyProperties(SecCertificateRef certificate) { - if (!certificate->_properties) { - CFAllocatorRef allocator = CFGetAllocator(certificate); - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - - /* First we put the Subject Name in the property list. */ - CFArrayRef subject_plist = createPropertiesForX501NameContent(allocator, - &certificate->_subject); - appendProperty(properties, kSecPropertyTypeSection, - SEC_SUBJECT_NAME_KEY, NULL, subject_plist); - CFRelease(subject_plist); - -#if 0 - /* Put Normalized subject in for testing. */ - if (certificate->_normalizedSubject) { - DERItem nsubject = { - (DERByte *)CFDataGetBytePtr(certificate->_normalizedSubject), - CFDataGetLength(certificate->_normalizedSubject) - }; - CFArrayRef nsubject_plist = createPropertiesForX501NameContent(allocator, - &nsubject); - appendProperty(properties, kSecPropertyTypeSection, - CFSTR("Normalized Subject Name"), nsubject_plist); - CFRelease(nsubject_plist); - } -#endif - - /* Next we put the Issuer Name in the property list. */ - CFArrayRef issuer_plist = createPropertiesForX501NameContent(allocator, - &certificate->_issuer); - appendProperty(properties, kSecPropertyTypeSection, - SEC_ISSUER_NAME_KEY, NULL, issuer_plist); - CFRelease(issuer_plist); - -#if 0 - /* Certificate version/type. */ - bool isRoot = false; - CFStringRef fmt = SecCopyCertString(SEC_X509_VERSION_KEY); - CFStringRef typeString = CFStringCreateWithFormat(allocator, NULL, - fmt, certificate->_version + 1, isRoot ? "root " : ""); - CFRelease(fmt); - appendProperty(properties, kSecPropertyTypeString, - SEC_CERTIFICATE_TYPE_KEY, typeString); - CFRelease(typeString); -#endif - - /* Version */ - CFStringRef fmt = SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY); - CFStringRef versionString = CFStringCreateWithFormat(allocator, - NULL, fmt, certificate->_version + 1); - CFRelease(fmt); - appendProperty(properties, kSecPropertyTypeString, - SEC_VERSION_KEY, NULL, versionString); - CFRelease(versionString); - - /* Serial Number */ - if (certificate->_serialNum.length) { - appendIntegerProperty(properties, SEC_SERIAL_NUMBER_KEY, - &certificate->_serialNum); - } - - /* Signature algorithm. */ -#if 0 - appendAlgorithmProperty(properties, SEC_SIGNATURE_ALGORITHM_KEY, - &certificate->_sigAlg); -#endif - appendAlgorithmProperty(properties, SEC_SIGNATURE_ALGORITHM_KEY, - &certificate->_tbsSigAlg); - - - /* Validity dates. */ - appendDateProperty(properties, SEC_NOT_VALID_BEFORE_KEY, - certificate->_notBefore); - appendDateProperty(properties, SEC_NOT_VALID_AFTER_KEY, - certificate->_notAfter); - - if (certificate->_subjectUniqueID.length) { - appendDataProperty(properties, SEC_SUBJECT_UNIQUE_ID_KEY, NULL, - &certificate->_subjectUniqueID); - } - if (certificate->_issuerUniqueID.length) { - appendDataProperty(properties, SEC_ISSUER_UNIQUE_ID_KEY, NULL, - &certificate->_issuerUniqueID); - } - - /* Public key algorithm. */ - appendAlgorithmProperty(properties, SEC_PUBLIC_KEY_ALG_KEY, - &certificate->_algId); - - /* Consider breaking down an RSA public key into modulus and - exponent? */ - appendDataProperty(properties, SEC_PULIC_KEY_DATA_KEY, NULL, - &certificate->_pubKeyDER); - /* TODO: Add Key Size. */ - /* TODO: Add Key Usage. */ - - appendDataProperty(properties, SEC_SIGNATURE_KEY, NULL, - &certificate->_signature); - - CFIndex ix; - for (ix = 0; ix < certificate->_extensionCount; ++ix) { - appendExtension(properties, &certificate->_extensions[ix]); - } - - /* TODO: Certificate/Key Fingerprints. */ - - certificate->_properties = properties; - } - - CFRetain(certificate->_properties); - return certificate->_properties; -} - -CFDataRef SecCertificateCopySerialNumber( - SecCertificateRef certificate) { - if (certificate->_serialNumber) { - CFRetain(certificate->_serialNumber); - } - return certificate->_serialNumber; -} - -CFDataRef SecCertificateGetNormalizedIssuerContent( - SecCertificateRef certificate) { - return certificate->_normalizedIssuer; -} - -CFDataRef SecCertificateGetNormalizedSubjectContent( - SecCertificateRef certificate) { - return certificate->_normalizedSubject; -} - -/* Verify that certificate was signed by issuerKey. */ -OSStatus SecCertificateIsSignedBy(SecCertificateRef certificate, - SecKeyRef issuerKey) { - /* Setup algId in SecAsn1AlgId format. */ - SecAsn1AlgId algId; - algId.algorithm.Length = certificate->_tbsSigAlg.oid.length; - algId.algorithm.Data = certificate->_tbsSigAlg.oid.data; - algId.parameters.Length = certificate->_tbsSigAlg.params.length; - algId.parameters.Data = certificate->_tbsSigAlg.params.data; - - OSStatus status = SecKeyDigestAndVerify(issuerKey, &algId, - certificate->_tbs.data, certificate->_tbs.length, - certificate->_signature.data, certificate->_signature.length); - if (status) { - secdebug("verify", "signature verify failed: %" PRIdOSStatus, status); - return errSecNotSigner; - } - - return errSecSuccess; -} - -#if 0 -static OSStatus SecCertificateIsIssuedBy(SecCertificateRef certificate, - SecCertificateRef issuer, bool signatureCheckOnly) { - if (!signatureCheckOnly) { - /* It turns out we don't actually need to use normalized subject and - issuer according to rfc2459. */ - - /* If present we should check issuerID against the issuer subjectID. */ - - /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier - then we should look for a SubjectKeyIdentifier in the issuer - certificate. - If we have a authorityCertSerialNumber we can use that for chaining. - If we have a authorityCertIssuer we can use that? (or not) */ - - /* Verify that this cert was issued by issuer. Do so by chaining - either issuerID to subjectID or normalized issuer to normalized - subject. */ - CFDataRef normalizedIssuer = - SecCertificateGetNormalizedIssuerContent(certificate); - CFDataRef normalizedIssuerSubject = - SecCertificateGetNormalizedSubjectContent(issuer); - if (normalizedIssuer && normalizedIssuerSubject && - !CFEqual(normalizedIssuer, normalizedIssuerSubject)) - return errSecIssuerMismatch; - } - - /* Next verify that this cert was signed by issuer. */ - SecKeyRef issuerKey = SecCertificateGetPublicKey(issuer); - - /* Get the encodedDigestInfo from the digest of the subject's TBSCert */ - /* FIXME: We sould cache this (or at least the digest) until we find - a suitable issuer. */ - uint8_t signedData[DER_SHA1_DIGEST_INFO_LEN]; - CFIndex signedDataLength; - CertVerifyReturn crtn; - if (DEROidCompare(&certificate->_tbsSigAlg.oid, &oidSha1Rsa)) { - signedDataLength = DER_SHA1_DIGEST_INFO_LEN; - crtn = sha1DigestInfo(&certificate->_tbs, signedData); - } else if(DEROidCompare(&certificate->_tbsSigAlg.oid, &oidMd5Rsa)) { - signedDataLength = DER_MD_DIGEST_INFO_LEN; - crtn = mdDigestInfo(WD_MD5, &certificate->_tbs, signedData); - } else if(DEROidCompare(&certificate->_tbsSigAlg.oid, &oidMd2Rsa)) { - signedDataLength = DER_MD_DIGEST_INFO_LEN; - crtn = mdDigestInfo(WD_MD2, &certificate->_tbs, signedData); - } else { - secdebug("verify", "unsupported algorithm"); - return errSecUnsupportedAlgorithm; - } - if (crtn) { - secdebug("verify", "*DigestInfo returned: %d", crtn); - /* FIXME: Do proper error code translation. */ - return errSecUnsupportedAlgorithm; - } - - OSStatus status = SecKeyRawVerify(issuerKey, kSecPaddingPKCS1, - signedData, signedDataLength, - certificate->_signature.data, certificate->_signature.length); - if (status) { - secdebug("verify", "signature verify failed: %d", status); - return errSecNotSigner; - } - - return errSecSuccess; -} - -static OSStatus _SecCertificateSetParent(SecCertificateRef certificate, - SecCertificateRef issuer, bool signatureCheckOnly) { - check(issuer); - if (certificate->_parent) { - /* Setting a certificates issuer twice is only allowed if the new - issuer is equal to the current one. */ - return issuer && CFEqual(certificate->_parent, issuer); - } - -#if 0 - OSStatus status = SecCertificateIsIssuedBy(certificate, issuer, - signatureCheckOnly); -#else - OSStatus status = errSecSuccess; -#endif - if (!status) { - if (CFEqual(certificate, issuer)) { - /* We don't retain ourselves cause that would be bad mojo, - however we do record that we are properly self signed. */ - certificate->_isSelfSigned = kSecSelfSignedTrue; - secdebug("cert", "set self as parent"); - return errSecSuccess; - } - - CFRetain(issuer); - certificate->_parent = issuer; - certificate->_isSelfSigned = kSecSelfSignedFalse; - } - - return status; -} - -static bool SecCertificateIsSelfSigned(SecCertificateRef certificate) { - if (certificate->_isSelfSigned == kSecSelfSignedUnknown) { - certificate->_isSelfSigned = - (SecCertificateIsIssuedBy(certificate, certificate, false) ? - kSecSelfSignedTrue : kSecSelfSignedFalse); - } - - return certificate->_isSelfSigned == kSecSelfSignedTrue; -} - -/* Return true iff we were able to set our own parent from one of the - certificates in other_certificates, return false otherwise. If - signatureCheckOnly is true, we can skip the subject == issuer or - authorityKeyIdentifier tests. */ -static bool SecCertificateSetParentFrom(SecCertificateRef certificate, - CFArrayRef other_certificates, bool signatureCheckOnly) { - CFIndex count = CFArrayGetCount(other_certificates); - CFIndex ix; - for (ix = 0; ix < count; ++ix) { - SecCertificateRef candidate = (SecCertificateRef) - CFArrayGetValueAtIndex(other_certificates, ix); - if (_SecCertificateSetParent(certificate, candidate, - signatureCheckOnly)) - return true; - } - return false; -} - -/* Lookup the parent of certificate in the keychain and set it. */ -static bool SecCertificateFindParent(SecCertificateRef certificate) { - /* FIXME: Search for things other than just subject of our issuer if we - have a subjectID or authorityKeyIdentifier. */ - CFDataRef normalizedIssuer = - SecCertificateGetNormalizedIssuerContent(certificate); - const void *keys[] = { - kSecClass, - kSecReturnRef, - kSecMatchLimit, - kSecAttrSubject - }, - *values[] = { - kSecClassCertificate, - kCFBooleanTrue, - kSecMatchLimitAll, - normalizedIssuer - }; - CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFTypeRef results; - OSStatus status = SecItemCopyMatching(query, &results); - CFRelease(query); - if (status) { - secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d", - status); - return false; - } - CFArrayRef certs = (CFArrayRef)results; - /* Since we already know the certificates we are providing as candidates - have been checked for subject matching, we can ask - SecCertificateSetParentFrom to skip everything except the signature - checks. */ - bool result = SecCertificateSetParentFrom(certificate, certs, true); - CFRelease(certs); - return result; -} - -OSStatus SecCertificateCompleteChain(SecCertificateRef certificate, - CFArrayRef other_certificates) { - for (;;) { - if (certificate->_parent == NULL) { - if (SecCertificateIsSelfSigned(certificate)) - return errSecSuccess; - if (!other_certificates || - !SecCertificateSetParentFrom(certificate, other_certificates,\ - false)) { - if (!SecCertificateFindParent(certificate)) - return errSecIssuerNotFound; - } - } - certificate = certificate->_parent; - } -} -#endif - -static OSStatus appendIPAddressesFromGeneralNames(void *context, - SecCEGeneralNameType gnType, const DERItem *generalName) { - CFMutableArrayRef ipAddresses = (CFMutableArrayRef)context; - if (gnType == GNT_IPAddress) { - CFStringRef string = copyIPAddressContentDescription( - kCFAllocatorDefault, generalName); - if (string) { - CFArrayAppendValue(ipAddresses, string); - CFRelease(string); - } else { - return errSecInvalidCertificate; - } - } - return errSecSuccess; -} - -CFArrayRef SecCertificateCopyIPAddresses(SecCertificateRef certificate) { - /* These can only exist in the subject alt name. */ - if (!certificate->_subjectAltName) - return NULL; - - CFMutableArrayRef ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault, - 0, &kCFTypeArrayCallBacks); - OSStatus status = parseGeneralNames(&certificate->_subjectAltName->extnValue, - ipAddresses, appendIPAddressesFromGeneralNames); - if (status || CFArrayGetCount(ipAddresses) == 0) { - CFRelease(ipAddresses); - ipAddresses = NULL; - } - return ipAddresses; -} - -static OSStatus appendDNSNamesFromGeneralNames(void *context, SecCEGeneralNameType gnType, - const DERItem *generalName) { - CFMutableArrayRef dnsNames = (CFMutableArrayRef)context; - if (gnType == GNT_DNSName) { - CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, - generalName->data, generalName->length, - kCFStringEncodingUTF8, FALSE); - if (string) { - CFArrayAppendValue(dnsNames, string); - CFRelease(string); - } else { - return errSecInvalidCertificate; - } - } - return errSecSuccess; -} - -/* Return true if the passed in string matches the - Preferred name syntax from sections 2.3.1. in RFC 1035. - With the added check that we disallow empty dns names. - Also in order to support wildcard DNSNames we allow for the '*' - character anywhere in a dns component where we currently allow - a letter. - - ::= | " " - - ::=