// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/x509_certificate.h"

#include <CommonCrypto/CommonDigest.h>
#include <CoreServices/CoreServices.h>
#include <Security/Security.h>
#include <time.h>

#include <vector>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/memory/singleton.h"
#include "base/pickle.h"
#include "base/sha1.h"
#include "base/sys_string_conversions.h"
#include "crypto/cssm_init.h"
#include "crypto/nss_util.h"
#include "crypto/rsa_private_key.h"
#include "net/base/asn1_util.h"
#include "net/base/cert_status_flags.h"
#include "net/base/cert_verify_result.h"
#include "net/base/net_errors.h"
#include "net/base/test_root_certs.h"
#include "net/base/x509_certificate_known_roots_mac.h"
#include "third_party/apple_apsl/cssmapplePriv.h"
#include "third_party/nss/mozilla/security/nss/lib/certdb/cert.h"

using base::mac::ScopedCFTypeRef;
using base::Time;

namespace net {

namespace {

typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
                                                      CFDictionaryRef*);

int NetErrorFromOSStatus(OSStatus status) {
  switch (status) {
    case noErr:
      return OK;
    case errSecNotAvailable:
    case errSecNoCertificateModule:
    case errSecNoPolicyModule:
      return ERR_NOT_IMPLEMENTED;
    case errSecAuthFailed:
      return ERR_ACCESS_DENIED;
    default:
      LOG(ERROR) << "Unknown error " << status << " mapped to ERR_FAILED";
      return ERR_FAILED;
  }
}

int CertStatusFromOSStatus(OSStatus status) {
  switch (status) {
    case noErr:
      return 0;

    case CSSMERR_TP_INVALID_ANCHOR_CERT:
    case CSSMERR_TP_NOT_TRUSTED:
    case CSSMERR_TP_INVALID_CERT_AUTHORITY:
      return CERT_STATUS_AUTHORITY_INVALID;

    case CSSMERR_TP_CERT_EXPIRED:
    case CSSMERR_TP_CERT_NOT_VALID_YET:
      // "Expired" and "not yet valid" collapse into a single status.
      return CERT_STATUS_DATE_INVALID;

    case CSSMERR_TP_CERT_REVOKED:
    case CSSMERR_TP_CERT_SUSPENDED:
      return CERT_STATUS_REVOKED;

    case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
      return CERT_STATUS_COMMON_NAME_INVALID;

    case CSSMERR_APPLETP_CRL_NOT_FOUND:
    case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
    case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
      return CERT_STATUS_NO_REVOCATION_MECHANISM;

    case CSSMERR_APPLETP_CRL_EXPIRED:
    case CSSMERR_APPLETP_CRL_NOT_VALID_YET:
    case CSSMERR_APPLETP_CRL_SERVER_DOWN:
    case CSSMERR_APPLETP_CRL_NOT_TRUSTED:
    case CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT:
    case CSSMERR_APPLETP_CRL_POLICY_FAIL:
    case CSSMERR_APPLETP_OCSP_BAD_RESPONSE:
    case CSSMERR_APPLETP_OCSP_BAD_REQUEST:
    case CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED:
    case CSSMERR_APPLETP_NETWORK_FAILURE:
    case CSSMERR_APPLETP_OCSP_NOT_TRUSTED:
    case CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT:
    case CSSMERR_APPLETP_OCSP_SIG_ERROR:
    case CSSMERR_APPLETP_OCSP_NO_SIGNER:
    case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ:
    case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR:
    case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER:
    case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED:
    case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED:
    case CSSMERR_APPLETP_OCSP_NONCE_MISMATCH:
      // We asked for a revocation check, but didn't get it.
      return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;

    case CSSMERR_APPLETP_CRL_BAD_URI:
    case CSSMERR_APPLETP_IDP_FAIL:
      return CERT_STATUS_INVALID;

    default:
      // Failure was due to something Chromium doesn't define a
      // specific status for (such as basic constraints violation, or
      // unknown critical extension)
      LOG(WARNING) << "Unknown error " << status
                   << " mapped to CERT_STATUS_INVALID";
      return CERT_STATUS_INVALID;
  }
}

bool OverrideHostnameMismatch(const std::string& hostname,
                              std::vector<std::string>* dns_names) {
  // SecTrustEvaluate() does not check dotted IP addresses. If
  // hostname is provided as, say, 127.0.0.1, then the error
  // CSSMERR_APPLETP_HOSTNAME_MISMATCH will always be returned,
  // even if the certificate contains 127.0.0.1 as one of its names.
  // We, however, want to allow that behavior. SecTrustEvaluate()
  // only checks for digits and dots when considering whether a
  // hostname is an IP address, so IPv6 and hex addresses go through
  // its normal comparison.
  bool is_dotted_ip = true;
  bool override_hostname_mismatch = false;
  for (std::string::const_iterator c = hostname.begin();
       c != hostname.end() && is_dotted_ip; ++c)
    is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.';
  if (is_dotted_ip) {
    for (std::vector<std::string>::const_iterator name = dns_names->begin();
         name != dns_names->end() && !override_hostname_mismatch; ++name)
      override_hostname_mismatch = (*name == hostname);
  }
  return override_hostname_mismatch;
}

struct CSSMFields {
  CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
  ~CSSMFields() {
    if (cl_handle)
      CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields);
  }

  CSSM_CL_HANDLE cl_handle;
  uint32 num_of_fields;
  CSSM_FIELD_PTR fields;
};

OSStatus GetCertFields(X509Certificate::OSCertHandle cert_handle,
                       CSSMFields* fields) {
  DCHECK(cert_handle);
  DCHECK(fields);

  CSSM_DATA cert_data;
  OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
  if (status)
    return status;

  status = SecCertificateGetCLHandle(cert_handle, &fields->cl_handle);
  if (status) {
    DCHECK(!fields->cl_handle);
    return status;
  }

  status = CSSM_CL_CertGetAllFields(fields->cl_handle, &cert_data,
                                    &fields->num_of_fields, &fields->fields);
  return status;
}

