You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2020/05/27 18:13:44 UTC

[trafficserver] branch 9.0.x updated: Generalize callbacks for TLS session resumption

This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new cc78478  Generalize callbacks for TLS session resumption
cc78478 is described below

commit cc78478c04f000017e351a65ae8dc025544fe01a
Author: Masakazu Kitajo <ma...@apache.org>
AuthorDate: Tue May 5 12:38:29 2020 +0900

    Generalize callbacks for TLS session resumption
    
    Callback functions for session resumption were not usable with QUICNetVC because
    those expect that Session Resumption is used only on SSLNetVC.
    
    This commit introduces TLSSessionResumptionSupport that helps supporting Session
    Resumption and provides generalized interface to remove the direct dependency
    for SSLNetVC.
    
    Also, by ensuring a pointer stored in SSL application specific data is always
    TLSSessionResumptionSupport, it removes a use of dynamic_cast.
    
    (cherry picked from commit 16fb8092efd128723e7ccd6e374ffd7add155070)
---
 iocore/net/Makefile.am                    |   1 +
 iocore/net/P_SSLNetVConnection.h          |  37 ++---
 iocore/net/SSLNetVConnection.cc           |  13 +-
 iocore/net/SSLSessionTicket.cc            |  85 ++---------
 iocore/net/SSLUtils.cc                    |  52 +------
 iocore/net/TLSSessionResumptionSupport.cc | 233 ++++++++++++++++++++++++++++++
 iocore/net/TLSSessionResumptionSupport.h  |  64 ++++++++
 7 files changed, 327 insertions(+), 158 deletions(-)

diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index 2ec6f4e..6d6f3dc 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -154,6 +154,7 @@ libinknet_a_SOURCES = \
 	SSLUtils.cc \
 	OCSPStapling.cc \
 	Socks.cc \
+	TLSSessionResumptionSupport.cc \
 	UDPIOEvent.cc \
 	UnixConnection.cc \
 	UnixNet.cc \
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index 850b105..ae719ff 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -47,6 +47,7 @@
 #include "P_UnixNetVConnection.h"
 #include "P_UnixNet.h"
 #include "P_ALPNSupport.h"
+#include "TLSSessionResumptionSupport.h"
 #include "P_SSLUtils.h"
 
 // These are included here because older OpenSSL libraries don't have them.
@@ -91,7 +92,7 @@ enum SSLHandshakeStatus { SSL_HANDSHAKE_ONGOING, SSL_HANDSHAKE_DONE, SSL_HANDSHA
 //  A VConnection for a network socket.
 //
 //////////////////////////////////////////////////////////////////
-class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport
+class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport, public TLSSessionResumptionSupport
 {
   typedef UnixNetVConnection super; ///< Parent type.
 
@@ -129,18 +130,6 @@ public:
     sslHandshakeStatus = state;
   }
 
-  void
-  setSSLSessionCacheHit(bool state)
-  {
-    sslSessionCacheHit = state;
-  }
-
-  bool
-  getSSLSessionCacheHit() const
-  {
-    return sslSessionCacheHit;
-  }
-
   int sslServerHandShakeEvent(int &err);
   int sslClientHandShakeEvent(int &err);
   void net_read_io(NetHandler *nh, EThread *lthread) override;
@@ -307,18 +296,6 @@ public:
     return ssl ? SSL_get_cipher_name(ssl) : nullptr;
   }
 
-  void
-  setSSLCurveNID(ssl_curve_id curve_nid)
-  {
-    sslCurveNID = curve_nid;
-  }
-
-  ssl_curve_id
-  getSSLCurveNID() const
-  {
-    return sslCurveNID;
-  }
-
   const char *
   getSSLCurve() const
   {
@@ -460,20 +437,24 @@ public:
     return SSL_get_servername(this->ssl, TLSEXT_NAMETYPE_host_name);
   }
 
+protected:
+  const IpEndpoint &
+  _getLocalEndpoint() override
+  {
+    return local_addr;
+  }
+
 private:
   std::string_view map_tls_protocol_to_tag(const char *proto_string) const;
   bool update_rbio(bool move_to_socket);
   void increment_ssl_version_metric(int version) const;
-  void fetch_ssl_curve();
 
   enum SSLHandshakeStatus sslHandshakeStatus = SSL_HANDSHAKE_ONGOING;
   bool sslClientRenegotiationAbort           = false;
