Add Net.SSLHandshakeDetails metric

Between TLS 1.2 (and earlier) and TLS 1.3 and the various optimizations
applied to either, we have many different handshake modes with different
performance and sensitivity to system state. E.g., losing a TLS 1.2
resumption to False Start is less of a big deal to losing one to a full
handshake.  Add a metric that breaks down the various scenarios.

This is relevant for both double-keying the TLS session cache and 0-RTT.
As part of testing this, add ALPN support to SSLServerSocket.

Bug: 641225, 974910
Change-Id: I9b24ae361f21444c3217b7c0e1619ab11e8eccee
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1676987
Reviewed-by: Steven Holte <holte@chromium.org>
Reviewed-by: Steven Valdez <svaldez@chromium.org>
Commit-Queue: David Benjamin <davidben@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672761}
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 99033f1..6385886 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -364,6 +364,7 @@
     "ssl/ssl_config_service.cc",
     "ssl/ssl_config_service.h",
     "ssl/ssl_connection_status_flags.h",
+    "ssl/ssl_handshake_details.h",
     "ssl/ssl_info.cc",
     "ssl/ssl_info.h",
     "ssl/ssl_key_logger.h",
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 119863ec..a565677b 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -50,6 +50,7 @@
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_client_session_cache.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "net/ssl/ssl_handshake_details.h"
 #include "net/ssl/ssl_info.h"
 #include "net/ssl/ssl_key_logger.h"
 #include "net/ssl/ssl_private_key.h"
@@ -872,9 +873,7 @@
   if (!ssl_config_.alpn_protos.empty()) {
     std::vector<uint8_t> wire_protos =
         SerializeNextProtos(ssl_config_.alpn_protos);
-    SSL_set_alpn_protos(ssl_.get(),
-                        wire_protos.empty() ? nullptr : &wire_protos[0],
-                        wire_protos.size());
+    SSL_set_alpn_protos(ssl_.get(), wire_protos.data(), wire_protos.size());
   }
 
   SSL_enable_signed_cert_timestamps(ssl_.get());
@@ -1075,6 +1074,26 @@
     }
   }
 