void GetCertGeneralNamesForOID(X509Certificate::OSCertHandle cert_handle,
                               CSSM_OID oid, CE_GeneralNameType name_type,
                               std::vector<std::string>* result) {
  // For future extension: We only support general names of types
  // GNT_RFC822Name, GNT_DNSName or GNT_URI.
  DCHECK(name_type == GNT_RFC822Name ||
         name_type == GNT_DNSName ||
         name_type == GNT_URI);

  CSSMFields fields;
  OSStatus status = GetCertFields(cert_handle, &fields);
  if (status)
    return;

  for (size_t field = 0; field < fields.num_of_fields; ++field) {
    if (CSSMOIDEqual(&fields.fields[field].FieldOid, &oid)) {
      CSSM_X509_EXTENSION_PTR cssm_ext =
          reinterpret_cast<CSSM_X509_EXTENSION_PTR>(
              fields.fields[field].FieldValue.Data);
      CE_GeneralNames* alt_name =
          reinterpret_cast<CE_GeneralNames*>(cssm_ext->value.parsedValue);

      for (size_t name = 0; name < alt_name->numNames; ++name) {
        const CE_GeneralName& name_struct = alt_name->generalName[name];
        // All of the general name types we support are encoded as
        // IA5String. In general, we should be switching off
        // |name_struct.nameType| and doing type-appropriate conversions. See
        // certextensions.h and the comment immediately preceding
        // CE_GeneralNameType for more information.
        if (name_struct.nameType == name_type) {
          const CSSM_DATA& name_data = name_struct.name;
          std::string value = std::string(
              reinterpret_cast<const char*>(name_data.Data),
              name_data.Length);
          result->push_back(value);
        }
      }
    }
  }
}

void GetCertDateForOID(X509Certificate::OSCertHandle cert_handle,
                       CSSM_OID oid, Time* result) {
  *result = Time::Time();

  CSSMFields fields;
  OSStatus status = GetCertFields(cert_handle, &fields);
  if (status)
    return;

  for (size_t field = 0; field < fields.num_of_fields; ++field) {
    if (CSSMOIDEqual(&fields.fields[field].FieldOid, &oid)) {
      CSSM_X509_TIME* x509_time = reinterpret_cast<CSSM_X509_TIME*>(
          fields.fields[field].FieldValue.Data);
      if (x509_time->timeType != BER_TAG_UTC_TIME &&
          x509_time->timeType != BER_TAG_GENERALIZED_TIME) {
        LOG(ERROR) << "Unsupported date/time format "
                   << x509_time->timeType;
        return;
      }

      base::StringPiece time_string(
          reinterpret_cast<const char*>(x509_time->time.Data),
          x509_time->time.Length);
      CertDateFormat format = x509_time->timeType == BER_TAG_UTC_TIME ?
          CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
      if (!ParseCertificateDate(time_string, format, result))
        LOG(ERROR) << "Invalid certificate date/time " << time_string;
      return;
    }
  }
}

std::string GetCertSerialNumber(X509Certificate::OSCertHandle cert_handle) {
  CSSMFields fields;
  OSStatus status = GetCertFields(cert_handle, &fields);
  if (status)
    return "";

  std::string ret;
  for (size_t field = 0; field < fields.num_of_fields; ++field) {
    if (!CSSMOIDEqual(&fields.fields[field].FieldOid,
                      &CSSMOID_X509V1SerialNumber)) {
      continue;
    }
    ret.assign(
        reinterpret_cast<char*>(fields.fields[field].FieldValue.Data),
        fields.fields[field].FieldValue.Length);
    break;
  }

  // Remove leading zeros.
  while (ret.size() > 1 && ret[0] == 0)
    ret = ret.substr(1, ret.size() - 1);

  return ret;
}

// Creates a SecPolicyRef for the given OID, with optional value.
OSStatus CreatePolicy(const CSSM_OID* policy_OID,
                      void* option_data,
                      size_t option_length,
                      SecPolicyRef* policy) {
  SecPolicySearchRef search;
  OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL,
                                       &search);
  if (err)
    return err;
  err = SecPolicySearchCopyNext(search, policy);
  CFRelease(search);
  if (err)
    return err;

  if (option_data) {
    CSSM_DATA options_data = {
      option_length,
      reinterpret_cast<uint8_t*>(option_data)
    };
    err = SecPolicySetValue(*policy, &options_data);
    if (err) {
      CFRelease(*policy);
      return err;
    }
  }
  return noErr;
}

// Creates a series of SecPolicyRefs to be added to a SecTrustRef used to
// validate a certificate for an SSL server. |hostname| contains the name of
// the SSL server that the certificate should be verified against. |flags| is
// a bitwise-OR of VerifyFlags that can further alter how trust is
// validated, such as how revocation is checked. If successful, returns
// noErr, and stores the resultant array of SecPolicyRefs in |policies|.
OSStatus CreateTrustPolicies(const std::string& hostname, int flags,
                             ScopedCFTypeRef<CFArrayRef>* policies) {
  ScopedCFTypeRef<CFMutableArrayRef> local_policies(
      CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
  if (!local_policies)
    return memFullErr;

  // Create an SSL SecPolicyRef, and configure it to perform hostname
  // validation. The hostname check does 99% of what we want, with the
  // exception of dotted IPv4 addreses, which we handle ourselves below.
  SecPolicyRef ssl_policy;
  OSStatus status = X509Certificate::CreateSSLServerPolicy(hostname,
                                                           &ssl_policy);
  if (status)
    return status;
  CFArrayAppendValue(local_policies, ssl_policy);
  CFRelease(ssl_policy);

  // Explicitly add revocation policies, in order to override system
  // revocation checking policies and instead respect the application-level
  // revocation preference.
  status = X509Certificate::CreateRevocationPolicies(
      (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED),
      local_policies);
  if (status)
    return status;

  policies->reset(local_policies.release());
  return noErr;
}

// Gets the issuer for a given cert, starting with the cert itself and
// including the intermediate and finally root certificates (if any).
// This function calls SecTrust but doesn't actually pay attention to the trust
// result: it shouldn't be used to determine trust, just to traverse the chain.
// Caller is responsible for releasing the value stored into *out_cert_chain.
OSStatus CopyCertChain(SecCertificateRef cert_handle,
                       CFArrayRef* out_cert_chain) {
  DCHECK(cert_handle);
  DCHECK(out_cert_chain);
  // Create an SSL policy ref configured for client cert evaluation.
  SecPolicyRef ssl_policy;
  OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy);
  if (result)
    return result;
  ScopedCFTypeRef<SecPolicyRef> scoped_ssl_policy(ssl_policy);

  // Create a SecTrustRef.
  ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate(
      NULL, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)),
      1, &kCFTypeArrayCallBacks));
  SecTrustRef trust_ref = NULL;
  result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref);
  if (result)
    return result;
  ScopedCFTypeRef<SecTrustRef> trust(trust_ref);

  // Evaluate trust, which creates the cert chain.
  SecTrustResultType status;
  CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
  result = SecTrustEvaluate(trust, &status);
  if (result)
    return result;
  return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
}