-  bool sslSessionCacheHit                    = false;
   MIOBuffer *handShakeBuffer                 = nullptr;
   IOBufferReader *handShakeHolder            = nullptr;
   IOBufferReader *handShakeReader            = nullptr;
   int handShakeBioStored                     = 0;
-  int sslCurveNID                            = NID_undef;
 
   bool transparentPassThrough = false;
 
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index b3e842a..94a9815 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -212,6 +212,7 @@ make_ssl_connection(SSL_CTX *ctx, SSLNetVConnection *netvc)
     }
 
     SSLNetVCAttach(ssl, netvc);
+    TLSSessionResumptionSupport::bind(ssl, netvc);
   }
 
   return ssl;
@@ -929,13 +930,13 @@ SSLNetVConnection::clear()
     ssl = nullptr;
   }
   ALPNSupport::clear();
+  TLSSessionResumptionSupport::clear();
 
   sslHandshakeStatus          = SSL_HANDSHAKE_ONGOING;
   sslHandshakeBeginTime       = 0;
   sslLastWriteTime            = 0;
   sslTotalBytesSent           = 0;
   sslClientRenegotiationAbort = false;
-  sslSessionCacheHit          = false;
 
   curHook         = nullptr;
   hookOpRequested = SSL_HOOK_OP_DEFAULT;
@@ -1303,7 +1304,6 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
       unsigned len               = 0;
 
       increment_ssl_version_metric(SSL_version(ssl));
-      fetch_ssl_curve();
 
       // If it's possible to negotiate both NPN and ALPN, then ALPN
       // is preferred since it is the server's preference.  The server
@@ -1826,6 +1826,7 @@ SSLNetVConnection::populate(Connection &con, Continuation *c, void *arg)
 
   sslHandshakeStatus = SSL_HANDSHAKE_DONE;
   SSLNetVCAttach(this->ssl, this);
+  TLSSessionResumptionSupport::bind(this->ssl, this);
   return EVENT_DONE;
 }
 
@@ -1856,14 +1857,6 @@ SSLNetVConnection::increment_ssl_version_metric(int version) const
   }
 }
 