+  SSLHandshakeDetails details;
+  if (SSL_version(ssl_.get()) < TLS1_3_VERSION) {
+    if (SSL_session_reused(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS12Resume;
+    } else if (SSL_in_false_start(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS12FalseStart;
+    } else {
+      details = SSLHandshakeDetails::kTLS12Full;
+    }
+  } else {
+    if (SSL_in_early_data(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS13Early;
+    } else if (SSL_session_reused(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS13Resume;
+    } else {
+      details = SSLHandshakeDetails::kTLS13Full;
+    }
+  }
+  UMA_HISTOGRAM_ENUMERATION("Net.SSLHandshakeDetails", details);
+
   completed_connect_ = true;
   next_handshake_state_ = STATE_NONE;
   return OK;
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 108be75..4a96fff 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -67,6 +67,7 @@
 #include "net/ssl/ssl_client_session_cache.h"
 #include "net/ssl/ssl_config_service.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "net/ssl/ssl_handshake_details.h"
 #include "net/ssl/ssl_info.h"
 #include "net/ssl/ssl_server_config.h"
 #include "net/ssl/test_ssl_private_key.h"
@@ -5512,4 +5513,134 @@
   }
 }
 
+struct SSLHandshakeDetailsParams {
+  bool alpn;
+  bool early_data;
+  uint16_t version;
+  SSLHandshakeDetails expected_initial;
+  SSLHandshakeDetails expected_resume;
+};
+
+const SSLHandshakeDetailsParams kSSLHandshakeDetailsParams[] = {
+    // TLS 1.0 and 1.1 never do False Start.
+    {false /* no ALPN */, false /* no early data */, SSL_PROTOCOL_VERSION_TLS1,
+     SSLHandshakeDetails::kTLS12Full, SSLHandshakeDetails::kTLS12Resume},
+    {false /* no ALPN */, false /* no early data */,
+     SSL_PROTOCOL_VERSION_TLS1_1, SSLHandshakeDetails::kTLS12Full,
+     SSLHandshakeDetails::kTLS12Resume},
+
+    // TLS 1.2 does False Start if ALPN is enabled.
+    {false /* no ALPN */, false /* no early data */,
+     SSL_PROTOCOL_VERSION_TLS1_2, SSLHandshakeDetails::kTLS12Full,
+     SSLHandshakeDetails::kTLS12Resume},
+    {true /* ALPN */, false /* no early data */, SSL_PROTOCOL_VERSION_TLS1_2,
+     SSLHandshakeDetails::kTLS12FalseStart, SSLHandshakeDetails::kTLS12Resume},
+
+    // TLS 1.3 supports full handshakes, resumption, and 0-RTT.
+    {false /* no ALPN */, false /* no early data */,
+     SSL_PROTOCOL_VERSION_TLS1_3, SSLHandshakeDetails::kTLS13Full,
+     SSLHandshakeDetails::kTLS13Resume},
+    {false /* no ALPN */, true /* early data */, SSL_PROTOCOL_VERSION_TLS1_3,
+     SSLHandshakeDetails::kTLS13Full, SSLHandshakeDetails::kTLS13Early},
+};
+
+class SSLHandshakeDetailsTest
+    : public SSLClientSocketTest,
+      public ::testing::WithParamInterface<SSLHandshakeDetailsParams> {};
+
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         SSLHandshakeDetailsTest,
+                         ::testing::ValuesIn(kSSLHandshakeDetailsParams));
+
+TEST_P(SSLHandshakeDetailsTest, Metrics) {
+  // Enable all test features in the server.
+  SSLServerConfig server_config;
+  server_config.version_max = SSL_PROTOCOL_VERSION_TLS1;
+  server_config.version_max = SSL_PROTOCOL_VERSION_TLS1_3;
+  server_config.early_data_enabled = true;
+  server_config.alpn_protos = {kProtoHTTP11};
+  ASSERT_TRUE(
+      StartEmbeddedTestServer(EmbeddedTestServer::CERT_OK, server_config));
+
+  SSLConfig client_config;
+  client_config.version_min = GetParam().version;
+  client_config.version_max = GetParam().version;
+  client_config.early_data_enabled = GetParam().early_data;
+  if (GetParam().alpn) {
+    client_config.alpn_protos = {kProtoHTTP11};
+  }
+
+  SSLVersion version;
+  switch (GetParam().version) {
+    case SSL_PROTOCOL_VERSION_TLS1:
+      version = SSL_CONNECTION_VERSION_TLS1;
+      break;
+    case SSL_PROTOCOL_VERSION_TLS1_1:
+      version = SSL_CONNECTION_VERSION_TLS1_1;
+      break;
+    case SSL_PROTOCOL_VERSION_TLS1_2:
+      version = SSL_CONNECTION_VERSION_TLS1_2;
+      break;
+    case SSL_PROTOCOL_VERSION_TLS1_3:
+      version = SSL_CONNECTION_VERSION_TLS1_3;
+      break;
+    default:
+      FAIL() << GetParam().version;
+  }
+
+  // Make the initial connection.
+  {
+    base::HistogramTester histograms;
+    int rv;
+    ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv));
+    EXPECT_THAT(rv, IsOk());
+
+    // Sanity-check the socket matches the test parameters.
+    SSLInfo info;
+    ASSERT_TRUE(sock_->GetSSLInfo(&info));
+    EXPECT_EQ(version, SSLConnectionStatusToVersion(info.connection_status));
+    EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, info.handshake_type);
+    EXPECT_EQ(GetParam().alpn, sock_->WasAlpnNegotiated());
+
+    histograms.ExpectUniqueSample("Net.SSLHandshakeDetails",
+                                  GetParam().expected_initial, 1);
+
+    // Use the socket to ensure the session ticket has been picked up.
+    base::StringPiece request = "GET / HTTP/1.0\r\n\r\n";
+    TestCompletionCallback callback;
+    while (!request.empty()) {
+      auto request_buffer =
+          base::MakeRefCounted<StringIOBuffer>(request.as_string());
+      rv = callback.GetResult(
+          sock_->Write(request_buffer.get(), request_buffer->size(),
+                       callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
+      ASSERT_GT(rv, 0);
+      request = request.substr(rv);
+    }
+
+    auto response_buffer = base::MakeRefCounted<IOBuffer>(1024);
+    rv = callback.GetResult(
+        sock_->Read(response_buffer.get(), 1024, callback.callback()));
+    ASSERT_GT(rv, 0);
+  }
+
+  // Make a resumption connection.
+  {
+    base::HistogramTester histograms;
+    int rv;
+    ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv));
+    EXPECT_THAT(rv, IsOk());
+
+    // Sanity-check the socket matches the test parameters.
+    SSLInfo info;
+    ASSERT_TRUE(sock_->GetSSLInfo(&info));
+    EXPECT_EQ(version, SSLConnectionStatusToVersion(info.connection_status));
+    EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, info.handshake_type);
+    EXPECT_EQ(GetParam().alpn, sock_->WasAlpnNegotiated());
+
+    histograms.ExpectUniqueSample("Net.SSLHandshakeDetails",
+                                  GetParam().expected_resume, 1);
+  }
+}
+
 }  // namespace net
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc
index bb3e4fc8..dac7d7e 100644
--- a/net/socket/ssl_server_socket_impl.cc
+++ b/net/socket/ssl_server_socket_impl.cc
@@ -26,6 +26,7 @@
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "third_party/boringssl/src/include/openssl/err.h"
 #include "third_party/boringssl/src/include/openssl/pool.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
