-/*
- * Copyright (c) 2006-2015 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
- */
-
-
-#include "SecCertificateInternalP.h"
-
-#include <CommonCrypto/CommonDigest.h>
-#include <CoreFoundation/CFRuntime.h>
-#include <CoreFoundation/CFString.h>
-#include <CoreFoundation/CFBundle.h>
-#include <CoreFoundation/CFDictionary.h>
-#include <CoreFoundation/CFNumber.h>
-#include <CoreFoundation/CFTimeZone.h>
-#include <pthread.h>
-#include <string.h>
-#include <AssertMacros.h>
-#include <libDER/libDER.h>
-#include <libDER/DER_CertCrl.h>
-#include <libDER/DER_Encode.h>
-#include <libDER/DER_Keys.h>
-#include <libDER/asn1Types.h>
-#include <libDER/oidsPriv.h>
-
-#include "SecBasePriv.h"
-
-#include "SecRSAKeyP.h"
-#include "SecFrameworkP.h"
-#include "SecItem.h"
-#include "SecItemPriv.h"
-#include <stdbool.h>
-#include <stdlib.h>
-#include <libkern/OSByteOrder.h>
-#include <ctype.h>
-#include "SecInternal.h"
-#include "SecBase64P.h"
-
-#include <security_utilities/debugging.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;
-
-};
-
-/* Public Constants for property list keys. */
-const CFStringRef kSecPropertyKeyType = CFSTR("type");
-const CFStringRef kSecPropertyKeyLabel = CFSTR("label");
-const CFStringRef kSecPropertyKeyLocalizedLabel = CFSTR("localized label");
-const CFStringRef kSecPropertyKeyValue = CFSTR("value");
-
-/* Public Constants for property list values. */
-const CFStringRef kSecPropertyTypeWarning = CFSTR("warning");
-const CFStringRef kSecPropertyTypeError = CFSTR("error");
-const CFStringRef kSecPropertyTypeSuccess = CFSTR("success");
-const CFStringRef kSecPropertyTypeTitle = CFSTR("title");
-const CFStringRef kSecPropertyTypeSection = CFSTR("section");
-const CFStringRef kSecPropertyTypeData = CFSTR("data");
-const CFStringRef kSecPropertyTypeString = CFSTR("string");
-const CFStringRef kSecPropertyTypeURL = CFSTR("url");
-const CFStringRef kSecPropertyTypeDate = CFSTR("date");
-
-/* Extension parsing routine. */
-typedef void (*SecCertificateExtensionParser)(SecCertificateRefP certificate,
- const SecCertificateExtension *extn);
-
-/* CFRuntime regsitration 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 declartions of static functions. */
-static CFStringRef SecCertificateCopyDescription(CFTypeRef cf);
-static void SecCertificateDestroy(CFTypeRef cf);
-static bool derDateGetAbsoluteTime(const DERItem *dateChoice,
- CFAbsoluteTime *absTime);
-
-/* Static functions. */
-static CFStringRef SecCertificateCopyDescription(CFTypeRef cf) {
- SecCertificateRefP certificate = (SecCertificateRefP)cf;
- CFStringRef ret = NULL;
- CFStringRef subjectSummary = SecCertificateCopySubjectSummaryP(certificate);
- CFStringRef issuerSummary = SecCertificateCopyIssuerSummaryP(certificate);
-
- ret = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("<cert(%p) s: %@ i: %@>"), certificate,
- subjectSummary,
- issuerSummary);
-
- CFReleaseNull(subjectSummary);
- CFReleaseNull(issuerSummary);
- return ret;
-}
-
-static void SecCertificateDestroy(CFTypeRef cf) {
- SecCertificateRefP certificate = (SecCertificateRefP)cf;
- if (certificate->_certificatePolicies.policies) {
- free(certificate->_certificatePolicies.policies);
- certificate->_certificatePolicies.policies = NULL;
- }
- CFReleaseNull(certificate->_policyMappings);
- CFReleaseNull(certificate->_crlDistributionPoints);
- CFReleaseNull(certificate->_ocspResponders);
- CFReleaseNull(certificate->_caIssuers);
- if (certificate->_extensions) {
- free(certificate->_extensions);
- certificate->_extensions = NULL;
- }
- CFReleaseNull(certificate->_pubKey);
- CFReleaseNull(certificate->_der_data);
- CFReleaseNull(certificate->_properties);
- CFReleaseNull(certificate->_serialNumber);
- CFReleaseNull(certificate->_normalizedIssuer);
- CFReleaseNull(certificate->_normalizedSubject);
- CFReleaseNull(certificate->_authorityKeyID);
- CFReleaseNull(certificate->_subjectKeyID);
- CFReleaseNull(certificate->_sha1Digest);
-}
-
-static Boolean SecCertificateEqual(CFTypeRef cf1, CFTypeRef cf2) {
- SecCertificateRefP cert1 = (SecCertificateRefP)cf1;
- SecCertificateRefP cert2 = (SecCertificateRefP)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) {
- SecCertificateRefP certificate = (SecCertificateRefP)cf;
- DERSize der_length = certificate->_der.length;
- DERSize sig_length = certificate->_signature.length;
- DERSize 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(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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:
- secinfo("cert", "Invalid SubjectKeyIdentifier Extension");
-}
-
-static void SecCEPKeyUsage(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPSubjectAltName(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- certificate->_subjectAltName = extn;
-}
-
-static void SecCEPIssuerAltName(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPBasicConstraints(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERBasicConstraints basicConstraints;
- require_noerr_quiet(DERParseSequence(&extn->extnValue,
- DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs,
- &basicConstraints, sizeof(basicConstraints)), badDER);
- require_noerr_quiet(DERParseBooleanWithDefault(&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;
- secinfo("cert", "Invalid BasicConstraints Extension");
-}
-
-static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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 }
-*/
-/* maximum number of policies of 8192 seems more than adequate */
-#define MAX_CERTIFICATE_POLICIES 8192
-static void SecCEPCertificatePolicies(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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 ((policy_count < MAX_CERTIFICATE_POLICIES) &&
- (drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) {
- require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- policy_count++;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- DERSize malloc_policies = policy_count > 0 ? policy_count : 1;
- require_quiet(policies = (SecCEPolicyInformation *)malloc(sizeof(SecCEPolicyInformation)
- * malloc_policies), badDER);
- drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq);
- require_noerr_quiet(drtn, badDER);
- DERSize policy_ix = 0;
- while ((policy_ix < (policy_count > 0 ? policy_count : 1)) &&
- (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 = (uint32_t)policy_count;
- certificate->_certificatePolicies.policies = policies;
- return;
-badDER:
- if (policies)
- free(policies);
- certificate->_certificatePolicies.present = false;
- secinfo("cert", "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(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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);
- drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq);
- require_noerr_quiet(drtn, badDER);
- 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;
- secinfo("cert", "Invalid CertificatePolicies Extension");
-}
-#else
-static void SecCEPPolicyMappings(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERTag tag;
- DERSequence pmSeq;
- CFMutableDictionaryRef mappings = NULL;
- CFDataRef idp = NULL, sdp = 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);
-
- 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);
- }
- CFReleaseNull(idp);
- CFReleaseNull(sdp);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- certificate->_policyMappings = mappings;
- return;
-badDER:
- CFReleaseNull(idp);
- CFReleaseNull(sdp);
- CFReleaseSafe(mappings);
- certificate->_policyMappings = NULL;
- secinfo("cert", "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(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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:
- secinfo("cert", "Invalid AuthorityKeyIdentifier Extension");
-}
-
-static void SecCEPPolicyConstraints(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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;
- secinfo("cert", "Invalid PolicyConstraints Extension");
-}
-
-static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-/*
- InhibitAnyPolicy ::= SkipCerts
-
- SkipCerts ::= INTEGER (0..MAX)
-*/
-static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- require_noerr_quiet(DERParseInteger(
- &extn->extnValue,
- &certificate->_inhibitAnyPolicySkipCerts), badDER);
- return;
-badDER:
- certificate->_inhibitAnyPolicySkipCerts = UINT32_MAX;
- secinfo("cert", "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(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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:
- secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
- generalNameContent.tag, (int)generalNameContent.content.length, generalNameContent.content.data);
- goto badDER;
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- secinfo("cert", "failed to parse Authority Information Access extension");
-}
-
-static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPNetscapeCertType(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPEntrustVersInfo(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("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 */
- SecCertificateCopyDescription /* 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
- };
- static const void *extnParsers[] = {
- SecCEPSubjectKeyIdentifier,
- SecCEPKeyUsage,
- SecCEPPrivateKeyUsagePeriod,
- SecCEPSubjectAltName,
- SecCEPIssuerAltName,
- SecCEPBasicConstraints,
- SecCEPCrlDistributionPoints,
- SecCEPCertificatePolicies,
- SecCEPPolicyMappings,
- SecCEPAuthorityKeyIdentifier,
- SecCEPPolicyConstraints,
- SecCEPExtendedKeyUsage,
- SecCEPInhibitAnyPolicy,
- SecCEPAuthorityInfoAccess,
- SecCEPSubjectInfoAccess,
- SecCEPNetscapeCertType,
- SecCEPEntrustVersInfo
- };
- gExtensionParsers = CFDictionaryCreate(kCFAllocatorDefault, extnOIDs,
- extnParsers, sizeof(extnOIDs) / sizeof(*extnOIDs),
- &SecDERItemKeyCallBacks, NULL);
-}
-
-/* Given the contents of an X.501 Name return the contents of a normalized
- X.501 name. */
-static 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;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- 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;
-}
-
-/* AUDIT[securityd]:
- certificate->_der is a caller provided data of any length (might be 0).
-
- Top level certificate decode.
- */
-static bool SecCertificateParse(SecCertificateRefP certificate)
-{
- DERReturn drtn;
-
- check(certificate);
- 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 - it was saved in full DER form */
- DERSubjPubKeyInfo pubKeyInfo;
- drtn = DERParseSequence(&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 extentions allowed. */
- require_quiet(extensionCount < 10000, badCert);
- certificate->_extensionCount = extensionCount;
- CFIndex mallocCount = extensionCount > 0 ? extensionCount : 1;
- certificate->_extensions =
- malloc(sizeof(SecCertificateExtension) * mallocCount);
- require_quiet(certificate->_extensions, badCert);
-
- 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 = DERParseBooleanWithDefault(&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) {
- secinfo("cert", "Found unknown critical extension");
- certificate->_foundUnknownCriticalExtension = true;
- } else {
- secinfo("cert", "Found unknown non critical extension");
- }
- }
- }
-
- return true;
-
-badCert:
- return false;
-}
-
-
-/* Public API functions. */
-CFTypeID SecCertificateGetTypeIDP(void) {
- pthread_once(&kSecCertificateRegisterClass, SecCertificateRegisterClass);
- return kSecCertificateTypeID;
-}
-
-SecCertificateRefP SecCertificateCreateWithBytesP(CFAllocatorRef allocator,
- const UInt8 *der_bytes, CFIndex der_length) {
- check(der_bytes);
- check(der_length);
- CFIndex size = sizeof(struct __SecCertificate) + der_length;
- SecCertificateRefP result = (SecCertificateRefP)_CFRuntimeCreateInstance(
- allocator, SecCertificateGetTypeIDP(), 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;
- if(der_bytes) {
- memcpy(result->_der.data, der_bytes, der_length);
- }
- if (!SecCertificateParse(result)) {
- CFRelease(result);
- return NULL;
- }
- }
- return result;
-}
-
-/* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
-SecCertificateRefP SecCertificateCreate(CFAllocatorRef allocator,
- const UInt8 *der_bytes, CFIndex der_length);
-
-SecCertificateRefP SecCertificateCreate(CFAllocatorRef allocator,
- const UInt8 *der_bytes, CFIndex der_length) {
- return SecCertificateCreateWithBytesP(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.
- */
-SecCertificateRefP SecCertificateCreateWithDataP(CFAllocatorRef allocator,
- CFDataRef der_certificate) {
- check(der_certificate);
- CFIndex size = sizeof(struct __SecCertificate);
- SecCertificateRefP result = (SecCertificateRefP)_CFRuntimeCreateInstance(
- allocator, SecCertificateGetTypeIDP(), 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 SecCertificateCopyDataP(SecCertificateRefP 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 SecCertificateGetLengthP(SecCertificateRefP certificate) {
- return certificate->_der.length;
-}
-
-const UInt8 *SecCertificateGetBytePtrP(SecCertificateRefP 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
-
-static CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator,
- const DERItem *oid) {
-
- if (oid->length == 0) {
- return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
- CFSTR("SecCertificate"));
- }
- if (oid->length > MAX_OID_SIZE) {
- return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
- CFSTR("SecCertificate"));
- }
-
- 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(".%lu"), (unsigned long)value);
- value = 0;
- }
- }
- return result;
-}
-
-static CFStringRef copyLocalizedOidDescription(CFAllocatorRef allocator,
- const DERItem *oid) {
- if (oid->length == 0) {
- return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
- CFSTR("SecCertificate"));
- }
-
- /* 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"), (unsigned long)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 appendPropertyP(CFMutableArrayRef properties,
- CFStringRef propertyType, CFStringRef label, CFTypeRef value) {
- CFDictionaryRef property;
- if (label) {
- CFStringRef localizedLabel = SecFrameworkCopyLocalizedString(label,
- CFSTR("SecCertificate"));
- 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);
- CFRelease(localizedLabel);
- } 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 SInt32 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. */
-static 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;
- }
- }
-
- /* Initialize the fields in a gregorian date struct. */
- CFGregorianDate gdate;
- if (isUtcLength) {
- SInt32 year = parseDecimalPair(&cp);
- if (year < 50) {
- /* 0 <= year < 50 : assume century 21 */
- gdate.year = 2000 + year;
- } else if (year < 70) {
- /* 50 <= year < 70 : illegal per PKIX */
- return false;
- } else {
- /* 70 < year <= 99 : assume century 20 */
- gdate.year = 1900 + year;
- }
- } else {
- gdate.year = 100 * parseDecimalPair(&cp) + parseDecimalPair(&cp);
- }
- gdate.month = parseDecimalPair(&cp);
- gdate.day = parseDecimalPair(&cp);
- gdate.hour = parseDecimalPair(&cp);
- gdate.minute = parseDecimalPair(&cp);
- if (noSeconds) {
- gdate.second = 0;
- } else {
- gdate.second = parseDecimalPair(&cp);
- }
-
- CFTimeInterval timeZoneOffset = 0;
- if (isLocalized) {
- /* ZONE INDICATOR */
- SInt32 multiplier = *cp++ == '+' ? 60 : -60;
- timeZoneOffset = multiplier *
- (parseDecimalPair(&cp) + 60 * parseDecimalPair(&cp));
- } else {
- timeZoneOffset = 0;
- }
-
- secinfo("dateparse",
- "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
- (int)length, bytes, (int)gdate.year, gdate.month,
- gdate.day, gdate.hour, gdate.minute, gdate.second,
- timeZoneOffset / 60);
-
- if (!CFGregorianDateIsValid(gdate, kCFGregorianAllUnits))
- return false;
- CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL,
- timeZoneOffset);
- if (!timeZone)
- return NULL_TIME;
- CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(gdate, timeZone);
- CFRelease(timeZone);
- return absTime;
-}
-
-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, const DERItem *der_data) {
- CFDataRef data = CFDataCreate(CFGetAllocator(properties),
- der_data->data, der_data->length);
- appendPropertyP(properties, kSecPropertyTypeData, label, data);
- CFRelease(data);
-}
-
-static void appendUnparsedProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *der_data) {
- CFStringRef newLabel = CFStringCreateWithFormat(CFGetAllocator(properties),
- NULL, CFSTR("Unparsed %@"), label);
- appendDataProperty(properties, newLabel, der_data);
- CFRelease(newLabel);
-}
-
-static void appendInvalidProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *der_data) {
- CFStringRef newLabel = CFStringCreateWithFormat(CFGetAllocator(properties),
- NULL, CFSTR("Invalid %@"), label);
- appendDataProperty(properties, newLabel, der_data);
- CFRelease(newLabel);
-}
-
-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);
- appendPropertyP(properties, kSecPropertyTypeDate, label, date);
- CFRelease(date);
-}
-
-static void appendDateProperty(CFMutableArrayRef properties,
- CFStringRef label, CFAbsoluteTime absTime) {
- CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime);
- appendPropertyP(properties, kSecPropertyTypeDate, label, date);
- CFRelease(date);
-}
-
-static void appendIPAddressContentProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *ip) {
- CFStringRef value =
- copyIPAddressContentDescription(CFGetAllocator(properties), ip);
- if (value) {
- appendPropertyP(properties, kSecPropertyTypeString, label, value);
- CFRelease(value);
- } else {
- appendUnparsedProperty(properties, label, ip);
- }
-}
-
-static void appendURLContentProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *urlContent) {
- CFURLRef url = CFURLCreateWithBytes(CFGetAllocator(properties),
- urlContent->data, urlContent->length, kCFStringEncodingASCII, NULL);
- if (url) {
- appendPropertyP(properties, kSecPropertyTypeURL, label, 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, const DERItem *oid) {
- CFStringRef oid_string = copyLocalizedOidDescription(CFGetAllocator(properties),
- oid);
- appendPropertyP(properties, kSecPropertyTypeString, label, 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, CFSTR("Algorithm"), &algorithm->oid);
- if (algorithm->params.length) {
- if (algorithm->params.length == 2 &&
- algorithm->params.data[0] == ASN1_NULL &&
- algorithm->params.data[1] == 0) {
- /* @@@ Localize <NULL> or perhaps skip it? */
- appendPropertyP(alg_props, kSecPropertyTypeString,
- CFSTR("Parameters"), CFSTR("none"));
- } else {
- appendUnparsedProperty(alg_props, CFSTR("Parameters"),
- &algorithm->params);
- }
- }
- appendPropertyP(properties, kSecPropertyTypeSection, label, 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;
-}
-
-static CFStringRef copyBlobString(CFAllocatorRef allocator,
- CFStringRef blobType, CFStringRef quanta, const DERItem *blob) {
- CFStringRef blobFormat = SecFrameworkCopyLocalizedString(
- CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
- /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
- "data = 00 00 ...)" */);
- CFStringRef hex = copyHexDescription(allocator, blob);
- CFStringRef result = CFStringCreateWithFormat(allocator, NULL,
- blobFormat, blobType, blob->length, quanta, hex);
- CFRelease(hex);
- CFRelease(blobFormat);
-
- return result;
-}
-
-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, CFSTR("Byte string"), CFSTR("bytes"),
- derThing);
- //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
- case ASN1_BIT_STRING:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Bit string"), CFSTR("bits"),
- derThing);
- case (DERByte)ASN1_CONSTR_SEQUENCE:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Sequence"), CFSTR("bytes"),
- derThing);
- case (DERByte)ASN1_CONSTR_SET:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Set"), CFSTR("bytes"),
- derThing);
- case ASN1_OBJECT_ID:
- return printableOnly ? NULL : copyLocalizedOidDescription(allocator, derThing);
- default:
- /* @@@ Localize. */
- /* "format string for undisplayed field data with a given DER tag" */
- return printableOnly ? NULL : CFStringCreateWithFormat(allocator, NULL,
- CFSTR("not displayed (tag = %llu; length %d)"),
- tag, (int)derThing->length);
- }
-}
-
-static CFStringRef copyDERThingDescription(CFAllocatorRef allocator,
- const DERItem *derThing, bool printableOnly) {
- DERDecodedInfo decoded;
- DERReturn drtn;
-
- drtn = DERDecodeItem(derThing, &decoded);
- if (drtn) {
- return printableOnly ? NULL : copyHexDescription(allocator, derThing);
- } else {
- return copyDERThingContentDescription(allocator, decoded.tag,
- &decoded.content, false);
- }
-}
-
-static void appendDERThingProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *derThing) {
- CFStringRef value = copyDERThingDescription(CFGetAllocator(properties),
- derThing, false);
- appendPropertyP(properties, kSecPropertyTypeString, label, value);
- CFReleaseSafe(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);
- appendPropertyP(properties, kSecPropertyTypeSection, 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 = copyLocalizedOidDescription(CFGetAllocator(properties),
- rdnType);
- if (label) {
- appendDERThingProperty(properties, label, rdnValue);
- CFRelease(label);
- return errSecSuccess;
- } else {
- return errSecInvalidCertificate;
- }
-}
-
-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, CFSTR("RDN"), 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, CFSTR("X.501 Name"), 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, CFSTR("X.501 Name"), x501Name);
- }
-
- return properties;
-}
-
-static void appendIntegerProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *integer) {
- CFStringRef string = copyIntegerContentDescription(
- CFGetAllocator(properties), integer);
- appendPropertyP(properties, kSecPropertyTypeString, label, string);
- CFRelease(string);
-}
-
-static void appendBoolProperty(CFMutableArrayRef properties,
- CFStringRef label, bool boolean) {
- appendPropertyP(properties, kSecPropertyTypeString,
- label, boolean ? CFSTR("Yes") : CFSTR("No"));
-}
-
-static void appendBooleanProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *boolean, bool defaultValue) {
- bool result;
- DERReturn drtn = DERParseBooleanWithDefault(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;
- bool didOne = false;
- CFMutableStringRef string =
- CFStringCreateMutable(CFGetAllocator(properties), 0);
- for (ix = 0; ix < bits; ++ix) {
- if (value & mask) {
- if (didOne) {
- CFStringAppend(string, CFSTR(", "));
- } else {
- didOne = true;
- }
- CFStringAppend(string, names[ix]);
- }
- mask >>= 1;
- }
- appendPropertyP(properties, kSecPropertyTypeString, label, string);
- CFRelease(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];
- secinfo("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;
- }
- appendPropertyP(properties, kSecPropertyTypeString, CFSTR("Usage"),
- string);
- CFRelease(string);
- }
-}
-#else
-static void appendKeyUsage(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- 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 Only"),
- CFSTR("Decipher Only")
- };
- appendBitStringNames(properties, CFSTR("Usage"), extnValue,
- usageNames, sizeof(usageNames) / sizeof(*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, CFSTR("Not Valid Before"),
- ASN1_GENERALIZED_TIME, &pkup.notBefore);
- }
- if (pkup.notAfter.length) {
- appendDateContentProperty(properties, CFSTR("Not Valid After"),
- ASN1_GENERALIZED_TIME, &pkup.notAfter);
- }
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Private Key Usage Period"),
- 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) {
- appendPropertyP(properties, kSecPropertyTypeString, label, 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 oid_string = copyLocalizedOidDescription(allocator,
- &on.typeIdentifier);
- CFStringRef value_string = copyDERThingDescription(allocator, &on.value, false);
- if (value_string)
- appendPropertyP(properties, kSecPropertyTypeString, oid_string,
- value_string);
- else
- appendUnparsedProperty(properties, oid_string, &on.value);
-
- CFReleaseNull(value_string);
- CFReleaseNull(oid_string);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Other Name"), 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, CFSTR("Email Address"),
- generalName, kCFStringEncodingASCII);
- break;
- case ASN1_CONTEXT_SPECIFIC | 2:
- /* IA5String. */
- appendStringContentProperty(properties, CFSTR("DNS Name"), generalName,
- kCFStringEncodingASCII);
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3:
- appendUnparsedProperty(properties, CFSTR("X.400 Address"),
- generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4:
- {
- CFArrayRef directory_plist =
- createPropertiesForX501Name(CFGetAllocator(properties),
- generalName);
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Directory Name"), directory_plist);
- CFRelease(directory_plist);
- break;
- }
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5:
- appendUnparsedProperty(properties, CFSTR("EDI Party Name"),
- 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, CFSTR("URI"), generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 6:
- appendURLContentProperty(properties, CFSTR("URI"), generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 7:
- appendIPAddressContentProperty(properties, CFSTR("IP Address"),
- generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 8:
- appendOIDProperty(properties, CFSTR("Registered ID"), generalName);
- break;
- default:
- goto badDER;
- }
- 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, CFSTR("General Name"), 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, CFSTR("General Names"),
- 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, CFSTR("General Names"), 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, CFSTR("Certificate Authority"),
- &basicConstraints.cA, false);
-
- if (basicConstraints.pathLenConstraint.length != 0) {
- appendIntegerProperty(properties, CFSTR("Path Length Constraint"),
- &basicConstraints.pathLenConstraint);
- }
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Basic Constraints"), 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);
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Name Relative To CRL Issuer"), rdn_props);
- CFRelease(rdn_props);
- } else {
- goto badDER;
- }
- }
- if (dp.reasons.length) {
- static const CFStringRef reasonNames[] = {
- CFSTR("Unused"),
- CFSTR("Key Compromise"),
- CFSTR("CA Compromise"),
- CFSTR("Affiliation Changed"),
- CFSTR("Superseded"),
- CFSTR("Cessation Of Operation"),
- CFSTR("Certificate Hold"),
- CFSTR("Priviledge Withdrawn"),
- CFSTR("AA Compromise")
- };
- appendBitStringContentNames(properties, CFSTR("Reasons"),
- &dp.reasons,
- reasonNames, sizeof(reasonNames) / sizeof(*reasonNames));
- }
- if (dp.cRLIssuer.length) {
- CFMutableArrayRef crlIssuer = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("CRL Issuer"), crlIssuer);
- CFRelease(crlIssuer);
- appendGeneralNames(crlIssuer, &dp.cRLIssuer);
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Crl Distribution Points"),
- extnValue);
-}
-
-/* Decode a sequence of integers into a comma separated list of ints. */
-static void appendIntegerSequenceContent(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *intSequenceContent) {
- CFMutableStringRef value = NULL;
- CFStringRef intDesc = NULL;
- CFAllocatorRef allocator = CFGetAllocator(properties);
- DERSequence intSeq;
- DERReturn drtn = DERDecodeSeqContentInit(intSequenceContent, &intSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo intContent;
-
- while ((drtn = DERDecodeSeqNext(&intSeq, &intContent))
- == DR_Success) {
- require_quiet(intContent.tag == ASN1_INTEGER, badDER);
- intDesc = copyIntegerContentDescription(
- allocator, &intContent.content);
- require_quiet(intDesc, badDER);
- if (value) {
- CFStringAppendFormat(value, NULL, CFSTR(", %@"), intDesc);
- } else {
- value = CFStringCreateMutableCopy(allocator, 0, intDesc);
- require_quiet(value, badDER);
- }
- CFReleaseNull(intDesc);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- if (value) {
- appendPropertyP(properties, kSecPropertyTypeString,
- CFSTR("Notice Numbers"), value);
- CFRelease(value);
- return;
- }
- /* DROPTHOUGH if !value. */
-badDER:
- CFReleaseNull(value);
- CFReleaseNull(intDesc);
- appendInvalidProperty(properties, label, intSequenceContent);
-}
-
-static void appendCertificatePolicies(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- CFAllocatorRef allocator = CFGetAllocator(properties);
- CFStringRef piLabel = NULL, pqLabel = NULL;
- 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);
- require_quiet(piLabel = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("Policy Identifier #%d"), pin++), badDER);
- appendOIDProperty(properties, piLabel, &pi.policyIdentifier);
- CFReleaseNull(piLabel);
- 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);
- require_quiet(pqLabel = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("Policy Qualifier #%d"), pqn++), badDER);
- appendOIDProperty(properties, pqLabel, &pqi.policyQualifierID);
- CFReleaseNull(pqLabel);
- if (DEROidCompare(&oidQtCps, &pqi.policyQualifierID)) {
- require_quiet(qualifierContent.tag == ASN1_IA5_STRING, badDER);
- appendURLContentProperty(properties,
- CFSTR("CPS URI"),
- &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,
- CFSTR("Organization"),
- &nr.organization);
- appendIntegerSequenceContent(properties,
- CFSTR("Notice Numbers"), &nr.noticeNumbers);
- }
- if (un.explicitText.length) {
- appendDERThingProperty(properties, CFSTR("Explicit Text"),
- &un.explicitText);
- }
- } else {
- appendUnparsedProperty(properties, CFSTR("Qualifier"),
- &pqi.qualifier);
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- CFReleaseNull(piLabel);
- CFReleaseNull(pqLabel);
- appendInvalidProperty(properties, CFSTR("Certificate Policies"),
- 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, CFSTR("Key Identifier"),
- &keyIdentifier.content);
-
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Invalid Subject Key Identifier"),
- 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, CFSTR("Key Identifier"),
- &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,
- CFSTR("Authority Certificate Serial Number"),
- &akid.authorityCertSerialNumber);
- }
-
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Authority Key Identifier"),
- 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,
- CFSTR("Require Explicit Policy"), &pc.requireExplicitPolicy);
- }
- if (pc.inhibitPolicyMapping.length) {
- appendIntegerProperty(properties,
- CFSTR("Inhibit Policy Mapping"), &pc.inhibitPolicyMapping);
- }
-
- return;
-
-badDER:
- appendInvalidProperty(properties, CFSTR("Policy Constraints"), 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, CFSTR("Purpose"),
- &currDecoded.content);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Extended Key Usage"), 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, CFSTR("Access Method"),
- &ad.accessMethod);
- //CFSTR("Access Location");
- appendGeneralNameProperty(properties, &ad.accessLocation);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Authority Information Access"),
- extnValue);
-}
-
-static void appendNetscapeCertType(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- static const CFStringRef certTypes[] = {
- CFSTR("SSL client"),
- CFSTR("SSL server"),
- CFSTR("S/MIME"),
- CFSTR("Object Signing"),
- CFSTR("Reserved"),
- CFSTR("SSL CA"),
- CFSTR("S/MIME CA"),
- CFSTR("Object Signing CA")
- };
- appendBitStringNames(properties, CFSTR("Usage"), extnValue,
- certTypes, sizeof(certTypes) / sizeof(*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 appendPrintableDERSequenceP(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);
- require_quiet(string, badSequence);
-
- appendPropertyP(properties, kSecPropertyTypeString, label,
- string);
- CFReleaseNull(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, CFSTR("Critical"), extn->critical);
-
-#if 1
- bool handled = 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 */
- handled = 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 */
- handled = 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 */
- handled = false;
- break;
- case 54: /* InhibitAnyPolicy id-ce 54 */
- handled = false;
- break;
- default:
- handled = 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 */
- handled = false;
- break;
- case 11: /* SubjectInfoAccess id-pe 11 */
- appendInfoAccess(properties, extnValue);
- break;
- default:
- handled = false;
- break;
- }
- } else if (DEROidCompare(extnID, &oidNetscapeCertType)) {
- /* 2.16.840.1.113730.1.1 netscape 1 1 */
- appendNetscapeCertType(properties, extnValue);
- } else {
- handled = false;
- }
-
- if (!handled) {
- /* Try to parse and display printable string(s). */
- if (appendPrintableDERSequenceP(properties, CFSTR("Data"), extnValue)) {
- /* Nothing to do here appendPrintableDERSequenceP did the work. */
- } else {
- /* Couldn't parse extension; dump the raw unparsed data as hex. */
- appendUnparsedProperty(properties, CFSTR("Data"), 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 (appendPrintableDERSequenceP(properties, CFSTR("Data"), extnValue)) {
- /* Nothing to do here appendPrintableDERSequenceP did the work. */
- } else {
- /* Couldn't parse extension; dump the raw unparsed data as hex. */
- appendUnparsedProperty(properties, CFSTR("Data"), extnValue);
- }
-#endif
- CFStringRef oid_string = copyLocalizedOidDescription(allocator, extnID);
- appendPropertyP(parent, kSecPropertyTypeSection, oid_string, properties);
- CFRelease(oid_string);
- 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)) {
- /* We skip Common Names that have generic values. */
- const char tfm[] = "Thawte Freemail Member";
- if ((value->length == sizeof(tfm) + 1) &&
- !memcmp(value->data + 2, tfm, sizeof(tfm) - 1)) {
- return errSecSuccess;
- }
- stype = kSummaryTypeCommonName;
- } else if (DEROidCompare(type, &oidOrganizationalUnitName)) {
- stype = kSummaryTypeOrganizationalUnitName;
- } else if (DEROidCompare(type, &oidOrganizationName)) {
- stype = kSummaryTypeOrganizationName;
- } else if (DEROidCompare(type, &oidDescription)) {
- if (!summary->description) {
- summary->description = string = copyDERThingDescription(kCFAllocatorDefault, value, true);
- CFRetain(string);
- }
- stype = kSummaryTypePrintable;
- } else {
- stype = kSummaryTypePrintable;
- }
-
- /* Use the first field we encounter of the highest priority type. */
- if (summary->type < stype) {
- if (!string) {
- string = copyDERThingDescription(kCFAllocatorDefault, value, true);
- }
-
- if (string) {
- CFReleaseSafe(summary->summary);
- summary->summary = string;
- summary->type = stype;
- }
- } else {
- CFReleaseSafe(string);
- }
-
- return errSecSuccess;
-}
-
-CFStringRef SecCertificateCopySubjectSummaryP(SecCertificateRefP 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 newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%@ (%@)"), summary.summary, summary.description);
- 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 = SecCertificateCopyRFC822NamesP(certificate);
- if (!names) {
- /* If we didn't find any email addresses in the certificate, we try finding
- a DNS name instead. */
- names = SecCertificateCopyDNSNamesP(certificate);
- }
- if (names) {
- summary.summary = CFArrayGetValueAtIndex(names, 0);
- CFRetain(summary.summary);
- CFRelease(names);
- }
- }
-
- return summary.summary;
-}
-
-CFStringRef SecCertificateCopyIssuerSummaryP(SecCertificateRefP 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 newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%@ (%@)"), summary.summary, summary.description);
- 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(
- SecCertificateRefP 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(
- SecCertificateRefP certificate) {
- CFAbsoluteTime latest = certificate->_notBefore;
-#if 0
- while (certificate->_parent) {
- certificate = certificate->_parent;
- if (latest < certificate->_notBefore)
- latest = certificate->_notBefore;
- }
-#endif
-
- return latest;
-}
-
-bool SecCertificateIsValidP(SecCertificateRefP certificate,
- CFAbsoluteTime verifyTime) {
- check(certificate);
- return certificate->_notBefore <= verifyTime &&
- verifyTime <= certificate->_notAfter;
-}
-
-CFIndex SecCertificateVersionP(SecCertificateRefP certificate) {
- return certificate->_version + 1;
-}
-
-CFAbsoluteTime SecCertificateNotValidBeforeP(SecCertificateRefP certificate) {
- return certificate->_notBefore;
-}
-
-CFAbsoluteTime SecCertificateNotValidAfterP(SecCertificateRefP certificate) {
- return certificate->_notAfter;
-}
-
-CFMutableArrayRef SecCertificateCopySummaryPropertiesP(
- SecCertificateRefP certificate, CFAbsoluteTime verifyTime) {
- CFAllocatorRef allocator = CFGetAllocator(certificate);
- CFMutableArrayRef summary = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
-
- /* First we put the subject summary name. */
- CFStringRef ssummary = SecCertificateCopySubjectSummaryP(certificate);
- if (ssummary) {
- appendPropertyP(summary, kSecPropertyTypeTitle,
- NULL, ssummary);
- CFRelease(ssummary);
- }
-#if 0
- CFStringRef isummary = CFSTR("Issuer Summary");
- appendPropertyP(summary, kSecPropertyTypeString,
- CFSTR("Issued By"), 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 = CFSTR("Expired");
- when = certificate->_notAfter;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate has expired");
- } else if (certificate->_notBefore > verifyTime) {
- label = CFSTR("Valid from");
- when = certificate->_notBefore;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate is not yet valid");
- } else {
- CFAbsoluteTime last = SecCertificateGetChainsLastValidity(certificate);
- CFAbsoluteTime first = SecCertificateGetChainsFirstValidity(certificate);
- if (verifyTime > last) {
- label = CFSTR("Expired");
- when = last;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate has an issuer that has expired");
- } else if (verifyTime < first) {
- label = CFSTR("Valid from");
- when = first;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate has an issuer that is not yet valid");
- } else {
- label = CFSTR("Expires");
- when = certificate->_notAfter;
- ptype = kSecPropertyTypeSuccess;
- message = CFSTR("This certificate is valid");
- }
- }
-
- appendDateProperty(summary, label, when);
- appendPropertyP(summary, ptype, NULL, message);
-
- return summary;
-}
-
-CFArrayRef SecCertificateCopyPropertiesP(SecCertificateRefP certificate) {
- if (!certificate->_properties) {
- CFAllocatorRef allocator = CFGetAllocator(certificate);
- CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- require_quiet(properties, out);
-
- /* First we put the Subject Name in the property list. */
- CFArrayRef subject_plist = createPropertiesForX501NameContent(allocator,
- &certificate->_subject);
- if (subject_plist) {
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Subject Name"), subject_plist);
- }
- CFReleaseNull(subject_plist);
-
- /* Next we put the Issuer Name in the property list. */
- CFArrayRef issuer_plist = createPropertiesForX501NameContent(allocator,
- &certificate->_issuer);
- if (issuer_plist) {
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Issuer Name"), issuer_plist);
- }
- CFReleaseNull(issuer_plist);
-
- /* Version */
- CFStringRef versionString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"),
- certificate->_version + 1);
- if (versionString) {
- appendPropertyP(properties, kSecPropertyTypeString,
- CFSTR("Version"), versionString);
- }
- CFReleaseNull(versionString);
-
- /* Serial Number */
- if (certificate->_serialNum.length) {
- appendIntegerProperty(properties, CFSTR("Serial Number"),
- &certificate->_serialNum);
- }
-
- /* Signature algorithm. */
- appendAlgorithmProperty(properties, CFSTR("Signature Algorithm"),
- &certificate->_tbsSigAlg);
-
-
- /* Validity dates. */
- appendDateProperty(properties, CFSTR("Not Valid Before"),
- certificate->_notBefore);
- appendDateProperty(properties, CFSTR("Not Valid After"),
- certificate->_notAfter);
-
- if (certificate->_subjectUniqueID.length) {
- appendDataProperty(properties, CFSTR("Subject Unique ID"),
- &certificate->_subjectUniqueID);
- }
- if (certificate->_issuerUniqueID.length) {
- appendDataProperty(properties, CFSTR("Issuer Unique ID"),
- &certificate->_issuerUniqueID);
- }
-
- /* Public key algorithm. */
- appendAlgorithmProperty(properties, CFSTR("Public Key Algorithm"),
- &certificate->_algId);
-
- /* Consider breaking down an RSA public key into modulus and
- exponent? */
- appendDataProperty(properties, CFSTR("Public Key Data"),
- &certificate->_pubKeyDER);
- /* @@@ Key Size. */
- /* @@@ Key Usage. */
-
- appendDataProperty(properties, CFSTR("Signature"),
- &certificate->_signature);
-
- CFIndex ix;
- for (ix = 0; ix < certificate->_extensionCount; ++ix) {
- appendExtension(properties, &certificate->_extensions[ix]);
- }
-
- /* @@@ Key Fingerprints. */
-
- certificate->_properties = properties;
- }
-
-out:
- CFRetainSafe(certificate->_properties);
- return certificate->_properties;
-}
-
-CFDataRef SecCertificateCopySerialNumberP(
- SecCertificateRefP certificate) {
- if (certificate->_serialNumber) {
- CFRetain(certificate->_serialNumber);
- }
- return certificate->_serialNumber;
-}
-
-/*
- * Accessor for normalized issuer content
- */
-CFDataRef SecCertificateGetNormalizedIssuerContentP(
- SecCertificateRefP certificate) {
- return certificate->_normalizedIssuer;
-}
-
-/*
- * Accessor for normalized subject content
- */
-CFDataRef SecCertificateGetNormalizedSubjectContentP(
- SecCertificateRefP certificate) {
- return certificate->_normalizedSubject;
-}
-
-/*
- * Returns DER-encoded normalized issuer sequence
- * for use with SecItemCopyMatching; caller must release
- */
-CFDataRef SecCertificateCopyNormalizedIssuerSequenceP(
- SecCertificateRefP certificate) {
- if (!certificate || !certificate->_normalizedIssuer) {
- return NULL;
- }
- DERItem tmpdi;
- tmpdi.data = (DERByte *)CFDataGetBytePtr(certificate->_normalizedIssuer);
- tmpdi.length = CFDataGetLength(certificate->_normalizedIssuer);
-
- return SecDERItemCopySequenceP(&tmpdi);
-}
-
-/*
- * Returns DER-encoded normalized subject sequence
- * for use with SecItemCopyMatching; caller must release
- */
-CFDataRef SecCertificateCopyNormalizedSubjectSequenceP(
- SecCertificateRefP certificate) {
- if (!certificate || !certificate->_normalizedSubject) {
- return NULL;
- }
- DERItem tmpdi;
- tmpdi.data = (DERByte *)CFDataGetBytePtr(certificate->_normalizedSubject);
- tmpdi.length = CFDataGetLength(certificate->_normalizedSubject);
-
- return SecDERItemCopySequenceP(&tmpdi);
-}
-
-/* Verify that certificate was signed by issuerKey. */
-OSStatus SecCertificateIsSignedByP(SecCertificateRefP certificate,
- SecKeyRefP 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;
-
-#if 0
- OSStatus status = SecKeyDigestAndVerify(issuerKey, &algId,
- certificate->_tbs.data, certificate->_tbs.length,
- certificate->_signature.data, certificate->_signature.length);
- if (status) {
- secinfo("verify", "signature verify failed: %d", status);
- return errSecNotSigner;
- }
-#endif
-
- return errSecSuccess;
-}
-
-#if 0
-static OSStatus SecCertificateIsIssuedBy(SecCertificateRefP certificate,
- SecCertificateRefP 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 =
- SecCertificateGetNormalizedIssuerContentP(certificate);
- CFDataRef normalizedIssuerSubject =
- SecCertificateGetNormalizedSubjectContentP(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 {
- secinfo("verify", "unsupported algorithm");
- return errSecUnsupportedAlgorithm;
- }
- if (crtn) {
- secinfo("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) {
- secinfo("verify", "signature verify failed: %d", status);
- return errSecNotSigner;
- }
-
- return errSecSuccess;
-}
-
-static OSStatus _SecCertificateSetParent(SecCertificateRefP certificate,
- SecCertificateRefP 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;
- secinfo("cert", "set self as parent");
- return errSecSuccess;
- }
-
- CFRetain(issuer);
- certificate->_parent = issuer;
- certificate->_isSelfSigned = kSecSelfSignedFalse;
- }
-
- return status;
-}
-
-static bool SecCertificateIsSelfSignedP(SecCertificateRefP 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(SecCertificateRefP certificate,
- CFArrayRef other_certificates, bool signatureCheckOnly) {
- CFIndex count = CFArrayGetCount(other_certificates);
- CFIndex ix;
- for (ix = 0; ix < count; ++ix) {
- SecCertificateRefP candidate = (SecCertificateRefP)
- 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(SecCertificateRefP certificate) {
- /* FIXME: Search for things other than just subject of our issuer if we
- have a subjectID or authorityKeyIdentifier. */
- CFDataRef normalizedIssuer =
- SecCertificateGetNormalizedIssuerContentP(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) {
- secinfo("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 SecCertificateCompleteChainP(SecCertificateRefP certificate,
- CFArrayRef other_certificates) {
- for (;;) {
- if (certificate->_parent == NULL) {
- if (SecCertificateIsSelfSignedP(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 SecCertificateCopyIPAddressesP(SecCertificateRefP 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.
-
- <domain> ::= <subdomain> | " "
-
- <subdomain> ::= <label> | <subdomain> "." <label>
-
- <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-
- <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-
- <let-dig-hyp> ::= <let-dig> | "-"
-
- <let-dig> ::= <letter> | <digit>
-
- <letter> ::= any one of the 52 alphabetic characters A through Z in
- upper case and a through z in lower case
-
- <digit> ::= any one of the ten digits 0 through 9
- */
-static bool isDNSName(CFStringRef string) {
- CFStringInlineBuffer buf;
- CFIndex ix, labelLength = 0, length = CFStringGetLength(string);
- /* From RFC 1035 2.3.4. Size limits:
- labels 63 octets or less
- names 255 octets or less */
- require_quiet(length <= 255, notDNS);
- CFRange range = { 0, length };
- CFStringInitInlineBuffer(string, &buf, range);
- enum {
- kDNSStateInital,
- kDNSStateAfterDot,
- kDNSStateAfterAlpha,
- kDNSStateAfterDigit,
- kDNSStateAfterDash,
- } state = kDNSStateInital;
-
- bool nonAlpha = false;
- for (ix = 0; ix < length; ++ix) {
- UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, ix);
- labelLength++;
- if (ch == '.') {
- require_quiet(labelLength <= 64 &&
- (state == kDNSStateAfterAlpha || state == kDNSStateAfterDigit),
- notDNS);
- state = kDNSStateAfterDot;
- labelLength = 0;
- nonAlpha = false;
- } else if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') ||
- ch == '*') {
- state = kDNSStateAfterAlpha;
- } else if ('0' <= ch && ch <= '9') {
-#if 0
- /* The requirement for labels to start with a letter was
- dropped so we don't check this anymore. */
- require_quiet(state == kDNSStateAfterAlpha ||
- state == kDNSStateAfterDigit ||
- state == kDNSStateAfterDash, notDNS);
-#endif
- state = kDNSStateAfterDigit;
- nonAlpha = true;
- } else if (ch == '-') {
- require_quiet(state == kDNSStateAfterAlpha ||
- state == kDNSStateAfterDigit ||
- state == kDNSStateAfterDash, notDNS);
- state = kDNSStateAfterDash;
- nonAlpha = true;
- } else {
- goto notDNS;
- }
- }
-
- /* We don't allow a dns name to end in a dot, and we require the
- final name component to only have alphanumeric chars. */
- require_quiet(!nonAlpha && labelLength <= 63 &&
- (state == kDNSStateAfterAlpha || state == kDNSStateAfterDigit),
- notDNS);
-
- return true;
-notDNS:
- return false;
-}
-
-static OSStatus appendDNSNamesFromX501Name(void *context, const DERItem *type,
- const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidCommonName)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- if (isDNSName(string)) {
- /* We found a common name that is formatted like a valid
- dns name. */
- CFArrayAppendValue(dnsNames, string);
- }
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-/* Not everything returned by this function is going to be a proper DNS name,
- we also return the certificates common name entries from the subject,
- assuming they look like dns names as specified in RFC 1035. */
-CFArrayRef SecCertificateCopyDNSNamesP(SecCertificateRefP certificate) {
- /* These can exist in the subject alt name or in the subject. */
- CFMutableArrayRef dnsNames = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = errSecSuccess;
- if (certificate->_subjectAltName) {
- status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- dnsNames, appendDNSNamesFromGeneralNames);
- }
- /* RFC 2818 section 3.1. Server Identity
- [...]
- If a subjectAltName extension of type dNSName is present, that MUST
- be used as the identity. Otherwise, the (most specific) Common Name
- field in the Subject field of the certificate MUST be used. Although
- the use of the Common Name is existing practice, it is deprecated and
- Certification Authorities are encouraged to use the dNSName instead.
- [...]
-
- This implies that if we found 1 or more DNSNames in the
- subjectAltName, we should not use the Common Name of the subject as
- a DNSName.
- */
- if (!status && CFArrayGetCount(dnsNames) == 0) {
- status = parseX501NameContent(&certificate->_subject, dnsNames,
- appendDNSNamesFromX501Name);
- }
- if (status || CFArrayGetCount(dnsNames) == 0) {
- CFRelease(dnsNames);
- dnsNames = NULL;
- }
- return dnsNames;
-}
-
-static OSStatus appendRFC822NamesFromGeneralNames(void *context,
- SecCEGeneralNameType gnType, const DERItem *generalName) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (gnType == GNT_RFC822Name) {
- CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault,
- generalName->data, generalName->length,
- kCFStringEncodingASCII, FALSE);
- if (string) {
- CFArrayAppendValue(dnsNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-static OSStatus appendRFC822NamesFromX501Name(void *context, const DERItem *type,
- const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidEmailAddress)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- CFArrayAppendValue(dnsNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate) {
- /* These can exist in the subject alt name or in the subject. */
- CFMutableArrayRef rfc822Names = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = errSecSuccess;
- if (certificate->_subjectAltName) {
- status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- rfc822Names, appendRFC822NamesFromGeneralNames);
- }
- if (!status) {
- status = parseX501NameContent(&certificate->_subject, rfc822Names,
- appendRFC822NamesFromX501Name);
- }
- if (status || CFArrayGetCount(rfc822Names) == 0) {
- CFRelease(rfc822Names);
- rfc822Names = NULL;
- }
- return rfc822Names;
-}
-
-static OSStatus appendCommonNamesFromX501Name(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef commonNames = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidCommonName)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- CFArrayAppendValue(commonNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyCommonNamesP(SecCertificateRefP certificate) {
- CFMutableArrayRef commonNames = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status;
- status = parseX501NameContent(&certificate->_subject, commonNames,
- appendCommonNamesFromX501Name);
- if (status || CFArrayGetCount(commonNames) == 0) {
- CFRelease(commonNames);
- commonNames = NULL;
- }
- return commonNames;
-}
-
-static OSStatus appendOrganizationFromX501Name(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef organization = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidOrganizationName)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- CFArrayAppendValue(organization, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyOrganizationP(SecCertificateRefP certificate) {
- CFMutableArrayRef organization = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status;
- status = parseX501NameContent(&certificate->_subject, organization,
- appendOrganizationFromX501Name);
- if (status || CFArrayGetCount(organization) == 0) {
- CFRelease(organization);
- organization = NULL;
- }
- return organization;
-}
-
-const SecCEBasicConstraints *
-SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate) {
- if (certificate->_basicConstraints.present)
- return &certificate->_basicConstraints;
- else
- return NULL;
-}
-
-const SecCEPolicyConstraints *
-SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate) {
- if (certificate->_policyConstraints.present)
- return &certificate->_policyConstraints;
- else
- return NULL;
-}
-
-CFDictionaryRef
-SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate) {
- return certificate->_policyMappings;
-}
-
-const SecCECertificatePolicies *
-SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate) {
- if (certificate->_certificatePolicies.present)
- return &certificate->_certificatePolicies;
- else
- return NULL;
-}
-
-uint32_t
-SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate) {
- return certificate->_inhibitAnyPolicySkipCerts;
-}
-
-static OSStatus appendNTPrincipalNamesFromGeneralNames(void *context,
- SecCEGeneralNameType gnType, const DERItem *generalName) {
- CFMutableArrayRef ntPrincipalNames = (CFMutableArrayRef)context;
- if (gnType == GNT_OtherName) {
- DEROtherName on;
- DERReturn drtn = DERParseSequenceContent(generalName,
- DERNumOtherNameItemSpecs, DEROtherNameItemSpecs,
- &on, sizeof(on));
- require_noerr_quiet(drtn, badDER);
- if (DEROidCompare(&on.typeIdentifier, &oidMSNTPrincipalName)) {
- CFStringRef string;
- require_quiet(string = copyDERThingDescription(kCFAllocatorDefault,
- &on.value, true), badDER);
- CFArrayAppendValue(ntPrincipalNames, string);
- CFRelease(string);
- }
- }
- return errSecSuccess;
-
-badDER:
- return errSecInvalidCertificate;
-
-}
-
-CFArrayRef SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate) {
- CFMutableArrayRef ntPrincipalNames = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = errSecSuccess;
- if (certificate->_subjectAltName) {
- status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- ntPrincipalNames, appendNTPrincipalNamesFromGeneralNames);
- }
- if (status || CFArrayGetCount(ntPrincipalNames) == 0) {
- CFRelease(ntPrincipalNames);
- ntPrincipalNames = NULL;
- }
- return ntPrincipalNames;
-}
-
-static OSStatus appendToRFC2253String(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableStringRef string = (CFMutableStringRef)context;
- /*
- CN commonName
- L localityName
- ST stateOrProvinceName
- O organizationName
- OU organizationalUnitName
- C countryName
- STREET streetAddress
- DC domainComponent
- UID userid
- */
- /* Prepend a + if this is not the first RDN in an RDN set.
- Otherwise prepend a , if this is not the first RDN. */
- if (rdnIX > 0)
- CFStringAppend(string, CFSTR("+"));
- else if (CFStringGetLength(string)) {
- CFStringAppend(string, CFSTR(","));
- }
-
- CFStringRef label, oid = NULL;
- /* @@@ Consider changing this to a dictionary lookup keyed by the
- decimal representation. */
-#if 0 // represent all labels as oids
- if (DEROidCompare(type, &oidCommonName)) {
- label = CFSTR("CN");
- } else if (DEROidCompare(type, &oidLocalityName)) {
- label = CFSTR("L");
- } else if (DEROidCompare(type, &oidStateOrProvinceName)) {
- label = CFSTR("ST");
- } else if (DEROidCompare(type, &oidOrganizationName)) {
- label = CFSTR("O");
- } else if (DEROidCompare(type, &oidOrganizationalUnitName)) {
- label = CFSTR("OU");
- } else if (DEROidCompare(type, &oidCountryName)) {
- label = CFSTR("C");
-#if 0
- } else if (DEROidCompare(type, &oidStreetAddress)) {
- label = CFSTR("STREET");
- } else if (DEROidCompare(type, &oidDomainComponent)) {
- label = CFSTR("DC");
- } else if (DEROidCompare(type, &oidUserID)) {
- label = CFSTR("UID");
-#endif
- } else
-#endif
- {
- label = oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, type);
- }
-
- CFStringAppend(string, label);
- CFStringAppend(string, CFSTR("="));
- CFStringRef raw = NULL;
- if (!oid)
- raw = copyDERThingDescription(kCFAllocatorDefault, value, true);
-
- if (raw) {
- /* Append raw to string while escaping:
- a space or "#" character occurring at the beginning of the string
- a space character occurring at the end of the string
- one of the characters ",", "+", """, "\", "<", ">" or ";"
- */
- CFStringInlineBuffer buffer;
- CFIndex ix, length = CFStringGetLength(raw);
- CFRange range = { 0, length };
- CFStringInitInlineBuffer(raw, &buffer, range);
- for (ix = 0; ix < length; ++ix) {
- UniChar ch = CFStringGetCharacterFromInlineBuffer(&buffer, ix);
- if (ch < 0x20) {
- CFStringAppendFormat(string, NULL, CFSTR("\\%02X"), ch);
- } else if (ch == ',' || ch == '+' || ch == '"' || ch == '\\' ||
- ch == '<' || ch == '>' || ch == ';' ||
- (ch == ' ' && (ix == 0 || ix == length - 1)) ||
- (ch == '#' && ix == 0)) {
- UniChar chars[] = { '\\', ch };
- CFStringAppendCharacters(string, chars, 2);
- } else {
- CFStringAppendCharacters(string, &ch, 1);
- }
- }
- CFRelease(raw);
- } else {
- /* Append the value in hex. */
- CFStringAppend(string, CFSTR("#"));
- DERSize ix;
- for (ix = 0; ix < value->length; ++ix)
- CFStringAppendFormat(string, NULL, CFSTR("%02X"), value->data[ix]);
- }
-
- CFReleaseSafe(oid);
-
- return errSecSuccess;
-}
-
-CFStringRef SecCertificateCopySubjectStringP(SecCertificateRefP certificate) {
- CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0);
- OSStatus status = parseX501NameContent(&certificate->_subject, string, appendToRFC2253String);
- if (status || CFStringGetLength(string) == 0) {
- CFRelease(string);
- string = NULL;
- }
- return string;
-}
-
-static OSStatus appendToCompanyNameString(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableStringRef string = (CFMutableStringRef)context;
- if (CFStringGetLength(string) != 0)
- return errSecSuccess;
-
- if (!DEROidCompare(type, &oidOrganizationName))
- return errSecSuccess;
-
- CFStringRef raw;
- raw = copyDERThingDescription(kCFAllocatorDefault, value, true);
- if (!raw)
- return errSecSuccess;
- CFStringAppend(string, raw);
- CFRelease(raw);
-
- return errSecSuccess;
-}
-
-CFStringRef SecCertificateCopyCompanyNameP(SecCertificateRefP certificate) {
- CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0);
- OSStatus status = parseX501NameContent(&certificate->_subject, string,
- appendToCompanyNameString);
- if (status || CFStringGetLength(string) == 0) {
- CFRelease(string);
- string = NULL;
- }
- return string;
-}
-
-CFDataRef SecDERItemCopySequenceP(DERItem *content) {
- DERSize seq_len_length = DERLengthOfLength(content->length);
- size_t sequence_length = 1 + seq_len_length + content->length;
- CFMutableDataRef sequence = CFDataCreateMutable(kCFAllocatorDefault,
- sequence_length);
- CFDataSetLength(sequence, sequence_length);
- uint8_t *sequence_ptr = CFDataGetMutableBytePtr(sequence);
- *sequence_ptr++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE;
- require_noerr_quiet(DEREncodeLength(content->length,
- sequence_ptr, &seq_len_length), out);
- sequence_ptr += seq_len_length;
- memcpy(sequence_ptr, content->data, content->length);
- return sequence;
-out:
- CFReleaseSafe(sequence);
- return NULL;
-}
-
-CFDataRef SecCertificateCopyIssuerSequenceP(
- SecCertificateRefP certificate) {
- return SecDERItemCopySequenceP(&certificate->_issuer);
-}
-
-CFDataRef SecCertificateCopySubjectSequenceP(
- SecCertificateRefP certificate) {
- return SecDERItemCopySequenceP(&certificate->_subject);
-}
-
-const DERAlgorithmId *SecCertificateGetPublicKeyAlgorithmP(
- SecCertificateRefP certificate) {
- return &certificate->_algId;
-}
-
-const DERItem *SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate) {
- return &certificate->_pubKeyDER;
-}
-
-SecKeyRefP SecCertificateCopyPublicKeyP(SecCertificateRefP certificate) {
- SecKeyRefP publicKey = NULL;
-#if 0
- const DERAlgorithmId *algId =
- SecCertificateGetPublicKeyAlgorithmP(certificate);
- const DERItem *keyData = SecCertificateGetPublicKeyData(certificate);
- if (DEROidCompare(&algId->oid, &oidRsa)) {
- publicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault,
- keyData->data, keyData->length, kSecKeyEncodingPkcs1);
- } else {
- secinfo("cert", "Unsupported algorithm oid");
- }
-#endif
-
- return publicKey;
-}
-
-CFDataRef SecCertificateGetSHA1DigestP(SecCertificateRefP certificate) {
- if (!certificate->_sha1Digest) {
- certificate->_sha1Digest =
- SecSHA1DigestCreate(CFGetAllocator(certificate),
- certificate->_der.data, certificate->_der.length);
- }
-
- return certificate->_sha1Digest;
-}
-
-CFDataRef SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate) {
- CFDataRef digest = NULL;
- CFDataRef issuer = SecCertificateCopyIssuerSequenceP(certificate);
- if (issuer) {
- digest = SecSHA1DigestCreate(kCFAllocatorDefault,
- CFDataGetBytePtr(issuer), CFDataGetLength(issuer));
- CFRelease(issuer);
- }
- return digest;
-}
-
-CFDataRef SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate) {
- return SecSHA1DigestCreate(CFGetAllocator(certificate),
- certificate->_pubKeyDER.data, certificate->_pubKeyDER.length);
-}
-
-/* note: this function is exported with a non-P-suffix name.
- * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
- */
-CFDataRef SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator,
- CFDataRef der_certificate)
-{
- CFDataRef result = NULL;
- SecCertificateRefP iosCertRef = SecCertificateCreateWithDataP(allocator, der_certificate);
- if (NULL == iosCertRef)
- {
- return result;
- }
-
- result = SecCertificateCopyPublicKeySHA1DigestP(iosCertRef);
- CFRelease(iosCertRef);
- return result;
-}
-
-CFDataRef SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate) {
- if (!certificate->_authorityKeyID &&
- certificate->_authorityKeyIdentifier.length) {
- certificate->_authorityKeyID = CFDataCreate(kCFAllocatorDefault,
- certificate->_authorityKeyIdentifier.data,
- certificate->_authorityKeyIdentifier.length);
- }
-
- return certificate->_authorityKeyID;
-}
-
-CFDataRef SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate) {
- if (!certificate->_subjectKeyID &&
- certificate->_subjectKeyIdentifier.length) {
- certificate->_subjectKeyID = CFDataCreate(kCFAllocatorDefault,
- certificate->_subjectKeyIdentifier.data,
- certificate->_subjectKeyIdentifier.length);
- }
-
- return certificate->_subjectKeyID;
-}
-
-CFArrayRef SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate) {
- return certificate->_crlDistributionPoints;
-}
-
-CFArrayRef SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate) {
- return certificate->_ocspResponders;
-}
-
-CFArrayRef SecCertificateGetCAIssuersP(SecCertificateRefP certificate) {
- return certificate->_caIssuers;
-}
-
-bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate) {
- return certificate->_subjectAltName &&
- certificate->_subjectAltName->critical;
-}
-
-bool SecCertificateHasSubjectP(SecCertificateRefP certificate) {
- /* Since the _subject field is the content of the subject and not the
- whole thing, we can simply check for a 0 length subject here. */
- return certificate->_subject.length != 0;
-}
-
-bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate) {
- return certificate->_foundUnknownCriticalExtension;
-}
-
-/* Private API functions. */
-void SecCertificateShowP(SecCertificateRefP certificate) {
- check(certificate);
- fprintf(stderr, "SecCertificate instance %p:\n", certificate);
- fprintf(stderr, "\n");
-}
-
-CFDictionaryRef SecCertificateCopyAttributeDictionaryP(
- SecCertificateRefP certificate) {
- CFAllocatorRef allocator = CFGetAllocator(certificate);
- CFNumberRef certificateType, certificateEncoding;
- CFStringRef label, alias;
- CFDataRef skid, pubKeyDigest, certData;
- CFDictionaryRef dict = NULL;
-
- DICT_DECLARE(11);
-
- /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
- SInt32 ctv = certificate->_version + 1;
- SInt32 cev = 3; /* CSSM_CERT_ENCODING_DER */
- certificateType = CFNumberCreate(allocator, kCFNumberSInt32Type, &ctv);
- certificateEncoding = CFNumberCreate(allocator, kCFNumberSInt32Type, &cev);
- certData = SecCertificateCopyDataP(certificate);
- skid = SecCertificateGetSubjectKeyIDP(certificate);
- pubKeyDigest = SecSHA1DigestCreate(allocator, certificate->_pubKeyDER.data,
- certificate->_pubKeyDER.length);
-#if 0
- /* We still need to figure out how to deal with multi valued attributes. */
- alias = SecCertificateCopyRFC822NamesP(certificate);
- label = SecCertificateCopySubjectSummary(certificate);
-#else
- alias = NULL;
- label = NULL;
-#endif
-
- DICT_ADDPAIR(kSecClass, kSecClassCertificate);
- DICT_ADDPAIR(kSecAttrCertificateType, certificateType);
- DICT_ADDPAIR(kSecAttrCertificateEncoding, certificateEncoding);
- if (label)
- DICT_ADDPAIR(kSecAttrLabel, label);
- if (alias)
- DICT_ADDPAIR(kSecAttrAlias, alias);
- DICT_ADDPAIR(kSecAttrSubject, certificate->_normalizedSubject);
- DICT_ADDPAIR(kSecAttrIssuer, certificate->_normalizedIssuer);
- DICT_ADDPAIR(kSecAttrSerialNumber, certificate->_serialNumber);
- if (skid)
- DICT_ADDPAIR(kSecAttrSubjectKeyID, skid);
- DICT_ADDPAIR(kSecAttrPublicKeyHash, pubKeyDigest);
- DICT_ADDPAIR(kSecValueData, certData);
- dict = DICT_CREATE(allocator);
-
- CFReleaseSafe(label);
- CFReleaseSafe(pubKeyDigest);
- CFReleaseSafe(certData);
- CFReleaseSafe(certificateEncoding);
- CFReleaseSafe(certificateType);
-
- return dict;
-}
-
-SecCertificateRefP SecCertificateCreateFromAttributeDictionaryP(
- CFDictionaryRef refAttributes) {
- /* @@@ Support having an allocator in refAttributes. */
- CFAllocatorRef allocator = NULL;
- CFDataRef data = CFDictionaryGetValue(refAttributes, kSecValueData);
- return SecCertificateCreateWithDataP(allocator, data);
-}
-
-bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate) {
- bool result = false;
- SecKeyRefP publicKey;
- require(publicKey = SecCertificateCopyPublicKeyP(certificate), out);
- CFDataRef normalizedIssuer =
- SecCertificateGetNormalizedIssuerContentP(certificate);
- CFDataRef normalizedSubject =
- SecCertificateGetNormalizedSubjectContentP(certificate);
- require_quiet(normalizedIssuer && normalizedSubject &&
- CFEqual(normalizedIssuer, normalizedSubject), out);
-
- CFDataRef authorityKeyID = SecCertificateGetAuthorityKeyIDP(certificate);
- CFDataRef subjectKeyID = SecCertificateGetSubjectKeyIDP(certificate);
- if (authorityKeyID) {
- require_quiet(subjectKeyID && CFEqual(subjectKeyID, authorityKeyID), out);
- }
-
- if (SecCertificateVersionP(certificate) >= 3) {
- const SecCEBasicConstraints *basicConstraints = SecCertificateGetBasicConstraintsP(certificate);
- require_quiet(basicConstraints && basicConstraints->isCA, out);
- require_noerr_quiet(SecCertificateIsSignedByP(certificate, publicKey), out);
- }
-
- result = true;
-out:
- CFReleaseSafe(publicKey);
- return result;
-}
-
-SecKeyUsage SecCertificateGetKeyUsageP(SecCertificateRefP certificate) {
- return certificate->_keyUsage;
-}
-
-CFArrayRef SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate)
-{
- CFMutableArrayRef extended_key_usage_oids =
- CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- require_quiet(extended_key_usage_oids, out);
- int ix;
- for (ix = 0; ix < certificate->_extensionCount; ++ix) {
- const SecCertificateExtension *extn = &certificate->_extensions[ix];
- if (extn->extnID.length == oidExtendedKeyUsage.length &&
- !memcmp(extn->extnID.data, oidExtendedKeyUsage.data, extn->extnID.length)) {
- DERTag tag;
- DERSequence derSeq;
- DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &derSeq);
- require_noerr_quiet(drtn, out);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, out);
- DERDecodedInfo currDecoded;
-
- while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) {
- require_quiet(currDecoded.tag == ASN1_OBJECT_ID, out);
- CFDataRef oid = CFDataCreate(kCFAllocatorDefault,
- currDecoded.content.data, currDecoded.content.length);
- require_quiet(oid, out);
- CFArrayAppendValue(extended_key_usage_oids, oid);
- CFReleaseNull(oid);
- }
- require_quiet(drtn == DR_EndOfSequence, out);
- return extended_key_usage_oids;
- }
- }
-out:
- CFReleaseSafe(extended_key_usage_oids);
- return NULL;
-}
-
-SecCertificateRefP SecCertificateCreateWithPEMP(CFAllocatorRef allocator,
- CFDataRef pem_certificate)
-{
- static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\n";
- static const char end_cert[] = "-----END CERTIFICATE-----\n";
- uint8_t *base64_data = NULL;
- SecCertificateRefP cert = NULL;
- const unsigned char *data = CFDataGetBytePtr(pem_certificate);
- //const size_t length = CFDataGetLength(pem_certificate);
- char *begin = strstr((const char *)data, begin_cert);
- char *end = strstr((const char *)data, end_cert);
- if (!begin || !end)
- return NULL;
- begin += sizeof(begin_cert) - 1;
- size_t base64_length = SecBase64Decode(begin, end - begin, NULL, 0);
- if (base64_length) {
- require_quiet(base64_data = calloc(1, base64_length), out);
- require_quiet(base64_length = SecBase64Decode(begin, end - begin, base64_data, base64_length), out);
- cert = SecCertificateCreateWithBytesP(kCFAllocatorDefault, base64_data, base64_length);
- free(base64_data);
- }
-out:
- return cert;
-}
-
-static void convertCertificateToCFData(const void *value, void *context) {
- CFMutableArrayRef result = (CFMutableArrayRef)context;
- SecCertificateRefP certificate = (SecCertificateRefP)value;
- CFDataRef data = SecCertificateCopyDataP(certificate);
- CFArrayAppendValue(result, data);
- CFRelease(data);
-}
-
-/* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
-CFArrayRef SecCertificateArrayCopyDataArrayP(CFArrayRef certificates) {
- CFIndex count = CFArrayGetCount(certificates);
- CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
- CFRange all_certs = { 0, count };
- CFArrayApplyFunction(certificates, all_certs, convertCertificateToCFData, result);
- return result;
-}
-
-/* AUDIT[securityd](done):
- value (ok) is an element in a caller provided array.
- */
-static void convertCFDataToCertificate(const void *value, void *context) {
- CFMutableArrayRef result = (CFMutableArrayRef)context;
- CFDataRef data = (CFDataRef)value;
- if (data && CFGetTypeID(data) == CFDataGetTypeID()) {
- SecCertificateRefP certificate = SecCertificateCreateWithDataP(kCFAllocatorDefault, data);
- if (certificate) {
- CFArrayAppendValue(result, certificate);
- CFRelease(certificate);
- }
- }
-}
-
-/* AUDIT[securityd](done):
- certificates (ok) is a caller provided array, only its cf type has
- been checked.
- */
-CFArrayRef SecCertificateDataArrayCopyArrayP(CFArrayRef certificates) {
- CFIndex count = CFArrayGetCount(certificates);
- CFMutableArrayRef result = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
- CFRange all_certs = { 0, count };
- CFArrayApplyFunction(certificates, all_certs, convertCFDataToCertificate, result);
- return result;
-}