-void
-SSLNetVConnection::fetch_ssl_curve()
-{
-  if (!getSSLSessionCacheHit()) {
-    setSSLCurveNID(SSLGetCurveNID(ssl));
-  }
-}
-
 std::string_view
 SSLNetVConnection::map_tls_protocol_to_tag(const char *proto_string) const
 {
diff --git a/iocore/net/SSLSessionTicket.cc b/iocore/net/SSLSessionTicket.cc
index 63769f9..2e7d397 100644
--- a/iocore/net/SSLSessionTicket.cc
+++ b/iocore/net/SSLSessionTicket.cc
@@ -25,20 +25,8 @@
 
 #if TS_HAVE_OPENSSL_SESSION_TICKETS
 
-#include "tscore/MatcherUtils.h"
-
-#include "P_Net.h"
-#include "SSLStats.h"
-#include "P_SSLConfig.h"
-
-// Remove this when drop OpenSSL 1.0.2 support
-#ifndef evp_md_func
-#ifdef OPENSSL_NO_SHA256
-#define evp_md_func EVP_sha1()
-#else
-#define evp_md_func EVP_sha256()
-#endif
-#endif
+#include "P_SSLCertLookup.h"
+#include "TLSSessionResumptionSupport.h"
 
 void
 ssl_session_ticket_free(void * /*parent*/, void *ptr, CRYPTO_EX_DATA * /*ad*/, int /*idx*/, long /*argl*/, void * /*argp*/)
@@ -55,71 +43,18 @@ int
 ssl_callback_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv, EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hctx,
                             int enc)
 {
-  SSLConfig::scoped_config config;
-  SSLCertificateConfig::scoped_config lookup;
-  SSLTicketKeyConfig::scoped_config params;
-  SSLNetVConnection &netvc = *SSLNetVCAccess(ssl);
+  TLSSessionResumptionSupport *srs = TLSSessionResumptionSupport::getInstance(ssl);
 
-  // Get the IP address to look up the keyblock
-  IpEndpoint ip;
-  int namelen        = sizeof(ip);
-  SSLCertContext *cc = nullptr;
-  if (0 == safe_getsockname(netvc.get_socket(), &ip.sa, &namelen)) {
-    cc = lookup->find(ip);
-  }
-  ssl_ticket_key_block *keyblock = nullptr;
-  if (cc == nullptr || cc->keyblock == nullptr) {
-    // Try the default
-    keyblock = params->default_global_keyblock;
+  if (srs) {
+    return srs->processSessionTicket(ssl, keyname, iv, cipher_ctx, hctx, enc);
   } else {
-    keyblock = cc->keyblock.get();
-  }
-  ink_release_assert(keyblock != nullptr && keyblock->num_keys > 0);
-
-  if (enc == 1) {
-    const ssl_ticket_key_t &most_recent_key = keyblock->keys[0];
-    memcpy(keyname, most_recent_key.key_name, sizeof(most_recent_key.key_name));
-    RAND_bytes(iv, EVP_MAX_IV_LENGTH);
-    EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), nullptr, most_recent_key.aes_key, iv);
-    HMAC_Init_ex(hctx, most_recent_key.hmac_secret, sizeof(most_recent_key.hmac_secret), evp_md_func, nullptr);
-
-    Debug("ssl_session_ticket", "create ticket for a new session.");
-    SSL_INCREMENT_DYN_STAT(ssl_total_tickets_created_stat);
-    return 1;
-  } else if (enc == 0) {
-    for (unsigned i = 0; i < keyblock->num_keys; ++i) {
-      if (memcmp(keyname, keyblock->keys[i].key_name, sizeof(keyblock->keys[i].key_name)) == 0) {
-        EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), nullptr, keyblock->keys[i].aes_key, iv);
-        HMAC_Init_ex(hctx, keyblock->keys[i].hmac_secret, sizeof(keyblock->keys[i].hmac_secret), evp_md_func, nullptr);
+    // We could implement a default behavior that would have been done if this callback was not registred, but it's not necessary at
+    // the moment because TLSSessionResumptionSupport is alawys available when the callback is registerd.
+    ink_assert(!"srs should be available");
 
-        Debug("ssl_session_ticket", "verify the ticket for an existing session.");
-        // Increase the total number of decrypted tickets.
-        SSL_INCREMENT_DYN_STAT(ssl_total_tickets_verified_stat);
-
-        if (i != 0) { // The number of tickets decrypted with "older" keys.
-          SSL_INCREMENT_DYN_STAT(ssl_total_tickets_verified_old_key_stat);
-        }
-
-        netvc.setSSLSessionCacheHit(true);
-
-#ifdef TLS1_3_VERSION
-        if (SSL_version(ssl) >= TLS1_3_VERSION) {
-          Debug("ssl_session_ticket", "make sure tickets are only used once.");
-          return 2;
-        }
-#endif
-
-        // When we decrypt with an "older" key, encrypt the ticket again with the most recent key.
-        return (i == 0) ? 1 : 2;
-      }
-    }
-
-    Debug("ssl_session_ticket", "keyname is not consistent.");
-    SSL_INCREMENT_DYN_STAT(ssl_total_tickets_not_found_stat);
-    return 0;
+    // For now, make it an error (this would cause handshake failure)
+    return -1;
   }
-
-  return -1;
 }
 
 #endif /* TS_HAVE_OPENSSL_SESSION_TICKETS */
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index f204aed..8c283e7 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -170,14 +170,6 @@ SSL_CTX_add_extra_chain_cert_file(SSL_CTX *ctx, const char *chainfile)
   return SSL_CTX_add_extra_chain_cert_bio(ctx, bio);
 }
 