@@ -142,6 +143,13 @@
                                                       size_t max_out);
   void OnPrivateKeyComplete(Error error, const std::vector<uint8_t>& signature);
 
+  static int ALPNSelectCallback(SSL* ssl,
+                                const uint8_t** out,
+                                uint8_t* out_len,
+                                const uint8_t* in,
+                                unsigned in_len,
+                                void* arg);
+
   // SocketBIOAdapter::Delegate implementation.
   void OnReadReady() override;
   void OnWriteReady() override;
@@ -202,6 +210,8 @@
   State next_handshake_state_;
   bool completed_handshake_;
 
+  NextProto negotiated_protocol_;
+
   base::WeakPtrFactory<SocketImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SocketImpl);
@@ -218,6 +228,7 @@
       transport_socket_(std::move(transport_socket)),
       next_handshake_state_(STATE_NONE),
       completed_handshake_(false),
+      negotiated_protocol_(kProtoUnknown),
       weak_factory_(this) {
   ssl_.reset(SSL_new(context_->ssl_ctx_.get()));
   SSL_set_app_data(ssl_.get(), this);
@@ -335,6 +346,41 @@
   DoHandshakeLoop(ERR_IO_PENDING);
 }
 
+int SSLServerContextImpl::SocketImpl::ALPNSelectCallback(SSL* ssl,
+                                                         const uint8_t** out,
+                                                         uint8_t* out_len,
+                                                         const uint8_t* in,
+                                                         unsigned in_len,
+                                                         void* arg) {
+  SSLServerContextImpl::SocketImpl* socket =
+      static_cast<SSLServerContextImpl::SocketImpl*>(SSL_get_ex_data(
+          ssl, SocketDataIndex::GetInstance()->ssl_socket_data_index_));
+
+  // Iterate over the server protocols in preference order.
+  for (NextProto server_proto :
+       socket->context_->ssl_server_config_.alpn_protos) {
+    const char* server_proto_str = NextProtoToString(server_proto);
+
+    // See if the client advertised the corresponding protocol.
+    CBS cbs;
+    CBS_init(&cbs, in, in_len);
+    while (CBS_len(&cbs) != 0) {
+      CBS client_proto;
+      if (!CBS_get_u8_length_prefixed(&cbs, &client_proto)) {
+        return SSL_TLSEXT_ERR_NOACK;
+      }
+      if (base::StringPiece(
+              reinterpret_cast<const char*>(CBS_data(&client_proto)),
+              CBS_len(&client_proto)) == server_proto_str) {
+        *out = CBS_data(&client_proto);
+        *out_len = CBS_len(&client_proto);
+        return SSL_TLSEXT_ERR_OK;
+      }
+    }
+  }
+  return SSL_TLSEXT_ERR_NOACK;
+}
+
 int SSLServerContextImpl::SocketImpl::Handshake(
     CompletionOnceCallback callback) {
   net_log_.BeginEvent(NetLogEventType::SSL_SERVER_HANDSHAKE);
@@ -486,13 +532,11 @@
 }
 
 bool SSLServerContextImpl::SocketImpl::WasAlpnNegotiated() const {
-  NOTIMPLEMENTED();
-  return false;
+  return negotiated_protocol_ != kProtoUnknown;
 }
 
 NextProto SSLServerContextImpl::SocketImpl::GetNegotiatedProtocol() const {
-  // ALPN is not supported by this class.
-  return kProtoUnknown;
+  return negotiated_protocol_;
 }
 
 bool SSLServerContextImpl::SocketImpl::GetSSLInfo(SSLInfo* ssl_info) {
@@ -659,6 +703,15 @@
       if (!client_cert_)
         return ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT;
     }
+
+    const uint8_t* alpn_proto = nullptr;
+    unsigned alpn_len = 0;
+    SSL_get0_alpn_selected(ssl_.get(), &alpn_proto, &alpn_len);
+    if (alpn_len > 0) {
+      base::StringPiece proto(reinterpret_cast<const char*>(alpn_proto),
+                              alpn_len);
+      negotiated_protocol_ = NextProtoFromString(proto);
+    }
   } else {
     int ssl_error = SSL_get_error(ssl_.get(), rv);
 
@@ -915,6 +968,9 @@
     }
     SSL_CTX_set0_client_CAs(ssl_ctx_.get(), stack.release());
   }
