summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorsvaldez <svaldez@chromium.org>2016-03-25 13:37:13 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-25 20:38:05 +0000
commit303d7d45b88c2fefcffbc5b24a267c6fd159c7a0 (patch)
tree590c52d93acdc800827397f5c9907532c95994fb /net
parenta5fdfbe215a6d9c9a8822082a45085625b5477c2 (diff)
downloadchromium_src-303d7d45b88c2fefcffbc5b24a267c6fd159c7a0.zip
chromium_src-303d7d45b88c2fefcffbc5b24a267c6fd159c7a0.tar.gz
chromium_src-303d7d45b88c2fefcffbc5b24a267c6fd159c7a0.tar.bz2
Adding iOS OpenSSL Implementation
This adds the OpenSSL-specific implementations for iOS, using SecTrustEvaluate in order to determine the validity of the certificate chain. (Relanding without nist-pkits dependency) TBR=rsleevi@chromium.org,davidben@chromium.org BUG=591545 Review URL: https://codereview.chromium.org/1833183002 Cr-Commit-Position: refs/heads/master@{#383348}
Diffstat (limited to 'net')
-rw-r--r--net/BUILD.gn12
-rw-r--r--net/cert/cert_verify_proc.cc4
-rw-r--r--net/cert/cert_verify_proc_ios.cc239
-rw-r--r--net/cert/cert_verify_proc_ios.h36
-rw-r--r--net/cert/x509_certificate_openssl_ios.cc475
-rw-r--r--net/net.gyp7
-rw-r--r--net/net.gypi3
-rw-r--r--net/net_common.gypi12
8 files changed, 788 insertions, 0 deletions
diff --git a/net/BUILD.gn b/net/BUILD.gn
index b6390a7..6fd7fc7 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -320,6 +320,14 @@ if (!is_nacl) {
]
}
+ if (is_ios && use_nss_verifier) {
+ net_shared_sources -= [
+ "cert/cert_verify_proc_ios.cc",
+ "cert/cert_verify_proc_ios.h",
+ "cert/x509_certificate_openssl_ios.cc",
+ ]
+ }
+
if (is_chromecast && use_nss_certs) {
net_shared_sources += [ "ssl/ssl_platform_key_chromecast.cc" ]
net_shared_sources -= [ "ssl/ssl_platform_key_nss.cc" ]
@@ -376,6 +384,10 @@ if (!is_nacl) {
]
}
+ if (is_ios && !use_nss_verifier) {
+ net_shared_sources += [ "cert/test_root_certs_mac.cc" ]
+ }
+
if (is_ios || is_mac) {
net_shared_sources += gypi_values.net_base_mac_ios_sources
}
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index eba84e6..9c8267d 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -32,6 +32,8 @@
#include "net/cert/cert_verify_proc_openssl.h"
#elif defined(OS_ANDROID)
#include "net/cert/cert_verify_proc_android.h"
+#elif defined(OS_IOS)
+#include "net/cert/cert_verify_proc_ios.h"
#elif defined(OS_MACOSX)
#include "net/cert/cert_verify_proc_mac.h"
#elif defined(OS_WIN)
@@ -209,6 +211,8 @@ CertVerifyProc* CertVerifyProc::CreateDefault() {
return new CertVerifyProcOpenSSL();
#elif defined(OS_ANDROID)
return new CertVerifyProcAndroid();
+#elif defined(OS_IOS)
+ return new CertVerifyProcIOS();
#elif defined(OS_MACOSX)
return new CertVerifyProcMac();
#elif defined(OS_WIN)
diff --git a/net/cert/cert_verify_proc_ios.cc b/net/cert/cert_verify_proc_ios.cc
new file mode 100644
index 0000000..5f2884f
--- /dev/null
+++ b/net/cert/cert_verify_proc_ios.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2016 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/cert/cert_verify_proc_ios.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#include <Security/Security.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/ssl/openssl_ssl_util.h"
+
+using base::ScopedCFTypeRef;
+
+namespace net {
+
+namespace {
+
+int NetErrorFromOSStatus(OSStatus status) {
+ switch (status) {
+ case noErr:
+ return OK;
+ case errSecNotAvailable:
+ return ERR_NOT_IMPLEMENTED;
+ case errSecAuthFailed:
+ return ERR_ACCESS_DENIED;
+ default:
+ return ERR_FAILED;
+ }
+}
+
+// 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. If
+// successful, returns noErr, and stores the resultant array of SecPolicyRefs
+// in |policies|.
+OSStatus CreateTrustPolicies(ScopedCFTypeRef<CFArrayRef>* policies) {
+ ScopedCFTypeRef<CFMutableArrayRef> local_policies(
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+ if (!local_policies)
+ return errSecAllocate;
+
+ SecPolicyRef ssl_policy = SecPolicyCreateBasicX509();
+ CFArrayAppendValue(local_policies, ssl_policy);
+ CFRelease(ssl_policy);
+ ssl_policy = SecPolicyCreateSSL(true, nullptr);
+ CFArrayAppendValue(local_policies, ssl_policy);
+ CFRelease(ssl_policy);
+
+ policies->reset(local_policies.release());
+ return noErr;
+}
+
+// Builds and evaluates a SecTrustRef for the certificate chain contained
+// in |cert_array|, using the verification policies in |trust_policies|. On
+// success, returns OK, and updates |trust_ref| and |trust_result|. On failure,
+// no output parameters are modified.
+//
+// Note: An OK return does not mean that |cert_array| is trusted, merely that
+// verification was performed successfully.
+int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
+ CFArrayRef trust_policies,
+ ScopedCFTypeRef<SecTrustRef>* trust_ref,
+ ScopedCFTypeRef<CFArrayRef>* verified_chain,
+ SecTrustResultType* trust_result) {
+ SecTrustRef tmp_trust = nullptr;
+ OSStatus status =
+ SecTrustCreateWithCertificates(cert_array, trust_policies, &tmp_trust);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust);
+
+ if (TestRootCerts::HasInstance()) {
+ status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ }
+
+ SecTrustResultType tmp_trust_result;
+ status = SecTrustEvaluate(tmp_trust, &tmp_trust_result);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ ScopedCFTypeRef<CFMutableArrayRef> tmp_verified_chain(
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+ const CFIndex chain_length = SecTrustGetCertificateCount(tmp_trust);
+ for (CFIndex i = 0; i < chain_length; ++i) {
+ SecCertificateRef chain_cert = SecTrustGetCertificateAtIndex(tmp_trust, i);
+ CFArrayAppendValue(tmp_verified_chain, chain_cert);
+ }
+
+ trust_ref->swap(scoped_tmp_trust);
+ *trust_result = tmp_trust_result;
+ verified_chain->reset(tmp_verified_chain.release());
+ return OK;
+}
+
+void GetCertChainInfo(CFArrayRef cert_chain, CertVerifyResult* verify_result) {
+ DCHECK_LT(0, CFArrayGetCount(cert_chain));
+
+ verify_result->has_md2 = false;
+ verify_result->has_md4 = false;
+ verify_result->has_md5 = false;
+ verify_result->has_sha1 = false;
+ verify_result->has_sha1_leaf = false;
+
+ SecCertificateRef verified_cert = nullptr;
+ std::vector<SecCertificateRef> verified_chain;
+ for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) {
+ SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
+ if (i == 0) {
+ verified_cert = chain_cert;
+ } else {
+ verified_chain.push_back(chain_cert);
+ }
+
+ std::string der_bytes;
+ if (!X509Certificate::GetDEREncoded(chain_cert, &der_bytes))
+ return;
+ const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_bytes.data());
+ ScopedX509 x509_cert(d2i_X509(NULL, &bytes, der_bytes.size()));
+
+ base::StringPiece spki_bytes;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
+ continue;
+
+ HashValue sha1(HASH_VALUE_SHA1);
+ CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data());
+ verify_result->public_key_hashes.push_back(sha1);
+
+ HashValue sha256(HASH_VALUE_SHA256);
+ CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data());
+ verify_result->public_key_hashes.push_back(sha256);
+
+ int sig_alg = OBJ_obj2nid(x509_cert->sig_alg->algorithm);
+ if (sig_alg == NID_md2WithRSAEncryption) {
+ verify_result->has_md2 = true;
+ } else if (sig_alg == NID_md4WithRSAEncryption) {
+ verify_result->has_md4 = true;
+ } else if (sig_alg == NID_md5WithRSAEncryption ||
+ sig_alg == NID_md5WithRSA) {
+ verify_result->has_md5 = true;
+ } else if (sig_alg == NID_sha1WithRSAEncryption ||
+ sig_alg == NID_dsaWithSHA || sig_alg == NID_dsaWithSHA1 ||
+ sig_alg == NID_dsaWithSHA1_2 || sig_alg == NID_sha1WithRSA ||
+ sig_alg == NID_ecdsa_with_SHA1) {
+ verify_result->has_sha1 = true;
+ if (i == 0)
+ verify_result->has_sha1_leaf = true;
+ }
+ }
+ if (!verified_cert) {
+ NOTREACHED();
+ return;
+ }
+
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+}
+
+} // namespace
+
+CertVerifyProcIOS::CertVerifyProcIOS() {}
+
+CertVerifyProcIOS::~CertVerifyProcIOS() {}
+
+bool CertVerifyProcIOS::SupportsAdditionalTrustAnchors() const {
+ return false;
+}
+
+bool CertVerifyProcIOS::SupportsOCSPStapling() const {
+ return false;
+}
+
+int CertVerifyProcIOS::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ const std::string& ocsp_response,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ ScopedCFTypeRef<CFArrayRef> trust_policies;
+ OSStatus status = CreateTrustPolicies(&trust_policies);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ ScopedCFTypeRef<CFMutableArrayRef> cert_array(
+ cert->CreateOSCertChainForCert());
+ ScopedCFTypeRef<SecTrustRef> trust_ref;
+ SecTrustResultType trust_result = kSecTrustResultDeny;
+ ScopedCFTypeRef<CFArrayRef> final_chain;
+
+ status = BuildAndEvaluateSecTrustRef(cert_array, trust_policies, &trust_ref,
+ &final_chain, &trust_result);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ if (CFArrayGetCount(final_chain) == 0)
+ return ERR_FAILED;
+
+ GetCertChainInfo(final_chain, verify_result);
+
+ // TODO(sleevi): Support CRLSet revocation.
+ // TODO(svaldez): Add specific error codes for trust errors resulting from
+ // expired/not-yet-valid certs.
+ switch (trust_result) {
+ case kSecTrustResultUnspecified:
+ case kSecTrustResultProceed:
+ break;
+ case kSecTrustResultDeny:
+ verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+ default:
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ }
+
+ // Perform hostname verification independent of SecTrustEvaluate.
+ if (!verify_result->verified_cert->VerifyNameMatch(
+ hostname, &verify_result->common_name_fallback_used)) {
+ verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
+ }
+
+ verify_result->is_issued_by_known_root = false;
+
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ return OK;
+}
+
+} // namespace net
diff --git a/net/cert/cert_verify_proc_ios.h b/net/cert/cert_verify_proc_ios.h
new file mode 100644
index 0000000..e965a48
--- /dev/null
+++ b/net/cert/cert_verify_proc_ios.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 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.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_IOS_H_
+#define NET_CERT_CERT_VERIFY_PROC_IOS_H_
+
+#include "net/cert/cert_verify_proc.h"
+
+namespace net {
+
+// Performs certificate path construction and validation using iOS's
+// Security.framework.
+class CertVerifyProcIOS : public CertVerifyProc {
+ public:
+ CertVerifyProcIOS();
+
+ bool SupportsAdditionalTrustAnchors() const override;
+ bool SupportsOCSPStapling() const override;
+
+ protected:
+ ~CertVerifyProcIOS() override;
+
+ private:
+ int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ const std::string& ocsp_response,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) override;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_IOS_H_
diff --git a/net/cert/x509_certificate_openssl_ios.cc b/net/cert/x509_certificate_openssl_ios.cc
new file mode 100644
index 0000000..49a88d7
--- /dev/null
+++ b/net/cert/x509_certificate_openssl_ios.cc
@@ -0,0 +1,475 @@
+// Copyright (c) 2016 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/cert/x509_certificate.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#include <Security/Security.h>
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/pickle.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "crypto/openssl_util.h"
+#include "crypto/scoped_openssl_types.h"
+#include "net/base/ip_address_number.h"
+#include "net/cert/x509_util_openssl.h"
+#include "net/ssl/openssl_ssl_util.h"
+
+using base::ScopedCFTypeRef;
+
+namespace net {
+
+namespace {
+
+using ScopedGENERAL_NAMES =
+ crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free>;
+
+// Returns true if a given |cert_handle| is actually a valid X.509 certificate
+// handle.
+//
+// SecCertificateCreateFromData() does not always force the immediate parsing of
+// the certificate, and as such, may return a SecCertificateRef for an
+// invalid/unparsable certificate. Force parsing to occur to ensure that the
+// SecCertificateRef is correct. On later versions where
+// SecCertificateCreateFromData() immediately parses, rather than lazily, this
+// call is cheap, as the subject is cached.
+bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
+ ScopedCFTypeRef<CFStringRef> sanity_check(
+ SecCertificateCopySubjectSummary(cert_handle));
+ return sanity_check != nullptr;
+}
+
+void CreateOSCertHandlesFromPKCS7Bytes(
+ const char* data,
+ size_t length,
+ X509Certificate::OSCertHandles* handles) {
+ crypto::EnsureOpenSSLInit();
+ crypto::OpenSSLErrStackTracer err_cleaner(FROM_HERE);
+
+ CBS der_data;
+ CBS_init(&der_data, reinterpret_cast<const uint8_t*>(data), length);
+ STACK_OF(X509)* certs = sk_X509_new_null();
+
+ if (PKCS7_get_certificates(certs, &der_data)) {
+ for (size_t i = 0; i < sk_X509_num(certs); ++i) {
+ X509* x509_cert = sk_X509_value(certs, i);
+ base::StringPiece der;
+ if (!x509_util::GetDER(x509_cert, &der))
+ return;
+ handles->push_back(X509Certificate::CreateOSCertHandleFromBytes(
+ der.data(), der.length()));
+ }
+ }
+ sk_X509_pop_free(certs, X509_free);
+}
+
+void ParsePrincipalValues(X509_NAME* name,
+ int nid,
+ std::vector<std::string>* fields) {
+ for (int index = -1;
+ (index = X509_NAME_get_index_by_NID(name, nid, index)) != -1;) {
+ std::string field;
+ if (!x509_util::ParsePrincipalValueByIndex(name, index, &field))
+ break;
+ fields->push_back(field);
+ }
+}
+
+void ParsePrincipal(X509Certificate::OSCertHandle os_cert,
+ X509_NAME* x509_name,
+ CertPrincipal* principal) {
+ if (!x509_name)
+ return;
+
+ ParsePrincipalValues(x509_name, NID_streetAddress,
+ &principal->street_addresses);
+ ParsePrincipalValues(x509_name, NID_organizationName,
+ &principal->organization_names);
+ ParsePrincipalValues(x509_name, NID_organizationalUnitName,
+ &principal->organization_unit_names);
+ ParsePrincipalValues(x509_name, NID_domainComponent,
+ &principal->domain_components);
+
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_commonName,
+ &principal->common_name);
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_localityName,
+ &principal->locality_name);
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_stateOrProvinceName,
+ &principal->state_or_province_name);
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_countryName,
+ &principal->country_name);
+}
+
+void ParseSubjectAltName(X509Certificate::OSCertHandle os_cert,
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addresses) {
+ DCHECK(dns_names || ip_addresses);
+ ScopedX509 cert = OSCertHandleToOpenSSL(os_cert);
+ if (!cert.get())
+ return;
+ int index = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1);
+ X509_EXTENSION* alt_name_ext = X509_get_ext(cert.get(), index);
+ if (!alt_name_ext)
+ return;
+
+ ScopedGENERAL_NAMES alt_names(
+ reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext)));
+ if (!alt_names.get())
+ return;
+
+ for (size_t i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) {
+ const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i);
+ if (name->type == GEN_DNS && dns_names) {
+ const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName);
+ if (!dns_name)
+ continue;
+ int dns_name_len = ASN1_STRING_length(name->d.dNSName);
+ dns_names->push_back(
+ std::string(reinterpret_cast<const char*>(dns_name), dns_name_len));
+ } else if (name->type == GEN_IPADD && ip_addresses) {
+ const unsigned char* ip_addr = name->d.iPAddress->data;
+ if (!ip_addr)
+ continue;
+ int ip_addr_len = name->d.iPAddress->length;
+ if (ip_addr_len != static_cast<int>(kIPv4AddressSize) &&
+ ip_addr_len != static_cast<int>(kIPv6AddressSize)) {
+ // http://www.ietf.org/rfc/rfc3280.txt requires subjectAltName iPAddress
+ // to have 4 or 16 bytes, whereas in a name constraint it includes a
+ // net mask hence 8 or 32 bytes. Logging to help diagnose any mixup.
+ LOG(WARNING) << "Bad sized IP Address in cert: " << ip_addr_len;
+ continue;
+ }
+ ip_addresses->push_back(
+ std::string(reinterpret_cast<const char*>(ip_addr), ip_addr_len));
+ }
+ }
+}
+
+// Used to free a list of X509_NAMEs and the objects it points to.
+void sk_X509_NAME_free_all(STACK_OF(X509_NAME) * sk) {
+ sk_X509_NAME_pop_free(sk, X509_NAME_free);
+}
+
+} // namespace
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle handle) {
+ if (!handle)
+ return nullptr;
+ return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ if (cert_handle)
+ CFRelease(cert_handle);
+}
+
+void X509Certificate::Initialize() {
+ crypto::EnsureOpenSSLInit();
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+ ScopedX509 x509_cert = OSCertHandleToOpenSSL(cert_handle_);
+ if (!x509_cert)
+ return;
+ ASN1_INTEGER* serial_num = X509_get_serialNumber(x509_cert.get());
+ if (serial_num) {
+ // ASN1_INTEGERS represent the decoded number, in a format internal to
+ // OpenSSL. Most notably, this may have leading zeroes stripped off for
+ // numbers whose first byte is >= 0x80. Thus, it is necessary to
+ // re-encoded the integer back into DER, which is what the interface
+ // of X509Certificate exposes, to ensure callers get the proper (DER)
+ // value.
+ int bytes_required = i2c_ASN1_INTEGER(serial_num, nullptr);
+ unsigned char* buffer = reinterpret_cast<unsigned char*>(
+ base::WriteInto(&serial_number_, bytes_required + 1));
+ int bytes_written = i2c_ASN1_INTEGER(serial_num, &buffer);
+ DCHECK_EQ(static_cast<size_t>(bytes_written), serial_number_.size());
+ }
+
+ ParsePrincipal(cert_handle_, X509_get_subject_name(x509_cert.get()),
+ &subject_);
+ ParsePrincipal(cert_handle_, X509_get_issuer_name(x509_cert.get()), &issuer_);
+ x509_util::ParseDate(X509_get_notBefore(x509_cert.get()), &valid_start_);
+ x509_util::ParseDate(X509_get_notAfter(x509_cert.get()), &valid_expiry_);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(OSCertHandle cert) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert));
+ if (!cert_data)
+ return sha1;
+ DCHECK(CFDataGetBytePtr(cert_data));
+ DCHECK_NE(0, CFDataGetLength(cert_data));
+ CC_SHA1(CFDataGetBytePtr(cert_data), CFDataGetLength(cert_data), sha1.data);
+
+ return sha1;
+}
+
+// static
+SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) {
+ SHA256HashValue sha256;
+ memset(sha256.data, 0, sizeof(sha256.data));
+
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert));
+ if (!cert_data)
+ return sha256;
+ DCHECK(CFDataGetBytePtr(cert_data));
+ DCHECK_NE(0, CFDataGetLength(cert_data));
+ CC_SHA256(CFDataGetBytePtr(cert_data), CFDataGetLength(cert_data),
+ sha256.data);
+
+ return sha256;
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ CC_SHA1_CTX sha1_ctx;
+ CC_SHA1_Init(&sha1_ctx);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ ScopedCFTypeRef<CFDataRef> cert_data(
+ SecCertificateCopyData(intermediates[i]));
+ if (!cert_data)
+ return sha1;
+ CC_SHA1_Update(&sha1_ctx, CFDataGetBytePtr(cert_data),
+ CFDataGetLength(cert_data));
+ }
+ CC_SHA1_Final(sha1.data, &sha1_ctx);
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data,
+ size_t length) {
+ ScopedCFTypeRef<CFDataRef> cert_data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data),
+ base::checked_cast<CFIndex>(length), kCFAllocatorNull));
+ if (!cert_data)
+ return nullptr;
+ OSCertHandle cert_handle = SecCertificateCreateWithData(nullptr, cert_data);
+ if (!cert_handle)
+ return nullptr;
+ if (!IsValidOSCertHandle(cert_handle)) {
+ CFRelease(cert_handle);
+ return nullptr;
+ }
+ return cert_handle;
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data,
+ size_t length,
+ Format format) {
+ OSCertHandles results;
+
+ switch (format) {
+ case FORMAT_SINGLE_CERTIFICATE: {
+ OSCertHandle handle =
+ X509Certificate::CreateOSCertHandleFromBytes(data, length);
+ if (handle)
+ results.push_back(handle);
+ break;
+ }
+ case FORMAT_PKCS7: {
+ CreateOSCertHandlesFromPKCS7Bytes(data, length, &results);
+ break;
+ }
+ default: {
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+ }
+
+ return results;
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ ParseSubjectAltName(cert_handle_, dns_names, ip_addrs);
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+ std::string* encoded) {
+ base::StringPiece der;
+ if (!cert_handle)
+ return false;
+ ScopedCFTypeRef<CFDataRef> der_data(SecCertificateCopyData(cert_handle));
+ if (!der_data)
+ return false;
+ encoded->assign(reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+ CFDataGetLength(der_data));
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ return CFEqual(a, b);
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle(
+ base::PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return nullptr;
+
+ return X509Certificate::CreateOSCertHandleFromBytes(data, length);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ base::Pickle* pickle) {
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert_handle));
+ if (!cert_data)
+ return false;
+
+ return pickle->WriteData(
+ reinterpret_cast<const char*>(CFDataGetBytePtr(cert_data)),
+ CFDataGetLength(cert_data));
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle os_cert,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ *type = kPublicKeyTypeUnknown;
+ *size_bits = 0;
+ ScopedX509 cert = OSCertHandleToOpenSSL(os_cert);
+ if (!cert)
+ return;
+ crypto::ScopedEVP_PKEY scoped_key(X509_get_pubkey(cert.get()));
+ if (!scoped_key)
+ return;
+
+ EVP_PKEY* key = scoped_key.get();
+
+ switch (key->type) {
+ case EVP_PKEY_RSA:
+ *type = kPublicKeyTypeRSA;
+ break;
+ case EVP_PKEY_DSA:
+ *type = kPublicKeyTypeDSA;
+ break;
+ case EVP_PKEY_EC:
+ *type = kPublicKeyTypeECDSA;
+ break;
+ case EVP_PKEY_DH:
+ *type = kPublicKeyTypeDH;
+ break;
+ }
+ *size_bits = EVP_PKEY_bits(key);
+}
+
+bool X509Certificate::SupportsSSLClientAuth() const {
+ return false;
+}
+
+CFMutableArrayRef X509Certificate::CreateOSCertChainForCert() const {
+ CFMutableArrayRef cert_list =
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ if (!cert_list)
+ return nullptr;
+
+ CFArrayAppendValue(cert_list, os_cert_handle());
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i)
+ CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]);
+
+ return cert_list;
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+ if (valid_issuers.empty())
+ return false;
+
+ // Convert to a temporary list of X509_NAME objects.
+ // It will own the objects it points to.
+ crypto::ScopedOpenSSL<STACK_OF(X509_NAME), sk_X509_NAME_free_all>
+ issuer_names(sk_X509_NAME_new_null());
+ if (!issuer_names)
+ return false;
+
+ for (std::vector<std::string>::const_iterator it = valid_issuers.begin();
+ it != valid_issuers.end(); ++it) {
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(it->data());
+ long len = static_cast<long>(it->length());
+ X509_NAME* ca_name = d2i_X509_NAME(nullptr, &p, len);
+ if (ca_name == nullptr)
+ return false;
+ sk_X509_NAME_push(issuer_names.get(), ca_name);
+ }
+
+ // Create a temporary list of X509_NAME objects corresponding
+ // to the certificate chain. It doesn't own the object it points to.
+ std::vector<X509_NAME*> cert_names;
+ ScopedX509 x509_cert = OSCertHandleToOpenSSL(cert_handle_);
+ if (!x509_cert)
+ return false;
+ X509_NAME* issuer = X509_get_issuer_name(x509_cert.get());
+ if (issuer == nullptr)
+ return false;
+
+ cert_names.push_back(issuer);
+ for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
+ it != intermediate_ca_certs_.end(); ++it) {
+ ScopedX509 intermediate_cert = OSCertHandleToOpenSSL(*it);
+ if (!intermediate_cert)
+ return false;
+ issuer = X509_get_issuer_name(intermediate_cert.get());
+ if (issuer == nullptr)
+ return false;
+ cert_names.push_back(issuer);
+ }
+
+ // and 'cert_names'.
+ for (size_t n = 0; n < cert_names.size(); ++n) {
+ for (size_t m = 0; m < sk_X509_NAME_num(issuer_names.get()); ++m) {
+ X509_NAME* issuer = sk_X509_NAME_value(issuer_names.get(), m);
+ if (X509_NAME_cmp(issuer, cert_names[n]) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
+bool X509Certificate::IsSelfSigned(OSCertHandle os_cert) {
+ ScopedX509 cert = OSCertHandleToOpenSSL(os_cert);
+ if (!cert)
+ return false;
+ crypto::ScopedEVP_PKEY scoped_key(X509_get_pubkey(cert.get()));
+ if (!scoped_key)
+ return false;
+
+ // NOTE: X509_verify() returns 1 in case of success, 0 or -1 on error.
+ return X509_verify(cert.get(), scoped_key.get()) == 1;
+}
+
+} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index 1af2162..e0df0b3 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -240,6 +240,13 @@
'cert/x509_util_ios.h',
],
}],
+ [ 'use_nss_verifier == 1 and OS == "ios"', {
+ 'sources!': [
+ 'cert/cert_verify_proc_ios.cc',
+ 'cert/cert_verify_proc_ios.h',
+ 'cert/x509_certificate_openssl_ios.cc',
+ ],
+ }],
[ 'use_openssl==1', {
'sources!': [
'quic/test_tools/crypto_test_utils_nss.cc',
diff --git a/net/net.gypi b/net/net.gypi
index a224d4b..f946a5b 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -570,6 +570,8 @@
'cert/cert_verify_proc.h',
'cert/cert_verify_proc_android.cc',
'cert/cert_verify_proc_android.h',
+ 'cert/cert_verify_proc_ios.cc',
+ 'cert/cert_verify_proc_ios.h',
'cert/cert_verify_proc_mac.cc',
'cert/cert_verify_proc_mac.h',
'cert/cert_verify_proc_nss.cc',
@@ -629,6 +631,7 @@
'cert/x509_certificate_ios.cc',
'cert/x509_certificate_mac.cc',
'cert/x509_certificate_nss.cc',
+ 'cert/x509_certificate_openssl_ios.cc',
'cert/x509_certificate_win.cc',
'cert/x509_util_android.cc',
'cert/x509_util_android.h',
diff --git a/net/net_common.gypi b/net/net_common.gypi
index 9bd4aac..330c617 100644
--- a/net/net_common.gypi
+++ b/net/net_common.gypi
@@ -309,6 +309,13 @@
'cert/x509_util_ios.h',
],
}],
+ [ 'OS == "ios" and use_nss_verifier == 1', {
+ 'sources!': [
+ 'cert/cert_verify_proc_ios.cc',
+ 'cert/cert_verify_proc_ios.h',
+ 'cert/x509_certificate_openssl_ios.cc',
+ ],
+ }],
[ 'enable_websockets == 1', {
'sources': ['<@(net_websockets_sources)']
}],
@@ -426,5 +433,10 @@
['include', '^proxy/proxy_server_mac\\.cc$'],
],
}],
+ ['OS == "ios" and <(use_nss_verifier) == 0', {
+ 'sources/': [
+ ['include', '^cert/test_root_certs_mac\\.cc$'],
+ ],
+ }],
],
}