-static bool
-ssl_session_timed_out(SSL_SESSION *session)
-{
-  return SSL_SESSION_get_timeout(session) < (time(nullptr) - SSL_SESSION_get_time(session));
-}
-
-static void ssl_rm_cached_session(SSL_CTX *ctx, SSL_SESSION *sess);
-
 static SSL_SESSION *
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 ssl_get_cached_session(SSL *ssl, unsigned char *id, int len, int *copy)
@@ -185,46 +177,14 @@ ssl_get_cached_session(SSL *ssl, unsigned char *id, int len, int *copy)
 ssl_get_cached_session(SSL *ssl, const unsigned char *id, int len, int *copy)
 #endif
 {
-  SSLSessionID sid(id, len);
-
-  *copy = 0;
-  if (diags->tag_activated("ssl.session_cache")) {
-    char printable_buf[(len * 2) + 1];
-    sid.toString(printable_buf, sizeof(printable_buf));
-    Debug("ssl.session_cache.get", "ssl_get_cached_session cached session '%s' context %p", printable_buf, SSL_get_SSL_CTX(ssl));
-  }
+  TLSSessionResumptionSupport *srs = TLSSessionResumptionSupport::getInstance(ssl);
 
-  APIHook *hook = ssl_hooks->get(TSSslHookInternalID(TS_SSL_SESSION_HOOK));
-  while (hook) {
-    hook->invoke(TS_EVENT_SSL_SESSION_GET, &sid);
-    hook = hook->m_link.next;
+  ink_assert(srs);
+  if (srs) {
+    return srs->getSession(ssl, id, len, copy);
   }
 
-  SSL_SESSION *session             = nullptr;
-  ssl_session_cache_exdata *exdata = nullptr;
-  if (session_cache->getSession(sid, &session, &exdata)) {
-    ink_assert(session);
-    ink_assert(exdata);
-
-    // Double check the timeout
-    if (ssl_session_timed_out(session)) {
-      SSL_INCREMENT_DYN_STAT(ssl_session_cache_miss);
-// Due to bug in openssl, the timeout is checked, but only removed
-// from the openssl built-in hash table.  The external remove cb is not called
-#if 0 // This is currently eliminated, since it breaks things in odd ways (see TS-3710)
-      ssl_rm_cached_session(SSL_get_SSL_CTX(ssl), session);
-#endif
-      session = nullptr;
-    } else {
-      SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
-      SSL_INCREMENT_DYN_STAT(ssl_session_cache_hit);
-      netvc->setSSLSessionCacheHit(true);
-      netvc->setSSLCurveNID(exdata->curve);
-    }
-  } else {
-    SSL_INCREMENT_DYN_STAT(ssl_session_cache_miss);
-  }
-  return session;
+  return nullptr;
 }
 
 static int
@@ -916,6 +876,8 @@ SSLInitializeLibrary()
   // the SSLNetVConnection to the SSL session.
   ssl_vc_index = SSL_get_ex_new_index(0, (void *)"NetVC index", nullptr, nullptr, nullptr);
 
+  TLSSessionResumptionSupport::initialize();
+
   open_ssl_initialized = true;
 }
 
