enable ssl false start with Next Protocol Negotiation (NPN) extension
authorShijin Kong <shikong@fb.com>
Thu, 29 Jan 2015 18:54:53 +0000 (10:54 -0800)
committerwoo <woo@fb.com>
Mon, 2 Feb 2015 21:14:20 +0000 (13:14 -0800)
Summary:
This speeds up TLS handshake and might be a factor of liger perf regression.
The enabling is guarded by an #ifdef. The condition itself is defined in an
openssl patch.

Test Plan: folly unit tests pass. Tried on devices as well and from tcpdump data was sent before the new ticket was received, essentially speeded up the handshake process.

Reviewed By: subodh@fb.com

Subscribers: trunkagent, kmdent, seanc, benyluo, ssl-diffs@, ranjeeth, folly-diffs@

FB internal diff: D1806856

Tasks: 5284979

Signature: t1:1806856:1422494521:0a048ea9001da13b5d698b5a764d1e66dcbedc99

folly/io/async/SSLContext.cpp
folly/io/async/SSLContext.h

index 1b0018fdf5a5851a2ac4502585358da62bf151b2..4f73260943136d0fa956e40d014d4c95a5909dc1 100644 (file)
@@ -419,6 +419,90 @@ int SSLContext::advertisedNextProtocolCallback(SSL* ssl,
   return SSL_TLSEXT_ERR_OK;
 }
 
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) && \
+  FOLLY_SSLCONTEXT_USE_TLS_FALSE_START
+SSLContext::SSLFalseStartChecker::SSLFalseStartChecker() :
+  // The list was generated as follows:
+  //   grep "_CK_" openssl-1.0.1e/ssl/s3_lib.c -A 4 | while read A && read B && read C && read D && read E && read F; do echo $A $B $C $D $E; done | \
+  //     grep "\(SSL_kDHr\|SSL_kDHd\|SSL_kEDH\|SSL_kECDHr\|SSL_kECDHe\|SSL_kEECDH\)" | grep -v SSL_aNULL | grep SSL_AES | awk -F, '{ print $1"," }'
+  ciphers_{
+    TLS1_CK_DH_DSS_WITH_AES_128_SHA,
+    TLS1_CK_DH_RSA_WITH_AES_128_SHA,
+    TLS1_CK_DHE_DSS_WITH_AES_128_SHA,
+    TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
+    TLS1_CK_DH_DSS_WITH_AES_256_SHA,
+    TLS1_CK_DH_RSA_WITH_AES_256_SHA,
+    TLS1_CK_DHE_DSS_WITH_AES_256_SHA,
+    TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
+    TLS1_CK_DH_DSS_WITH_AES_128_SHA256,
+    TLS1_CK_DH_RSA_WITH_AES_128_SHA256,
+    TLS1_CK_DHE_DSS_WITH_AES_128_SHA256,
+    TLS1_CK_DHE_RSA_WITH_AES_128_SHA256,
+    TLS1_CK_DH_DSS_WITH_AES_256_SHA256,
+    TLS1_CK_DH_RSA_WITH_AES_256_SHA256,
+    TLS1_CK_DHE_DSS_WITH_AES_256_SHA256,
+    TLS1_CK_DHE_RSA_WITH_AES_256_SHA256,
+    TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_DH_RSA_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_DH_RSA_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_DHE_DSS_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_DHE_DSS_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_DH_DSS_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_DH_DSS_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+    TLS1_CK_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+    TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+    TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+    TLS1_CK_ECDH_RSA_WITH_AES_128_CBC_SHA,
+    TLS1_CK_ECDH_RSA_WITH_AES_256_CBC_SHA,
+    TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+    TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+    TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
+    TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
+    TLS1_CK_ECDH_ECDSA_WITH_AES_128_SHA256,
+    TLS1_CK_ECDH_ECDSA_WITH_AES_256_SHA384,
+    TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+    TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+    TLS1_CK_ECDH_RSA_WITH_AES_128_SHA256,
+    TLS1_CK_ECDH_RSA_WITH_AES_256_SHA384,
+    TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+    TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+    TLS1_CK_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+  } {
+  length_ = sizeof(ciphers_)/sizeof(ciphers_[0]);
+  width_ = sizeof(ciphers_[0]);
+  qsort(ciphers_, length_, width_, compare_ulong);
+}
+
+bool SSLContext::SSLFalseStartChecker::canUseFalseStartWithCipher(
+  const SSL_CIPHER *cipher) {
+  unsigned long cid = cipher->id;
+  unsigned long *r =
+    (unsigned long*)bsearch(&cid, ciphers_, length_, width_, compare_ulong);
+  return r != nullptr;
+}
+
+int
+SSLContext::SSLFalseStartChecker::compare_ulong(const void *x, const void *y) {
+  if (*(unsigned long *)x < *(unsigned long *)y) {
+    return -1;
+  }
+  if (*(unsigned long *)x > *(unsigned long *)y) {
+    return 1;
+  }
+  return 0;
+};
+
+bool SSLContext::canUseFalseStartWithCipher(const SSL_CIPHER *cipher) {
+  return falseStartChecker_.canUseFalseStartWithCipher(cipher);
+}
+#endif
+
 int SSLContext::selectNextProtocolCallback(
   SSL* ssl, unsigned char **out, unsigned char *outlen,
   const unsigned char *server, unsigned int server_len, void *data) {
@@ -444,6 +528,14 @@ int SSLContext::selectNextProtocolCallback(
   if (retval != OPENSSL_NPN_NEGOTIATED) {
     VLOG(3) << "SSLContext::selectNextProcolCallback() "
             << "unable to pick a next protocol.";
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) && \
+  FOLLY_SSLCONTEXT_USE_TLS_FALSE_START
+  } else {
+    const SSL_CIPHER *cipher = ssl->s3->tmp.new_cipher;
+    if (cipher && ctx->canUseFalseStartWithCipher(cipher)) {
+      SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
+    }
+#endif
   }
   return SSL_TLSEXT_ERR_OK;
 }