// Returns true if |purpose| is listed as allowed in |usage|. This
// function also considers the "Any" purpose. If the attribute is
// present and empty, we return false.
bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage,
                            const CSSM_OID* purpose) {
  for (unsigned p = 0; p < usage->numPurposes; ++p) {
    if (CSSMOIDEqual(&usage->purposes[p], purpose))
      return true;
    if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ExtendedKeyUsageAny))
      return true;
  }
  return false;
}

// Test that a given |cert_handle| is actually a valid X.509 certificate, and
// return true if it is.
//
// On OS X, SecCertificateCreateFromData() does not return any errors if
// called with invalid data, as long as data is present. The actual decoding
// of the certificate does not happen until an API that requires a CSSM
// handle is called. While SecCertificateGetCLHandle is the most likely
// candidate, as it performs the parsing, it does not check whether the
// parsing was actually successful. Instead, SecCertificateGetSubject is
// used (supported since 10.3), as a means to check that the certificate
// parsed as a valid X.509 certificate.
bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
  const CSSM_X509_NAME* sanity_check = NULL;
  OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check);
  return status == noErr && sanity_check;
}

// Parses |data| of length |length|, attempting to decode it as the specified
// |format|. If |data| is in the specified format, any certificates contained
// within are stored into |output|.
void AddCertificatesFromBytes(const char* data, size_t length,
                              SecExternalFormat format,
                              X509Certificate::OSCertHandles* output) {
  SecExternalFormat input_format = format;
  ScopedCFTypeRef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy(
      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), length,
      kCFAllocatorNull));

  CFArrayRef items = NULL;
  OSStatus status = SecKeychainItemImport(local_data, NULL, &input_format,
                                          NULL, 0, NULL, NULL, &items);
  if (status) {
    DLOG(WARNING) << status << " Unable to import items from data of length "
                  << length;
    return;
  }

  ScopedCFTypeRef<CFArrayRef> scoped_items(items);
  CFTypeID cert_type_id = SecCertificateGetTypeID();

  for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) {
    SecKeychainItemRef item = reinterpret_cast<SecKeychainItemRef>(
        const_cast<void*>(CFArrayGetValueAtIndex(items, i)));

    // While inputFormat implies only certificates will be imported, if/when
    // other formats (eg: PKCS#12) are supported, this may also include
    // private keys or other items types, so filter appropriately.
    if (CFGetTypeID(item) == cert_type_id) {
      SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(item);
      // OS X ignores |input_format| if it detects that |local_data| is PEM
      // encoded, attempting to decode data based on internal rules for PEM
      // block headers. If a PKCS#7 blob is encoded with a PEM block of
      // CERTIFICATE, OS X 10.5 will return a single, invalid certificate
      // based on the decoded data. If this happens, the certificate should
      // not be included in |output|. Because |output| is empty,
      // CreateCertificateListfromBytes will use PEMTokenizer to decode the
      // data. When called again with the decoded data, OS X will honor
      // |input_format|, causing decode to succeed. On OS X 10.6, the data
      // is properly decoded as a PKCS#7, whether PEM or not, which avoids
      // the need to fallback to internal decoding.
      if (IsValidOSCertHandle(cert)) {
        CFRetain(cert);
        output->push_back(cert);
      }
    }
  }
}

struct CSSMOIDString {
  const CSSM_OID* oid_;
  std::string string_;
};

typedef std::vector<CSSMOIDString> CSSMOIDStringVector;

bool CERTNameToCSSMOIDVector(CERTName* name, CSSMOIDStringVector* out_values) {
  struct OIDCSSMMap {
    SECOidTag sec_OID_;
    const CSSM_OID* cssm_OID_;
  };

  const OIDCSSMMap kOIDs[] = {
      { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName },
      { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName },
      { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName },
      { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName },
      { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress },
      { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName },
      { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName },
      { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier },
      { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier },
      { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress },
  };

  CERTRDN** rdns = name->rdns;
  for (size_t rdn = 0; rdns[rdn]; ++rdn) {
    CERTAVA** avas = rdns[rdn]->avas;
    for (size_t pair = 0; avas[pair] != 0; ++pair) {
      SECOidTag tag = CERT_GetAVATag(avas[pair]);
      if (tag == SEC_OID_UNKNOWN) {
        return false;
      }
      CSSMOIDString oidString;
      bool found_oid = false;
      for (size_t oid = 0; oid < ARRAYSIZE_UNSAFE(kOIDs); ++oid) {
        if (kOIDs[oid].sec_OID_ == tag) {
          SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
          if (!decode_item)
            return false;

          // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
          std::string value(reinterpret_cast<char*>(decode_item->data),
                            decode_item->len);
          oidString.oid_ = kOIDs[oid].cssm_OID_;
          oidString.string_ = value;
          out_values->push_back(oidString);
          SECITEM_FreeItem(decode_item, PR_TRUE);
          found_oid = true;
          break;
        }
      }
      if (!found_oid) {
        DLOG(ERROR) << "Unrecognized OID: " << tag;
      }
    }
  }
  return true;
}

class ScopedCertName {
 public:
  explicit ScopedCertName(CERTName* name) : name_(name) { }
  ~ScopedCertName() {
    if (name_) CERT_DestroyName(name_);
  }
  operator CERTName*() { return name_; }