diff --git a/iocore/net/TLSSessionResumptionSupport.cc b/iocore/net/TLSSessionResumptionSupport.cc
new file mode 100644
index 0000000..77b237c
--- /dev/null
+++ b/iocore/net/TLSSessionResumptionSupport.cc
@@ -0,0 +1,233 @@
+/** @file
+
+  TLSSessionResumptionSupport.cc provides implmentations for
+  TLSSessionResumptionSupport methods
+
+  @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.
+ */
+
+// Check if the ticket_key callback #define is available, and if so, enable session tickets.
+
+#include "TLSSessionResumptionSupport.h"
+
+#ifdef SSL_CTX_set_tlsext_ticket_key_cb
+#define TS_HAVE_OPENSSL_SESSION_TICKETS 1
+#endif
+
+#ifdef TS_HAVE_OPENSSL_SESSION_TICKETS
+
+#include "P_SSLConfig.h"
+#include "SSLStats.h"
+#include <openssl/evp.h>
+#include "InkAPIInternal.h"
+
+// Remove this when drop OpenSSL 1.0.2 support
+#ifndef evp_md_func
+#ifdef OPENSSL_NO_SHA256
+#define evp_md_func EVP_sha1()
+#else
+#define evp_md_func EVP_sha256()
+#endif
+#endif
+
+int TLSSessionResumptionSupport::_ex_data_index = -1;
+
+static bool
+is_ssl_session_timed_out(SSL_SESSION *session)
+{
+  return SSL_SESSION_get_timeout(session) < (time(nullptr) - SSL_SESSION_get_time(session));
+}
+
+void
+TLSSessionResumptionSupport::initialize()
+{
+  ink_assert(_ex_data_index == -1);
+  if (_ex_data_index == -1) {
+    _ex_data_index = SSL_get_ex_new_index(0, (void *)"TLSSessionResumptionSupport index", nullptr, nullptr, nullptr);
+  }
+}
+
+TLSSessionResumptionSupport *
+TLSSessionResumptionSupport::getInstance(SSL *ssl)
+{
+  return static_cast<TLSSessionResumptionSupport *>(SSL_get_ex_data(ssl, _ex_data_index));
+}
+
+void
+TLSSessionResumptionSupport::bind(SSL *ssl, TLSSessionResumptionSupport *srs)
+{
+  SSL_set_ex_data(ssl, _ex_data_index, srs);
+}
+
+int
+TLSSessionResumptionSupport::processSessionTicket(SSL *ssl, unsigned char *keyname, unsigned char *iv, EVP_CIPHER_CTX *cipher_ctx,
+                                                  HMAC_CTX *hctx, int enc)
+{
+  SSLConfig::scoped_config config;
+  SSLCertificateConfig::scoped_config lookup;
+  SSLTicketKeyConfig::scoped_config params;
+
+  // Get the IP address to look up the keyblock
+  const IpEndpoint &ip           = this->_getLocalEndpoint();
+  SSLCertContext *cc             = lookup->find(ip);
+  ssl_ticket_key_block *keyblock = nullptr;
+  if (cc == nullptr || cc->keyblock == nullptr) {
+    // Try the default
+    keyblock = params->default_global_keyblock;
+  } else {
+    keyblock = cc->keyblock.get();
+  }
+  ink_release_assert(keyblock != nullptr && keyblock->num_keys > 0);
+
+  if (enc == 1) {
+    return this->_setSessionInformation(keyblock, ssl, keyname, iv, cipher_ctx, hctx);
+  } else if (enc == 0) {
+    return this->_getSessionInformation(keyblock, ssl, keyname, iv, cipher_ctx, hctx);
+  }
+
+  return -1;
+}
+
+bool
+TLSSessionResumptionSupport::getSSLSessionCacheHit() const
+{
+  return this->_sslSessionCacheHit;
+}
+
+ssl_curve_id
+TLSSessionResumptionSupport::getSSLCurveNID() const
+{
+  return this->_sslCurveNID;
+}
+
+SSL_SESSION *
+TLSSessionResumptionSupport::getSession(SSL *ssl, const unsigned char *id, int len, int *copy)
+{
+  SSLSessionID sid(id, len);
+
+  *copy = 0;
+  if (diags->tag_activated("ssl.session_cache")) {
+    char printable_buf[(len * 2) + 1];
+    sid.toString(printable_buf, sizeof(printable_buf));
+    Debug("ssl.session_cache.get", "ssl_get_cached_session cached session '%s' context %p", printable_buf, SSL_get_SSL_CTX(ssl));
+  }
+
+  APIHook *hook = ssl_hooks->get(TSSslHookInternalID(TS_SSL_SESSION_HOOK));
+  while (hook) {
+    hook->invoke(TS_EVENT_SSL_SESSION_GET, &sid);
+    hook = hook->m_link.next;
+  }
+
+  SSL_SESSION *session             = nullptr;
+  ssl_session_cache_exdata *exdata = nullptr;
+  if (session_cache->getSession(sid, &session, &exdata)) {
+    ink_assert(session);
+    ink_assert(exdata);
+
+    // Double check the timeout
+    if (is_ssl_session_timed_out(session)) {
+      SSL_INCREMENT_DYN_STAT(ssl_session_cache_miss);
+// Due to bug in openssl, the timeout is checked, but only removed
+// from the openssl built-in hash table.  The external remove cb is not called
+#if 0 // This is currently eliminated, since it breaks things in odd ways (see TS-3710)
+      ssl_rm_cached_session(SSL_get_SSL_CTX(ssl), session);
+#endif
+      session = nullptr;
+    } else {
+      SSL_INCREMENT_DYN_STAT(ssl_session_cache_hit);
+      this->_setSSLSessionCacheHit(true);
+      this->_setSSLCurveNID(exdata->curve);
+    }
+  } else {
+    SSL_INCREMENT_DYN_STAT(ssl_session_cache_miss);
+  }
+  return session;
+}
+
+void
+TLSSessionResumptionSupport::clear()
+{
+  this->_sslSessionCacheHit = false;
+}
+
+int
+TLSSessionResumptionSupport::_setSessionInformation(ssl_ticket_key_block *keyblock, SSL *ssl, unsigned char *keyname,
+                                                    unsigned char *iv, EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hctx)
+{
+  const ssl_ticket_key_t &most_recent_key = keyblock->keys[0];
+  memcpy(keyname, most_recent_key.key_name, sizeof(most_recent_key.key_name));
+  RAND_bytes(iv, EVP_MAX_IV_LENGTH);
+  EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), nullptr, most_recent_key.aes_key, iv);
+  HMAC_Init_ex(hctx, most_recent_key.hmac_secret, sizeof(most_recent_key.hmac_secret), evp_md_func, nullptr);
+
+  Debug("ssl_session_ticket", "create ticket for a new session.");
+  SSL_INCREMENT_DYN_STAT(ssl_total_tickets_created_stat);
+  return 1;
+}
+
+int
+TLSSessionResumptionSupport::_getSessionInformation(ssl_ticket_key_block *keyblock, SSL *ssl, unsigned char *keyname,
+                                                    unsigned char *iv, EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hctx)
+{
+  for (unsigned i = 0; i < keyblock->num_keys; ++i) {
+    if (memcmp(keyname, keyblock->keys[i].key_name, sizeof(keyblock->keys[i].key_name)) == 0) {
+      EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), nullptr, keyblock->keys[i].aes_key, iv);
+      HMAC_Init_ex(hctx, keyblock->keys[i].hmac_secret, sizeof(keyblock->keys[i].hmac_secret), evp_md_func, nullptr);
+
+      Debug("ssl_session_ticket", "verify the ticket for an existing session.");
+      // Increase the total number of decrypted tickets.
+      SSL_INCREMENT_DYN_STAT(ssl_total_tickets_verified_stat);
+
+      if (i != 0) { // The number of tickets decrypted with "older" keys.
+        SSL_INCREMENT_DYN_STAT(ssl_total_tickets_verified_old_key_stat);
+      }
+
+      this->_setSSLSessionCacheHit(true);
+      this->_setSSLCurveNID(SSLGetCurveNID(ssl));
+
+#ifdef TLS1_3_VERSION
+      if (SSL_version(ssl) >= TLS1_3_VERSION) {
+        Debug("ssl_session_ticket", "make sure tickets are only used once.");
+        return 2;
+      }
+#endif
+
+      // When we decrypt with an "older" key, encrypt the ticket again with the most recent key.
+      return (i == 0) ? 1 : 2;
+    }
+  }
+
+  Debug("ssl_session_ticket", "keyname is not consistent.");
+  SSL_INCREMENT_DYN_STAT(ssl_total_tickets_not_found_stat);
+  return 0;
+}
+
+void
+TLSSessionResumptionSupport::_setSSLSessionCacheHit(bool state)
+{
+  this->_sslSessionCacheHit = state;
+}
+
+void
+TLSSessionResumptionSupport::_setSSLCurveNID(ssl_curve_id curve_nid)
+{
+  this->_sslCurveNID = curve_nid;
+}
+
+#endif /* TS_HAVE_OPENSSL_SESSION_TICKETS */
diff --git a/iocore/net/TLSSessionResumptionSupport.h b/iocore/net/TLSSessionResumptionSupport.h
new file mode 100644
index 0000000..e0cb807
--- /dev/null
+++ b/iocore/net/TLSSessionResumptionSupport.h
@@ -0,0 +1,64 @@
+/** @file
+
+  TLSSessionResumptionSupport implements common methods and members to
+  support TLS Ssssion Resumption
+
+  @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.
+ */
+
+#pragma once
+
+#include <openssl/ssl.h>
+
+#include "tscore/ink_inet.h"
+#include "P_SSLCertLookup.h"
+#include "P_SSLUtils.h"
+
+class TLSSessionResumptionSupport
+{
+public:
+  static void initialize();
+  static TLSSessionResumptionSupport *getInstance(SSL *ssl);
+  static void bind(SSL *ssl, TLSSessionResumptionSupport *srs);
+
+  int processSessionTicket(SSL *ssl, unsigned char *keyname, unsigned char *iv, EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hctx,
+                           int enc);
+  bool getSSLSessionCacheHit() const;
+  ssl_curve_id getSSLCurveNID() const;
+
+  SSL_SESSION *getSession(SSL *ssl, const unsigned char *id, int len, int *copy);
+
+protected:
+  void clear();
+  virtual const IpEndpoint &_getLocalEndpoint() = 0;
+
+private:
+  static int _ex_data_index;
+
+  bool _sslSessionCacheHit = false;
+  int _sslCurveNID         = NID_undef;
+
+  int _setSessionInformation(ssl_ticket_key_block *keyblock, SSL *ssl, unsigned char *keyname, unsigned char *iv,
+                             EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hctx);
+  int _getSessionInformation(ssl_ticket_key_block *keyblock, SSL *ssl, unsigned char *keyname, unsigned char *iv,
+                             EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hctx);
+
+  void _setSSLSessionCacheHit(bool state);
+  void _setSSLCurveNID(ssl_curve_id curve_nid);
+};