index fb7eafac5938c2a6d4c586feaa95c1ec8f73dbc5..1718bfcaf210f01e656529c84e1729395a016ae0 100644 (file)
 
 #include <glog/logging.h>
 
+#ifndef FOLLY_NO_CONFIG
+#include <folly/folly-config.h>
+#endif
+
 namespace folly {
 
 /**
@@ -326,6 +330,11 @@ class SSLContext {
    */
   void unsetNextProtocols();
   void deleteNextProtocolsStrings();
+
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) && \
+  FOLLY_SSLCONTEXT_USE_TLS_FALSE_START
+  bool canUseFalseStartWithCipher(const SSL_CIPHER *cipher);
+#endif
 #endif // OPENSSL_NPN_NEGOTIATED
 
   /**
@@ -432,6 +441,29 @@ class SSLContext {
   static int selectNextProtocolCallback(
     SSL* ssl, unsigned char **out, unsigned char *outlen,
     const unsigned char *server, unsigned int server_len, void *args);
+
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) && \
+  FOLLY_SSLCONTEXT_USE_TLS_FALSE_START
+  // This class contains all allowed ciphers for SSL false start. Call its
+  // `canUseFalseStartWithCipher` to check for cipher qualification.
+  class SSLFalseStartChecker {
+   public:
+    SSLFalseStartChecker();
+
+    bool canUseFalseStartWithCipher(const SSL_CIPHER *cipher);
+
+   private:
+    static int compare_ulong(const void *x, const void *y);
+
+    // All ciphers that are allowed to use false start.
+    unsigned long ciphers_[47];
+    unsigned int length_;
+    unsigned int width_;
+  };
+
+  SSLFalseStartChecker falseStartChecker_;
+#endif
+
 #endif // OPENSSL_NPN_NEGOTIATED
 
   static int passwordCallback(char* password, int size, int, void* data);