trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r..@apache.org
Subject [trafficserver] branch master updated: Dual cert support for BoringSSL (#8014)
Date Tue, 20 Jul 2021 15:39:03 GMT
This is an automated email from the ASF dual-hosted git repository.

rrm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 46422df  Dual cert support for BoringSSL (#8014)
46422df is described below

commit 46422dfaf007eccbda03183557805902ac853cef
Author: Randall Meyer <rrm@apache.org>
AuthorDate: Tue Jul 20 08:38:51 2021 -0700

    Dual cert support for BoringSSL (#8014)
    
    * fixes preprocessor test for BoringSSL
    
    * Dual cert support for BoringSSL
    
    BoringSSL removed support for slotted SSL_CTXs. This is key for
    supporting multiple certs for a CTX. When BoringSSL is used,
    multiple CTXs will be created if more than one cert type is specified.
    When a client connection does come in, ATS will interrogate the
    connection and present the approriate CTX (preferring ECDSA)
---
 doc/admin-guide/files/sni.yaml.en.rst          |   4 +-
 iocore/net/BoringSSLUtils.cc                   | 138 ++++++++
 iocore/net/BoringSSLUtils.h                    |  30 ++
 iocore/net/Makefile.am                         |   6 +-
 iocore/net/P_SSLCertLookup.h                   |  24 +-
 iocore/net/P_SSLUtils.h                        |  28 +-
 iocore/net/SSLCertLookup.cc                    |  63 +++-
 iocore/net/SSLConfig.cc                        |   2 +-
 iocore/net/SSLUtils.cc                         | 450 +++++++++++++++++--------
 iocore/net/TLSSNISupport.cc                    |  13 +-
 iocore/net/TLSSNISupport.h                     |   6 +-
 iocore/net/YamlSNIConfig.cc                    |   2 +-
 tests/gold_tests/tls_hooks/tls_hooks14.test.py |   4 -
 tests/gold_tests/tls_hooks/tls_hooks16.test.py |   3 +-
 14 files changed, 612 insertions(+), 161 deletions(-)

diff --git a/doc/admin-guide/files/sni.yaml.en.rst b/doc/admin-guide/files/sni.yaml.en.rst
index 8478c50..f1771f2 100644
--- a/doc/admin-guide/files/sni.yaml.en.rst
+++ b/doc/admin-guide/files/sni.yaml.en.rst
@@ -96,8 +96,8 @@ valid_tls_versions_in     This specifies the list of TLS protocols that will be
                           :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`,
                           :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential
                           values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3.  You must list all protocols that |TS|
-                          should offer to the client when using this key.  This key is only valid for openssl
-                          1.1.0 and later. Older versions of openssl do not provide a hook early enough to update
+                          should offer to the client when using this key.  This key is only valid for OpenSSL
+                          1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update
                           the SSL object.  It is a syntax error for |TS| built against earlier versions.
 
 client_cert               The file containing the client certificate to use for the outbound connection.
diff --git a/iocore/net/BoringSSLUtils.cc b/iocore/net/BoringSSLUtils.cc
new file mode 100644
index 0000000..a5a6349
--- /dev/null
+++ b/iocore/net/BoringSSLUtils.cc
@@ -0,0 +1,138 @@
+/** @file
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+// Borrowed from Envoy
+// https://github.com/envoyproxy/envoy/blob/329b2491949fc52f4dc5c4a778ea158bfe6fe979/source/extensions/transport_sockets/tls/context_impl.cc#L962
+
+#include "BoringSSLUtils.h"
+
+#ifdef OPENSSL_IS_BORINGSSL
+namespace BoringSSLUtils
+{
+bool
+cbsContainsU16(CBS &cbs, uint16_t n)
+{
+  while (CBS_len(&cbs) > 0) {
+    uint16_t v;
+    if (!CBS_get_u16(&cbs, &v)) {
+      return false;
+    }
+    if (v == n) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool
+isCipherEnabled(SSL_CTX *ctx, uint16_t cipher_id, uint16_t client_version)
+{
+  const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_id);
+  if (c == nullptr) {
+    return false;
+  }
+  // Skip TLS 1.2 only ciphersuites unless the client supports it.
+  if (SSL_CIPHER_get_min_version(c) > client_version) {
+    return false;
+  }
+  if (SSL_CIPHER_get_auth_nid(c) != NID_auth_ecdsa) {
+    return false;
+  }
+  for (const SSL_CIPHER *our_c : SSL_CTX_get_ciphers(ctx)) {
+    if (SSL_CIPHER_get_id(our_c) == SSL_CIPHER_get_id(c)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
+isClientEcdsaCapable(const SSL_CLIENT_HELLO *ssl_client_hello)
+{
+  CBS client_hello;
+  CBS_init(&client_hello, ssl_client_hello->client_hello, ssl_client_hello->client_hello_len);
+
+  // This is the TLSv1.3 case (TLSv1.2 on the wire and the supported_versions extensions present).
+  // We just need to look at signature algorithms.
+  const uint16_t client_version = ssl_client_hello->version;
+  if (client_version == TLS1_2_VERSION && true) {
+    // If the supported_versions extension is found then we assume that the client is competent
+    // enough that just checking the signature_algorithms is sufficient.
+    const uint8_t *supported_versions_data;
+    size_t supported_versions_len;
+    if (SSL_early_callback_ctx_extension_get(ssl_client_hello, TLSEXT_TYPE_supported_versions, &supported_versions_data,
+                                             &supported_versions_len)) {
+      const uint8_t *signature_algorithms_data;
+      size_t signature_algorithms_len;
+      if (SSL_early_callback_ctx_extension_get(ssl_client_hello, TLSEXT_TYPE_signature_algorithms, &signature_algorithms_data,
+                                               &signature_algorithms_len)) {
+        CBS signature_algorithms_ext, signature_algorithms;
+        CBS_init(&signature_algorithms_ext, signature_algorithms_data, signature_algorithms_len);
+        if (!CBS_get_u16_length_prefixed(&signature_algorithms_ext, &signature_algorithms) ||
+            CBS_len(&signature_algorithms_ext) != 0) {
+          return false;
+        }
+        if (cbsContainsU16(signature_algorithms, SSL_SIGN_ECDSA_SECP256R1_SHA256)) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+  }
+
+  // Otherwise we are < TLSv1.3 and need to look at both the curves in the supported_groups for
+  // ECDSA and also for a compatible cipher suite. https://tools.ietf.org/html/rfc4492#section-5.1.1
+  const uint8_t *curvelist_data;
+  size_t curvelist_len;
+  if (!SSL_early_callback_ctx_extension_get(ssl_client_hello, TLSEXT_TYPE_supported_groups, &curvelist_data, &curvelist_len)) {
+    return false;
+  }
+
+  CBS curvelist;
+  CBS_init(&curvelist, curvelist_data, curvelist_len);
+
+  // We only support P256 ECDSA curves today.
+  if (!cbsContainsU16(curvelist, SSL_CURVE_SECP256R1)) {
+    return false;
+  }
+
+  // The client must have offered an ECDSA ciphersuite that we like.
+  CBS cipher_suites;
+  CBS_init(&cipher_suites, ssl_client_hello->cipher_suites, ssl_client_hello->cipher_suites_len);
+
+  while (CBS_len(&cipher_suites) > 0) {
+    uint16_t cipher_id;
+    if (!CBS_get_u16(&cipher_suites, &cipher_id)) {
+      return false;
+    }
+
+    SSL_CTX *ctx = SSL_get_SSL_CTX(ssl_client_hello->ssl);
+    if (isCipherEnabled(ctx, cipher_id, client_version)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+} // namespace BoringSSLUtils
+#endif
diff --git a/iocore/net/BoringSSLUtils.h b/iocore/net/BoringSSLUtils.h
new file mode 100644
index 0000000..dcd1760
--- /dev/null
+++ b/iocore/net/BoringSSLUtils.h
@@ -0,0 +1,30 @@
+/** @file
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include <openssl/ssl.h>
+
+#ifdef OPENSSL_IS_BORINGSSL
+namespace BoringSSLUtils
+{
+bool isClientEcdsaCapable(const SSL_CLIENT_HELLO *ssl_client_hello);
+
+} // namespace BoringSSLUtils
+#endif
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index 1b97bb8..e94e6e1 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -119,6 +119,8 @@ libinknet_a_SOURCES = \
 	ALPNSupport.cc \
 	BIO_fastopen.cc \
 	BIO_fastopen.h \
+	BoringSSLUtils.cc \
+	BoringSSLUtils.h \
 	Connection.cc \
 	I_Net.h \
 	I_NetProcessor.h \
@@ -143,7 +145,7 @@ libinknet_a_SOURCES = \
 	P_Socks.h \
 	P_SSLCertLookup.h \
 	P_SSLConfig.h \
-        P_SSLSecret.h \
+	P_SSLSecret.h \
 	P_SSLNetAccept.h \
 	P_SSLNetProcessor.h \
 	P_SSLNetVConnection.h \
@@ -172,7 +174,7 @@ libinknet_a_SOURCES = \
 	SSLClientCoordinator.cc \
 	SSLClientUtils.cc \
 	SSLConfig.cc \
-        SSLSecret.cc \
+	SSLSecret.cc \
 	SSLDiags.cc \
 	SSLInternal.cc \
 	SSLNetAccept.cc \
diff --git a/iocore/net/P_SSLCertLookup.h b/iocore/net/P_SSLCertLookup.h
index 6d03f70..a36aba9 100644
--- a/iocore/net/P_SSLCertLookup.h
+++ b/iocore/net/P_SSLCertLookup.h
@@ -40,6 +40,14 @@ enum class SSLCertContextOption {
   OPT_TUNNEL ///< Just tunnel, don't terminate.
 };
 
+/** Used to discern the context type when BoringSSL is used for the SSL implementation.
+ */
+enum class SSLCertContextType {
+  GENERIC, ///< Generic Context (can be either EC or RSA)
+  RSA,     ///< RSA-based Context
+  EC       ///< EC-based Context
+};
+
 /**
    @brief Gather user provided settings from ssl_multicert.config in to this single struct
  */
@@ -100,14 +108,17 @@ public:
     : ctx_mutex(), ctx(c, SSL_CTX_free), opt(SSLCertContextOption::OPT_NONE), userconfig(nullptr), keyblock(nullptr)
   {
   }
-  SSLCertContext(shared_SSL_CTX sc, shared_SSLMultiCertConfigParams u)
-    : ctx_mutex(), ctx(sc), opt(u->opt), userconfig(u), keyblock(nullptr)
+
+  SSLCertContext(shared_SSL_CTX sc, SSLCertContextType ctx_type, shared_SSLMultiCertConfigParams u)
+    : ctx_mutex(), ctx(sc), ctx_type(ctx_type), opt(u->opt), userconfig(u), keyblock(nullptr)
   {
   }
-  SSLCertContext(shared_SSL_CTX sc, shared_SSLMultiCertConfigParams u, shared_ssl_ticket_key_block kb)
-    : ctx_mutex(), ctx(sc), opt(u->opt), userconfig(u), keyblock(kb)
+
+  SSLCertContext(shared_SSL_CTX sc, SSLCertContextType ctx_type, shared_SSLMultiCertConfigParams u, shared_ssl_ticket_key_block kb)
+    : ctx_mutex(), ctx(sc), ctx_type(ctx_type), opt(u->opt), userconfig(u), keyblock(kb)
   {
   }
+
   SSLCertContext(SSLCertContext const &other);
   SSLCertContext &operator=(SSLCertContext const &other);
   ~SSLCertContext() {}
@@ -117,6 +128,7 @@ public:
   void setCtx(shared_SSL_CTX sc);
   void release();
 
+  SSLCertContextType ctx_type                = SSLCertContextType::GENERIC;
   SSLCertContextOption opt                   = SSLCertContextOption::OPT_NONE; ///< Special handling option.
   shared_SSLMultiCertConfigParams userconfig = nullptr;                        ///< User provided settings
   shared_ssl_ticket_key_block keyblock       = nullptr;                        ///< session keys associated with this address
@@ -124,6 +136,8 @@ public:
 
 struct SSLCertLookup : public ConfigInfo {
   SSLContextStorage *ssl_storage;
+  SSLContextStorage *ec_storage;
+
   shared_SSL_CTX ssl_default;
   bool is_valid = true;
 
@@ -141,7 +155,7 @@ struct SSLCertLookup : public ConfigInfo {
       Exact matches have priority, then wildcards. Only destination based matches are checked.
       @return @c A pointer to the matched context, @c nullptr if no match is found.
   */
-  SSLCertContext *find(const std::string &name) const;
+  SSLCertContext *find(const std::string &name, SSLCertContextType ctxType = SSLCertContextType::GENERIC) const;
 
   // Return the last-resort default TLS context if there is no name or address match.
   SSL_CTX *
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index 6c0d7d7..37221b7 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -42,7 +42,7 @@ class SSLNetVConnection;
 
 typedef int ssl_error_t;
 
-#ifndef OPENSSL_IS_BORING
+#ifndef OPENSSL_IS_BORINGSSL
 typedef int ssl_curve_id;
 #else
 typedef uint16_t ssl_curve_id;
@@ -51,6 +51,15 @@ typedef uint16_t ssl_curve_id;
 // Return the SSL Curve ID associated to the specified SSL connection
 ssl_curve_id SSLGetCurveNID(SSL *ssl);
 
+enum class SSLCertContextType;
+
+struct SSLLoadingContext {
+  SSL_CTX *ctx;
+  SSLCertContextType ctx_type;
+
+  explicit SSLLoadingContext(SSL_CTX *c, SSLCertContextType ctx_type) : ctx(c), ctx_type(ctx_type) {}
+};
+
 /**
     @brief Load SSL certificates from ssl_multicert.config and setup SSLCertLookup for SSLCertificateConfig
  */
@@ -59,6 +68,7 @@ class SSLMultiCertConfigLoader
 public:
   struct CertLoadData {
     std::vector<std::string> cert_names_list, key_list, ca_list, ocsp_list;
+    std::vector<SSLCertContextType> cert_type_list;
   };
   SSLMultiCertConfigLoader(const SSLConfigParams *p) : _params(p) {}
   virtual ~SSLMultiCertConfigLoader(){};
@@ -66,15 +76,21 @@ public:
   bool load(SSLCertLookup *lookup);
 
   virtual SSL_CTX *default_server_ssl_ctx();
-  virtual SSL_CTX *init_server_ssl_ctx(CertLoadData const &data, const SSLMultiCertConfigParams *sslMultCertSettings,
-                                       std::set<std::string> &names);
 
-  static bool load_certs(SSL_CTX *ctx, CertLoadData const &data, const SSLConfigParams *params,
+  virtual std::vector<SSLLoadingContext> init_server_ssl_ctx(CertLoadData const &data,
+                                                             const SSLMultiCertConfigParams *sslMultCertSettings,
+                                                             std::set<std::string> &names);
+
+  static bool load_certs(SSL_CTX *ctx, const std::vector<std::string> &cert_names_list,
+                         const std::vector<std::string> &key_names_list, CertLoadData const &data, const SSLConfigParams *params,
                          const SSLMultiCertConfigParams *sslMultCertSettings);
+
   bool load_certs_and_cross_reference_names(std::vector<X509 *> &cert_list, CertLoadData &data, const SSLConfigParams *params,
                                             const SSLMultiCertConfigParams *sslMultCertSettings,
                                             std::set<std::string> &common_names,
-                                            std::unordered_map<int, std::set<std::string>> &unique_names);
+                                            std::unordered_map<int, std::set<std::string>> &unique_names,
+                                            SSLCertContextType *certType);
+
   static bool set_session_id_context(SSL_CTX *ctx, const SSLConfigParams *params,
                                      const SSLMultiCertConfigParams *sslMultCertSettings);
 
@@ -87,7 +103,7 @@ protected:
   const SSLConfigParams *_params;
 
   bool _store_single_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams &sslMultCertSettings, shared_SSL_CTX ctx,
-                             std::set<std::string> &names);
+                             SSLCertContextType ctx_type, std::set<std::string> &names);
 
 private:
   virtual const char *_debug_tag() const;
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index 69c6680..06d5c4d 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -253,6 +253,7 @@ SSLCertContext::SSLCertContext(SSLCertContext const &other)
   opt        = other.opt;
   userconfig = other.userconfig;
   keyblock   = other.keyblock;
+  ctx_type   = other.ctx_type;
   std::lock_guard<std::mutex> lock(other.ctx_mutex);
   ctx = other.ctx;
 }
@@ -264,6 +265,7 @@ SSLCertContext::operator=(SSLCertContext const &other)
     this->opt        = other.opt;
     this->userconfig = other.userconfig;
     this->keyblock   = other.keyblock;
+    this->ctx_type   = other.ctx_type;
     std::lock_guard<std::mutex> lock(other.ctx_mutex);
     this->ctx = other.ctx;
   }
@@ -284,16 +286,30 @@ SSLCertContext::setCtx(shared_SSL_CTX sc)
   ctx = std::move(sc);
 }
 
-SSLCertLookup::SSLCertLookup() : ssl_storage(new SSLContextStorage()), ssl_default(nullptr), is_valid(true) {}
+SSLCertLookup::SSLCertLookup()
+  : ssl_storage(new SSLContextStorage()), ec_storage(new SSLContextStorage()), ssl_default(nullptr), is_valid(true)
+{
+}
 
 SSLCertLookup::~SSLCertLookup()
 {
   delete this->ssl_storage;
+  delete this->ec_storage;
 }
 
 SSLCertContext *
-SSLCertLookup::find(const std::string &address) const
+SSLCertLookup::find(const std::string &address, SSLCertContextType ctxType) const
 {
+#ifdef OPENSSL_IS_BORINGSSL
+  // If the context is EC supportable, try finding that first.
+  if (ctxType == SSLCertContextType::EC) {
+    auto ctx = this->ec_storage->lookup(address);
+    if (ctx != nullptr) {
+      return ctx;
+    }
+  }
+#endif
+  // non-EC last resort
   return this->ssl_storage->lookup(address);
 }
 
@@ -303,6 +319,24 @@ SSLCertLookup::find(const IpEndpoint &address) const
   SSLCertContext *cc;
   SSLAddressLookupKey key(address);
 
+#ifdef OPENSSL_IS_BORINGSSL
+  // If the context is EC supportable, try finding that first.
+  if ((cc = this->ec_storage->lookup(key.get()))) {
+    return cc;
+  }
+
+  // If that failed, try the address without the port.
+  if (address.port()) {
+    key.split();
+    if ((cc = this->ec_storage->lookup(key.get()))) {
+      return cc;
+    }
+  }
+
+  // reset for search across RSA
+  key = SSLAddressLookupKey(address);
+#endif
+
   // First try the full address.
   if ((cc = this->ssl_storage->lookup(key.get()))) {
     return cc;
@@ -320,14 +354,39 @@ SSLCertLookup::find(const IpEndpoint &address) const
 int
 SSLCertLookup::insert(const char *name, SSLCertContext const &cc)
 {
+#ifdef OPENSSL_IS_BORINGSSL
+  switch (cc.ctx_type) {
+  case SSLCertContextType::GENERIC:
+  case SSLCertContextType::RSA:
+    return this->ssl_storage->insert(name, cc);
+  case SSLCertContextType::EC:
+    return this->ec_storage->insert(name, cc);
+  default:
+    ink_assert(false);
+  }
+#else
   return this->ssl_storage->insert(name, cc);
+#endif
 }
 
 int
 SSLCertLookup::insert(const IpEndpoint &address, SSLCertContext const &cc)
 {
   SSLAddressLookupKey key(address);
+
+#ifdef OPENSSL_IS_BORINGSSL
+  switch (cc.ctx_type) {
+  case SSLCertContextType::GENERIC:
+  case SSLCertContextType::RSA:
+    return this->ssl_storage->insert(key.get(), cc);
+  case SSLCertContextType::EC:
+    return this->ec_storage->insert(key.get(), cc);
+  default:
+    ink_assert(false);
+  }
+#else
   return this->ssl_storage->insert(key.get(), cc);
+#endif
 }
 
 unsigned
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index 912f6ba..b81506e 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -710,7 +710,7 @@ cleanup_bio(BIO *&biop)
 void
 SSLConfigParams::updateCTX(const std::string &cert_secret_name) const
 {
-  // Clear the corresponding client CTX's.  They will be lazy loaded later
+  // Clear the corresponding client CTXs.  They will be lazy loaded later
   Debug("ssl", "Update cert %s", cert_secret_name.c_str());
   this->clearCTX(cert_secret_name);
 
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index c106017..aa3a1c7 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -38,6 +38,7 @@
 #include "P_OCSPStapling.h"
 #include "P_SSLSNI.h"
 #include "P_SSLConfig.h"
+#include "BoringSSLUtils.h"
 #include "ProxyProtocol.h"
 #include "SSLSessionCache.h"
 #include "SSLSessionTicket.h"
@@ -253,16 +254,18 @@ ssl_rm_cached_session(SSL_CTX *ctx, SSL_SESSION *sess)
 }
 
 static int
-set_context_cert(SSL *ssl)
+set_context_cert(SSL *ssl, void *arg)
 {
   shared_SSL_CTX ctx  = nullptr;
   SSL_CTX *verify_ctx = nullptr;
   SSLCertContext *cc  = nullptr;
   SSLCertificateConfig::scoped_config lookup;
-  const char *servername   = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-  SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
-  bool found               = true;
-  int retval               = 1;
+
+  const char *servername     = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  SSLNetVConnection *netvc   = SSLNetVCAccess(ssl);
+  bool found                 = true;
+  int retval                 = 1;
+  SSLCertContextType ctxType = SSLCertContextType::GENERIC;
 
   if (!netvc || netvc->ssl != ssl) {
     Debug("ssl.error", "set_context_cert call back on stale netvc");
@@ -279,11 +282,19 @@ set_context_cert(SSL *ssl)
     goto done;
   }
 
+#ifdef OPENSSL_IS_BORINGSSL
+  if (arg != nullptr) {
+    const SSL_CLIENT_HELLO *client_hello = (const SSL_CLIENT_HELLO *)arg;
+    const bool client_ecdsa_capable      = BoringSSLUtils::isClientEcdsaCapable(client_hello);
+    ctxType                              = client_ecdsa_capable ? SSLCertContextType::EC : SSLCertContextType::RSA;
+  }
+#endif
+
   // The incoming SSL_CTX is either the one mapped from the inbound IP address or the default one. If we
   // don't find a name-based match at this point, we *do not* want to mess with the context because we've
   // already made a best effort to find the best match.
   if (likely(servername)) {
-    cc = lookup->find(const_cast<char *>(servername));
+    cc = lookup->find(servername, ctxType);
     if (cc) {
       ctx = cc->getCtx();
     }
@@ -342,6 +353,7 @@ set_context_cert(SSL *ssl)
   }
 
   verify_ctx = SSL_get_SSL_CTX(ssl);
+  // set_context_cert found SSL context for ...
   Debug("ssl", "ssl_cert_callback %s SSL context %p for requested name '%s'", found ? "found" : "using", verify_ctx, servername);
 
   if (verify_ctx == nullptr) {
@@ -410,14 +422,48 @@ ssl_client_hello_callback(SSL *s, int *al, void *arg)
   }
   return SSL_CLIENT_HELLO_SUCCESS;
 }
+#elif defined(OPENSSL_IS_BORINGSSL)
+static ssl_select_cert_result_t
+ssl_client_hello_callback(const SSL_CLIENT_HELLO *client_hello)
+{
+  SSL *s = client_hello->ssl;
+  TLSSNISupport *snis = TLSSNISupport::getInstance(s);
+
+  if (snis) {
+    snis->on_client_hello(client_hello);
+    int ret = snis->perform_sni_action();
+    if (ret != SSL_TLSEXT_ERR_OK) {
+      return ssl_select_cert_error;
+    }
+  } else {
+    // This error suggests either of these:
+    // 1) Call back on unsupported netvc -- Don't register callback unnecessarily
+    // 2) Call back on stale netvc
+    Debug("ssl.error", "ssl_client_hello_callback was called unexpectedly");
+    return ssl_select_cert_error;
+  }
+
+  SSLNetVConnection *netvc = SSLNetVCAccess(s);
+  if (!netvc || netvc->ssl != s) {
+    Debug("ssl.error", "ssl_client_hello_callback call back on stale netvc");
+    return ssl_select_cert_error;
+  }
+
+  bool reenabled = netvc->callHooks(TS_EVENT_SSL_CLIENT_HELLO);
+
+  if (!reenabled) {
+    return ssl_select_cert_retry;
+  }
+  return ssl_select_cert_success;
+}
 #endif
 
 /**
  * Called before either the server or the client certificate is used
- * Return 1 on success, 0 on error, or -1 to pause
+ * Return 1 on success, 0 on error, or -1 to pause, -2 to retry
  */
 static int
-ssl_cert_callback(SSL *ssl, void * /*arg*/)
+ssl_cert_callback(SSL *ssl, void *arg)
 {
   SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
   bool reenabled;
@@ -430,13 +476,17 @@ ssl_cert_callback(SSL *ssl, void * /*arg*/)
 
   // If we are in tunnel mode, don't select a cert.  Pause!
   if (HttpProxyPort::TRANSPORT_BLIND_TUNNEL == netvc->attributes) {
+#ifdef OPENSSL_IS_BORINGSSL
+    return -2; // Retry
+#else
     return -1; // Pause
+#endif
   }
 
   // Do the common certificate lookup only once.  If we pause
   // and restart processing, do not execute the common logic again
   if (!netvc->calledHooks(TS_EVENT_SSL_CERT)) {
-    retval = set_context_cert(ssl);
+    retval = set_context_cert(ssl, arg);
     if (retval != 1) {
       return retval;
     }
@@ -463,7 +513,7 @@ ssl_servername_callback(SSL *ssl, int *al, void *arg)
   TLSSNISupport *snis = TLSSNISupport::getInstance(ssl);
   if (snis) {
     snis->on_servername(ssl, al, arg);
-#if !TS_USE_HELLO_CB
+#if !TS_USE_HELLO_CB && !defined(OPENSSL_IS_BORINGSSL)
     // Only call the SNI actions here if not already performed in the HELLO_CB
     int ret = snis->perform_sni_action();
     if (ret != SSL_TLSEXT_ERR_OK) {
@@ -1099,12 +1149,48 @@ void
 SSLMultiCertConfigLoader::_set_handshake_callbacks(SSL_CTX *ctx)
 {
   // Make sure the callbacks are set
+#ifndef OPENSSL_IS_BORINGSSL
   SSL_CTX_set_cert_cb(ctx, ssl_cert_callback, nullptr);
   SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_callback);
 
 #if TS_USE_HELLO_CB
   SSL_CTX_set_client_hello_cb(ctx, ssl_client_hello_callback, nullptr);
 #endif
+#else
+  SSL_CTX_set_select_certificate_cb(ctx, [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t {
+    ssl_select_cert_result_t res;
+    res = ssl_client_hello_callback(client_hello);
+    if (res == ssl_select_cert_error) {
+      return res;
+    }
+
+    res = (ssl_servername_callback(client_hello->ssl, nullptr, nullptr) == SSL_TLSEXT_ERR_OK) ? ssl_select_cert_success :
+                                                                                                ssl_select_cert_error;
+    if (res == ssl_select_cert_error) {
+      return res;
+    }
+
+    int cbres = ssl_cert_callback(client_hello->ssl, (void *)client_hello);
+    switch (cbres) {
+    case -2:
+      res = ssl_select_cert_retry;
+      break;
+    case -1:
+      res = ssl_select_cert_success;
+      break;
+    case 0:
+      res = ssl_select_cert_error;
+      break;
+    case 1:
+      res = ssl_select_cert_success;
+      break;
+    default:
+      ink_assert(!"unhandled cert result");
+    }
+
+    return res;
+  });
+#endif
 }
 
 void
@@ -1154,99 +1240,147 @@ setClientCertCACerts(SSL *ssl, const char *file, const char *dir)
    Initialize SSL_CTX for server
    This is public function because of used by SSLCreateServerContext.
  */
-SSL_CTX *
+std::vector<SSLLoadingContext>
 SSLMultiCertConfigLoader::init_server_ssl_ctx(CertLoadData const &data, const SSLMultiCertConfigParams *sslMultCertSettings,
                                               std::set<std::string> &names)
 {
-  const SSLConfigParams *params = this->_params;
+  std::vector<std::vector<std::string>> cert_names;
+  std::vector<std::vector<std::string>> key_names;
+  std::vector<std::string> key_names_list;
+
+  bool generate_default_ctx = data.cert_names_list.empty();
+  if (!generate_default_ctx) {
+#ifdef OPENSSL_IS_BORINGSSL
+    for (auto const &name : data.cert_names_list) {
+      cert_names.emplace_back(std::vector({name}));
+    }
+    for (auto const &name : data.key_list) {
+      key_names.emplace_back(std::vector({name}));
+    }
+#else
+    if (!data.cert_names_list.empty()) {
+      cert_names.emplace_back(data.cert_names_list);
+      key_names.emplace_back(data.key_list);
+    }
+#endif
+  } else {
+    // In the case of no cert_names, we still want to create a
+    // ctx with all the bells and whistles (as much as possible)
+    cert_names.emplace_back(std::vector({std::string("default")}));
+    key_names.emplace_back(std::vector({std::string("default")}));
+  }
+
+  SSLCertContextType ctx_type = SSLCertContextType::GENERIC;
+  std::vector<SSLLoadingContext> ret;
+  unsigned int i = 0;
+  SSL_CTX *ctx   = nullptr;
+  for (auto const &cert_names_list : cert_names) {
+    if (i < key_names.size()) {
+      key_names_list = key_names[i];
+    } else {
+      key_names_list.clear();
+    }
 
-  SSL_CTX *ctx = this->default_server_ssl_ctx();
+    ctx = this->default_server_ssl_ctx();
 
-  // disable selected protocols
-  SSL_CTX_set_options(ctx, params->ssl_ctx_options);
+    ctx_type = (!generate_default_ctx && i < data.cert_type_list.size()) ? data.cert_type_list[i] : SSLCertContextType::GENERIC;
 
-  if (!this->_setup_session_cache(ctx)) {
-    goto fail;
-  }
+    Debug("ssl", "Creating new context %p cert_count=%ld initial: %s", ctx, cert_names_list.size(), cert_names_list[0].c_str());
+
+    // disable selected protocols
+    SSL_CTX_set_options(ctx, _params->ssl_ctx_options);
+
+    if (!this->_setup_session_cache(ctx)) {
+      goto fail;
+    }
 
 #ifdef SSL_MODE_RELEASE_BUFFERS
-  Debug("ssl", "enabling SSL_MODE_RELEASE_BUFFERS");
-  SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
+    Debug("ssl", "enabling SSL_MODE_RELEASE_BUFFERS");
+    SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
 #endif
 
 #ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
-  SSL_CTX_set_options(ctx, SSL_OP_SAFARI_ECDHE_ECDSA_BUG);
+    SSL_CTX_set_options(ctx, SSL_OP_SAFARI_ECDHE_ECDSA_BUG);
 #endif
 
-  if (sslMultCertSettings) {
-    if (!this->_setup_dialog(ctx, sslMultCertSettings)) {
-      goto fail;
-    }
+    if (sslMultCertSettings) {
+      if (!this->_setup_dialog(ctx, sslMultCertSettings)) {
+        goto fail;
+      }
+
+      if (sslMultCertSettings->cert && !generate_default_ctx) {
+        if (!SSLMultiCertConfigLoader::load_certs(ctx, cert_names_list, key_names_list, data, _params, sslMultCertSettings)) {
+          goto fail;
+        }
+      }
 
-    if (sslMultCertSettings->cert) {
-      if (!SSLMultiCertConfigLoader::load_certs(ctx, data, params, sslMultCertSettings)) {
+      if (!this->_set_verify_path(ctx, sslMultCertSettings)) {
+        goto fail;
+      }
+
+      if (!this->_setup_session_ticket(ctx, sslMultCertSettings)) {
         goto fail;
       }
     }
 
-    if (!this->_set_verify_path(ctx, sslMultCertSettings)) {
+    if (!this->_setup_client_cert_verification(ctx)) {
       goto fail;
     }
 
-    if (!this->_setup_session_ticket(ctx, sslMultCertSettings)) {
+    if (!SSLMultiCertConfigLoader::set_session_id_context(ctx, _params, sslMultCertSettings)) {
       goto fail;
     }
-  }
 
-  if (!this->_setup_client_cert_verification(ctx)) {
-    goto fail;
-  }
+    if (!this->_set_cipher_suites_for_legacy_versions(ctx)) {
+      goto fail;
+    }
 
-  if (!SSLMultiCertConfigLoader::set_session_id_context(ctx, params, sslMultCertSettings)) {
-    goto fail;
-  }
+    if (!this->_set_cipher_suites(ctx)) {
+      goto fail;
+    }
 
-  if (!this->_set_cipher_suites_for_legacy_versions(ctx)) {
-    goto fail;
-    ;
-  }
-  if (!this->_set_cipher_suites(ctx)) {
-    goto fail;
-  }
-  if (!this->_set_curves(ctx)) {
-    goto fail;
-  }
+    if (!this->_set_curves(ctx)) {
+      goto fail;
+    }
 
-  if (!ssl_context_enable_dhe(params->dhparamsFile, ctx)) {
-    goto fail;
-  }
+    if (!ssl_context_enable_dhe(_params->dhparamsFile, ctx)) {
+      goto fail;
+    }
 
-  ssl_context_enable_ecdh(ctx);
+    ssl_context_enable_ecdh(ctx);
 
-  if (sslMultCertSettings && sslMultCertSettings->dialog) {
-    SSLMultiCertConfigLoader::clear_pw_references(ctx);
-  }
+    if (sslMultCertSettings && sslMultCertSettings->dialog) {
+      SSLMultiCertConfigLoader::clear_pw_references(ctx);
+    }
 
-  if (!this->_set_info_callback(ctx)) {
-    goto fail;
-  };
-  if (!this->_set_npn_callback(ctx)) {
-    goto fail;
-  }
-  if (!this->_set_alpn_callback(ctx)) {
-    goto fail;
-  }
+    if (!this->_set_info_callback(ctx)) {
+      goto fail;
+    }
 
-  if (SSLConfigParams::init_ssl_ctx_cb) {
-    SSLConfigParams::init_ssl_ctx_cb(ctx, true);
-  }
+    if (!this->_set_npn_callback(ctx)) {
+      goto fail;
+    }
 
-  return ctx;
+    if (!this->_set_alpn_callback(ctx)) {
+      goto fail;
+    }
+
+    if (SSLConfigParams::init_ssl_ctx_cb) {
+      SSLConfigParams::init_ssl_ctx_cb(ctx, true);
+    }
+
+    ret.emplace_back(SSLLoadingContext(ctx, ctx_type));
+    i++;
+  }
+  return ret;
 
 fail:
+  ink_assert(ctx != nullptr);
   SSLMultiCertConfigLoader::clear_pw_references(ctx);
-  SSLReleaseContext(ctx);
-  return nullptr;
+  SSL_CTX_free(ctx);
+  ret.emplace_back(SSLLoadingContext(ctx, ctx_type));
+
+  return ret;
 }
 
 bool
@@ -1470,31 +1604,40 @@ SSLCreateServerContext(const SSLConfigParams *params, const SSLMultiCertConfigPa
                        const char *key_path)
 {
   SSLMultiCertConfigLoader loader(params);
-  std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx(nullptr, &SSL_CTX_free);
   std::vector<X509 *> cert_list;
   std::set<std::string> common_names;
   std::unordered_map<int, std::set<std::string>> unique_names;
   SSLMultiCertConfigLoader::CertLoadData data;
-  if (loader.load_certs_and_cross_reference_names(cert_list, data, params, sslMultiCertSettings, common_names, unique_names)) {
-    ctx.reset(loader.init_server_ssl_ctx(data, sslMultiCertSettings, common_names));
+  SSLCertContextType cert_type;
+  if (!loader.load_certs_and_cross_reference_names(cert_list, data, params, sslMultiCertSettings, common_names, unique_names,
+                                                   &cert_type)) {
+    return nullptr;
   }
   for (auto &i : cert_list) {
     X509_free(i);
   }
-  if (ctx && cert_path) {
-    if (!SSL_CTX_use_certificate_file(ctx.get(), cert_path, SSL_FILETYPE_PEM)) {
-      SSLError("SSLCreateServerContext(): failed to load server certificate.");
-      ctx = nullptr;
-    } else if (!key_path || key_path[0] == '\0') {
-      key_path = cert_path;
-    }
-    if (ctx) {
-      if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key_path, SSL_FILETYPE_PEM)) {
-        SSLError("SSLCreateServerContext(): failed to load server private key.");
-        ctx = nullptr;
-      } else if (!SSL_CTX_check_private_key(ctx.get())) {
-        SSLError("SSLCreateServerContext(): server private key does not match server certificate.");
+
+  std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx(nullptr, &SSL_CTX_free);
+
+  std::vector<SSLLoadingContext> ctxs = loader.init_server_ssl_ctx(data, sslMultiCertSettings, common_names);
+  for (auto const &loaderctx : ctxs) {
+    ctx.reset(loaderctx.ctx);
+
+    if (ctx && cert_path) {
+      if (!SSL_CTX_use_certificate_file(ctx.get(), cert_path, SSL_FILETYPE_PEM)) {
+        SSLError("SSLCreateServerContext(): failed to load server certificate.");
         ctx = nullptr;
+      } else if (!key_path || key_path[0] == '\0') {
+        key_path = cert_path;
+      }
+      if (ctx) {
+        if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key_path, SSL_FILETYPE_PEM)) {
+          SSLError("SSLCreateServerContext(): failed to load server private key.");
+          ctx = nullptr;
+        } else if (!SSL_CTX_check_private_key(ctx.get())) {
+          SSLError("SSLCreateServerContext(): server private key does not match server certificate.");
+          ctx = nullptr;
+        }
       }
     }
   }
@@ -1512,11 +1655,15 @@ SSLMultiCertConfigLoader::_prep_ssl_ctx(const shared_SSLMultiCertConfigParams ss
   std::vector<X509 *> cert_list;
   const SSLConfigParams *params = this->_params;
 
-  this->load_certs_and_cross_reference_names(cert_list, data, params, sslMultCertSettings.get(), common_names, unique_names);
+  SSLCertContextType cert_type;
+  if (!this->load_certs_and_cross_reference_names(cert_list, data, params, sslMultCertSettings.get(), common_names, unique_names,
+                                                  &cert_type)) {
+    return false;
+  }
 
   int i           = 0;
   bool good_certs = true;
-  for (auto cert : cert_list) {
+  for (auto const &cert : cert_list) {
     const char *current_cert_name = data.cert_names_list[i].c_str();
     if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert, current_cert_name)) {
       /* At this point, we know cert is bad, and we've already printed a
@@ -1550,13 +1697,13 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
     return false;
   }
 
-  if (!common_names.empty()) {
-    shared_SSL_CTX ctx(this->init_server_ssl_ctx(data, sslMultCertSettings.get(), common_names), SSL_CTX_free);
-
-    if (!ctx || !sslMultCertSettings || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, ctx, common_names)) {
+  std::vector<SSLLoadingContext> ctxs = this->init_server_ssl_ctx(data, sslMultCertSettings.get(), common_names);
+  for (const auto &loadingctx : ctxs) {
+    shared_SSL_CTX ctx(loadingctx.ctx, SSL_CTX_free);
+    if (!sslMultCertSettings || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, ctx, loadingctx.ctx_type, common_names)) {
       retval = false;
       std::string names;
-      for (auto name : data.cert_names_list) {
+      for (auto const &name : data.cert_names_list) {
         names.append(name);
         names.append(" ");
       }
@@ -1577,11 +1724,14 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
     single_data.ca_list.push_back(i < data.ca_list.size() ? data.ca_list[i] : "");
     single_data.ocsp_list.push_back(i < data.ocsp_list.size() ? data.ocsp_list[i] : "");
 
-    shared_SSL_CTX unique_ctx(this->init_server_ssl_ctx(single_data, sslMultCertSettings.get(), iter->second), SSL_CTX_free);
-    if (!unique_ctx || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, unique_ctx, iter->second)) {
-      retval = false;
-    } else {
-      lookup->register_cert_secrets(data.cert_names_list, iter->second);
+    std::vector<SSLLoadingContext> ctxs = this->init_server_ssl_ctx(single_data, sslMultCertSettings.get(), iter->second);
+    for (const auto &loadingctx : ctxs) {
+      shared_SSL_CTX unique_ctx(loadingctx.ctx, SSL_CTX_free);
+      if (!this->_store_single_ssl_ctx(lookup, sslMultCertSettings, unique_ctx, loadingctx.ctx_type, iter->second)) {
+        retval = false;
+      } else {
+        lookup->register_cert_secrets(data.cert_names_list, iter->second);
+      }
     }
   }
   return retval;
@@ -1611,15 +1761,18 @@ SSLMultiCertConfigLoader::update_ssl_ctx(const std::string &secret_name)
     }
 
     if (!common_names.empty()) {
-      shared_SSL_CTX ctx(this->init_server_ssl_ctx(data, policy_iter->get(), common_names), SSL_CTX_free);
+      std::vector<SSLLoadingContext> ctxs = this->init_server_ssl_ctx(data, policy_iter->get(), common_names);
+      for (const auto &loadingctx : ctxs) {
+        shared_SSL_CTX ctx(loadingctx.ctx, SSL_CTX_free);
 
-      if (!ctx) {
-        retval = false;
-      } else {
-        for (auto name : common_names) {
-          SSLCertContext *cc = lookup->find(name);
-          if (cc && cc->userconfig.get() == policy_iter->get()) {
-            cc->setCtx(ctx);
+        if (!ctx) {
+          retval = false;
+        } else {
+          for (auto const &name : common_names) {
+            SSLCertContext *cc = lookup->find(name, loadingctx.ctx_type);
+            if (cc && cc->userconfig.get() == policy_iter->get()) {
+              cc->setCtx(ctx);
+            }
           }
         }
       }
@@ -1634,15 +1787,18 @@ SSLMultiCertConfigLoader::update_ssl_ctx(const std::string &secret_name)
       single_data.ca_list.push_back(i < data.ca_list.size() ? data.ca_list[i] : "");
       single_data.ocsp_list.push_back(i < data.ocsp_list.size() ? data.ocsp_list[i] : "");
 
-      shared_SSL_CTX unique_ctx(this->init_server_ssl_ctx(single_data, policy_iter->get(), iter->second), SSL_CTX_free);
+      std::vector<SSLLoadingContext> ctxs = this->init_server_ssl_ctx(single_data, policy_iter->get(), iter->second);
+      for (auto const &loadingctx : ctxs) {
+        shared_SSL_CTX unique_ctx(loadingctx.ctx, SSL_CTX_free);
 
-      if (!unique_ctx) {
-        retval = false;
-      } else {
-        for (auto name : iter->second) {
-          SSLCertContext *cc = lookup->find(name);
-          if (cc && cc->userconfig.get() == policy_iter->get()) {
-            cc->setCtx(unique_ctx);
+        if (!unique_ctx) {
+          retval = false;
+        } else {
+          for (auto const &name : iter->second) {
+            SSLCertContext *cc = lookup->find(name, loadingctx.ctx_type);
+            if (cc && cc->userconfig.get() == policy_iter->get()) {
+              cc->setCtx(unique_ctx);
+            }
           }
         }
       }
@@ -1653,7 +1809,7 @@ SSLMultiCertConfigLoader::update_ssl_ctx(const std::string &secret_name)
 
 bool
 SSLMultiCertConfigLoader::_store_single_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams &sslMultCertSettings,
-                                                shared_SSL_CTX ctx, std::set<std::string> &names)
+                                                shared_SSL_CTX ctx, SSLCertContextType ctx_type, std::set<std::string> &names)
 {
   bool inserted                        = false;
   shared_ssl_ticket_key_block keyblock = nullptr;
@@ -1665,7 +1821,8 @@ SSLMultiCertConfigLoader::_store_single_ssl_ctx(SSLCertLookup *lookup, const sha
   // Index this certificate by the specified IP(v6) address. If the address is "*", make it the default context.
   if (sslMultCertSettings->addr) {
     if (strcmp(sslMultCertSettings->addr, "*") == 0) {
-      if (lookup->insert(sslMultCertSettings->addr, SSLCertContext(ctx, sslMultCertSettings, keyblock)) >= 0) {
+      Debug("ssl", "Addr is '*'; setting %p to default", ctx.get());
+      if (lookup->insert(sslMultCertSettings->addr, SSLCertContext(ctx, ctx_type, sslMultCertSettings, keyblock)) >= 0) {
         inserted            = true;
         lookup->ssl_default = ctx;
         this->_set_handshake_callbacks(ctx.get());
@@ -1674,7 +1831,7 @@ SSLMultiCertConfigLoader::_store_single_ssl_ctx(SSLCertLookup *lookup, const sha
       IpEndpoint ep;
 
       if (ats_ip_pton(sslMultCertSettings->addr, &ep) == 0) {
-        if (lookup->insert(ep, SSLCertContext(ctx, sslMultCertSettings, keyblock)) >= 0) {
+        if (lookup->insert(ep, SSLCertContext(ctx, ctx_type, sslMultCertSettings, keyblock)) >= 0) {
           inserted = true;
         }
       } else {
@@ -1687,8 +1844,8 @@ SSLMultiCertConfigLoader::_store_single_ssl_ctx(SSLCertLookup *lookup, const sha
   // Insert additional mappings. Note that this maps multiple keys to the same value, so when
   // this code is updated to reconfigure the SSL certificates, it will need some sort of
   // refcounting or alternate way of avoiding double frees.
-  for (auto sni_name : names) {
-    if (lookup->insert(sni_name.c_str(), SSLCertContext(ctx, sslMultCertSettings, keyblock)) >= 0) {
+  for (auto const &sni_name : names) {
+    if (lookup->insert(sni_name.c_str(), SSLCertContext(ctx, ctx_type, sslMultCertSettings, keyblock)) >= 0) {
       inserted = true;
     }
   }
@@ -1797,7 +1954,7 @@ SSLMultiCertConfigLoader::load(SSLCertLookup *lookup)
     switch (ec.value()) {
     case ENOENT:
       Warning("Cannot open SSL certificate configuration from %s - %s", params->configFilePath, strerror(ec.value()));
-      break;
+      return false;
     default:
       Error("Failed to read SSL certificate configuration from %s - %s", params->configFilePath, strerror(ec.value()));
       return false;
@@ -1971,12 +2128,10 @@ get_verify_str(SSL *ssl)
  * of the including certificate
  */
 bool
-SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509 *> &cert_list,
-                                                               SSLMultiCertConfigLoader::CertLoadData &data,
-                                                               const SSLConfigParams *params,
-                                                               const SSLMultiCertConfigParams *sslMultCertSettings,
-                                                               std::set<std::string> &common_names,
-                                                               std::unordered_map<int, std::set<std::string>> &unique_names)
+SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(
+  std::vector<X509 *> &cert_list, SSLMultiCertConfigLoader::CertLoadData &data, const SSLConfigParams *params,
+  const SSLMultiCertConfigParams *sslMultCertSettings, std::set<std::string> &common_names,
+  std::unordered_map<int, std::set<std::string>> &unique_names, SSLCertContextType *certType)
 {
   SimpleTokenizer cert_tok(sslMultCertSettings && sslMultCertSettings->cert ? (const char *)sslMultCertSettings->cert : "",
                            SSL_CERT_SEPARATE_DELIM);
@@ -2050,6 +2205,27 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
       return false;
     }
 
+    if (certType != nullptr) {
+#ifdef OPENSSL_IS_BORINGSSL
+      std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(X509_get_pubkey(cert), &EVP_PKEY_free);
+      int pkey_id = EVP_PKEY_id(public_key.get());
+
+      switch (pkey_id) {
+      case EVP_PKEY_EC:
+        *certType = SSLCertContextType::EC;
+        break;
+      case EVP_PKEY_RSA:
+        *certType = SSLCertContextType::RSA;
+        break;
+      default:
+        ink_assert(false);
+      }
+#else
+      *certType = SSLCertContextType::GENERIC;
+#endif
+      data.cert_type_list.push_back(*certType);
+    }
+
     cert_list.push_back(cert);
 
     std::set<std::string> name_set;
@@ -2120,7 +2296,7 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
         }
       }
       // Anything still in name_set was not in common_names
-      for (auto name : name_set) {
+      for (auto const &name : name_set) {
         auto iter = unique_names.find(cert_index);
         if (iter == unique_names.end()) {
           std::set<std::string> new_set;
@@ -2141,7 +2317,8 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
    @static
  */
 bool
-SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, SSLMultiCertConfigLoader::CertLoadData const &data,
+SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, const std::vector<std::string> &cert_names_list,
+                                     const std::vector<std::string> &key_list, CertLoadData const &data,
                                      const SSLConfigParams *params, const SSLMultiCertConfigParams *sslMultCertSettings)
 {
 #if TS_USE_TLS_OCSP
@@ -2157,13 +2334,15 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, SSLMultiCertConfigLoader::Cer
   }
 #endif /* TS_USE_TLS_OCSP */
 
-  for (size_t i = 0; i < data.cert_names_list.size(); i++) {
-    std::string keyPath = (i < data.key_list.size()) ? data.key_list[i] : "";
+  ink_assert(!cert_names_list.empty());
+
+  for (size_t i = 0; i < cert_names_list.size(); i++) {
+    std::string keyPath = (i < key_list.size()) ? key_list[i] : "";
     std::string_view secret_data;
     std::string_view secret_key_data;
-    params->secrets.getOrLoadSecret(data.cert_names_list[i], keyPath, secret_data, secret_key_data);
+    params->secrets.getOrLoadSecret(cert_names_list[i], keyPath, secret_data, secret_key_data);
     if (secret_data.empty()) {
-      SSLError("failed to load certificate secret for %s", data.cert_names_list[i].c_str());
+      SSLError("failed to load certificate secret for %s", cert_names_list[i].c_str());
       return false;
     }
     scoped_BIO bio(BIO_new_mem_buf(secret_data.data(), secret_data.size()));
@@ -2171,18 +2350,23 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, SSLMultiCertConfigLoader::Cer
     if (bio) {
       cert = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr);
     }
+
     if (!bio || !cert) {
-      SSLError("failed to load certificate chain from %s", data.cert_names_list[i].c_str());
+      SSLError("failed to load certificate chain from %s", cert_names_list[i].c_str());
       return false;
     }
+
+    Debug("ssl", "for ctx=%p, using certificate %s", ctx, cert_names_list[i].c_str());
     if (!SSL_CTX_use_certificate(ctx, cert)) {
-      SSLError("Failed to assign cert from %s to SSL_CTX", data.cert_names_list[i].c_str());
+      SSLError("Failed to assign cert from %s to SSL_CTX", cert_names_list[i].c_str());
       X509_free(cert);
       return false;
     }
 
     // Load up any additional chain certificates
-    SSL_CTX_add_extra_chain_cert_bio(ctx, bio);
+    if (!SSL_CTX_add_extra_chain_cert_bio(ctx, bio)) {
+      Debug("ssl", "couldn't add chain to %p", ctx);
+    }
 
     if (secret_key_data.empty()) {
       secret_key_data = secret_data;
diff --git a/iocore/net/TLSSNISupport.cc b/iocore/net/TLSSNISupport.cc
index 52cd472..b3dd1aa 100644
--- a/iocore/net/TLSSNISupport.cc
+++ b/iocore/net/TLSSNISupport.cc
@@ -72,15 +72,24 @@ TLSSNISupport::perform_sni_action()
   return SSL_TLSEXT_ERR_OK;
 }
 
-#if TS_USE_HELLO_CB
+#if TS_USE_HELLO_CB || defined(OPENSSL_IS_BORINGSSL)
 void
+#ifdef OPENSSL_IS_BORINGSSL
+TLSSNISupport::on_client_hello(const SSL_CLIENT_HELLO *client_hello)
+#else
 TLSSNISupport::on_client_hello(SSL *ssl, int *al, void *arg)
+#endif
 {
   const char *servername = nullptr;
   const unsigned char *p;
   size_t remaining, len;
   // Parse the server name if the get extension call succeeds and there are more than 2 bytes to parse
-  if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &p, &remaining) && remaining > 2) {
+#ifdef OPENSSL_IS_BORINGSSL
+  if (SSL_early_callback_ctx_extension_get(client_hello, TLSEXT_TYPE_server_name, &p, &remaining) && remaining > 2)
+#else
+  if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &p, &remaining) && remaining > 2)
+#endif
+  {
     // Parse to get to the name, originally from test/handshake_helper.c in openssl tree
     /* Extract the length of the supplied list of names. */
     len = *(p++) << 8;
diff --git a/iocore/net/TLSSNISupport.h b/iocore/net/TLSSNISupport.h
index 8257202..0470b6c 100644
--- a/iocore/net/TLSSNISupport.h
+++ b/iocore/net/TLSSNISupport.h
@@ -40,9 +40,13 @@ public:
 
   int perform_sni_action();
   // Callback functions for OpenSSL libraries
-#if TS_USE_HELLO_CB
+#if TS_USE_HELLO_CB || defined(OPENSSL_IS_BORINGSSL)
+#ifdef OPENSSL_IS_BORINGSSL
+  void on_client_hello(const SSL_CLIENT_HELLO *client_hello);
+#else
   void on_client_hello(SSL *ssl, int *al, void *arg);
 #endif
+#endif
   void on_servername(SSL *ssl, int *al, void *arg);
 
 protected:
diff --git a/iocore/net/YamlSNIConfig.cc b/iocore/net/YamlSNIConfig.cc
index 9b9b0d3..e38231f 100644
--- a/iocore/net/YamlSNIConfig.cc
+++ b/iocore/net/YamlSNIConfig.cc
@@ -136,7 +136,7 @@ std::set<std::string> valid_sni_config_keys = {TS_fqdn,
                                                TS_client_sni_policy,
                                                TS_http2,
                                                TS_ip_allow,
-#if TS_USE_HELLO_CB
+#if TS_USE_HELLO_CB || defined(OPENSSL_IS_BORINGSSL)
                                                TS_valid_tls_versions_in,
 #endif
                                                TS_host_sni_policy};
diff --git a/tests/gold_tests/tls_hooks/tls_hooks14.test.py b/tests/gold_tests/tls_hooks/tls_hooks14.test.py
index cb5370e..e6c9ca0 100644
--- a/tests/gold_tests/tls_hooks/tls_hooks14.test.py
+++ b/tests/gold_tests/tls_hooks/tls_hooks14.test.py
@@ -23,10 +23,6 @@ Test.Summary = '''
 Test different combinations of TLS handshake hooks to ensure they are applied consistently.
 '''
 
-Test.SkipUnless(
-    Condition.IsOpenSSL()
-)
-
 ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
 server = Test.MakeOriginServer("server", ssl=True)
 request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
diff --git a/tests/gold_tests/tls_hooks/tls_hooks16.test.py b/tests/gold_tests/tls_hooks/tls_hooks16.test.py
index 9e3029e..844330f 100644
--- a/tests/gold_tests/tls_hooks/tls_hooks16.test.py
+++ b/tests/gold_tests/tls_hooks/tls_hooks16.test.py
@@ -25,8 +25,7 @@ Test different combinations of TLS handshake hooks to ensure they are applied co
 '''
 
 Test.SkipUnless(
-    Condition.HasOpenSSLVersion("1.1.1"),
-    Condition.IsOpenSSL()
+    Condition.HasOpenSSLVersion("1.1.1")
 )
 
 ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)

Mime
View raw message