 private:
  CERTName* name_;
};

class ScopedEncodedCertResults {
 public:
  explicit ScopedEncodedCertResults(CSSM_TP_RESULT_SET* results)
      : results_(results) { }
  ~ScopedEncodedCertResults() {
    if (results_) {
      CSSM_ENCODED_CERT* encCert =
          reinterpret_cast<CSSM_ENCODED_CERT*>(results_->Results);
      for (uint32 i = 0; i < results_->NumberOfResults; i++) {
        crypto::CSSMFree(encCert[i].CertBlob.Data);
      }
    }
    crypto::CSSMFree(results_->Results);
    crypto::CSSMFree(results_);
  }

private:
  CSSM_TP_RESULT_SET* results_;
};

void AppendPublicKeyHashes(CFArrayRef chain,
                           std::vector<SHA1Fingerprint>* hashes) {
  const CFIndex n = CFArrayGetCount(chain);
  for (CFIndex i = 0; i < n; i++) {
    SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
        const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));

    CSSM_DATA cert_data;
    OSStatus err = SecCertificateGetData(cert, &cert_data);
    DCHECK_EQ(err, noErr);
    base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
                               cert_data.Length);
    base::StringPiece spki_bytes;
    if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
      continue;

    SHA1Fingerprint hash;
    CC_SHA1(spki_bytes.data(), spki_bytes.size(), hash.data);
    hashes->push_back(hash);
  }
}

}  // namespace

void X509Certificate::Initialize() {
  const CSSM_X509_NAME* name;
  OSStatus status = SecCertificateGetSubject(cert_handle_, &name);
  if (!status)
    subject_.Parse(name);

  status = SecCertificateGetIssuer(cert_handle_, &name);
  if (!status)
    issuer_.Parse(name);

  GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore,
                    &valid_start_);
  GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter,
                    &valid_expiry_);

  fingerprint_ = CalculateFingerprint(cert_handle_);
  serial_number_ = GetCertSerialNumber(cert_handle_);
}

// IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
// that we recognise as a standard root.
// static
bool X509Certificate::IsIssuedByKnownRoot(CFArrayRef chain) {
  int n = CFArrayGetCount(chain);
  if (n < 1)
    return false;
  SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>(
      const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1)));
  SHA1Fingerprint hash = X509Certificate::CalculateFingerprint(root_ref);
  return IsSHA1HashInSortedArray(
      hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
}

// static
X509Certificate* X509Certificate::CreateSelfSigned(
    crypto::RSAPrivateKey* key,
    const std::string& subject,
    uint32 serial_number,
    base::TimeDelta valid_duration) {
  DCHECK(key);
  DCHECK(!subject.empty());

  if (valid_duration.InSeconds() > UINT32_MAX) {
     LOG(ERROR) << "valid_duration too big" << valid_duration.InSeconds();
     valid_duration = base::TimeDelta::FromSeconds(UINT32_MAX);
  }

  // There is a comment in
  // http://www.opensource.apple.com/source/security_certtool/security_certtool-31828/src/CertTool.cpp
  // that serial_numbers being passed into CSSM_TP_SubmitCredRequest can't have
  // their high bit set. We will continue though and mask it out below.
  if (serial_number & 0x80000000)
    LOG(ERROR) << "serial_number has high bit set " << serial_number;

  // NSS is used to parse the subject string into a set of
  // CSSM_OID/string pairs. There doesn't appear to be a system routine for
  // parsing Distinguished Name strings.
  crypto::EnsureNSSInit();

  CSSMOIDStringVector subject_name_oids;
  ScopedCertName subject_name(
      CERT_AsciiToName(const_cast<char*>(subject.c_str())));
  if (!CERTNameToCSSMOIDVector(subject_name, &subject_name_oids)) {
    DLOG(ERROR) << "Unable to generate CSSMOIDMap from " << subject;
    return NULL;
  }

  // Convert the map of oid/string pairs into an array of
  // CSSM_APPLE_TP_NAME_OIDs.
  std::vector<CSSM_APPLE_TP_NAME_OID> cssm_subject_names;
  for(CSSMOIDStringVector::iterator iter = subject_name_oids.begin();
      iter != subject_name_oids.end(); ++iter) {
    CSSM_APPLE_TP_NAME_OID cssm_subject_name;
    cssm_subject_name.oid = iter->oid_;
    cssm_subject_name.string = iter->string_.c_str();
    cssm_subject_names.push_back(cssm_subject_name);
  }

  if (cssm_subject_names.empty()) {
    DLOG(ERROR) << "cssm_subject_names.size() == 0. Input: " << subject;
    return NULL;
  }

  // Set up a certificate request.
  CSSM_APPLE_TP_CERT_REQUEST certReq;
  memset(&certReq, 0, sizeof(certReq));
  certReq.cspHand = crypto::GetSharedCSPHandle();
  certReq.clHand = crypto::GetSharedCLHandle();
    // See comment about serial numbers above.
  certReq.serialNumber = serial_number & 0x7fffffff;
  certReq.numSubjectNames = cssm_subject_names.size();
  certReq.subjectNames = &cssm_subject_names[0];
  certReq.numIssuerNames = 0; // Root.
  certReq.issuerNames = NULL;
  certReq.issuerNameX509 = NULL;
  certReq.certPublicKey = key->public_key();
  certReq.issuerPrivateKey = key->key();
  // These are the Apple defaults.
  certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA;
  certReq.signatureOid = CSSMOID_SHA1WithRSA;
  certReq.notBefore = 0;
  certReq.notAfter = static_cast<uint32>(valid_duration.InSeconds());
  certReq.numExtensions = 0;
  certReq.extensions = NULL;
  certReq.challengeString = NULL;

  CSSM_TP_REQUEST_SET reqSet;
  reqSet.NumberOfRequests = 1;
  reqSet.Requests = &certReq;

  CSSM_FIELD policyId;
  memset(&policyId, 0, sizeof(policyId));
  policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;

  CSSM_TP_CALLERAUTH_CONTEXT callerAuthContext;
  memset(&callerAuthContext, 0, sizeof(callerAuthContext));
  callerAuthContext.Policy.NumberOfPolicyIds = 1;
  callerAuthContext.Policy.PolicyIds = &policyId;

  CSSM_TP_HANDLE tp_handle = crypto::GetSharedTPHandle();
  CSSM_DATA refId;
  memset(&refId, 0, sizeof(refId));
  sint32 estTime;
  CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tp_handle, NULL,
      CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, &callerAuthContext,
       &estTime, &refId);
  if(crtn) {
    DLOG(ERROR) << "CSSM_TP_SubmitCredRequest failed " << crtn;
    return NULL;
  }

  CSSM_BOOL confirmRequired;
  CSSM_TP_RESULT_SET *resultSet = NULL;
  crtn = CSSM_TP_RetrieveCredResult(tp_handle, &refId, NULL, &estTime,
                                    &confirmRequired, &resultSet);
  ScopedEncodedCertResults scopedResults(resultSet);
  crypto::CSSMFree(refId.Data);
  if (crtn) {
    DLOG(ERROR) << "CSSM_TP_RetrieveCredResult failed " << crtn;
    return NULL;
  }

  if (confirmRequired) {
    // Potential leak here of resultSet. |confirmRequired| should never be
    // true.
    DLOG(ERROR) << "CSSM_TP_RetrieveCredResult required confirmation";
    return NULL;
  }

  if (resultSet->NumberOfResults != 1) {
     DLOG(ERROR) << "Unexpected number of results: "
                 << resultSet->NumberOfResults;
    return NULL;
  }

  CSSM_ENCODED_CERT* encCert =
      reinterpret_cast<CSSM_ENCODED_CERT*>(resultSet->Results);
  base::mac::ScopedCFTypeRef<SecCertificateRef> scoped_cert;
  SecCertificateRef certificate_ref = NULL;
  OSStatus os_status =
      SecCertificateCreateFromData(&encCert->CertBlob, encCert->CertType,
                                   encCert->CertEncoding, &certificate_ref);
  if (os_status != 0) {
    DLOG(ERROR) << "SecCertificateCreateFromData failed: " << os_status;
    return NULL;
  }
  scoped_cert.reset(certificate_ref);

  return CreateFromHandle(
     scoped_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT,
     X509Certificate::OSCertHandles());
}