+
+  SSL_CTX_set_alpn_select_cb(ssl_ctx_.get(), &SocketImpl::ALPNSelectCallback,
+                             nullptr);
 }
 
 SSLServerContextImpl::~SSLServerContextImpl() = default;
diff --git a/net/ssl/ssl_config.h b/net/ssl/ssl_config.h
index c31d970..88e4376 100644
--- a/net/ssl/ssl_config.h
+++ b/net/ssl/ssl_config.h
@@ -123,7 +123,7 @@
   bool send_client_cert;
 
   // The list of application level protocols supported with ALPN (Application
-  // Layer Protocol Negotation), in decreasing order of preference.  Protocols
+  // Layer Protocol Negotiation), in decreasing order of preference.  Protocols
   // will be advertised in this order during TLS handshake.
   NextProtoVector alpn_protos;
 
diff --git a/net/ssl/ssl_handshake_details.h b/net/ssl/ssl_handshake_details.h
new file mode 100644
index 0000000..a9a024f
--- /dev/null
+++ b/net/ssl/ssl_handshake_details.h
@@ -0,0 +1,29 @@
+// Copyright 2019 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_SSL_SSL_HANDSHAKE_DETAILS_H_
+#define NET_SSL_SSL_HANDSHAKE_DETAILS_H_
+
+namespace net {
+
+// This enum is persisted into histograms. Values may not be renumbered.
+enum class SSLHandshakeDetails {
+  // TLS 1.2 (or earlier) full handshake (2-RTT)
+  kTLS12Full = 0,
+  // TLS 1.2 (or earlier) resumption (1-RTT)
+  kTLS12Resume = 1,
+  // TLS 1.2 full handshake with False Start (1-RTT)
+  kTLS12FalseStart = 2,
+  // TLS 1.3 full handshake (1-RTT, usually)
+  kTLS13Full = 3,
+  // TLS 1.3 resumption handshake (1-RTT, usually)
+  kTLS13Resume = 4,
+  // TLS 1.3 0-RTT handshake (0-RTT)
+  kTLS13Early = 5,
+  kMaxValue = kTLS13Early,
+};
+
+}  // namespace net
+
+#endif  // NET_SSL_SSL_HANDSHAKE_DETAILS_H_
diff --git a/net/ssl/ssl_server_config.h b/net/ssl/ssl_server_config.h
index 5ae81d46..01bd0390 100644
--- a/net/ssl/ssl_server_config.h
+++ b/net/ssl/ssl_server_config.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "net/base/net_export.h"
+#include "net/socket/next_proto.h"
 #include "net/ssl/ssl_config.h"
 
 namespace net {
@@ -81,6 +82,11 @@
   // This field is meaningful only if client certificates are requested.
   // If a verifier is not provided then all certificates are accepted.
   ClientCertVerifier* client_cert_verifier;
+
+  // The list of application level protocols supported with ALPN (Application
+  // Layer Protocol Negotiation), in decreasing order of preference.  Protocols
+  // will be advertised in this order during TLS handshake.
+  NextProtoVector alpn_protos;
 };
 
 }  // namespace net
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1114de7..88aa4052 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -54782,6 +54782,15 @@
   <int value="1" label="HAD_PREVIOUS_EXCEPTION"/>
 </enum>
 
+<enum name="SSLHandshakeDetails">
+  <int value="0" label="TLS 1.2 (or earlier) full handshake (2-RTT)"/>
+  <int value="1" label="TLS 1.2 (or earlier) resumption (1-RTT)"/>
+  <int value="2" label="TLS 1.2 full handshake with False Start (1-RTT)"/>
+  <int value="3" label="TLS 1.3 full handshake (1-RTT, usually)"/>
+  <int value="4" label="TLS 1.3 resumption handshake (1-RTT, usually)"/>
+  <int value="5" label="TLS 1.3 0-RTT handshake (0-RTT)"/>
+</enum>
+
 <enum name="SSLHashAlgorithm">
   <obsolete>
     Removed June 2016.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 939b5a9..b105d44 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -75181,6 +75181,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.SSLHandshakeDetails" enum="SSLHandshakeDetails">
+  <owner>davidben@chromium.org</owner>
+  <summary>
+    For each successful TLS handshake, what kind of handshake was used. This
+    metric distinguishes TLS 1.2 (or earlier) and TLS 1.3 as they have very
+    different resumption and round-trip behaviors. It also distinguishes full
+    handshakes, resumption, 0-RTT, and False Start.
+  </summary>
+</histogram>
+
 <histogram name="Net.SSLHostInfoDNSLookup" units="ms"
     expires_after="2015-11-11">
   <obsolete>