void X509Certificate::GetDNSNames(std::vector<std::string>* dns_names) const {
  dns_names->clear();

  GetCertGeneralNamesForOID(cert_handle_, CSSMOID_SubjectAltName, GNT_DNSName,
                            dns_names);

  if (dns_names->empty())
    dns_names->push_back(subject_.common_name);
}

int X509Certificate::VerifyInternal(const std::string& hostname,
                                    int flags,
                                    CertVerifyResult* verify_result) const {
  ScopedCFTypeRef<CFArrayRef> trust_policies;
  OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies);
  if (status)
    return NetErrorFromOSStatus(status);

  // Create and configure a SecTrustRef, which takes our certificate(s)
  // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
  // array of certificates, the first of which is the certificate we're
  // verifying, and the subsequent (optional) certificates are used for
  // chain building.
  CFMutableArrayRef cert_array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
                                                      &kCFTypeArrayCallBacks);
  if (!cert_array)
    return ERR_OUT_OF_MEMORY;
  ScopedCFTypeRef<CFArrayRef> scoped_cert_array(cert_array);
  CFArrayAppendValue(cert_array, cert_handle_);
  for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i)
    CFArrayAppendValue(cert_array, intermediate_ca_certs_[i]);

  // From here on, only one thread can be active at a time. We have had a number
  // of sporadic crashes in the SecTrustEvaluate call below, way down inside
  // Apple's cert code, which we suspect are caused by a thread-safety issue.
  // So as a speculative fix allow only one thread to use SecTrust on this cert.
  base::AutoLock lock(verification_lock_);

  SecTrustRef trust_ref = NULL;
  status = SecTrustCreateWithCertificates(cert_array, trust_policies,
                                          &trust_ref);
  if (status)
    return NetErrorFromOSStatus(status);
  ScopedCFTypeRef<SecTrustRef> scoped_trust_ref(trust_ref);

  if (TestRootCerts::HasInstance()) {
    status = TestRootCerts::GetInstance()->FixupSecTrustRef(trust_ref);
    if (status)
      return NetErrorFromOSStatus(status);
  }

  CSSM_APPLE_TP_ACTION_DATA tp_action_data;
  memset(&tp_action_data, 0, sizeof(tp_action_data));
  tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION;
  // Allow CSSM to download any missing intermediate certificates if an
  // authorityInfoAccess extension or issuerAltName extension is present.
  tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET;

  if (flags & VERIFY_REV_CHECKING_ENABLED) {
    // Require a positive result from an OCSP responder or a CRL (or both)
    // for every certificate in the chain. The Apple TP automatically
    // excludes the self-signed root from this requirement. If a certificate
    // is missing both a crlDistributionPoints extension and an
    // authorityInfoAccess extension with an OCSP responder URL, then we
    // will get a kSecTrustResultRecoverableTrustFailure back from
    // SecTrustEvaluate(), with a
    // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case,
    // we'll set our own result to include
    // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are
    // present, and a check fails (server unavailable, OCSP retry later,
    // signature mismatch), then we'll set our own result to include
    // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
    tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
    verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
  } else {
    // EV requires revocation checking.
    // Note, under the hood, SecTrustEvaluate() will modify the OCSP options
    // so as to attempt OCSP checking if it believes a certificate may chain
    // to an EV root. However, because network fetches are disabled in
    // CreateTrustPolicies() when revocation checking is disabled, these
    // will only go against the local cache.
    flags &= ~VERIFY_EV_CERT;
  }

  CFDataRef action_data_ref =
      CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
                                  reinterpret_cast<UInt8*>(&tp_action_data),
                                  sizeof(tp_action_data), kCFAllocatorNull);
  if (!action_data_ref)
    return ERR_OUT_OF_MEMORY;
  ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref);
  status = SecTrustSetParameters(trust_ref, CSSM_TP_ACTION_DEFAULT,
                                 action_data_ref);
  if (status)
    return NetErrorFromOSStatus(status);

  // Verify the certificate. A non-zero result from SecTrustGetResult()
  // indicates that some fatal error occurred and the chain couldn't be
  // processed, not that the chain contains no errors. We need to examine the
  // output of SecTrustGetResult() to determine that.
  SecTrustResultType trust_result;
  status = SecTrustEvaluate(trust_ref, &trust_result);
  if (status)
    return NetErrorFromOSStatus(status);
  CFArrayRef completed_chain = NULL;
  CSSM_TP_APPLE_EVIDENCE_INFO* chain_info;
  status = SecTrustGetResult(trust_ref, &trust_result, &completed_chain,
                             &chain_info);
  if (status)
    return NetErrorFromOSStatus(status);
  ScopedCFTypeRef<CFArrayRef> scoped_completed_chain(completed_chain);

  // Evaluate the results
  OSStatus cssm_result;
  bool got_certificate_error = false;
  switch (trust_result) {
    case kSecTrustResultUnspecified:
    case kSecTrustResultProceed:
      // Certificate chain is valid and trusted ("unspecified" indicates that
      // the user has not explicitly set a trust setting)
      break;

    case kSecTrustResultDeny:
    case kSecTrustResultConfirm:
      // Certificate chain is explicitly untrusted. For kSecTrustResultConfirm,
      // we're following what Secure Transport does and treating it as
      // "deny".
      verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
      break;

    case kSecTrustResultRecoverableTrustFailure:
      // Certificate chain has a failure that can be overridden by the user.
      status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
      if (status)
        return NetErrorFromOSStatus(status);
      switch (cssm_result) {
        case CSSMERR_TP_NOT_TRUSTED:
        case CSSMERR_TP_INVALID_ANCHOR_CERT:
          verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
          break;
        case CSSMERR_TP_CERT_EXPIRED:
        case CSSMERR_TP_CERT_NOT_VALID_YET:
          verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
          break;
        case CSSMERR_TP_CERT_REVOKED:
        case CSSMERR_TP_CERT_SUSPENDED:
          verify_result->cert_status |= CERT_STATUS_REVOKED;
          break;
        default:
          // Look for specific per-certificate errors below.
          break;
      }
      // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO
      // structure which can catch multiple errors from each certificate.
      for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain);
           index < chain_count; ++index) {
        if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED ||
            chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET)
          verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
        if (chain_info[index].NumStatusCodes == 0) {
          LOG(WARNING) << "chain_info[" << index << "].NumStatusCodes is 0"
                          ", chain_info[" << index << "].StatusBits is "
                       << chain_info[index].StatusBits;
        }
        for (uint32 status_code_index = 0;
             status_code_index < chain_info[index].NumStatusCodes;
             ++status_code_index) {
          got_certificate_error = true;
          int cert_status = CertStatusFromOSStatus(
              chain_info[index].StatusCodes[status_code_index]);
          if (cert_status == CERT_STATUS_COMMON_NAME_INVALID) {
            std::vector<std::string> names;
            GetDNSNames(&names);
            if (OverrideHostnameMismatch(hostname, &names))
              cert_status = 0;
          }
          verify_result->cert_status |= cert_status;
        }
      }
      // Be paranoid and ensure that we recorded at least one certificate
      // status on receiving kSecTrustResultRecoverableTrustFailure. The
      // call to SecTrustGetCssmResultCode() should pick up when the chain
      // is not trusted and the loop through CSSM_TP_APPLE_EVIDENCE_INFO
      // should pick up everything else, but let's be safe.
      if (!verify_result->cert_status && !got_certificate_error) {
        LOG(ERROR) << "cssm_result=" << cssm_result;
        verify_result->cert_status |= CERT_STATUS_INVALID;
        NOTREACHED();
      }
      break;

    default:
      status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
      if (status)
        return NetErrorFromOSStatus(status);
      verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
      if (!verify_result->cert_status) {
        LOG(WARNING) << "trust_result=" << trust_result;
        verify_result->cert_status |= CERT_STATUS_INVALID;
      }
      break;
  }

  // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
  // compatible with Windows, which in turn implements this behavior to be
  // compatible with WinHTTP, which doesn't report this error (bug 3004).
  verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;

  if (IsCertStatusError(verify_result->cert_status))
    return MapCertStatusToNetError(verify_result->cert_status);

  if (flags & VERIFY_EV_CERT) {
    // Determine the certificate's EV status using SecTrustCopyExtendedResult(),
    // which we need to look up because the function wasn't added until
    // Mac OS X 10.5.7.
    // Note: "ExtendedResult" means extended validation results.
    CFBundleRef bundle =
        CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
    if (bundle) {
      SecTrustCopyExtendedResultFuncPtr copy_extended_result =
          reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>(
              CFBundleGetFunctionPointerForName(bundle,
                  CFSTR("SecTrustCopyExtendedResult")));
      if (copy_extended_result) {
        CFDictionaryRef ev_dict = NULL;
        status = copy_extended_result(trust_ref, &ev_dict);
        if (!status && ev_dict) {
          // The returned dictionary contains the EV organization name from the
          // server certificate, which we don't need at this point (and we
          // have other ways to access, anyway). All we care is that
          // SecTrustCopyExtendedResult() returned noErr and a non-NULL
          // dictionary.
          CFRelease(ev_dict);
          verify_result->cert_status |= CERT_STATUS_IS_EV;
        }
      }
    }
  }

  AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes);
  verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(completed_chain);

  return OK;
}

bool X509Certificate::GetDEREncoded(std::string* encoded) {
  encoded->clear();
  CSSM_DATA der_data;
  if(SecCertificateGetData(cert_handle_, &der_data) == noErr) {
    encoded->append(reinterpret_cast<char*>(der_data.Data),
                    der_data.Length);
    return true;
  }
  return false;
}

// static
bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
                                   X509Certificate::OSCertHandle b) {
  DCHECK(a && b);
  if (a == b)
    return true;
  if (CFEqual(a, b))
    return true;
  CSSM_DATA a_data, b_data;
  return SecCertificateGetData(a, &a_data) == noErr &&
      SecCertificateGetData(b, &b_data) == noErr &&
      a_data.Length == b_data.Length &&
      memcmp(a_data.Data, b_data.Data, a_data.Length) == 0;
}

// static
X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
    const char* data, int length) {
  CSSM_DATA cert_data;
  cert_data.Data = const_cast<uint8*>(reinterpret_cast<const uint8*>(data));
  cert_data.Length = length;

  OSCertHandle cert_handle = NULL;
  OSStatus status = SecCertificateCreateFromData(&cert_data,
                                                 CSSM_CERT_X_509v3,
                                                 CSSM_CERT_ENCODING_DER,
                                                 &cert_handle);
  if (status != noErr)
    return NULL;
  if (!IsValidOSCertHandle(cert_handle)) {
    CFRelease(cert_handle);
    return NULL;
  }
  return cert_handle;
}

// static
X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
    const char* data, int length, Format format) {
  OSCertHandles results;

  switch (format) {
    case FORMAT_SINGLE_CERTIFICATE: {
      OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
      if (handle)
        results.push_back(handle);
      break;
    }
    case FORMAT_PKCS7:
      AddCertificatesFromBytes(data, length, kSecFormatPKCS7, &results);
      break;
    default:
      NOTREACHED() << "Certificate format " << format << " unimplemented";
      break;
  }

  return results;
}

// static
X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
    OSCertHandle handle) {
  if (!handle)
    return NULL;
  return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
}

// static
void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
  CFRelease(cert_handle);
}

// static
SHA1Fingerprint X509Certificate::CalculateFingerprint(
    OSCertHandle cert) {
  SHA1Fingerprint sha1;
  memset(sha1.data, 0, sizeof(sha1.data));

  CSSM_DATA cert_data;
  OSStatus status = SecCertificateGetData(cert, &cert_data);
  if (status)
    return sha1;

  DCHECK(cert_data.Data);
  DCHECK_NE(cert_data.Length, 0U);

  CC_SHA1(cert_data.Data, cert_data.Length, sha1.data);

  return sha1;
}

bool X509Certificate::SupportsSSLClientAuth() const {
  CSSMFields fields;
  if (GetCertFields(cert_handle_, &fields) != noErr)
    return false;

  // Gather the extensions we care about. We do not support
  // CSSMOID_NetscapeCertType on OS X.
  const CE_ExtendedKeyUsage* ext_key_usage = NULL;
  const CE_KeyUsage* key_usage = NULL;
  for (unsigned f = 0; f < fields.num_of_fields; ++f) {
    const CSSM_FIELD& field = fields.fields[f];
    const CSSM_X509_EXTENSION* ext =
        reinterpret_cast<const CSSM_X509_EXTENSION*>(field.FieldValue.Data);
    if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_KeyUsage)) {
      key_usage = reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue);
    } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_ExtendedKeyUsage)) {
      ext_key_usage =
          reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
    }
  }

  // RFC5280 says to take the intersection of the two extensions.
  //
  // Our underlying crypto libraries don't expose
  // ClientCertificateType, so for now we will not support fixed
  // Diffie-Hellman mechanisms. For rsa_sign, we need the
  // digitalSignature bit.
  //
  // In particular, if a key has the nonRepudiation bit and not the
  // digitalSignature one, we will not offer it to the user.
  if (key_usage && !((*key_usage) & CE_KU_DigitalSignature))
    return false;
  if (ext_key_usage && !ExtendedKeyUsageAllows(ext_key_usage,
                                               &CSSMOID_ClientAuth))
    return false;
  return true;
}

bool X509Certificate::IsIssuedBy(
    const std::vector<CertPrincipal>& valid_issuers) {
  // Get the cert's issuer chain.
  CFArrayRef cert_chain = NULL;
  OSStatus result;
  result = CopyCertChain(os_cert_handle(), &cert_chain);
  if (result)
    return false;
  ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain);

  // Check all the certs in the chain for a match.
  int n = CFArrayGetCount(cert_chain);
  for (int i = 0; i < n; ++i) {
    SecCertificateRef cert_handle = reinterpret_cast<SecCertificateRef>(
        const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
    scoped_refptr<X509Certificate> cert(X509Certificate::CreateFromHandle(
        cert_handle,
        X509Certificate::SOURCE_LONE_CERT_IMPORT,
        X509Certificate::OSCertHandles()));
    for (unsigned j = 0; j < valid_issuers.size(); j++) {
      if (cert->issuer().Matches(valid_issuers[j]))
        return true;
    }
  }
  return false;
}

// static
OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* policy) {
  CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options;
  memset(&tp_ssl_options, 0, sizeof(tp_ssl_options));
  tp_ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
  tp_ssl_options.Flags |= CSSM_APPLE_TP_SSL_CLIENT;

  return CreatePolicy(&CSSMOID_APPLE_TP_SSL, &tp_ssl_options,
                      sizeof(tp_ssl_options), policy);
}

// static
OSStatus X509Certificate::CreateSSLServerPolicy(const std::string& hostname,
                                                SecPolicyRef* policy) {
  CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options;
  memset(&tp_ssl_options, 0, sizeof(tp_ssl_options));
  tp_ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
  if (!hostname.empty()) {
    tp_ssl_options.ServerName = hostname.data();
    tp_ssl_options.ServerNameLen = hostname.size();
  }

  return CreatePolicy(&CSSMOID_APPLE_TP_SSL, &tp_ssl_options,
                      sizeof(tp_ssl_options), policy);
}

// static
OSStatus X509Certificate::CreateBasicX509Policy(SecPolicyRef* policy) {
  return CreatePolicy(&CSSMOID_APPLE_X509_BASIC, NULL, 0, policy);
}

// static
OSStatus X509Certificate::CreateRevocationPolicies(
    bool enable_revocation_checking,
    CFMutableArrayRef policies) {
  // In order to actually disable revocation checking, the SecTrustRef must
  // have at least one revocation policy associated with it. If none are
  // present, the Apple TP will add policies according to the system
  // preferences, which will enable revocation checking even if the caller
  // explicitly disabled it. An OCSP policy is used, rather than a CRL policy,
  // because the Apple TP will force an OCSP policy to be present and enabled
  // if it believes the certificate may chain to an EV root. By explicitly
  // disabling network and OCSP cache access, then even if the Apple TP
  // enables OCSP checking, no revocation checking will actually succeed.
  CSSM_APPLE_TP_OCSP_OPTIONS tp_ocsp_options;
  memset(&tp_ocsp_options, 0, sizeof(tp_ocsp_options));
  tp_ocsp_options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;

  if (enable_revocation_checking) {
    // The default for the OCSP policy is to fetch responses via the network,
    // unlike the CRL policy default. The policy is further modified to
    // prefer OCSP over CRLs, if both are specified on the certificate. This
    // is because an OCSP response is both sufficient and typically
    // significantly smaller than the CRL counterpart.
    tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
  } else {
    // Effectively disable OCSP checking by making it impossible to get an
    // OCSP response. Even if the Apple TP forces OCSP, no checking will
    // be able to succeed. If this happens, the Apple TP will report an error
    // that OCSP was unavailable, but this will be handled and suppressed in
    // X509Certificate::Verify().
    tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_DISABLE_NET |
                            CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE;
  }

  SecPolicyRef ocsp_policy;
  OSStatus status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_OCSP,
                                 &tp_ocsp_options, sizeof(tp_ocsp_options),
                                 &ocsp_policy);
  if (status)
    return status;
  CFArrayAppendValue(policies, ocsp_policy);
  CFRelease(ocsp_policy);

  if (enable_revocation_checking) {
    CSSM_APPLE_TP_CRL_OPTIONS tp_crl_options;
    memset(&tp_crl_options, 0, sizeof(tp_crl_options));
    tp_crl_options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
    tp_crl_options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET;

    SecPolicyRef crl_policy;
    status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_CRL, &tp_crl_options,
                          sizeof(tp_crl_options), &crl_policy);
    if (status)
      return status;
    CFArrayAppendValue(policies, crl_policy);
    CFRelease(crl_policy);
  }

  return status;
}


// static
bool X509Certificate::GetSSLClientCertificates(
    const std::string& server_domain,
    const std::vector<CertPrincipal>& valid_issuers,
    CertificateList* certs) {
  ScopedCFTypeRef<SecIdentityRef> preferred_identity;
  if (!server_domain.empty()) {
    // See if there's an identity preference for this domain:
    ScopedCFTypeRef<CFStringRef> domain_str(
        base::SysUTF8ToCFStringRef("https://" + server_domain));
    SecIdentityRef identity = NULL;
    // While SecIdentityCopyPreferences appears to take a list of CA issuers
    // to restrict the identity search to, within Security.framework the
    // argument is ignored and filtering unimplemented. See
    // SecIdentity.cpp in libsecurity_keychain, specifically
    // _SecIdentityCopyPreferenceMatchingName().
    if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr)
      preferred_identity.reset(identity);
  }

  // Now enumerate the identities in the available keychains.
  SecIdentitySearchRef search = nil;
  OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
  ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
  while (!err) {
    SecIdentityRef identity = NULL;
    err = SecIdentitySearchCopyNext(search, &identity);
    if (err)
      break;
    ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);

    SecCertificateRef cert_handle;
    err = SecIdentityCopyCertificate(identity, &cert_handle);
    if (err != noErr)
      continue;
    ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);

    scoped_refptr<X509Certificate> cert(
        CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT,
                         OSCertHandles()));
    if (cert->HasExpired() || !cert->SupportsSSLClientAuth())
      continue;

    // Skip duplicates (a cert may be in multiple keychains).
    const SHA1Fingerprint& fingerprint = cert->fingerprint();
    unsigned i;
    for (i = 0; i < certs->size(); ++i) {
      if ((*certs)[i]->fingerprint().Equals(fingerprint))
        break;
    }
    if (i < certs->size())
      continue;

    bool is_preferred = preferred_identity &&
        CFEqual(preferred_identity, identity);

    // Make sure the issuer matches valid_issuers, if given.
    // But an explicit cert preference overrides this.
    if (!is_preferred &&
        !valid_issuers.empty() &&
        !cert->IsIssuedBy(valid_issuers))
      continue;

    // The cert passes, so add it to the vector.
    // If it's the preferred identity, add it at the start (so it'll be
    // selected by default in the UI.)
    if (is_preferred)
      certs->insert(certs->begin(), cert);
    else
      certs->push_back(cert);
  }

  if (err != errSecItemNotFound) {
    LOG(ERROR) << "SecIdentitySearch error " << err;
    return false;
  }
  return true;
}

CFArrayRef X509Certificate::CreateClientCertificateChain() const {
  // Initialize the result array with just the IdentityRef of the receiver:
  OSStatus result;
  SecIdentityRef identity;
  result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity);
  if (result) {
    LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result;
    return NULL;
  }
  ScopedCFTypeRef<CFMutableArrayRef> chain(
      CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
  CFArrayAppendValue(chain, identity);

  CFArrayRef cert_chain = NULL;
  result = CopyCertChain(cert_handle_, &cert_chain);
  ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain);
  if (result) {
    LOG(ERROR) << "CreateIdentityCertificateChain error " << result;
    return chain.release();
  }

  // Append the intermediate certs from SecTrust to the result array:
  if (cert_chain) {
    int chain_count = CFArrayGetCount(cert_chain);
    if (chain_count > 1) {
      CFArrayAppendArray(chain,
                         cert_chain,
                         CFRangeMake(1, chain_count - 1));
    }
  }

  return chain.release();
}

// static
X509Certificate::OSCertHandle
X509Certificate::ReadOSCertHandleFromPickle(const Pickle& pickle,
                                            void** pickle_iter) {
  const char* data;
  int length;
  if (!pickle.ReadData(pickle_iter, &data, &length))
    return NULL;

  return CreateOSCertHandleFromBytes(data, length);
}

// static
bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
                                                Pickle* pickle) {
  CSSM_DATA cert_data;
  OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
  if (status)
    return false;

  return pickle->WriteData(reinterpret_cast<char*>(cert_data.Data),
                           cert_data.Length);
}

}  // namespace net
